I have an ansible playbook to create new users and create a docker for each user. The information of the users is gathered from a yaml file there are more that 200 records. The yaml file consists of username and password. To create docker container I have to give PUID and PGID of the user to the docker run command. See below for example command
docker run --restart=always -it --init -td -p {port from ansible}:8443 -e PUID={PUID from ansible} -e PGID={PGID from Ansible} linuxserver/code-server:latest
I want to get PUID and PGID of a user and register it to a variable to use them to create docker container. I tried to use the following command but since it appends the output to a variable dictionary, I am not able to match the username with PUID/PGID.
- name: GET PUID
shell: id -u "{{ item.username }}"
loop: "{{ user }}"
register: puid
- name: pgid Variable
debug: msg="{{ pgid.stdout }}"
YAML for user
user:
- username: john.doe
password: password
The docker image that I want to use: https://hub.docker.com/r/linuxserver/code-server
For example, get the UID and GID of user admin at test_11
shell> ssh admin#test_11 id admin
uid=1001(admin) gid=1001(admin) groups=1001(admin)
Use the module getent. The playbook below
- hosts: test_11
tasks:
- getent:
database: passwd
- set_fact:
admin_uid: "{{ ansible_facts.getent_passwd.admin.1 }}"
admin_gid: "{{ ansible_facts.getent_passwd.admin.2 }}"
- debug:
msg: |
admin_uid: {{ admin_uid }}
admin_gid: {{ admin_gid }}
gives (abridged)
TASK [debug] ********************************************************
ok: [test_11] =>
msg: |-
admin_uid: 1001
admin_gid: 1001
I am using self hosted github runners for vpn access to some software and I am trying to use a dockerized github action on the self hosted runners but I am having issues because I need to specify the --network host flag when github action runs docker run. Is there a way to have the github action use the network of the host?
As far as I know, it is not possible. It's not available on steps either. Options are available on jobs though. The only other way is for you to create a composite action and run docker run ... directly in it. Here is one that I wrote for my own workflow. It's slightly more complicated but it allows you to automatically pass environment variable from the runner to the docker container based on the variable name prefix:
name: Docker start container
description: Start a detached container
inputs:
image:
description: The image to use
required: true
name:
description: The container name
required: true
options:
description: Additional options to pass to docker run
required: false
default: ''
command:
description: The command to run
required: false
default: ''
env_pattern:
description: The environment variable pattern to pass to the container
required: false
default: ''
outputs:
cid:
description: Container ID
value: ${{ steps.info.outputs.cid }}
runs:
using: composite
steps:
- name: Run
shell: bash
run: >
variables='';
for i in $(env | grep '${{ inputs.env_pattern }}' | awk -F '=' '{print $1}'); do
variables="--env ${i} ${variables}";
done;
docker run -d
--name ${{ inputs.name }}
--network host
--cidfile ${{ inputs.name }}.cid
${variables}
${{ inputs.options }}
${{ inputs.image }}
${{ inputs.command }}
- name: Info
id: info
shell: bash
run: echo "::set-output name=cid::$(cat ${{ inputs.name }}.cid)"
and to use it:
- name: Start app container
uses: ./.github/actions/docker-start-container
with:
image: myapp/myapp:latest
name: myapp
env_pattern: 'MYAPP_'
options: --entrypoint entrypoint.sh
command: >
--check
-v
Below is the command generated by ansible plugin for Jenkins.
ansible-playbook /app/stop.yml -i /app/my-hosts -l test_west -e app_name=test -e environments=west -v
Here is my ansible inventory hosts file.
cat my-hosts
[test_west]
10.0.9.88
10.0.9.89
-l option helps match the ansible inventory host file 'test_west'
My question is ... what do I have to mention in the ansible playbook for the hosts ?
My playbook looks like below however this does not seem correct or needed as the hosts are match using the -l paramter passed to ansible:
---
- hosts: "{{ app_name + '_' + environments }}
Can you please suggest what should I set the hosts: to in my ansible playbook so it is the same as -l argument i.e 'test_west' ?
You should use: - hosts: "{{ app_name }}_{{ environments }}"
Sample output:
[root#greenhat-30 tests]$ ansible-playbook -i hosts test.yml -e app_name=test -e environments=west
[WARNING]: Could not match supplied host pattern, ignoring: test_west
PLAY [test_west] *******************************************************************************************************************************************************************************************************
skipping: no hosts matched
PLAY RECAP *************************************************************************************************************************************************************************************************************
[http_offline#greenhat-30 tests]$
You can see the group selected is PLAY [test_west].
Here is my exact requirement.
In jenkins:
User will choose the env parameters: TEST OR DEV
In ansible playbook:
I need to run the sed command based on the ENV type being selected.
- vars:
environ1: "TEST"
environ2: "PROD"
- command: sed -i "s/test.abc.com/"{{SITE_URL}}"/g" /home/ubuntu/mysql.sql
when: '{{backup_from}}' == '{{environ1}}'
- command: sed -i "s/abc.com/"{{SITE_URL}}"/g" /home/ubuntu/mysql.sql
when: '{{backup_from}}' == '{{environ2}}'
ERROR:
[0;31m when: '{{backup_from}}' == '{{environ}}'[0m
[0;31m ^ here[0m
[0;31mWe could be wrong, but this one looks like it might be an issue with[0m
[0;31mmissing quotes. Always quote template expression brackets when they[0m
[0;31mstart a value. For instance:[0m
[0;31m[0m
[0;31m with_items:[0m
[0;31m - {{ foo }}[0m
[0;31m[0m
[0;31mShould be written as:[0m
[0;31m[0m
[0;31m with_items:[0m
[0;31m - "{{ foo }}"[0m
[0;31m[0m
Connection to 18.221.160.190 closed.
Build step 'Execute shell' marked build as failure
Finished: FAILURE
You have multiple problems with quotes and indentation:
if you start an argument in YAML with a quote, you must quote the whole YAML value (this is the reason for the error)
when should be on the task indentation level
argument of when should be a Jinja2 expression, no need to use braces inside (although it works with a warning)
you cannot nest double quotes in the argument of command
there is no need to use quotes around Jinja2 expressions {{ ... }} except for the case when the whole YAML value starts with a brace
tasks:
- command: sed -i "s/test.abc.com/{{SITE_URL}}/g" /home/ubuntu/mysql.sql
when: backup_from == environ1
- command: sed -i "s/abc.com/{{SITE_URL}}/g" /home/ubuntu/mysql.sql
when: backup_from == environ2
You might also consider writing this as a single task (if there are only two environment values possible):
tasks:
- command: sed -i "{{ expression }}" /home/ubuntu/mysql.sql
vars:
expression: "{{ (backup_from == 'TEST') | ternary('s/test.abc.com/{{SITE_URL}}/g', 's/abc.com/"{{SITE_URL}}"/g') }}"
I think that the entire command has to be without quotes, try with:
- vars:
environ1: "TEST"
environ2: "PROD"
- command: sed -i "s/test.abc.com/"{{SITE_URL}}"/g" /home/ubuntu/mysql.sql
when: backup_from == environ1
- command: sed -i "s/abc.com/"{{SITE_URL}}"/g" /home/ubuntu/mysql.sql
when: backup_from == environ2
How to escape double curly braces in Ansible 1.9.2?
For instance, how can I escape double curly braces in the following shell command?
- name: Test
shell: "docker inspect --format '{{ .NetworkSettings.IPAddress }}' instance1"
Whenever you have problems with conflicting characters in Ansible, a rule of thumb is to output them as a string in a Jinja expression.
So instead of {{ you would use {{ '{{' }}:
- debug: msg="docker inspect --format '{{ '{{' }} .NetworkSettings.IPAddress {{ '}}' }}' instance1"
Topic "Escaping" in the Jinja2 docs.
This:
- name: Test
shell: "docker inspect --format {% raw %}'{{ .NetworkSettings.IPAddress }}' {% endraw %} instance1"
Should work
Another way to do is using backslashes like \{\{ .NetworkSettings.IPAddress \}\}
Hope it helps
Tried on with ansible 2.1.1.0
{%raw%}...{%endraw%} block seems the clear way
- name: list container images and name date on the server
shell: docker ps --format {%raw%}"{{.Image}} {{.Names}}"{%endraw%}
Only need to escape leading '{{'
tasks:
- name: list container images and names
shell: docker ps --format "{{'{{'}}.Image}} {{'{{'}}.Names}}"
No harm to escap the tailing '}}', except more difficult to read.
tasks:
- name: list container images and names
shell: docker ps --format "{{'{{'}}.Image{{'}}'}} {{'{{'}}.Names{{'}}'}}"
Backslash '\' seems do not work
New in Ansible 2.0 is the ability to declare a value as unsafe with the !unsafe tag.
In your example you could do:
- name: Test
shell: !unsafe "docker inspect --format '{{ .NetworkSettings.IPAddress }}' instance1"
See the docs for details.
I have a similar issue: i need to post a JSON doc made from a jinja2 template containing some go templates variables (yes, i know :-P), such as
"NAME_TEMPLATE": %{{service_name}}.%{{stack_name}}.%{{environment_name}}
Trying to fence this part of the template between
{% raw %} ... {% endraw %}
didn't work because there is some sort of magic in ansible which will run the template and variable substition twice (i'm not sure about that, but it definitively looks like this)
You end up with "undefined variable service_name" when trying to use the template...
So i ended up using a combination of !unsafe and {% raw %} ... {% endraw %} to define a fact that's later used in the template.
- set_fact:
__rancher_init_root_domain: "{{ rancher_root_domain }}"
#!unsafe: try to trick ansible into not doing substitutions in that string, then use %raw% so the value won't substituted another time
__rancher_init_name_template: !unsafe "{%raw%}%{{service_name}}.%{{stack_name}}.%{{environment_name}}{%endraw%}"
- name: build a template for a project
set_fact:
__rancher_init_template_doc: "{{ lookup('template', 'templates/project_template.json.j2') }}"
the template contains this:
"ROOT_DOMAIN":"{{__rancher_init_root_domain}}",
"ROUTE53_ZONE_ID":"",
"NAME_TEMPLATE":"{{__rancher_init_name_template }}",
"HEALTH_CHECK":"10000",
and the output is ok:
"NAME_TEMPLATE": "%{{service_name}}.%{{stack_name}}.%{{environment_name}}",
Here's a shorter alternative to udondan's answer; surround the whole string with double brackets:
shell: "docker inspect --format {{ '{{ .NetworkSettings.IPAddress }}' }} instance1"
The solution by using raw has been already mentioned but the command in the answer before unfortunately didn't work for me.
Without ansible:
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' docker_instance_name
With ansible:
- name: Get ip of db container
shell: "{% raw %}docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' docker_instance_name{% endraw %}"
register: db_ip_addr
- debug:
var: db_ip_addr.stdout
I managed to work around my issue using a small script:
#!/usr/bin/env bash
docker inspect --format '{{ .NetworkSettings.IPAddress }}' "$1"
And the following Ansible play
- copy:
src: files/get_docker_ip.sh
dest: /usr/local/bin/get_docker_ip.sh
owner: root
group: root
mode: 0770
- shell: "/usr/local/bin/get_docker_ip.sh {{ SWIFT_ACCOUNT_HOSTNAME }}"
register: swift_account_info
Nevertheless, it's very surprising that Ansible doesn't allow escaping double curly braces!
I was unable to get #Ben's answer to work (shell: !unsafe ...)
What follows here is a complete (and working!) answer to the OP's question, updated for Ansible >2.0
---
# file: play.yml
- hosts: localhost
connection: local
gather_facts: no
vars:
# regarding !unsafe, please see:
# https://docs.ansible.com/ansible/latest/user_guide/playbooks_advanced_syntax.html
#
- NetworkSettings_IPAddress: !unsafe "{{.NetworkSettings.IPAddress}}"
tasks:
- shell: "docker inspect --format '{{NetworkSettings_IPAddress}}' instance1"
register: out
- debug: var="{{item}}"
with_items:
- out.cmd
- out.stdout
outputs: ([WARNINGS] removed)
# ansible-playbook play.yml
PLAY [localhost] ***************************************************************
TASK [shell] *******************************************************************
changed: [localhost]
TASK [debug] *******************************************************************
ok: [localhost] => (item=out.cmd) => {
"item": "out.cmd",
"out.cmd": "docker inspect --format '{{.NetworkSettings.IPAddress}}' instance1"
}
ok: [localhost] => (item=out.stdout) => {
"item": "out.stdout",
"out.stdout": "172.17.0.2"
}
PLAY RECAP *********************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0
# ansible --version | head -1
ansible 2.6.1
Here is a mostly clean and Ansible native workaround not depending on docker --inspect with curly braces. We assume we have just referenced one container with the Ansible docker module before:
- name: query IP of client container
shell: "docker exec {{ docker_containers[0].Id }} hostname -I"
register: _container_query
- name: get IP of query result
set_fact:
_container_ip: "{{ _container_query.stdout | regex_replace('\\s','') }}"
You now have the IP of the Docker container in the Variable _container_ip. I also published this workaround on my article The Marriage of Ansible with Docker.
[Update 2015-11-03] Removed whitespaces of the stdout of the container query.
[Update 2015-11-04] BTW, there were two pull requests in the official Ansible repository, that would made this workaround needless by recovering the facts returned by the Docker module. So you could acces the IP of a docker container via docker_containers[0].NetworkSettings.IPAddress. So please vote for those pull requests:
fixed broken facts #1457
docker module: fix regressions introduced by f38186c and 80aca4b #2093