I am creating a simple dictionary in ansible which is this one:
"types_dict": {
"name1": "Ethernet",
"name2": "Ethernet",
"name3": "Software-Pseudo",
"name4": "Ethernet",
"name5": "Software-Pseudo",
"name6": "Ethernet" }
My goal is to loop the dictionary and replace some specific values, the ones "Software-Pseudo" with "Virtual". I have tried the following:
- set_fact:
types_dict: "{{ types_dict | combine(new_item, recursive=true) }}"
vars:
new_item: "{ '{{ item.key }}': { 'type': 'Virtual' } }"
with_dict: "{{ types_dict }}"
but the problem here is that this one updates all the values in my dictionary, which is something I do not want at all. I tried also the following by adding the "when" statement, but also it is not working:
- set_fact:
types_dict: "{{ types_dict | combine(new_item, recursive=true) }}"
vars:
new_item: "{ '{{ item.key }}': { 'type': 'Virtual' } }"
when: "{{ item.value }} == Software-Pseudo"
with_dict: "{{ types_dict }}"
I also tried when: "{{ item.value }} == 'Software-Pseudo'" and many other things like this.
Any ideas on how to fix this ?
The task does the job. Items can be added to the list types_new if needed
- set_fact:
types_dict: "{{ types_dict|combine({item.0.key: item.1.replace}) }}"
with_nested:
- "{{ types_dict|dict2items }}"
- "{{ types_new }}"
when: item.0.value is search(item.1.regex)
vars:
types_new:
- {regex: 'Software-Pseudo', replace: 'Virtual'}
- debug:
var: types_dict
gives
types_dict:
name1: Ethernet
name2: Ethernet
name3: Virtual
name4: Ethernet
name5: Virtual
name6: Ethernet
Q: "I have null values like "name2": null in my dictionary, could I handle this in any way so as to replace it with sth else (another value)."
A: Add a line to the types_new. For example
vars:
types_new:
- {regex: 'Software-Pseudo', replace: 'Virtual'}
- {regex: 'None', replace: 'another_value'}
See the task below how null, None, and 'None' are treated by the search test
- debug:
msg: "{{ item.key }} {{ item.value is search('None') }}"
loop: "{{ my_vars|dict2items }}"
vars:
my_vars:
var1: abc
var2:
var3: None
var4: 'None'
gives
msg: var1 False
msg: var2 True
msg: var3 True
msg: var4 True
Related
I'm unsure if the title matches to what I'm trying to ask but I didn't know what other title to use.
So my question:
What I'm trying to do is gather disk info from VMWare and post the output of capacity_in_kb for EACH disk in a file (but currently only debug).
- name: Gather disk info from virtual machine
community.vmware.vmware_guest_disk_info:
hostname: "{{ lookup('ansible.builtin.env', 'VMWARE_HOST') }}"
username: "{{ lookup('ansible.builtin.env', 'VMWARE_USER') }}"
password: "{{ lookup('ansible.builtin.env', 'VMWARE_PASSWORD') }}"
validate_certs: no
name: "{{ ansible_hostname }}"
datacenter: "{{ lookup('ansible.builtin.env', 'VMWARE_HOST') }}"
delegate_to: 127.0.0.1
register: disk_info
- debug:
var: disk_info
This outputs the following:
{
"disk_info": {
"guest_disk_info": {
"0": {
"key": 2000,
"label": "Hard disk 1",
"summary": "16,777,216 KB",
"backing_filename": "[xxx] xxx.vmdk",
"backing_datastore": "xxx_log",
"controller_key": 1000,
"unit_number": 0,
"capacity_in_kb": 16777216,
"capacity_in_bytes": 17179869184,
"backing_type": "FlatVer2",
"backing_writethrough": false,
"backing_thinprovisioned": false,
"backing_eagerlyscrub": false,
"backing_diskmode": "persistent",
"backing_disk_mode": "persistent",
"backing_uuid": "xxxxxxxxxxxxxxxx",
"controller_bus_number": 0,
"controller_type": "paravirtual"
},
"1": {
"key": 2001,
"label": "Hard disk 2",
"summary": "262,144,000 KB",
"backing_filename": "[xxx] xxx.vmdk",
"backing_datastore": "xxx_log",
"controller_key": 1000,
"unit_number": 1,
"capacity_in_kb": 262144000,
"capacity_in_bytes": 268435456000,
"backing_type": "FlatVer2",
"backing_writethrough": false,
"backing_thinprovisioned": false,
"backing_eagerlyscrub": false,
"backing_diskmode": "persistent",
"backing_disk_mode": "persistent",
"backing_uuid": "xxx",
"controller_bus_number": 0,
"controller_type": "paravirtual"
}
},
"failed": false,
"changed": false
},
"_ansible_verbose_always": true,
"_ansible_no_log": false,
"changed": false
}
We now want to get capacity_in_kb for every disk. Some VMs only have one disk attached, some have two, some three and so on...
We tried
- name: with_dict
ansible.builtin.debug:
msg: "{{ item.value.label }} - {{ item.value.capacity_in_kb }}"
loop: "{{ disk_info.guest_disk_info|dict2items }}"
but that also doesn't work for us and only gives us the output of the first disk [0] in the list.
Does anyone have an idea? Thanks in advance :)
Edit: as #β.εηοιτ.βε pointet out it's
- loop: "{{ disk_info|dict2items }}"
+ loop: "{{ disk_info.guest_disk_info|dict2items }}"
but still it only iterates over the first disk and not the second, third, etc..
EDIT2: it worked, but I just didn't see it...dumb me
If you want to iterate the data, the task below
- debug:
msg: "{{ item.value.label }}: {{ item.value.capacity_in_kb }}"
loop: "{{ disk_info.guest_disk_info|dict2items }}"
loop_control:
label: "{{ item.key }}"
gives
ok: [localhost] => (item=0) =>
msg: 'Hard disk 1: 16777216'
ok: [localhost] => (item=1) =>
msg: 'Hard disk 2: 262144000'
The next option is to avoid the iteration and create a dictionary
label_capacity_q: '*.[label, capacity_in_kb]'
label_capacity: "{{ dict(disk_info.guest_disk_info|
json_query(label_capacity_q)) }}"
gives
label_capacity:
Hard disk 1: 16777216
Hard disk 2: 262144000
Example of a complete playbook for testing
- hosts: localhost
vars:
label_capacity_q: '*.[label, capacity_in_kb]'
label_capacity: "{{ dict(disk_info.guest_disk_info|
json_query(label_capacity_q)) }}"
tasks:
- debug:
var: label_capacity
- debug:
msg: "{{ item.key }}: {{ item.value }}"
loop: "{{ label_capacity|dict2items }}"
- debug:
msg: |-
{% for k,v in label_capacity.items() %}
{{ k }}: {{ v }}
{% endfor %}
gives (abridged)
TASK [debug] *********************************************************************************
ok: [localhost] =>
label_capacity:
Hard disk 1: 16777216
Hard disk 2: 262144000
TASK [debug] *********************************************************************************
ok: [localhost] => (item={'key': 'Hard disk 1', 'value': 16777216}) =>
msg: 'Hard disk 1: 16777216'
ok: [localhost] => (item={'key': 'Hard disk 2', 'value': 262144000}) =>
msg: 'Hard disk 2: 262144000'
TASK [debug] *********************************************************************************
ok: [localhost] =>
msg: |-
Hard disk 1: 16777216
Hard disk 2: 262144000
Given the data, for example in group_vars
shell> cat group_vars/all/disk_info.yml
---
disk_info:
changed: false
failed: false
guest_disk_info:
'0':
backing_datastore: xxx_log
backing_disk_mode: persistent
backing_diskmode: persistent
backing_eagerlyscrub: false
backing_filename: '[xxx] xxx.vmdk'
backing_thinprovisioned: false
backing_type: FlatVer2
backing_uuid: xxxxxxxxxxxxxxxx
backing_writethrough: false
capacity_in_bytes: 17179869184
capacity_in_kb: 16777216
controller_bus_number: 0
controller_key: 1000
controller_type: paravirtual
key: 2000
label: Hard disk 1
summary: 16,777,216 KB
unit_number: 0
'1':
backing_datastore: xxx_log
backing_disk_mode: persistent
backing_diskmode: persistent
backing_eagerlyscrub: false
backing_filename: '[xxx] xxx.vmdk'
backing_thinprovisioned: false
backing_type: FlatVer2
backing_uuid: xxx
backing_writethrough: false
capacity_in_bytes: 268435456000
capacity_in_kb: 262144000
controller_bus_number: 0
controller_key: 1000
controller_type: paravirtual
key: 2001
label: Hard disk 2
summary: 262,144,000 KB
unit_number: 1
remote_address_phase2:
- 192.168.88.0/24
- 192.168.1.0/24
task:
i have to create a firewall group
- name: "addrgrp"
fortios_firewall_addrgrp:
vdom: "{{ vdom }}"
state: "present"
firewall_addrgrp:
allow_routing: "disable"
#category: "default"
color: "21"
comment: "try"
exclude: "disable"
fabric_object: "disable"
member:
- name: "NET-{{ item}}"
name: "try"
type: "default"
with_items: "{{ remote_address_phase2 }}"
if i made this activity i have 2 different task but the last operation overwrite the first
any idea?
Going by the example in the documentation for the module, it seems that the member: parameter takes a list of dicts.
Example from module documentation:
member:
-
name: "default_name_7 (source firewall.address.name firewall.addrgrp.name)"
Haven't tested it, but we can create a similar structure before "addrgrp" task with set_fact and use the newly created variable.
- set_fact:
fw_members: "{{ fw_members | default([]) + [{'name': 'NET-' ~ item}] }}"
loop: "{{ remote_address_phase2 }}"
This gives:
"fw_members": [
{
"name": "NET-192.168.88.0/24"
},
{
"name": "NET-192.168.1.0/24"
}
]
It should then be possible to pass this variable as a value to the member: parameter. Example:
- set_fact:
fw_members: "{{ fw_members | default([]) + [{'name': 'NET-' ~ item}] }}"
loop: "{{ remote_address_phase2 }}"
- name: "addrgrp"
fortios_firewall_addrgrp:
vdom: "{{ vdom }}"
state: "present"
firewall_addrgrp:
allow_routing: "disable"
#category: "default"
color: "21"
comment: "try"
exclude: "disable"
fabric_object: "disable"
member: "{{ fw_members }}"
name: "try"
type: "default"
I have a list of AD users and trying to create a distinguishedName-list
vars:
admin_users: "user1;user2"
tasks:
- set_fact:
admin: "{{ admin_users.split(';') }}"
- name: Search account
community.general.ldap_search:
<...>
loop: "{{ admin }}"
register: ldap_result
- name: Build DN-array
set_fact:
group: "{{ group | default([]) }} + {{ item.value | json_query(query) }}"
with_dict: "{{ ldap_result.results }}"
when: item.key == 'results'
vars:
query: "[*].distinguishedName"
- debug:
var: "{{ group }}"
And get
"<class 'list'>": "VARIABLE IS NOT DEFINED!"
Also tried
group: "{{ group | default([]) }} + [ {{ item.value | json_query(query) }} ]"
group: "{{ group | default([]) }} + {{ [ item.value | json_query(query) ] }}"
get same message
"<class 'list'>": "VARIABLE IS NOT DEFINED!"
and
group: "{{ group | default([]) }} + [ '{{ item.value | json_query(query) }}' ]"
Then I get the error
FAILED! => {"msg": "template error while templating string: expected
token ',', got 'CN'. String: {{[] + [ '['CN=***']' ] + [ '['CN=***']' ]}}"}
Any tips?
Ok, that's right, I just needed to use a construction like this
<...>
group: "{{ group | default([]) }} + [ {{ item.value | json_query(query) }} ]"
<...>
- debug: msg="{{ group }}"
not this
- debug:
var: "{{ group_list_two }}"
correct output
ok: [localhost] => {
"msg": [
[
"CN=***"
],
[
"CN=***"
]
]
}
I have a list of dicts and I want to loop through the list of dicts, and then have an inner loop that splits a string from each dict and loops through that split string. Is there a way to do this?
I've tried a few things:
- debug:
msg: '{{ "Group: " + item.Group + ", AddMembers: " + item.1 }}'
with_nested:
- '{{ domainGroups.list | selectattr("AddMembers", "ne", "") | list }}'
- '{{ AddMembers.split("|") }}'
- debug:
msg: '{{ "Group: " + item.Group + ", AddMembers: " + item.1 }}'
loop: '{{ domainGroups.list | selectattr("AddMembers", "ne", "" ) | list | subelements(AddMembers.split("|")) }}'
The list of dicts:
{
"AddMembers": "",
"Group": "Group1",
"Delete": "1",
},
{
"AddMembers": "members1|members2",
"Group": "Group2",
"Delete": "",
},
{
"AddMembers": "members1|members2|members3",
"Group": "Group3",
"Delete": "",
},
I hoped that one of the things I've tried would work but usually I get some variation of "AddMembers is not defined" or "unicode thing has no attribute "AddMembers""
Update
Declare the variables
my_groups: "{{ domainGroups|map(attribute='Group')|list }}"
my_member: "{{ domainGroups|map(attribute='AddMembers')|
map('split', '|')|
map('select')|list }}"
groups_members: "{{ dict(my_groups|zip(my_member)) }}"
gives
groups_members:
Group1: []
Group2:
- members1
- members2
Group3:
- members1
- members2
- members3
Iterate with subelements
- debug:
msg: "Group: {{ item.0.key }} AddMember: {{ item.1 }}"
with_subelements:
- "{{ groups_members|dict2items }}"
- value
gives (abridged)
msg: 'Group: Group2 AddMember: members1'
msg: 'Group: Group2 AddMember: members2'
msg: 'Group: Group3 AddMember: members1'
msg: 'Group: Group3 AddMember: members2'
msg: 'Group: Group3 AddMember: members3'
Example of a complete playbook for testing
- hosts: localhost
vars:
domainGroups:
- AddMembers: ''
Delete: '1'
Group: Group1
- AddMembers: members1|members2
Delete: ''
Group: Group2
- AddMembers: members1|members2|members3
Delete: ''
Group: Group3
my_groups: "{{ domainGroups|map(attribute='Group')|list }}"
my_member: "{{ domainGroups|map(attribute='AddMembers')|
map('split', '|')|
map('select')|list }}"
groups_members: "{{ dict(my_groups|zip(my_member)) }}"
tasks:
- debug:
var: groups_members
- debug:
msg: "Group: {{ item.0.key }} AddMember: {{ item.1 }}"
with_subelements:
- "{{ groups_members|dict2items }}"
- value
Deprecated
Let's simplify the dictionaries in the first step. The tasks below
- set_fact:
domainGroups_selected: "{{ domainGroups_selected|default([]) +
[ {'Group': item.Group,
'AddMembers': item.AddMembers.split('|')}] }}"
loop: "{{ domainGroups }}"
when: item.AddMembers|length > 0
- debug:
var: item
loop: "{{ domainGroups_selected }}"
give
"item": {
"AddMembers": [
"members1",
"members2"
],
"Group": "Group2"
}
...
"item": {
"AddMembers": [
"members1",
"members2",
"members3"
],
"Group": "Group3"
}
Then loop the list with subelements. The task below
- debug:
msg: "Group: {{ item.0.Group }} AddMember: {{ item.1 }}"
loop: "{{ domainGroups_selected|subelements('AddMembers')|list }}"
gives
"msg": "Group: Group2 AddMember: members1"
"msg": "Group: Group2 AddMember: members2"
"msg": "Group: Group3 AddMember: members1"
"msg": "Group: Group3 AddMember: members2"
"msg": "Group: Group3 AddMember: members3"
Related Post: ansible parse json array reply from api
I have an Ansible playlist which registers a return variable:
- name: Create Instance
ec2_instance:
aws_access_key: "{{access_key}}"
aws_secret_key: "{{secret_key}}"
key_name: ***
instance_type: t2.micro
security_group: ***
image_id: ami-39f8215b
region: ***
register: details
So the details is a JSON object like this:
{
"details": {
"changed": false,
"changes": [],
"failed": false,
"instance_ids": [
"i-1111abcde"
],
...
}
All I want to do is write a text file with each instance_id in there:
i-1111abcde
I've tried all of the following, none working:
debug:
var: item
with_items: details['instance_ids']
debug:
var: item.item
with_items: details['instance_ids']
debug:
var: details.instance_ids
with_items: details
# This works, but prints the entire JSON array...
Solution
- name: Debug Info
debug:
var: item
loop: "{{details.instance_ids}}"
- name: Write Temp File
lineinfile:
path: /tmp/temp.txt
line: "{{ item }}"
loop: "{{ details.instance_ids }}"
Note: loop is a more modern Ansible concept that with_items or with_*
Solution
- name: Debug Info
debug:
var: item
loop: "{{details.instance_ids}}"
- name: Write Temp File
lineinfile:
path: /tmp/temp.txt
line: "{{ item }}"
loop: "{{ details.instance_ids }}"
Note: loop is a more modern Ansible concept that with_items or with_*