Ansible - Iterate through array using with items - ansible

I do have a simple json file, where i need to pull a set of value from EACH array item, but during iteration it fails.
My playbook looks like:
code:
---
- name: direct - this works like charm
set_fact:
bb: "{{ pr_json.json.issues[0].fields.customfield_11756.value }}"
- debug:
var: bb
- name: via array - this is not working since iteration is not happening
set_fact:
dd_branch: "{{ pr_json.json.issues[{{ item }}].fields.customfield_11756.value }}"
register: mass
- debug:
var: mass
Getting output as:
TASK [jira_update : direct - this works like charm] ********************************************************************************************************************
task path: /home/test/ansible_jira/roles/jira_update/tasks/call.yml:3
ok: [localhost] => {
"ansible_facts": {
"bb": "R4.19"
},
"changed": false
}
TASK [jira_update : debug] *********************************************************************************************************************************************
task path: /home/test/ansible_jira/roles/jira_update/tasks/call.yml:7
ok: [localhost] => {
"bb": "R4.19"
}
TASK [jira_update : via array - this is not working since iteratoin is not happening] **********************************************************************************
task path: /home/test/ansible_jira/roles/jira_update/tasks/call.yml:10
fatal: [localhost]: FAILED! => {
"msg": "template error while templating string: expected token ':', got '}'. String: {{ pr_json.json.issues[{{ item }}].fields.customfield_11756.value }}"
}
Please do let us know how can I iterate through an array variable value on every sequence.
tried this too, but can somebody help to iterate the array values, please.
- name: Create PR request in TEMS JIRA
jira:
uri: "{{ tems_jira }}"
username: "{{ user }}"
password: "{{ pass }}"
operation: create
project: PR
issuetype: 'PR-Form'
summary: "{{ pr_json.json| json_query('issues[].fields.summary') }}"
description: "{{ pr_json.json | json_query('issues[].fields.description') }}"
args:
fields:
customfield_10303:
value: "{{ pr_json.json | json_query('issues[].fields.customfield_11756.value') }}"

Youy need to feed your list into a with_items iterator. Thats what sets the item variable for looping purposes.
- name: via array - this is not working since iteration is not happening
set_fact:
dd_branch: "{{ pr_json.json.issues[ item ].fields.customfield_11756.value }}"
register: mass
with_items:
- 0
- 1
That will iterate through all of the list items of pr_json.json.issues which will let you dive deeper into the variable structure like you are looking for. There are a lot of other factors that you can feed into the loop that might interest you that you can find detailed here.
https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html

Related

Ansible - List of dictionaries

Let me introduce my problem. I have some list of dictionary in my Ansible code:
my_example_list = [
{
"key1" : "value_of_first_key"
},
{
"key2": "value_of_second_key"
},
{
"key3": "value_of_third_key"
}
]
I need execute command which will iterate over this list and it should look something like:
- name: 'Example'
shell: 'Here is my {{ item.key }} and here is {{ item.value }}'
What I've do or try to do:
I was trying to do that with with_items but i'm not able to point into value of particular key.
I've also try to filter values using | first and | last but it's not worked in my case.
What I want to achieve:
Creating loop which will iterate via that list and inject separated key and value into command.
I was asked to show how I was trying to resolve my issue:
Here is some code:
# Showing last component failing
- name: "Try to show last component of my list"
debug:
msg: "{{ my_example_list[1] | last }}"
# When i'm trying to show first component of my list i get "key1"
- name: "Try to show first component of my list"
debug:
msg: "{{ my_example_list[1] | first }}"
# This shows me my list of dict
- name: "Trying use with_items"
debug:
msg: "{{ item }}"
with_items: "{{ my_example_list }}"
# But when i'm trying point to key and value for example
- name: "Trying use with_items point to key and value"
debug:
msg: "Here is my {{ item.key }} which keep {{ item.value }}"
with_items: "{{ my_example_list }}"
# It's failing.
Sorry it's not maybe solution with using loop. I'm just stack with that issue over few days... And as first step I want to know how correctly point to pair keys and values.
It also works well:
- name: Correct solution
debug:
msg: "This is my {{ item.key }} and my value {{ item.value }}"
with_dict: "{{ my_example_list }}"
Thanks #U880D for help! I'm not able to add some plus for your solution because I'm new joiner. Appreciate your answer! :)
Your data structure and naming seems to be unfavorable. There is no need to number the key name and therefore it should be avoided. Furthermore counting list elements in Python starts at 0 not 1.
The following minimal example playbook
---
- hosts: localhost
become: false
gather_facts: false
vars:
example_list: |
[
{
"key1" : "value_of_first_key"
},
{
"key2": "value_of_second_key"
},
{
"key3": "value_of_third_key"
}
]
tasks:
- name: Example loop
debug:
msg: "{{ item }} is of type {{ item | type_debug }}"
loop: "{{ example_list }}"
- name: Example loop
debug:
msg: "{{ item.values() }}"
loop: "{{ example_list }}"
will result into an output of
TASK [Example loop] ******************************************
ok: [localhost] => (item={u'key1': u'value_of_first_key'}) =>
msg: '{u''key1'': u''value_of_first_key''} is of type dict'
ok: [localhost] => (item={u'key2': u'value_of_second_key'}) =>
msg: '{u''key2'': u''value_of_second_key''} is of type dict'
ok: [localhost] => (item={u'key3': u'value_of_third_key'}) =>
msg: '{u''key3'': u''value_of_third_key''} is of type dict'
TASK [Example loop] ******************************************
ok: [localhost] => (item={u'key1': u'value_of_first_key'}) =>
msg:
- value_of_first_key
ok: [localhost] => (item={u'key2': u'value_of_second_key'}) =>
msg:
- value_of_second_key
ok: [localhost] => (item={u'key3': u'value_of_third_key'}) =>
msg:
- value_of_third_key
Further Readings
How to work with lists and dictionaries in Ansible
Extended loop variables

Ansible conditionals with with_items/nested

Given the following tasks:
- name: Gather security group info
amazon.aws.ec2_group_info:
filters:
"tag:vpn_ports": "*"
register: sec_group_info_output
- name: Extract security groups and ports
set_fact:
vpn_groups: "{{ vpn_groups + [{ 'group_id': item.group_id, 'ports': item.tags.vpn_ports.split(',') }] }}"
with_items:
- "{{ sec_group_info_output | json_query('security_groups') }}"
vars:
vpn_groups: []
when: sec_group_info_output != []
- name: Generate list with CIDRs
set_fact:
vpn_rules: "{{ vpn_rules + [{ 'group_id': item.0.group_id , 'port': item.1, 'cidr': item.2 }] }}"
with_nested:
- "{{ vpn_groups|subelements('ports') }}"
- "{{ cidr_ranges }}"
vars:
vpn_rules: []
when: sec_group_info_output != []
I am trying to skip the last two tasks if the first task returns an empty set.
My understanding is that the when conditional is evaluated for every loop, and not just for the task as a whole.
The below therefor gives me:
TASK [security_groups : Gather security group info] ****************************
ok: [localhost]
TASK [security_groups : Extract security groups and ports] *********************
TASK [security_groups : Generate list with CIDRs] ******************************
fatal: [localhost]: FAILED! => {"msg": "obj must be a list of dicts or a nested dict"}
PLAY RECAP *********************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=1 skipped=1 rescued=0 ignored=0
🚨 Error: The command exited with status 2
How would I go about fixing this error? I've tried putting |default([]) into my nested_items like below:
- name: Generate list with CIDRs
set_fact:
vpn_rules: "{{ vpn_rules + [{ 'group_id': item.0.group_id , 'port': item.1, 'cidr': item.2 }] |default([])}}"
with_nested:
- "{{ vpn_groups|subelements('ports') |default([])}}"
- "{{ cidr_ranges |default([])}}"
vars:
vpn_rules: []
when: sec_group_info_output != []
The error remains the same.
I've also tried putting both tasks in a block, but this had no effect and the error remains the same.
How would I be able to skip these tasks based on my condition?
Firstly, your condition is completely wrong. sec_group_info_output is a registered variable, so it can never be equal to an empty list. It will always be a dictionary containing information about the task execution. In order to have a chance of working as intended, it would need to be:
when: sec_group_info_output.security_groups != []
# more idiomatically, an empty list is false so you can just treat the value as a boolean
when: sec_group_info_output.security_groups
# or you can check the length
when: sec_group_info_output['security_groups'] | length > 0
However, in this case you don't need conditions at all. You're looping over the same list you're checking, and an empty loop will not execute any tasks. You just need a | default([]) in the loop definition on the third task in case the second didn't execute, and everything's fine.
- name: Gather security group info
amazon.aws.ec2_group_info:
filters:
"tag:vpn_ports": "*"
register: sec_group_info_output
- name: Extract security groups and ports
set_fact:
vpn_groups: "{{ vpn_groups | default([]) + [{ 'group_id': item.group_id, 'ports': item.tags.vpn_ports.split(',') }] }}"
loop: "{{ sec_group_info_output.security_groups }}"
- name: Generate list with CIDRs
set_fact:
vpn_rules: "{{ vpn_rules | default([]) + [{ 'group_id': item.0.0.group_id , 'port': item.0.1, 'cidr': item.1 }] }}"
loop: "{{ vpn_groups | default([]) | subelements('ports') | product(cidr_ranges) }}"
{{ vpn_groups | subelements('ports') | default([]) }} was headed in the right direction, but you put the default() in the wrong place. It needs to be before the subelements() filter so that that filter receives an empty list, not an undefined variable.

How do you iterate/parse through a CSV file to use in Ansible playbook?

I am new to Ansible and don't have much coding experience in general. I am trying to figure out how to provision RHEL servers using the DellEMC OpenManage modules.
The first step to this is figuring out how to parse a CSV, that we are putting necessary information for the later templating. (IP, hostname, MAC, etc. ...) I can get it to print the data in general, but cant figure out how to parse/iterate through it.
CSV Sample
server_name,idrac_ip,idrac_user,idrac_pwd,idrac_nw,idrac_gw,idrac_mac,mgmt_ip,mgmt_nw,mgmt_gw,mgmt_mac,bond0_100_ip,bond0_200_ip,dns_1,bond0_100_nw,bond0_100_gw,bond0_100_vip,bond0_200_nw,bond0_200_gw,bond0_200_vip,os,fs,sio_type,mdm_name
test1,1.1.1.10,root,Password1234,1.1.1.0/24,1.1.1.1,00:00:00:00:00:01,1.1.1.142,1.1.62.0/24,1.1.2.1,98:03:9B:46:25:B3,1.1.1.22,1.1.1.21,1.1.1.26,1.1.61.15/24,1.1.61.1,1.1.61.29,1.1.66.0/24,1.1.66.1,1.1.1.29,RHEL 7.6,1,Master,MDM-1
Here is my playbook to print the info in general.
---
- name: Parse
hosts: localhost
connection: local
tasks:
- name: Load CSV Data into object
read_csv:
path: 'Test_Lab_Servers.csv'
fieldnames: server_name,idrac_ip,idrac_user,idrac_pwd,idrac_nw,idrac_gw,idrac_mac,mgmt_ip,mgmt_nw,mgmt_gw,mgmt_mac,bond0_100_ip,bond0_200_ip,dns_1,bond0_100_nw,bond0_100_gw,bond0_100_vip,bond0_200_nw,bond0_200_gw,bond0_200_vip,os,fs,sio_type,mdm_name
delimiter: ','
register: csv_output
delegate_to: localhost
- name: Print data
debug:
msg: "{{ csv_output }}"
Any advice?
According the read_csv module parameter documentation fieldnames are
needed if the CSV does not have a header
only. Therefore you could remove it to get a list object which might be easier to parse.
- name: Print data
debug:
msg: "{{ csv_output.list }}"
As already mentioned within the comments, to iterate over the list elements you may use loop.
- name: Print data
debug:
msg: "{{ item }}"
loop: "{{ csv_output.list }}"
With extended loop variables you will have a better control about the loop output.
- name: Print data
debug:
msg: "{{ item }}"
loop: "{{ csv_output.list }}"
loop_control:
extended: yes
label: "{{ ansible_loop.index0 }}"
Resulting into an output of
TASK [Print data] ***************
ok: [localhost] => (item=0) =>
msg:
bond0_100_gw: 1.1.61.1
bond0_100_ip: 1.1.1.22
bond0_100_nw: 1.1.61.15/24
bond0_100_vip: 1.1.61.29
bond0_200_gw: 1.1.66.1
bond0_200_ip: 1.1.1.21
bond0_200_nw: 1.1.66.0/24
bond0_200_vip: 1.1.1.29
dns_1: 1.1.1.26
fs: '1'
idrac_gw: 1.1.1.1
idrac_ip: 1.1.1.10
idrac_mac: 00:00:00:00:00:01
idrac_nw: 1.1.1.0/24
idrac_pwd: Password1234
idrac_user: root
mdm_name: MDM-1
mgmt_gw: 1.1.2.1
mgmt_ip: 1.1.1.142
mgmt_mac: 98:03:9B:46:25:B3
mgmt_nw: 1.1.62.0/24
os: RHEL 7.6
server_name: test1
sio_type: Master
...
Specific items (fields) like server_name you could get simply by using
msg: "{{ item.server_name }}"
resulting into an output of
TASK [Print data] ************
ok: [localhost] => (item=0) =>
msg: test1
ok: [localhost] => (item=1) =>
msg: test2
ok: [localhost] => (item=2) =>
msg: test3
...

Issue with using Omit option

I am trying configure a attribute only if my_int_http is defined else I dont want it. So I coded it like below:
profiles:
- "{{ my_int_L4 }}"
- "{{ my_int_http | default(omit) }}"
However my execution fail and when check the arguments passed by the code in actual configuration it shows like below:
"profiles": [
"my_example.internal_tcp",
"__omit_place_holder__ef8c5b99e9707c044ac07fda72fa950565f248a4"
So how to pass absolutely no value where it is passing __omit_place_holder_****?
Q: "How to pass absolutely no value where it is passing omit_place_holder ?"
A1: Some filters also work with omit as expected. For example, the play
- hosts: localhost
vars:
test:
- "{{ var1|default(false) }}"
- "{{ var1|default(omit) }}"
tasks:
- debug:
msg: "{{ {'a': item}|combine({'b': true}) }}"
loop: "{{ test }}"
gives
msg:
a: false
b: true
msg:
b: true
As a sidenote, default(omit) is defined type string
- debug:
msg: "{{ item is defined }}"
loop: "{{ test }}"
- debug:
msg: "{{ item|type_debug }}"
loop: "{{ test }}"
give
TASK [debug] *************************************************************
ok: [localhost] => (item=False) =>
msg: true
ok: [localhost] => (item=__omit_place_holder__6e56f2f992faa6e262507cb77410946ea57dc7ef) =>
msg: true
TASK [debug] *************************************************************
ok: [localhost] => (item=False) =>
msg: bool
ok: [localhost] => (item=__omit_place_holder__6e56f2f992faa6e262507cb77410946ea57dc7ef) =>
msg: str
A2: No value in Ansible is YAML null. Quoting:
This is typically converted into any native null-like value (e.g., undef in Perl, None in Python).
(Given my_int_L4=bob). If the variable my_int_http defaults to null instead of omit
profiles:
- "{{ my_int_L4 }}"
- "{{ my_int_http | default(null) }}"
the list profiles will be undefined
profiles: VARIABLE IS NOT DEFINED!
Use None instead
profiles:
- "{{ my_int_L4 }}"
- "{{ my_int_http | default(None) }}"
The variable my_int_http will default to an empty string
profiles:
- bob
- ''
See also section "YAML tags and Python types" in PyYAML Documentation.
You can try something like this,
profiles:
- "{{ my_int_L4 }}"
- "{{ my_int_http | default(None) }}"
This will give you an empty string. And you can add a check while iterating over the profiles.
Please have a look at this GitHub Issue to get more understanding.

Ansible 'how to traverse over a list` to create a new list

I am trying to create a simple playbook task traversing over multiple dict items(v1) and create a new list var (list_var) with specific item in it, but it is not working list_var is only showing me one element, can anyone please suggest what am i missing?
if i do this:
set_fact:
list_var: "{{ v1.stdout }}"
with_items: "{{ v1.items }}"
Values in v1 are returned by simple shell output and have values from different hosts like hostname
Probably something list this?
- command: echo "{{ item }}"
register: v1
with_sequence: start=0 end=3
- set_fact:
list_var: "{{ list_var|default([]) }} + [ {{ item }} ]"
loop: "{{ v1.results | json_query('[].stdout') }}"
- debug: var=list_var
Which produces this list
TASK [debug] **********
ok: [localhost] => {
"list_var": [
0,
1,
2,
3
]
}

Resources