Ansible loop dict items - ansible

I am using ansible 2.9 and trying to use ansible loop instead of with_dict. I am getting below error. I need to pass the values at the task, I can't keep them in variable or load them at the beginning. do I need to switch back to with_dict?
fatal: [localhost]: FAILED! => {"msg": "template error while templating string: expected token 'end of print statement', got ':'. String: {{ key:/var/tmp/h vaule:/tmp/h|dict2items }}"}
- name: copy module
copy:
src: "{{ item.key }}"
dest: "{{ item.value}}"
loop:
- "{{ key: /var/tmp/a value:/tmp/h |dict2items }}"
- "{{ key: /var/tmp/b value:/tmp/z |dict2items }}"

You don't need dict2items here. Fix the syntax. For example, test it first
- hosts: localhost
tasks:
- debug:
msg: >-
src: {{ item.key }}
dest: {{ item.value}}
loop:
- {key: /var/tmp/a, value: /tmp/h}
- {key: /var/tmp/b, value: /tmp/z}
gives (abridged)
msg: 'src: /var/tmp/a dest: /tmp/h'
msg: 'src: /var/tmp/b dest: /tmp/z'
If this is what you want to copy the files
- name: copy module
copy:
src: "{{ item.key }}"
dest: "{{ item.value }}"
loop:
- {key: /var/tmp/a, value: /tmp/h}
- {key: /var/tmp/b, value: /tmp/z}
You'd need dict2items if the structure was a dictionary. For example,
dirs:
/var/tmp/a: /tmp/h
/var/tmp/b: /tmp/z
Then the task below would give the same result
- debug:
msg: >-
src: {{ item.key }}
dest: {{ item.value}}
loop: "{{ dirs|dict2items }}"

Related

how to pass many variables in ansible?

I faced problems when I don't know how to pass many variables in with_items:
I have these vars:
- vars:
paths:
- /tmp/tecom/python3/
- /tmp/tecom/pip/
- /tmp/tecom/psycopg2/
- /tmp/tecom/docker/
- /tmp/tecom/postgresql/
files:
- python3.tar
- pip.tar
- psycopg2.tar
- docker.tar
- postgresql.tar
And I have the task that should extract the archives:
- name: unarchive "{{ item }}"
unarchive:
src: "{{ item }}"
dest: # should take the paths above. Every path should match it's own `.tar` file.
with_items: "{{ files }}"
Every path should match its own .tar file.
For example,
- debug:
msg: "unarchive {{ item }} to {{ _path }}"
loop: "{{ files }}"
vars:
_path: "{{ path }}/{{ item|splitext|first }}/"
path: /tmp/tecom
files:
- python3.tar
- pip.tar
- psycopg2.tar
- docker.tar
- postgresql.tar
gives (abridged)
msg: unarchive python3.tar to /tmp/tecom/python3/
msg: unarchive pip.tar to /tmp/tecom/pip/
msg: unarchive psycopg2.tar to /tmp/tecom/psycopg2/
msg: unarchive docker.tar to /tmp/tecom/docker/
msg: unarchive postgresql.tar to /tmp/tecom/postgresql/
Q: "How do I unarchive those files to the folders?"
A: Use the expressions as appropriate, e.g.
- name: "unarchive {{ item }}"
unarchive:
src: "{{ item }}"
dest: "{{ path }}/{{ item|splitext|first }}/"
loop: "{{ files }}"
(not tested)
It's up to you where you put the variables files and path. See Variable precedence: Where should I put a variable?

Ansible loop through nested inventory lists

I'm trying to loop through a nested ansible inventory looks like this:
inventory:
group_one:
- name: 'entry-one-a'
description: 'one-a'
group_two:
- name: 'entry-two-aa'
description: 'two-aa'
group_three:
- name: 'entry-three-aaa'
description: 'three-aaa'
- name: 'entry-three-aab'
description: 'three-aab'
I've tried it with the following loop, but without success:
- name: print vars
ansible.builtin.debug:
msg: '{{ item }}'
loop: '{{ inventory.group_one.group_two|subelements("group_three") }}'
Any good idea how to loop through the inventory?
Iterate the third loop in the included task, e.g.
shell> cat group_three.yml
- debug:
msg: "{{ item.0.name }} {{ item.1.name }} {{ item2.name }}"
loop: "{{ item.1.group_three }}"
loop_control:
loop_var: item2
- include_tasks: group_three.yml
with_subelements:
- "{{ inventory.group_one }}"
- group_two
gives
msg: entry-one-a entry-two-aa entry-three-aaa
msg: entry-one-a entry-two-aa entry-three-aab

Unable to default omit when synchronize attributes are multiline in Ansible

Below Ansible works fine when final_file_perm is defined:
- name: Copying from "{{ inventory_hostname }}" to this ansible server.
synchronize:
src: "{{ item }}"
dest: "{{ playbook_dir }}/tmpfiles/{{ Latest_Build_Number }}/"
rsync_opts: "{{ final_file_perm | default(omit) }}"
mode: pull
with_items:
- "{{ source_file.split() }}"
However, my requirement is to have multiple multiline rsync_opts on new line like below:
rsync_opts:
- "--chmod=F0775"
- "--chmod=D0775"
So I tried the following:
- name: Copying from "{{ inventory_hostname }}" to this ansible server.
synchronize:
src: "{{ item }}"
dest: "{{ playbook_dir }}/tmpfiles/{{ Latest_Build_Number }}/"
rsync_opts:
- "{{ final_file_perm | default(omit) }}"
- "{{ final_folder_perm | default(omit) }}"
mode: pull
with_items:
- "{{ source_file.split() }}"
The above does not work and give me the following error:
TASK [Copying from "remhost" to this ansible server.] *********************
failed: [remhost] (item=/u/files/inst.zip) => {"changed": false, "cmd": "/bin/rsync --delay-updates -F --compress --archive --rsh=/usr/share/centrifydc/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null __omit_place_holder__6fd53eb1f5a7fbe7c6691ba6f3aada2e52378343 __omit_place_holder__6fd53eb1f5a7fbe7c6691ba6f3aada2e52378343 --out-format=<<CHANGED>>%i %n%L remuser#remhost:/u/files/inst.zip /web/playbooks/filecopy/tmpfiles/124/", "item": "/u/files/inst.zip", "msg": "Unexpected remote arg: remuser#remhost:/u/files/inst.zip\nrsync error: syntax or usage error (code 1) at main.c(1344) [sender=3.1.2]\n", "rc": 1}
to retry, use: --limit #/web/playbooks/filecopy/copyfiles.retry
As you can see ansible synchronize module is not liking __omit_place_holder__6fd53eb1f5a7fbe7c6691ba6f3aada2e52378343 right after -o UserKnownHostsFile=/dev/null
I also tried the following:
rsync_opts: [ '{{ final_file_perm | default(omit) }}', '{{ final_folder_perm | default(omit) }}' ]
But, I get similar error as shared above.
Note: this works fine when final_file_perm and final_folder_perm are defined. Errors only when they are undefined and I wish to omit them.
Can you please propose a solution where I can use default(omit) for attributes on multi newlines?
Then you will want to build up the rsync_opts list conditionally, rather than having what amounts to None items in the list
- name: Copying from "{{ inventory_hostname }}" to this ansible server.
synchronize:
src: "{{ item }}"
dest: "{{ playbook_dir }}/tmpfiles/{{ Latest_Build_Number }}/"
rsync_opts: "{{ my_rsync_opts }}"
mode: pull
with_items:
- "{{ source_file.split() }}"
vars:
my_rsync_opts: >-
{{ [] +
([final_file_perm] if final_file_perm|d("") else []) +
([final_folder_perm] if final_folder_perm|d("") else []) }}

Ansible: How to filter dict2items and run playbook only for the matched values

I have a dict playbook which looks like this:
x_php_versions_installed:
ea-php71:
- ea-php71-php-bcmath
- ea-php71-php-xmlrpc
- ea-php71-php-zip
- pecl-memcached
- pecl-imagick
ea-php72:
- ea-php72-php-cli
- ea-php72-php-common
- ea-php72-php-curl
- pecl-imagick
I would like to filter them, to write me each item.value which contains 'ea' string but not everything else. My task looks like this:
- name: Write out only the ea packages
debug:
msg: '{{ item.value }}'
when: item.value | selectattr(item.value, 'contains', 'ea')
loop: '{{ x_php_versions_installed | dict2items }}
But it does not work, because it will list all of the packages, not only the ea ones. The expected answer should look like this:
...
"msg": [
"ea-php71-php-bcmath",
"ea-php71-php-xmlrpc",
"ea-php71-php-zip"
]
...
"msg": [
"ea-php72-php-cli",
"ea-php72-php-common",
"ea-php72-php-curl"
]
...
Another possibility is to filter out the 'pecl' string, it will gave me the same result and it also works fine.
Q: "Filter item.value which contains ea string."
A: The task below does the job
- debug:
msg: "{{ item.value|select('match','^ea-(.*)$')|list }}"
loop: "{{ x_php_versions_installed|dict2items }}"
gives (abridged)
msg:
- ea-php71-php-bcmath
- ea-php71-php-xmlrpc
- ea-php71-php-zip
msg:
- ea-php72-php-cli
- ea-php72-php-common
- ea-php72-php-curl
Note: The test match by default "succeeds if it finds the pattern at the beginning of the string". The task below gives the same result
- debug:
msg: "{{ item.value|select('match', 'ea-')|list }}"
loop: "{{ x_php_versions_installed|dict2items }}"
Q: "Filter out the pecl string."
A: Change the filter to reject and fit the regex. For example, the task below gives the same result
- debug:
msg: "{{ item.value|reject('match','^pecl-(.*)$')|list }}"
loop: "{{ x_php_versions_installed|dict2items }}"
Notes:
Select the lists without iteration. Declare the variables
x_php_versions_installed_keys: "{{ x_php_versions_installed.keys()|list }}"
x_php_versions_installed_ea_vals: "{{ x_php_versions_installed|dict2items|
map(attribute='value')|
map('select', 'match', 'ea-')|list }}"
x_php_versions_installed_ea: "{{ dict(x_php_versions_installed_keys|
zip(x_php_versions_installed_ea_vals)) }}"
gives
x_php_versions_installed_ea:
ea-php71:
- ea-php71-php-bcmath
- ea-php71-php-xmlrpc
- ea-php71-php-zip
ea-php72:
- ea-php72-php-cli
- ea-php72-php-common
- ea-php72-php-curl
Example of a complete playbook for testing
- hosts: localhost
vars:
x_php_versions_installed:
ea-php71:
- ea-php71-php-bcmath
- ea-php71-php-xmlrpc
- ea-php71-php-zip
- pecl-memcached
- pecl-imagick
ea-php72:
- ea-php72-php-cli
- ea-php72-php-common
- ea-php72-php-curl
- pecl-imagick
x_php_versions_installed_keys: "{{ x_php_versions_installed.keys()|list }}"
x_php_versions_installed_ea_vals: "{{ x_php_versions_installed|dict2items|
map(attribute='value')|
map('select', 'match', 'ea-')|list }}"
x_php_versions_installed_ea: "{{ dict(x_php_versions_installed_keys|
zip(x_php_versions_installed_ea_vals)) }}"
tasks:
- debug:
msg: "{{ item.value|select('match','^ea-(.*)$')|list }}"
loop: "{{ x_php_versions_installed|dict2items }}"
- debug:
msg: "{{ item.value|select('match', 'ea-')|list }}"
loop: "{{ x_php_versions_installed|dict2items }}"
- debug:
msg: "{{ item.value|reject('match','^pecl-(.*)$')|list }}"
loop: "{{ x_php_versions_installed|dict2items }}"
- debug:
msg: "{{ item.value|reject('match','pecl-')|list }}"
loop: "{{ x_php_versions_installed|dict2items }}"
- debug:
var: x_php_versions_installed_ea

ansible.utils.unsafe_proxy.AnsibleUnsafeT ext object' has no attribute

I am trying to modify and use this httpd ansible role https://github.com/CentOS/ansible-role-httpd
I'm facing an issue with pki-tls.yml
This piece of code will reproduce the issue I am facing.
---
- name: Copy certificates
hosts: myhost.domain.com
remote_user: user
become: yes
vars:
httpd_vhost_shared_list:
- name: emacs
fqdn: domain.com
path: /var/www/emacs
acl:
- 10.10.40.0/24
pkistore: /home/user/certificates
tasks:
- name: Debug
debug:
var: httpd_vhost_shared_list
- name: TLS certs
copy:
src: "{{ pkistore }}/{{ item.name }}"
dest: "/etc/pki/tls/certs/{{ item.name }}"
with_items:
- "{{ httpd_vhost_shared_list }}.crt"
- "{{ httpd_vhost_shared_list }}-CAChain.crt"
- name: TLS key
copy:
src: "{{ pkistore }}/{{ item.name }}"
dest: "/etc/pki/tls/private/{{ item.name }}"
with_items:
- "{{ httpd_vhost_shared_list }}.key"
When I run my playbook, I get the following error:
The task includes an option with an undefined variable. The error was: 'ansible.utils.unsafe_proxy.AnsibleUnsafeT
ext object' has no attribute 'name'
However the variable is defined. How would one do to access name in variable httpd_vhost_shared_list?
Any feedback is welcome.
In the meantime, I figured out it is simple to split the play "TLS certs" in 2 plays. One for the server certificate and another one for chain certificate.
- name: TLS certificate
copy:
src: "{{ pkistore }}/{{ item.name }}.crt"
dest: "/etc/pki/tls/certs/{{ item.name }}.crt"
with_items:
- "{{ httpd_vhost_shared_list }}"
- name: TLS chain
copy:
src: "{{ pkistore }}/{{ item.name }}-CAChain.crt"
dest: "/etc/pki/tls/certs/{{ item.name }}-CAChain.crt"
with_items:
- "{{ httpd_vhost_shared_list }}"
- name: TLS key
copy:
src: "{{ pkistore }}/{{ item.name }}.key"
dest: "/etc/pki/tls/private/{{ item.name }}.key"
with_items:
- "{{ httpd_vhost_shared_list }}"

Resources