Ansible Parse JSON Array from Register - arrays

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_*

Related

looping through dictionary-formatted Ansible facts

Considering the output of ios_facts module (or any similar gathering fact module), I wrote the following Ansible playbook:
name: checking interface status
gather_facts: no
hosts: iosxe
tasks:
name: get cisco config
cisco.ios.ios_facts:
gather_subset: all
register: cisco_output
name: task001
debug:
var: cisco_output['ansible_facts']['ansible_net_interfaces']
loop: "{{ ansible_facts['ansible_net_interfaces'] | dict2items }}"
when: item.lineprotocol == "up"
For the sake of practicing Ansible, I wanted the playbook to show list of interfaces in "up" state. The relative part in the output of the "ios_fact" shows that it is a "dictionary" not "list".
"ansible_net_interfaces": {
"GigabitEthernet1": {
"bandwidth": 1000000,
"description": null,
"duplex": "Full",
"ipv4": [
{
"address": "10.106.31.229",
"subnet": "24"
}
],
"lineprotocol": "up",
"macaddress": "000c.29c3.a8f3",
"mediatype": "Virtual",
"mtu": 1500,
"operstatus": "up",
"type": "CSR vNIC"
},
"GigabitEthernet2": {
"bandwidth": 1000000,
"description": "CSR2",
"duplex": "Full",
"ipv4": [
{
"address": "10.171.73.33",
"subnet": "27"
}
],
"lineprotocol": "up",
"macaddress": "000c.29c3.a8fd",
"mediatype": "Virtual",
"mtu": 1500,
"operstatus": "up",
"type": "CSR vNIC"
}, ...
I got several different errors and tried to workaround by changing the playbook each time as these:
loop: "{{ ansible_facts['ansible_net_interfaces'] | dict2items }}"
loop: "{{ cisco_output['ansible_facts']['ansible_net_interfaces'] }}"
loop: "{{ cisco_output['ansible_facts']['ansible_net_interfaces'] | dict2items }}"
But was unsuccessful as I got different errors again. What would be the right way of this?
The dict2items filter will convert the dict to an array of items, where the keys will be GigabitEthernet1, GigabitEthernet2, and the values of these keys will sub-dicts.
"item.key": "GigabitEthernet1"
"item.key": "GigabitEthernet2"
So, the lineprotocol key will be in the item.value, i.e. item.value.lineprotocol. A simple task such as below can demonstrate the same:
- debug:
msg: "{{ item.key }} is up"
loop: "{{ cisco_output['ansible_facts']['ansible_net_interfaces | dict2items }}"
when: item.value.lineprotocol == "up"
The attribute ipv4 is a list. Use with_subelements if you want to take into account the fact that there might be more IP addresses configured, e.g.
- debug:
msg: "{{ item.0.key }} {{ item.1.address }}"
with_subelements:
- "{{ ansible_net_interfaces|dict2items|
selectattr('value.lineprotocol', 'eq', 'up') }}"
- value.ipv4
gives
msg: GigabitEthernet1 10.106.31.229
msg: GigabitEthernet2 10.171.73.33

Ansible: fortigate list to popolate a dictionary

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"

how can I update a dictionary in Ansible

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

Looping over an ansible dict for specific value

With ansible, looping over a list of items that return a dict(?) with listed values.
Basically I want to check a dict of packages to see if they are installed or not (and later on return a message for any missing packages).
Any loop/with_dict combo tried so far return various errors that the specific variable can't be found.
When querying the status itself (yum module) it does put the full output into the {{ pkg }} var, per package.
---
- name: Ansible tests playbook
hosts: vms
remote_user: root
vars:
pkgs:
- yum-utils
- mariadb-libs
tasks:
- name: Check packages
yum:
list: "{{ item }}"
disablerepo: '*'
register: pkg
loop: "{{ pkgs }}"
If I then simply output the contents of {{ pkg.results }} with :
- name: list
debug:
msg: "{{ item }}"
loop: "{{ pkg.results }}"
I get :
root#vm011:~/ovirt# ansible-playbook check.yml
PLAY [Ansible tests playbook] **************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************
ok: [vm017.warp]
TASK [Check paclages] **********************************************************************************************************************************************************************
ok: [vm017.warp] => (item=yum-utils)
ok: [vm017.warp] => (item=mariadb-libs)
TASK [list] ********************************************************************************************************************************************************************************
ok: [vm017.warp] => (item=None) =>
msg:
changed: false
failed: false
invocation:
module_args:
allow_downgrade: false
conf_file: null
disable_gpg_check: false
disable_plugin: []
disablerepo: '*'
enable_plugin: []
enablerepo: null
exclude: null
install_repoquery: true
installroot: /
list: yum-utils
name: null
security: false
skip_broken: false
state: installed
update_cache: false
update_only: false
validate_certs: true
item: yum-utils
results:
- arch: noarch
envra: 0:yum-utils-1.1.31-50.el7.noarch
epoch: '0'
name: yum-utils
release: 50.el7
repo: installed
version: 1.1.31
yumstate: installed
ok: [vm017.warp] => (item=None) =>
msg:
changed: false
failed: false
invocation:
module_args:
allow_downgrade: false
conf_file: null
disable_gpg_check: false
disable_plugin: []
disablerepo: '*'
enable_plugin: []
enablerepo: null
exclude: null
install_repoquery: true
installroot: /
list: mariadb-libs
name: null
security: false
skip_broken: false
state: installed
update_cache: false
update_only: false
validate_certs: true
item: mariadb-libs
results:
- arch: x86_64
envra: 1:mariadb-libs-5.5.60-1.el7_5.x86_64
epoch: '1'
name: mariadb-libs
release: 1.el7_5
repo: installed
version: 5.5.60
yumstate: installed
How can I get just the yumstate value (installed, or otherwise), per package, into a separate variable ?
If, for instance, I try to debug the msg with
- name: list
debug:
msg: "{{ item.yumstate }}"
loop: "{{ pkg.results }}"
I get:
fatal: [vm017.warp]: FAILED! =>
msg: |-
The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'yumstate'
you are looping correctly through the pkg.results, but in each item the yumstate is under another results key. you should use:
- name: list
debug:
msg: "{{ item.results.yumstate }}"
loop: "{{ pkg.results }}"
i tried your code on my fedora, and the results for one of the 2 packages has as value a list, so the item.results.yumstate would not work: Fedora response for yum utils:
"item.results": [
{
"arch": "noarch",
"epoch": "0",
"name": "yum-utils",
"nevra": "0:yum-utils-1.1.31-517.fc29.noarch",
"release": "517.fc29",
"repo": "fedora",
"version": "1.1.31",
"yumstate": "available"
},
{
"arch": "noarch",
"epoch": "0",
"name": "yum-utils",
"nevra": "0:yum-utils-1.1.31-518.fc29.noarch",
"release": "518.fc29",
"repo": "updates",
"version": "1.1.31",
"yumstate": "available"
}
]
for mariadb-libs, no packages found, response was:
"item.results": []
hope it helps
You did indeed point me in the correct way.
- name: list
debug:
msg: "Package {{ item.results[0].name }} is {{ item.results[0].yumstate }}"
loop: "{{ pkg.results }}"
Did the trick, with just "{{ item.results.yumstate }}" it still returned an error
TASK [list] ********************************************************************************************************************************************************************************
fatal: [vm017.warp]: FAILED! =>
msg: |-
The task includes an option with an undefined variable. The error was: 'list object' has no attribute 'yumstate'
Adding the [0] got me:
TASK [list] ********************************************************************************************************************************************************************************
ok: [vm017.warp] => (item=None) =>
msg: Package yum-utils is installed
ok: [vm017.warp] => (item=None) =>
msg: Package mariadb-libs is installed
You might want to consider nested loops , also known aa "loops and includes" if you're attempting fancy things.
https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#defining-inner-and-outer-variable-names-with-loop-var

How to iterate through nested items in Ansible

I'm not sure if i'm taking the correct way but i have the next problem.
I need a simple task like:
- name: Copying files
template:
src: "{{ item[1] }}.j2"
dest: "{{ path }}/{{ item[0] }}/{{ item[1] }}"
with_nested:
- [ 'env1' , 'env2' ]
- [ 'file1' , 'file2']
Actual results:
/path/env1/file1
/path/env1/file2
/path/env2/file1
/path/env2/file2
Expected results:
/path/env1/file1
/path/env2/file2
I just need that file1 generate template in directory env1 and the file2 generate template in env2.
I can't do it with a simple 'with_items' because i hace 2 items to iterate, the name of the directory and the name of the file.
I'm sure that there is a way to do that correctly..
Thanks in advance
Use zip filter. The play below
- hosts: localhost
vars:
list1: [ 'env1' , 'env2' ]
list2: [ 'file1' , 'file2']
tasks:
- debug:
msg: "/path/{{ item.0 }}/{{ item.1 }}"
loop: "{{ list1|zip(list2)|list }}"
gives (grep msg):
"msg": "/path/env1/file1"
"msg": "/path/env2/file2"
you can try following to get expected results :
- name: Copying files
template:
src: "{{ item[1] }}.j2"
dest: "{{ path }}/{{ item[0] }}/{{ item[1] }}"
with_together:
- [ 'env1' , 'env2' ]
- [ 'file1' , 'file2']
with_together explanation

Resources