Ansible create list with 'with_items' - ansible

I want to create a new list through a loop. I created a task like this. Ansible 2.9.10
- name: "Generate list"
set_fact:
dst: "{{dst_list | default([])}} + [ '{{ item[0] }}' ]"
with_items:
- "{{failed_connections}}"
- debug: var=dst
"failed_connections": [
[
"1.1.1.1",
8888
],
[
"1.1.1.1",
8080
],
[
"2.2.2.2",
8888
],
[
"2.2.2.2",
8080
],
[
"2.2.2.2",
443
]
]
I end up only getting the last item in the list.
TASK [access-consul-kv : Generate list]
*********************************************************************************************
ok: [127.0.0.1] => (item=[u'1.1.1.1', 8888])
ok: [127.0.0.1] => (item=[u'1.1.1.1', 8080])
ok: [127.0.0.1] => (item=[u'2.2.2.2', 8888])
ok: [127.0.0.1] => (item=[u'2.2.2.2', 8080])
ok: [127.0.0.1] => (item=[u'2.2.2.2', 443])
TASK [access-consul-kv : debug] *****************************************************************************************************ok: [127.0.0.1] => {
"dst": [
"2.2.2.2"
]
}
How can I get a list of all addresses and preferably only unique ones?

In your task:
- name: "Generate list"
set_fact:
dst: "{{dst_list | default([])}} + [ '{{ item[0] }}' ]"
with_items:
- "{{failed_connections}}"
You're setting dst to the value of dst_list + some value. Since you never define dst_list, you always get the value from the default([]) expression, so your set_fact task actually looks like this:
set_fact:
dst: [ '{{ item[0] }}' ]
If you want to append to a list, you need to use the name of the variable you're setting with set_fact inside the set_fact expression, like this:
set_fact:
dst: "{{ dst|default([]) + [ item[0] ] }}"
Note that we only need one set of {{...}} markers here.
I will often avoid the use of the default filter by using a vars key to set the default value, like this:
- name: "Generate list"
set_fact:
dst: "{{ dst + [ item[0] ] }}"
with_items:
- "{{failed_connections}}"
vars:
dst: []
I find this makes the expression a little simpler.

How can I get a list of all addresses and preferably only unique ones?
To append the items in the list
dst: "{{ dst + item[0] }}"
To get a unique list:
- set_fact:
unique_list: "{{ dst | unique }}"
If that doesn't work, try to look at this example.

Related

Unique values from ansible output dict's

I have some servers with a lot of wordpress instances, who I ask them what versions they have.
- name: CONTADOR WP VERSIONES
shell: mycommand
register: wp_versions
- debug: msg: "{{ wp_versions.stdout_lines }}"
For example:
TASK [debug] *********************************************************************
ok: [server1] => {
"msg": [
"5.1.13"
]
}
ok: [server2] => {
"msg": [
"5.1.12",
"5.1.13"
]
}
ok: [server3] => {
"msg": [
"5.1.10",
"5.1.13",
]
}
I need to list a unique values like this:
"msg": [
"5.1.10",
"5.1.12",
"5.1.13",
]
I have tried all that i found but nothing works as I want.
Thanks
Use special variable ansible_play_hosts and extract the variables from the hostvars
- set_fact:
all_vers: "{{ ansible_play_hosts|
map('extract', hostvars, ['wp_versions', 'stdout_lines'])|
flatten|unique }}"
run_once: true
gives
all_vers:
- 5.1.13
- 5.1.12
- 5.1.10
You could do something like this:
- hosts: all
gather_facts: false
tasks:
- name: CONTADOR WP VERSIONES
shell: mycommand
register: wp_versions
- hosts: localhost
gather_facts: false
tasks:
# This tasks builds a flattened list of all the
# wp_versions.stdout_lines values collected from your hosts.
- name: Collect wp_versions information
set_fact:
all_wp_versions_pre: "{{ all_wp_versions_pre + hostvars[item].wp_versions.stdout_lines }}"
loop: "{{ groups.all }}"
vars:
all_wp_versions_pre: []
# Here we use the `unique` filter to produce a list of
# unique versions.
- name: Set all_wp_versions fact
set_fact:
all_wp_versions: "{{ all_wp_versions_pre|unique }}"
- debug:
var: all_wp_versions
Given you examples, this would produce the following output:
TASK [debug] ********************************************************************************************
ok: [localhost] => {
"all_wp_versions": [
"5.1.13",
"5.1.12",
"5.1.10"
]
}

Why is ansible creating a new host scoped list variables for each host?

I need to create a variable of ip addresses that are derived from the run state of all hosts. I expect to insert a new string value and have it appended to the list.
When I run the following ansible-playbook, it creates what looks to be a new list instance for each host instead of modifying the playbook level vars.
My understanding is the set_fact below should concatenate the lists and assign them back to the play scoped var ip_list_from_terraform. Why am I getting host scoped results?
---
- name: Bootstrap nodes
hosts: all
become: true
gather_facts: true
vars:
ip_list_from_terraform: ['Verify']
tasks:
- name: assign a list of all the physical network private IPs
set_fact:
ip_list_from_terraform: "{{ ip_list_from_terraform + [ item ] }}"
with_items: " {{ hostvars[inventory_hostname]['ansible_' + ansible_default_ipv4['interface']]['ipv4']['address'] }} "
register: ip_list_from_terraform_list
- name: Debug global var
debug:
var: ip_list_from_terraform
- name: Debug register result
debug:
var: ip_list_from_terraform_list
Expected:
ok: [shrimp-master-0] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.41",
"10.0.2.172",
"10.0.2.33",
"10.0.2.215",
"10.0.2.131",
"10.0.2.168",
"10.0.2.118"
]
}
Actual:
TASK [Debug global var] ********************************************************************************************************************************************************************************************************************
ok: [shrimp-master-0] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.12"
]
}
ok: [shrimp-master-1] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.33"
]
}
ok: [shrimp-master-2] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.215"
]
}
ok: [shrimp-worker-0-super-wallaby] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.131"
]
}
ok: [shrimp-gpu-worker-0-settled-wombat] => {
"ip_list_from_terraform": [
"Verify",
"10.0.2.151"
]
}
Let's simplify the case. Given the inventory
shell> cat hosts
host1 test_ip=10.0.2.41
host2 test_ip=10.0.2.172
host3 test_ip=10.0.2.33
the playbook
- hosts: host1,host2,host3
vars:
ip_list_from_terraform: ['Verify']
tasks:
- set_fact:
ip_list_from_terraform: "{{ ip_list_from_terraform + [ item ] }}"
with_items: "{{ hostvars[inventory_hostname]['test_ip'] }}"
- debug:
var: ip_list_from_terraform
gives
ok: [host2] =>
ip_list_from_terraform:
- Verify
- 10.0.2.172
ok: [host1] =>
ip_list_from_terraform:
- Verify
- 10.0.2.41
ok: [host3] =>
ip_list_from_terraform:
- Verify
- 10.0.2.33
As a side-note, with_items is needed because the argument is a string. loop would crash with the error 'Invalid data passed to ''loop'', it requires a list,.
Q: "set_fact should concatenate the lists"
A: Run once and loop ansible_play_hosts. For example
- set_fact:
ip_list_from_terraform: "{{ ip_list_from_terraform +
[hostvars[item]['test_ip']] }}"
loop: "{{ ansible_play_hosts }}"
run_once: true
gives
ip_list_from_terraform:
- Verify
- 10.0.2.41
- 10.0.2.172
- 10.0.2.33

All combination of 2 lists in ansible

I have 2 variables as a list in ansible
host_list:
- 1.1.1.1
- 2.2.2.2
port_list:
- 443
- 80
and I want to get the 3rd variable as a list of lists:
all_comb = [[1.1.1.1, 443], [1.1.1.1, 80], [2.2.2.2, 443], [2.2.2.2, 80]]
How can i get it in Ansible?
Use product filter
all_comb: "{{ host_list|product(port_list) }}"
gives
all_comb:
- [1.1.1.1, 443]
- [1.1.1.1, 80]
- [2.2.2.2, 443]
- [2.2.2.2, 80]
you can use the with_nested or a query('nested', listA, listB) loop, see both implementations below:
with_nested:
- name: merge lists
set_fact:
merged_list: "{{ merged_list|default([]) + [item] }}"
with_nested:
- "{{ host_list }}"
- "{{ port_list }}"
- name: display result
debug:
var: merged_list
with query:
- name: merge lists
set_fact:
merged_list: "{{ merged_list|default([]) + [item] }}"
loop: "{{ query('nested', host_list, port_list) }}"
- name: display result
debug:
var: merged_list
result of the latter:
PLAY [localhost] ******************************************************************************************************************************************************************************************************
TASK [merge lists] ****************************************************************************************************************************************************************************************************
ok: [localhost] => (item=[u'1.1.1.1', 443])
ok: [localhost] => (item=[u'1.1.1.1', 80])
ok: [localhost] => (item=[u'2.2.2.2', 443])
ok: [localhost] => (item=[u'2.2.2.2', 80])
TASK [display result] *************************************************************************************************************************************************************************************************
ok: [localhost] => {
"merged_list": [
[
"1.1.1.1",
443
],
[
"1.1.1.1",
80
],
[
"2.2.2.2",
443
],
[
"2.2.2.2",
80
]
]
}
cheers

Processing embedded lists inside dict variables in Ansible

I got the example code in Ansible:
- name: testing
hosts: localhost
vars:
svc:
https: ['tcp/443']
app_svc: ['tcp/5543', 'udp/5543', 'tcp/3100']
tasks:
- name: print
debug:
msg: port={{item.key}} value={{item.value}}
with_dict:
- "{{svc}}"
And this outputs to:
ok: [127.0.0.1] => (item=None) => {
"msg": "port=app_svc value=[u'tcp/5543', u'udp/5543', u'tcp/3100']"
}
ok: [127.0.0.1] => (item=None) => {
"msg": "port=https value=[u'tcp/443']"
}
What I would like to achieve is, when there is more than one element in list of values it would split like this:
- name=https, prot=tcp, port=443
- name=app_svc, prot=tcp, port=5543
- name=app_svc, prot=udp, port=5543
- name=app_svc, prot=tcp, port=3100
with_dict stanza only displays me a whole list and I couldn't find a way do do it differently. Is it possible to do it like that without reorganizing the var section? Thanks in advance for input.
To see the syntax errors run
# ansible-lint <YOUR-PLAYBOOK>
Correct syntax should be
- hosts: localhost
gather_facts: no
vars:
svc:
https: ['tcp/443']
app_svc: ['tcp/5543', 'udp/5543', 'tcp/3100']
tasks:
- name: print
debug:
msg: "port={{ item.key }} value={{ item.value }}"
with_dict: "{{ svc }}"
gives
"msg": "port=app_svc value=[u'tcp/5543', u'udp/5543', u'tcp/3100']"
"msg": "port=https value=[u'tcp/443']"
Change the loop
- name: print
debug:
msg: "name={{ item.0.key }},
prot={{ item.1.split('/')[0] }},
port={{ item.1.split('/')[1] }}"
loop: "{{ lookup('subelements', svc|dict2items, 'value') }}"
to get the format
"msg": "name=app_svc, prot=tcp, port=5543"
"msg": "name=app_svc, prot=udp, port=5543"
"msg": "name=app_svc, prot=tcp, port=3100"
"msg": "name=https, prot=tcp, port=443"
dict2items is available since version 2.6 . Without "dict2items" transform the data first. See below (not tested).
https:
- {key: 'https', value: ['tcp/443']}
- {key: 'app_svc', value: ['tcp/5543', 'udp/5543', 'tcp/3100']}

Is it possible to Loop with with_dict and with_nested together using Ansible Playbook?

I am trying to loop over a hash and arrays associated with it like this:
Var:
dictionary:
aword: [ ant, den ]
bword: [ fat, slim ]
Task:
name: Create symlinks
command: do something with item[0] over item[1]
with_nested:
- "{{ item.key }}"
- "{{ item.value }}"
with_dict: dictionary
This does not work. Am I doing something wrong or Ansible doesn't support such iteration?
I solved this one using
with_subelements
like this
vars:
dictionary:
- name: aword
words:
- ant
- den
- name: bword
words:
- fat
- slim
Task:
name: Create symlinks
command: do something with item.0.name over item.1
with_subelements:
- dictionary
- words
For completeness, this can also be accomplished using with_nested by creating nested lists in Ansible. Doing so allows the same looping behavior without requiring setting a var/fact. This is useful for when you want to instantiate the nested lists on the task itself.
Like so:
---
- hosts: localhost
connection: local
become: false
tasks:
- debug:
msg: "do something with {{ item[0] }} over {{ item[1] }}"
with_nested:
- - ant # note the two hyphens to create a list of lists
- den
- - fat
- slim
Output:
TASK [debug] ********************************************************************************************
ok: [localhost] => (item=[u'ant', u'fat']) => {
"changed": false,
"item": [
"ant",
"fat"
],
"msg": "do something with ant over fat"
}
ok: [localhost] => (item=[u'ant', u'slim']) => {
"changed": false,
"item": [
"ant",
"slim"
],
"msg": "do something with ant over slim"
}
ok: [localhost] => (item=[u'den', u'fat']) => {
"changed": false,
"item": [
"den",
"fat"
],
"msg": "do something with den over fat"
}
ok: [localhost] => (item=[u'den', u'slim']) => {
"changed": false,
"item": [
"den",
"slim"
],
"msg": "do something with den over slim"
}

Resources