I'm trying find datasore of vm guest with ansible - ansible

I'm trying to extract value of vm datastore with ansible snippet:
- name: Gather disk facts from virtual machine using name
vmware_guest_disk_facts:
hostname: "{{ vcenter_server }}"
username: "{{ vcenter_user }}"
password: "{{ vcenter_pass }}"
datacenter: "{{ datacenter }}"
validate_certs: no
name: "{{ item }}"
delegate_to: localhost
register: disk_facts
with_items: "{{ vm_list }}"
- name: Get disk info
debug:
var: disk_facts.results.guest_disk_facts.backing_datastore
Output:
[root#sysmgttl1 lvm]# ansible-playbook -i hosts vmfacts.yml -e vcenter_server=vmimgtpw002 -e vm_list=sndprfql8
PLAY [sndprfql8] ***********************************************************************************************************************************************************
TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [sndprfql8]
TASK [Gather disk facts from virtual machine using name] *******************************************************************************************************************
ok: [sndprfql8 -> localhost] => (item=sndprfql8)
TASK [Get disk info] *******************************************************************************************************************************************************
ok: [sndprfql8] => {
"disk_facts.results.guest_disk_facts.backing_datastore": "VARIABLE IS NOT DEFINED!"
}
PLAY RECAP *****************************************************************************************************************************************************************
sndprfql8 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
This is what my disk_facts looks like.
ok: [sndprfql8] => {
"disk_facts": {
"changed": false,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"changed": false,
"failed": false,
"guest_disk_facts": {
"0": {
"backing_datastore": "QADEV07",
"backing_eagerlyscrub": false,
"backing_filename": "[QADEV07] sndprfql8/sndprfql8.vmdk",
"backing_thinprovisioned": true,
"backing_type": "FlatVer2",
"backing_uuid": "6000C292-7716-6296-de04-69bac9186661",
"backing_writethrough": false,
"capacity_in_bytes": 68719476736,
"capacity_in_kb": 67108864,
"controller_key": 1000,
"key": 2000,
"label": "Hard disk 1",
"summary": "67,108,864 KB",
"unit_number": 0
}
},
"invocation": {
"module_args": {
"datacenter": "RDC",
"folder": null,
"hostname": "vmimgtpw002",
"name": "sndprfql8",
"password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"port": 443,
"use_instance_uuid": false,
"username": "svcvread#chop.edu",
"uuid": null,
"validate_certs": false
}
},
"item": "sndprfql8"
}
]
}
}

If this is really for a one shot use, the following should do:
- name: Get disk info
debug:
var: disk_facts.results[0].guest_disk_facts['0'].backing_datastore
If you goal is to get all values for a list of vms in the result possibly having several disks, the following will give you a flattened list (that you can de-duplicate with the unique filter if needed)
- debug:
msg: "{{ disk_facts | json_query('results[].guest_disk_facts.*[].backing_datastore') }}"

Related

Comparing dictionaries in Ansible

Im trying to have a task that asserts two dictionaries defined as facts.
The following are two dictionaries in which the keys have the disk space that is available or required per hypervisor.
"available": {
"hypervisor-01": 2321,
"hypervisor-02": 46
}
"required": {
"hypervisor-01": "200",
"hypervisor-02": "75"
}
The previous facts assert should fail as hypervisor-02 requires more than what is available.
"available": {
"hypervisor-01": 2321,
"hypervisor-02": 46
}
"required": {
"hypervisor-01": "200",
"hypervisor-02": "5"
}
The previous dictionaries should pass as all the requirements are fulfilled. If at least one condition is not meet it should fail (there might be many hypervisors).
Try this
- set_fact:
ka: "{{ available.keys()|list }}"
kr: "{{ required.keys()|list }}"
- assert:
that: kr|difference(ka)|length == 0
fail_msg: Required hypervisor not available.
- assert:
that: sum_max == sum_avail
fail_msg: Required space not available.
vars:
va: "{{ kr|map('extract', available)|list }}"
vr: "{{ kr|map('extract', required)|list }}"
sum_max: "{{ vr|zip(va)|map('max')|sum }}"
sum_avail: "{{ va|sum }}"
Please see below playbook. I have added 2 more conditions that may not be applicable to your case (in case available or required don't have the same keys).
playbook:
---
- hosts: localhost
gather_facts: false
vars:
available:
hypervisor-01: 2321
hypervisor-02: 46
hypervisor-03: 462
required:
hypervisor-01: 200
hypervisor-02: 75
tasks:
- assert:
that:
- required[item] is defined
- available[item] > required[item]
with_items: "{{ available.keys() | list }}"
output:
TASK [assert] **********************************************************************************************************************************************************************************************************
ok: [localhost] => (item=hypervisor-01) => {
"ansible_loop_var": "item",
"changed": false,
"item": "hypervisor-01",
"msg": "All assertions passed"
}
failed: [localhost] (item=hypervisor-02) => {
"ansible_loop_var": "item",
"assertion": "available[item] > required[item]",
"changed": false,
"evaluated_to": false,
"item": "hypervisor-02",
"msg": "Assertion failed"
}
failed: [localhost] (item=hypervisor-03) => {
"ansible_loop_var": "item",
"assertion": "required[item] is defined",
"changed": false,
"evaluated_to": false,
"item": "hypervisor-03",
"msg": "Assertion failed"
}
PLAY RECAP *************************************************************************************************************************************************************************************************************
localhost : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0

Ansible variable in dictionary key

I am trying to execute a playbook for setting up Dell servers and I have some problems with a dictionary in module idrac_redfish_config. I need to enable SOL for a specific user, but for this I want to use a key in the dictionary with a variable because ID of user can be different from server to server.
How I try to add a variable to a dictionary key like this:
- name: Store id test-user
set_fact:
ID: "{{ result.redfish_facts.user.entries | json_query(\"[?UserName=='test-user'].Id\") }}"
- name: Enable SOL for test-user
community.general.idrac_redfish_config:
category: Manager
command: SetManagerAttributes
resource_id: iDRAC.Embedded.1
manager_attributes:
Users.{{ ID[0] }}.SolEnable: "Enabled" <---
Users.{{ ID[0] }}.IpmiLanPrivilege: "Administrator" <---
baseuri: "testhost"
username: "admin"
password: "admin"
I get this error:
TASK [Store id test-user] **************************************************************************************************************************************************************************************
ok: [localhost] => {
"ansible_facts": {
"ID": [
"8"
]
},
"changed": false
}
TASK [Enable SOL for test-user] ********************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {
"changed": false,
"invocation": {
"module_args": {
"baseuri": "testhost",
"category": "Manager",
"command": [
"SetManagerAttributes"
],
"manager_attribute_name": null,
"manager_attribute_value": null,
"manager_attributes": {
"Users.{{ ID[0] }}.IpmiLanPrivilege": "Administrator",
"Users.{{ ID[0] }}.SolEnable": "Enabled"
},
"password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"resource_id": "iDRAC.Embedded.1",
"timeout": 10,
"username": "admin"
}
},
"msg": "SetManagerAttributes: Manager attribute Users.{{ ID[0] }}.SolEnable not found"
}
If I do this:
manager_attributes: "{
'Users.{{ ID[0] }}.SolEnable': Enabled
'Users.{{ ID[0] }}.IpmiLanPrivilege': Administrator
}"
I get:
fatal: [localhost]: FAILED! => {
"changed": false,
"invocation": {
"module_args": {
"baseuri": "testhost",
"category": "Manager",
"command": [
"SetManagerAttributes"
],
"manager_attributes": "{ 'Users.8.SolEnable': Enabled 'Users.8.IpmiLanPrivilege': Administrator }",
"password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
"resource_id": "iDRAC.Embedded.1",
"timeout": 10,
"username": "admin"
}
},
"msg": "argument manager_attributes is of type <class 'str'> and we were unable to convert to dict: unable to evaluate string as dictionary"
}
I didn't find in Ansible documentation how to do this correctly.
According to documentation, manager_attributes should be a dict of key/value pairs to set on your device. The keys have dots in their names and you cannot "statically" create dynamic key names as you tried above (i.e. "prefix{{ dynamic_value }}suffix": "some content" does not work as you experienced by yourself since the key name does not go through jinja2 templating).
Below is a solution. It's far from being the only one but that's the first that came to my mind and I could setup an example for you quickly. In this case, I create a list of {key: X, value: Y} dicts with your dynamic names as keys and use the items2dict filter to transform this back to a dict itself.
I don't have a network device to play this against so I could not verify that the final result is actually accepted by the module. My example simply uses a debug with your input data to illustrate and outputs a dictionary as the module expects. You will have to tune the exact key names if they are wrong but at least you should be able to move forward.
- name: Dynamic dict
hosts: localhost
gather_facts: false
vars:
ID:
- "8"
my_attributes:
- key: "Users.{{ ID[0] }}.IpmiLandPrivilege"
value: Administrator
- key: "Users.{{ ID[0] }}.SolEnable"
value: Enabled
tasks:
- name: construct a dynamic dict and debug
vars:
manager_attributes: "{{ my_attributes | items2dict }}"
debug:
var: manager_attributes
Which gives:
PLAY [Dynamic dict demo] ***************************************************************************************************************************************************************************************************************
TASK [construct a dynamic dict and debug] **********************************************************************************************************************************************************************************************
ok: [localhost] => {
"manager_attributes": {
"Users.8.IpmiLandPrivilege": "Administrator",
"Users.8.SolEnable": "Enabled"
}
}
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Edit: An additional (out of many others) example to achieve the same goal. The output is exactly the same as above:
- name: Dynamic dict
hosts: localhost
gather_facts: false
vars:
ID:
- "8"
manager_attributes: "{{
{
'Users.' + ID[0] + '.IpmiLandPrivilege': 'Administrator',
'Users.' + ID[0] + '.SolEnable': 'Enabled'
}
}}"
tasks:
- name: construct a dynamic dict and debug
debug:
var: manager_attributes

ansible loop with failed_when on register variable list output

Team,
I have below task working fine when i only have one node or only one item but I need to modify it work for all items returned in the list that is stored in register variable.
- name: "Fetch all CPU nodes from clusters using K8s beta.kubernetes.io/instance-type"
k8s_info:
kind: Node
label_selectors:
- "beta.kubernetes.io/instance-type={{ kube_cpu_node_class }}"
verify_ssl: no
register: cpu_class_list
failed_when: not cpu_class_list.resources
How can i do this for all nodes in cpu_class_list variable with loop or with_items?
proposed solution but does not work
- name: "Fetch all CPU nodes from clusters using K8s beta.kubernetes.io/instance-type"
k8s_info:
kind: Node
label_selectors:
- "beta.kubernetes.io/instance-type={{ kube_cpu_node_class }}"
verify_ssl: no
register: cpu_class_list
failed_when: not {{ item }}
with_items: cpu_class_list.resources
sample output with two nodes is below
services-pre-install-checks : debug]
ok: [localhost] => {
"cpu_class_list": {
"changed": false,
"deprecations": [
{
"msg": "The 'k8s_facts' module has been renamed to 'k8s_info'",
"version": "2.13"
}
],
"failed": false,
"failed_when_result": false,
"resources": [
{
"apiVersion": "v1",
"kind": "Node",
"metadata": {
"annotations": {
"volumes.kubernetes.io/controller-managed-attach-detach": "true"
},
"creationTimestamp": "2019-07-16T00:23:27Z",
"labels": {
"nodeType": "cpu"
},
"name": "node1",
"nodeInfo": {
"architecture": "amd64",
}
}
},
{
{
"apiVersion": "v1",
"kind": "Node",
"metadata": {
"annotations": {
"volumes.kubernetes.io/controller-managed-attach-detach": "true"
},
"creationTimestamp": "2019-07-16T00:23:27Z",
"labels": {
"nodeType": "cpu"
},
"name": "node2",
"nodeInfo": {
"architecture": "amd64",
}
}
}
]
}
}
Proposed Solution:
- name: "Fetch all CPU nodes from clusters using K8s beta.kubernetes.io/instance-type"
k8s_facts:
kind: Node
label_selectors:
- "beta.kubernetes.io/instance-type={{ kube_cpu_node_class }}"
verify_ssl: no
register: cpu_class_list
failed_when: not cpu_class_list.resources
#above to fail when none of the nodes has label, that is resources list is empty.
#below to fail when any of the nodes has no label
- debug:
msg: "{{ item.metadata.labels.nodeType }}"
loop: "{{ cpu_class_list.resources }}"
loop_control:
label: "{{ item.metadata.name }}"
failed_when: not item.metadata.labels.nodeType
I would propose to split it up into two distinct tasks. First, register the variable, then use the fail ansible module (docs) to inspect the var and fail if a condition is met.
See this snippet for outlining the logic:
- hosts: localhost
vars:
test: # test array
- fail: false
- fail: false
- fail: true
- fail: false
tasks:
- name: iterate and fail
fail:
msg: "Fail as requested"
with_items: "{{ test }}"
when: item.fail
Running this, will output the following:
$ ansible-playbook failing.yml
PLAY [localhost] ***********************
TASK [Gathering Facts] *************************
ok: [localhost]
TASK [iterate and fail] **************************
skipping: [localhost] => (item={u'fail': False})
skipping: [localhost] => (item={u'fail': False})
failed: [localhost] (item={u'fail': True}) => {"ansible_loop_var": "item", "changed": false, "item": {"fail": true}, "msg": "Failed as requested"}
skipping: [localhost] => (item={u'fail': False})
PLAY RECAP *****************
localhost : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
Hope this helps!
#to fail when none of the nodes has label, that is resources list is empty.
- name: "Fetch all CPU nodes from clusters using K8s beta.kubernetes.io/instance-type"
k8s_facts:
kind: Node
label_selectors:
- "beta.kubernetes.io/instance-type={{ kube_cpu_node_class }}"
verify_ssl: no
register: cpu_class_list
failed_when: not cpu_class_list.resources
#below to fail when any of the nodes has no label
- debug:
msg: "{{ item.metadata.labels.nodeType }}"
loop: "{{ cpu_class_list.resources }}"
loop_control:
label: "{{ item.metadata.name }}"
failed_when: not item.metadata.labels.nodeType

Ansible: approach to loop through a complex variable in order to access different layers under the same task

What would be the correct approach to loop through a complex dictionary variable in an Ansible play in order to be able to call items from different layers of the variable in the same module/task?
The variable in question looks like this (but it could be any number of layers down, so far I only have about 3 layers down):
installation:
v10:
installs: yes
das_username: dasusr
das_group: dasadm
package_name: v10.tar.gz
binaries_folder: binaries
install_path: /opt/V10
instances:
inst1:
enabled: yes
username: inst1
group: dbiadm
port: 50000
databases:
jts:
name: 'JTS'
state: present
pagesize: 32768
rtc:
name: 'CCM'
state: absent
pagesize: 16384
rqm:
name: 'QM'
state: absent
pagesize: 32768
inst2:
enabled: no
username: inst2
group: dbiadm
port: 50005
databases:
warehouse:
name: 'DW'
state: present
pagesize: 32768
v9:
installs: no
instances:
inst3:
enabled: no
databases:
rqm:
name: 'RM'
state: absent
pagesize: 32768
lqe:
name: 'LQE'
state: absent
pagesize: 16384
Tasks include creating databases using the install_path (layer 1) and databases parameters (layer 3) under the same shell module or in a template.
For example (the task should look something like this) but with the correct method of looping:
- name: Creating the databases
become_user: "{{ item.0.instances.username }}"
shell: "./db2 create database {{ item.1.name }} using pagesize {{ item.1.pagesize }}"
args:
chdir: "{{ item.0.install_path }}/bin"
with_subelements:
- "{{ installation }}"
- instances.databases.clm
Thank you!
It's not a trivial feat to do such thing in ansible, becase loops in ansible are one-dimensional. There is a hackish way though, you can use nested includes with loop control. So let's say i put all of your variables inside vars.yml and have a following files:
playbook-nested.yml
- hosts: localhost
tasks:
- name: pass version forward
include: instances.yml v="{{ version_item }}"
with_items: "{{ installation }}"
loop_control:
loop_var: version_item
instances.yml
- name: Pass instance forward
include: databases.yml i={{ instance_item }}
with_items: "{{ installation[v].instances }}"
loop_control:
loop_var: instance_item
databases.yml
- name: echo command
debug: msg="./db2 create database {{ item.name }} using pagesize {{ item.pagesize }}"
with_items: "{{ installation[v].instances[i].databases.clm }}"
when: installation[v].instances[i].databases.clm is defined
So the output of command ansible-playbook playbook-nested.yml -e #vars.yml will be:
PLAY [localhost] ***************************************************************
TASK [setup] *******************************************************************
ok: [localhost]
TASK [pass version forward] ****************************************************
included: /home/user/instances.yml for localhost
included: /home/user/instances.yml for localhost
TASK [Pass instance forward] ***************************************************
included: /home/user/databases.yml for localhost
TASK [echo command] ************************************************************
ok: [localhost] => (item={u'state': u'absent', u'name': u'RQM', u'pagesize': 32768}) => {
"item": {
"name": "RQM",
"pagesize": 32768,
"state": "absent"
},
"msg": "./db2 create database RQM using pagesize 32768"
}
ok: [localhost] => (item={u'state': u'absent', u'name': u'LQE', u'pagesize': 16384}) => {
"item": {
"name": "LQE",
"pagesize": 16384,
"state": "absent"
},
"msg": "./db2 create database LQE using pagesize 16384"
}
TASK [Pass instance forward] ***************************************************
included: /home/user/databases.yml for localhost
included: /home/user/databases.yml for localhost
TASK [echo command] ************************************************************
ok: [localhost] => (item={u'state': u'present', u'name': u'JTS', u'pagesize': 32768}) => {
"item": {
"name": "JTS",
"pagesize": 32768,
"state": "present"
},
"msg": "./db2 create database JTS using pagesize 32768"
}
ok: [localhost] => (item={u'state': u'absent', u'name': u'CCM', u'pagesize': 16384}) => {
"item": {
"name": "CCM",
"pagesize": 16384,
"state": "absent"
},
"msg": "./db2 create database CCM using pagesize 16384"
}
ok: [localhost] => (item={u'state': u'absent', u'name': u'QM', u'pagesize': 32768}) => {
"item": {
"name": "QM",
"pagesize": 32768,
"state": "absent"
},
"msg": "./db2 create database QM using pagesize 32768"
}
TASK [echo command] ************************************************************
skipping: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=8 changed=0 unreachable=0 failed=0
I'm not sure what does clm mean and do i have to loop over databases as well, but i think the concept is clear to you now.
You can also use the nested_loops on the lower level i guess.

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