Ansible nested loop and jinja2 filter - ansible

I need to list some directories in my project, I have this kind of tree :
.
└── path
└── to
├── aa
│   └── x9999
├── bb
│   ├── x9997
│   └── x9998
├── cc
└── dd
I have made this task in my playbook to find directories:
---
- name: 'test of find'
hosts: localhost
vars:
loopVar:
- 'aa'
- 'bb'
- 'cc'
- 'dd'
tasks:
- name: 'find'
find:
path: '/path/to/{{item}}'
file_type: directory
patterns: '[^_]*[a-z]+[0-9]{4}[a-z]*$'
use_regex: yes
register: list
loop: '{{loopVar}}'
and the result is :
{
"list": {
"results": [
{
"files": [
{
"path": "/path/to/aa/x9999"
}
],
"item": "aa",
"matched": 1
},
{
"files": [
{
"path": "/path/to/bb/x9997"
},
{
"path": "/path/to/bb/x9998"
}
],
"item": "bb",
"matched": 2
},
{
"files": [],
"item": "cc",
"matched": 0
},
{
"files": [],
"item": "dd",
"matched": 0
}
]
}
}
I want to invoke a include_role with this array but I can't do it by this way because I need to have var at the same level than path.
I want to work on my result to have this kind of value :
{ "final": [
{ "path": "/path/to/aa/x9999", "var": "aa" },
{ "path": "/path/to/bb/x9998", "var": "bb" },
{ "path": "/path/to/bb/x9997", "var": "bb" }
]
}
I Try a lot of things with set_fact but I don't find how to do this kind of work.
Thank a lot for your help :-)

The play below
- find:
paths: "/path/to/{{ item }}"
file_type: directory
recurse: yes
register: list
loop: [ aa, bb, cc, dd]
- set_fact:
pre_final: "{{ list.results|json_query('[].{path: files[].path,
var: item}') }}"
- set_fact:
final: "{{ final|default([]) + [{'var': item.0.var,
'path': item.1}] }}"
loop: "{{ lookup('subelements', pre_final, 'path') }}"
- debug:
var: final
gives (abridged):
"final": [
{
"path": "/path/to/aa/x9999",
"var": "aa"
},
{
"path": "/path/to/bb/x9997",
"var": "bb"
},
{
"path": "/path/to/bb/x9998",
"var": "bb"
}
]

I have found a way to do it. It's not lovelly but it's works.
I have to use the include_tasks module and the json_query given :
playbook.yml
---
- name: 'test of find'
hosts: localhost
vars:
loopVar:
- 'aa'
- 'bb'
- 'cc'
- 'dd'
tasks:
- name: 'find'
find:
path: '/path/to/{{item}}'
file_type: directory
patterns: '[^_]*[a-z]+[0-9]{4}[a-z]*$'
use_regex: yes
register: list
loop: '{{loopVar}}'
- set_fact:
all: '{{ list.results|json_query("[].{path: files[].path, var: item, matched: matched}") }}'
- include_tasks: 'inner.yml'
vars:
paths: '{{item.path}}'
var: '{{item.var}}'
loop: '{{all}}'
when: 'item.matched != 0'
- debug:
var: final
inner.yaml
---
- set_fact:
final: '{{ final|default([]) + [{ "path": path|basename, "var": var }] }}'
loop: '{{paths}}'
loop_control:
loop_var: path
Result:
[root#xxxx path]# ansible-playbook playbook.yml
PLAY [test of find] *********************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] ******************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [find] *****************************************************************************************************************************************************************************************************************************
ok: [localhost] => (item=aa)
ok: [localhost] => (item=bb)
ok: [localhost] => (item=cc)
ok: [localhost] => (item=dd)
TASK [set_fact] *************************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [include_tasks] ********************************************************************************************************************************************************************************************************************
skipping: [localhost] => (item={'var': u'cc', 'path': [], 'matched': 0})
skipping: [localhost] => (item={'var': u'dd', 'path': [], 'matched': 0})
included: /mnt/hgfs/Documents/inner.yml for localhost => (item={'var': u'aa', 'path': [u'/path/to/aa/x9999'], 'matched': 1})
included: /mnt/hgfs/Documents/inner.yml for localhost => (item={'var': u'bb', 'path': [u'/path/to/bb/x9997', u'/path/to/bb/x9998'], 'matched': 2})
TASK [set_fact] *************************************************************************************************************************************************************************************************************************
ok: [localhost] => (item=/path/to/aa/x9999)
TASK [set_fact] *************************************************************************************************************************************************************************************************************************
ok: [localhost] => (item=/path/to/bb/x9997)
ok: [localhost] => (item=/path/to/bb/x9998)
TASK [debug] ****************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"final": [
{
"path": "x9999",
"var": "aa"
},
{
"path": "x9997",
"var": "bb"
},
{
"path": "x9998",
"var": "bb"
}
]
}
PLAY RECAP ******************************************************************************************************************************************************************************************************************************
localhost : ok=8 changed=0 unreachable=0 failed=0
I don't find a way to do it without use an external playbook.
If someone find a way, I'm happy to learn it :-)
Thank a lot.

Related

ansible_facts.packages "skipping" my package check

I would like to check if my package is installed, but I would like to have all package types with a name.
Let me explain: I am looking for the OB2 package, but there are plenty of them, so I am looking for OB2* but it skips my search.
I tested with the name without wildcard (*) but it doesn't work any better:
- name: Gather the package facts
ansible.builtin.package_facts:
manager: auto
- name: Print the package facts
ansible.builtin.debug:
var: ansible_facts.packages
- name: Check whether a package called OB2 is installed
ansible.builtin.debug:
msg: "{{ ansible_facts.packages['OB2*'] | length }} versions of OB2are installed!"
when: "'OB2*' in ansible_facts.packages"
Is there a solution to find all outputs that start with OB2 within ansible_facts.package?
ansible_facts.packages is a dictionary. There are no keys in that dictionary named OB2* nor OB2 since no package has this exact name.
If you want to get all keys which names start with the string "OB2", one way is to filter out all others.
Transform you dict to a key/value list with the dict2items filter
Use the selectattr filter and apply the match test to find the relevant entries.
Since the key name is also contained in the parameter name inside each element in the value list , you can retain only the value using the map(attribute='someattr') filter
Last, flatten the list to get one single list containing all packages version
Here is a playbook illustrating the concept. For the example, I used as a prefix "zlib". Just change it back to whatever suits your needs
---
- hosts: localhost
gather_facts: false
vars:
package_prefix: "zlib"
filtered_packages: "{{ ansible_facts.packages | dict2items
| selectattr('key', 'match', package_prefix)
| map(attribute='value') | flatten }}"
tasks:
- name: gather package facts
ansible.builtin.package_facts:
- name: debug the raw variable
debug:
var: filtered_packages
- name: count relevant packages
vars:
pkg_num: "{{ filtered_packages | count }}"
debug:
msg: "There are {{ pkg_num }} packages
which name starts with {{ package_prefix }}"
- name: show some info about relevant packages
debug:
msg: "Package named {{ item.name }} is in category {{ item.category }}
and has version {{ item.version }}"
loop: "{{ filtered_packages }}"
Which gives on my Ubuntu local machine:
PLAY [localhost] *****************************************************************************************************************
TASK [gather package facts] ******************************************************************************************************
ok: [localhost]
TASK [debug the raw variable] ****************************************************************************************************
ok: [localhost] => {
"filtered_packages": [
{
"arch": "amd64",
"category": "libs",
"name": "zlib1g",
"origin": "Ubuntu",
"source": "apt",
"version": "1:1.2.11.dfsg-2ubuntu1.3"
},
{
"arch": "amd64",
"category": "libdevel",
"name": "zlib1g-dev",
"origin": "Ubuntu",
"source": "apt",
"version": "1:1.2.11.dfsg-2ubuntu1.3"
}
]
}
TASK [count relevant packages] ***************************************************************************************************
ok: [localhost] => {
"msg": "There are 2 packages which name starts with zlib"
}
TASK [show some info about relevant packages] ************************************************************************************
ok: [localhost] => (item={'name': 'zlib1g', 'version': '1:1.2.11.dfsg-2ubuntu1.3', 'arch': 'amd64', 'category': 'libs', 'origin': 'Ubuntu', 'source': 'apt'}) => {
"msg": "Package named zlib1g is in category libs and has version 1:1.2.11.dfsg-2ubuntu1.3"
}
ok: [localhost] => (item={'name': 'zlib1g-dev', 'version': '1:1.2.11.dfsg-2ubuntu1.3', 'arch': 'amd64', 'category': 'libdevel', 'origin': 'Ubuntu', 'source': 'apt'}) => {
"msg": "Package named zlib1g-dev is in category libdevel and has version 1:1.2.11.dfsg-2ubuntu1.3"
}
PLAY RECAP ***********************************************************************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
You have to filter the keys, a direct address is not possible, because you don't know the exact name.
- name: Gather the package facts
ansible.builtin.package_facts:
- name: Filter package names
set_fact:
filtered_package_names: "{{ ansible_facts.packages | list
| map('regex_search', '^vim.*') | select('string') | list }}"
- name: Print filtered packages
debug:
var: filtered_package_names
- name: Print package details from all filtered packages
debug:
msg: "{{ ansible_facts.packages[item] }}"
with_items: "{{ filtered_package_names }}"
With list a list of the keys is created, then you can filter this list with regex_search, afterwards the list is reduced to the filter result.
== Edit begin
There is a smarter filter method. Instead of using map(regex_search) / select(string), you could use directly select(match), so the filtering would look like:
- name: Filter package names
set_fact:
filtered_package_names: "{{ ansible_facts.packages | list
| select('match', '^vim.*') | list }}"
== Edit end
The result is a list of package names that match your regex.
If you need more information about one of the packages, you can then use ansible_facts.packages[_your_item] to get the rest of the information.
Example output of the above tasks:
TASK [Gather the package facts] ****************************************************************************************
ok: [localhost]
TASK [Filter package names] ********************************************************************************************
ok: [localhost]
TASK [Print filtered packages] *****************************************************************************************
ok: [localhost] => {
"filtered_package_names": [
"vim",
"vim-common",
"vim-runtime",
"vim-tiny"
]
}
TASK [Print package details] *******************************************************************************************
ok: [localhost] => (item=vim) => {
"msg": [
{
"arch": "amd64",
"category": "editors",
"name": "vim",
"origin": "Ubuntu",
"source": "apt",
"version": "2:8.1.2269-1ubuntu5.7"
}
]
}
ok: [localhost] => (item=vim-common) => {
"msg": [
{
"arch": "all",
"category": "editors",
"name": "vim-common",
"origin": "Ubuntu",
"source": "apt",
"version": "2:8.1.2269-1ubuntu5.7"
}
]
}
ok: [localhost] => (item=vim-runtime) => {
"msg": [
{
"arch": "all",
"category": "editors",
"name": "vim-runtime",
"origin": "Ubuntu",
"source": "apt",
"version": "2:8.1.2269-1ubuntu5.7"
}
]
}
ok: [localhost] => (item=vim-tiny) => {
"msg": [
{
"arch": "amd64",
"category": "editors",
"name": "vim-tiny",
"origin": "Ubuntu",
"source": "apt",
"version": "2:8.1.2269-1ubuntu5.7"
}
]
}

Extracting json from ansible playbook

I am fairly new to Ansible, so apologies if this a naïve question.
When I execute Ansible Playbook using AnsibleRunner, I get output in the following format:
PLAY [MyPlay] ******************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Get list of volumes] *****************************************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [debug] *******************************************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"volumes": {
"Array_Software_Version": "2.0.0.0",
"Cluster": [
{
"id": "0",
"name": "111"
}
],
"Volumes": [
{
"id": "003ec9af-7a6d-473b-942f-3418da25d88c",
"name": "server1"
},
...
...
...
and my task
tasks:
- name: Get list of volumes
task: execute
register: volumes
- debug:
var: volumes
I'd like to extract just JSON payload, can I do this using Ansible?
Q: "Retrieve the actual JSON payload from ansible."
A: An option would be to use an intermediate file, for example
shell> cat test-280.yml
- hosts: localhost
become: true
vars:
volumes:
Array_Software_Version: "2.0.0.0"
Cluster:
- id: 0
name: 111
Volumes:
- id: 003ec9af-7a6d-473b-942f-3418da25d88c
name: server1
tasks:
- copy:
dest: /tmp/my_volumes.json
content: |
{{ {'volumes': volumes}|to_nice_json }}
shell> cat test-280.sh
#!/bin/bash
ansible-playbook test-280.yml > /dev/null
my_volumes=`cat /tmp/my_volumes.json`
echo $my_volumes
shell> ./test-280.sh
{ "volumes": { "Array_Software_Version": "2.0.0.0", "Cluster": [ { "id": 0, "name": 111 } ], "Volumes": [ { "id": "003ec9af-7a6d-473b-942f-3418da25d88c", "name": "server1" } ] } }

Ansible set_fact from dictionary based on item type

I am reading a dictionary from a Kubernetes config map (cm_config below) and am using it to replace variables set in defaults/main.yml like this:
- name: 'Overwrite defaults'
set_fact: "{{ item.key }}={{ item.value }}"
with_dict: "{{ cm_config }}"
This works fine as long as the items are simple variables. But as soon as an item is another dictionary, I'd like to combine the values.
How can I integrate this into the above task? I thought about running the loop twice, with some kind of type check. Not sure how this would work. Additionally, I believe there might be a better way?
One solution below to achieve your requirement in a single task whit just a bit of jinja2 templating and a vars lookup to get existing dict content. The key is to calculate the value based on the variable type.
Note that this does not take into account the situations when the var is a list which will be replaced as all other type of values. This will not either deal with type mismatch between existing vars and config map. e.g. if your existing var is string and the corresponding one in config map a dict it will break.
The following playbook:
---
- hosts: localhost
gather_facts: false
vars:
cm_config:
label1: toto
label2:
a_value: 1
other_value: 2
label3:
a_value: 3
other_value: 4
label4: tata
label1: I am set in play
label3:
some_value: I'm a poor lonesome cowboy
tasks:
- name: show initial state
debug:
var: "{{ item.key }}"
with_dict: "{{ cm_config }}"
- name: Process values from config map
vars:
my_value: >-
{% if item.value is mapping %}
{{ lookup('vars', item.key, default={}) | combine(item.value) }}
{% else %}
{{ item.value }}
{% endif %}
set_fact:
"{{ item.key }}": "{{ my_value }}"
with_dict: "{{ cm_config }}"
- name: Show the result after processing config map
debug:
var: "{{ item.key }}"
with_dict: "{{ cm_config }}"
gives the following result:
PLAY [localhost] ****************************************************************************************************************************************************************************************************************************
TASK [show initial state] *******************************************************************************************************************************************************************************************************************
ok: [localhost] => (item=label1) => {
"ansible_loop_var": "item",
"item": {
"key": "label1",
"value": "toto"
},
"label1": "I am set in play"
}
ok: [localhost] => (item=label2) => {
"ansible_loop_var": "item",
"item": {
"key": "label2",
"value": {
"a_value": 1,
"other_value": 2
}
},
"label2": "VARIABLE IS NOT DEFINED!"
}
ok: [localhost] => (item=label3) => {
"ansible_loop_var": "item",
"item": {
"key": "label3",
"value": {
"a_value": 3,
"other_value": 4
}
},
"label3": {
"some_value": "I'm a poor lonesome cowboy"
}
}
ok: [localhost] => (item=label4) => {
"ansible_loop_var": "item",
"item": {
"key": "label4",
"value": "tata"
},
"label4": "VARIABLE IS NOT DEFINED!"
}
TASK [Process values from config map] *******************************************************************************************************************************************************************************************************
ok: [localhost] => (item={'key': 'label1', 'value': 'toto'})
ok: [localhost] => (item={'key': 'label2', 'value': {'a_value': 1, 'other_value': 2}})
ok: [localhost] => (item={'key': 'label3', 'value': {'a_value': 3, 'other_value': 4}})
ok: [localhost] => (item={'key': 'label4', 'value': 'tata'})
TASK [Show the result after processing config map] ******************************************************************************************************************************************************************************************
ok: [localhost] => (item=label1) => {
"ansible_loop_var": "item",
"item": {
"key": "label1",
"value": "toto"
},
"label1": " toto "
}
ok: [localhost] => (item=label2) => {
"ansible_loop_var": "item",
"item": {
"key": "label2",
"value": {
"a_value": 1,
"other_value": 2
}
},
"label2": " {'a_value': 1, 'other_value': 2} "
}
ok: [localhost] => (item=label3) => {
"ansible_loop_var": "item",
"item": {
"key": "label3",
"value": {
"a_value": 3,
"other_value": 4
}
},
"label3": " {'some_value': \"I'm a poor lonesome cowboy\", 'a_value': 3, 'other_value': 4} "
}
ok: [localhost] => (item=label4) => {
"ansible_loop_var": "item",
"item": {
"key": "label4",
"value": "tata"
},
"label4": " tata "
}
PLAY RECAP **********************************************************************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Print only one item in list in ansible

I have a variable that contains a list
- debug:
var: plugin_versions
Output
ok: [localhost] => {
"plugin_versions": [
{
"name": "ace-editor",
"version": "1.1"
},
{
"name": "analysis-core",
"version": "1.95"
},
{
"name": "ant",
"version": "1.9"
}
]
Now I want to print out only the name
What Ive tried is
- debug:
var: plugin_versions.name
- debug:
var: plugin_versions[name]
But in both cases I get
TASK [plugins : debug] ***************************************************************************************************************
ok: [localhost] => {
"plugin_versions.name": "VARIABLE IS NOT DEFINED!"
}
TASK [plugins : debug] ***************************************************************************************************************
ok: [localhost] => {
"plugin_versions[name]": "VARIABLE IS NOT DEFINED!"
}
Im kinda clueless what else can I do here to print out the name only.
you could do it in a few ways. The plugin_versions is a list of dictionaries, you can print the name attribute of each dictionary using loops, here are 2 loop examples you could use:
---
- hosts: localhost
gather_facts: false
vars:
plugin_versions:
- name: ace-editor
version: '1.1'
- name: analysis-core
version: '1.95'
- name: ant
version: '1.9'
tasks:
- name: print variable - with_items
debug:
msg: "{{ item.name }}"
with_items:
- "{{ plugin_versions }}"
- name: print variable - with map filter
debug:
var: item
with_items:
- "{{ plugin_versions | map(attribute='name') | list }}"
output:
[http_offline#greenhat-29 tests]$ ansible-playbook -i hosts test.yml
PLAY [localhost] *******************************************************************************************************************************************************************************************************
TASK [print variable - with_items] *************************************************************************************************************************************************************************************
ok: [localhost] => (item={'name': 'ace-editor', 'version': '1.1'}) => {
"msg": "ace-editor"
}
ok: [localhost] => (item={'name': 'analysis-core', 'version': '1.95'}) => {
"msg": "analysis-core"
}
ok: [localhost] => (item={'name': 'ant', 'version': '1.9'}) => {
"msg": "ant"
}
TASK [print variable - with map filter] ********************************************************************************************************************************************************************************
ok: [localhost] => (item=ace-editor) => {
"item": "ace-editor"
}
ok: [localhost] => (item=analysis-core) => {
"item": "analysis-core"
}
ok: [localhost] => (item=ant) => {
"item": "ant"
}
PLAY RECAP *************************************************************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0
[http_offline#greenhat-29 tests]$
hope it helps
Since you have a list (plugin_versions) you'll need to access each element by index:
---
- hosts: all
vars:
plugin_versions:
- { name: "ace-editor", version: "1.1"}
- { name: "analysis-core", version: "1.95"}
- { name: "ant", version: "1.9"}
tasks:
- debug: var=plugin_versions[0].name
also, if you want to loop through all the items in the list you can do something like this:
- debug: "var=item['name']"
with_items: "{{ plugin_versions }}"
You can iterate over the items of your variable with:
- debug:
var: item.name
with_items: "{{plugin_versions}}"

How to loop over this dictionary in Ansible?

Say I have this dictionary
war_files:
server1:
- file1.war
- file2.war
server2:
- file1.war
- file2.war
- file3.war
and for now I just want to loop over each item (key), and then over each item in the key (value). I did this
- name: Loop over the dictionary
debug: msg="Key={{ item.key }} value={{ item.value }}"
with_dict: "{{ war_files }}"
And I get this. It is of course correct, but is NOT what I want.
ok: [localhost] => (item={'value': [u'file1.war', u'file2.war'], 'key': u'server1'}) => {
"item": {
"key": "server1",
"value": [
"file1.war",
"file2.war"
]
},
"msg": "Server=server1, WAR=[u'file1.war', u'file2.war']"
}
ok: [localhost] => (item={'value': [u'file1.war', u'file2.war', u'file3.war'], 'key': u'server2'}) => {
"item": {
"key": "server2",
"value": [
"file1.war",
"file2.war",
"file3.war"
]
},
"msg": "Server=server2, WAR=[u'file1.war', u'file2.war', u'file3.war']"
}
I want to get an output that says
"msg": "Server=server1, WAR=file1.war"
"msg": "Server=server1, WAR=file2.war"
"msg": "Server=server2, WAR=file1.war"
"msg": "Server=server2, WAR=file2.war"
"msg": "Server=server2, WAR=file3.war"
IOW, how can I write a task to iterates over the dictionary so it goes through each key, and then the items within each key? In essence, I have a nested array and want to iterate over it?
Hows this
- hosts: localhost
vars:
war_files:
server1:
- file1.war
- file2.war
server2:
- file1.war
- file2.war
- file3.war
tasks:
- name: Loop over subelements of the dictionary
debug:
msg: "Key={{ item.0.key }} value={{ item.1 }}"
loop: "{{ war_files | dict2items | subelements('value') }}"
dict2items, subelements filters are coming in Ansible 2.6.
FYI, if a filter for your objective doesn't exist, you can write your own in python without having to resort to jinja2 hacks. Ansible is easily extendable; filters in filter_plugins/*.py are searched by default adjacent to your plays/roles and are automatically included - see Developing Plugins for details.
Now Ansible allows this
- name: add several users
user:
name: "{{ item.name }}"
state: present
groups: "{{ item.groups }}"
with_items:
- { name: 'testuser1', groups: 'wheel' }
- { name: 'testuser2', groups: 'root' }
EDIT: At the time of writing this answer, Ansible 2.6 wasn't out. Please read the answer provided by #tmoschou, as it is much better.
Well, I couldn't find a very easy way to do it, however, with a little bit of jinja2, we can achieve something of this sort:
/tmp ❯❯❯ cat example.yml
---
- hosts: 127.0.0.1
vars:
war_files:
server1:
- file1.war
- file2.war
server2:
- file1.war
- file2.war
- file3.war
tasks:
- set_fact:
war_files_list_of_dicts: |
{% set res = [] -%}
{% for key in war_files.keys() -%}
{% for value in war_files[key] -%}
{% set ignored = res.extend([{'Server': key, 'WAR':value}]) -%}
{%- endfor %}
{%- endfor %}
{{ res }}
- name: let's debug the crap out of this
debug: var=war_files_list_of_dicts
- name: Servers and their WARs!!!
debug:
msg: "Server={{ item.Server }}, WAR={{ item.WAR }}"
with_items: "{{ war_files_list_of_dicts }}"
And, when the playbook is run:
/tmp ❯❯❯ ansible-playbook example.yml
[WARNING]: provided hosts list is empty, only localhost is available
PLAY [127.0.0.1] ***************************************************************
TASK [setup] *******************************************************************
ok: [127.0.0.1]
TASK [set_fact] ****************************************************************
ok: [127.0.0.1]
TASK [let's debug the crap out of this] ****************************************
ok: [127.0.0.1] => {
"war_files_list_of_dicts": [
{
"Server": "server1",
"WAR": "file1.war"
},
{
"Server": "server1",
"WAR": "file2.war"
},
{
"Server": "server2",
"WAR": "file1.war"
},
{
"Server": "server2",
"WAR": "file2.war"
},
{
"Server": "server2",
"WAR": "file3.war"
}
]
}
TASK [Servers and their WARs!!!] ***********************************************
ok: [127.0.0.1] => (item={'WAR': u'file1.war', 'Server': u'server1'}) => {
"item": {
"Server": "server1",
"WAR": "file1.war"
},
"msg": "Server=server1, WAR=file1.war"
}
ok: [127.0.0.1] => (item={'WAR': u'file2.war', 'Server': u'server1'}) => {
"item": {
"Server": "server1",
"WAR": "file2.war"
},
"msg": "Server=server1, WAR=file2.war"
}
ok: [127.0.0.1] => (item={'WAR': u'file1.war', 'Server': u'server2'}) => {
"item": {
"Server": "server2",
"WAR": "file1.war"
},
"msg": "Server=server2, WAR=file1.war"
}
ok: [127.0.0.1] => (item={'WAR': u'file2.war', 'Server': u'server2'}) => {
"item": {
"Server": "server2",
"WAR": "file2.war"
},
"msg": "Server=server2, WAR=file2.war"
}
ok: [127.0.0.1] => (item={'WAR': u'file3.war', 'Server': u'server2'}) => {
"item": {
"Server": "server2",
"WAR": "file3.war"
},
"msg": "Server=server2, WAR=file3.war"
}
PLAY RECAP *********************************************************************
127.0.0.1 : ok=4 changed=0 unreachable=0 failed=0
Here is my preferred way to loop over dictionaries:
input_data.yml contains the following:
----
input_data:
item_1:
id: 1
info: "Info field number 1"
item_2:
id: 2
info: "Info field number 2"
I then use a data structure like the above in a play using the keys() function and iterate over the data using with_items:
---
- hosts: localhost
gather_facts: false
connection: local
tasks:
- name: Include dictionary data
include_vars:
file: data.yml
- name: Show info field from data.yml
debug:
msg: "Id: {{ input_data[item]['id'] }} - info: {{ input_data[item]['info'] }}"
with_items: "{{ input_data.keys() | list }}"
The above playbook produces the following output:
PLAY [localhost] ***********************************************************
TASK [Include dictionary data] *********************************************
ok: [localhost]
TASK [Show info field from data.yml] ***************************************
ok: [localhost] => (item=item_2) => {
"msg": "Id: 2 - info: Info field item 2"
}
ok: [localhost] => (item=item_3) => {
"msg": "Id: 3 - info: Info field item 3"
}
ok: [localhost] => (item=item_1) => {
"msg": "Id: 1 - info: Info field item 1"
}
PLAY RECAP *****************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0
dict2items
I found myself wanting to iterate over a heterogeneous set of keys and their associated values and use the key-value pair in a task. The dict2items filter is the least painful way I've found. You can find dict2items in Ansible 2.6
Example Dict
systemsetup:
remotelogin: "On"
timezone: "Europe/Oslo"
usingnetworktime: "On"
sleep: 0
computersleep: 0
displaysleep: 0
harddisksleep: 0
allowpowerbuttontosleepcomputer: "Off"
wakeonnetworkaccess: "On"
restartfreeze: "On"
restartpowerfailure: "On"
Example Task
---
- debug:
msg: "KEY: {{ item.key }}, VALUE: {{ item.value }}"
loop: "{{ systemsetup | dict2items }}"
One way of doing it that worked for me was using with_dict. Note the dict should not be named. Just the key value pairs.
- name: ssh config
lineinfile:
dest: /etc/ssh/sshd_config
regexp: '^#?\s*{{item.key}}\s'
line: '{{item.key}} {{item.value}}'
state: present
with_dict:
LoginGraceTime: "1m"
PermitRootLogin: "yes"
PubkeyAuthentication: "yes"
PasswordAuthentication: "no"
PermitEmptyPasswords: "no"
IgnoreRhosts: "yes"
Protocol: 2

Resources