Ansible list of lists - flattening - ansible

I am using a set_fact in a playbook to gather data using a regex_findall(). I'm pulling out two groups with the regex and the ending result becomes a list of lists.
set_fact: nestedList="{{ myOutput.stdout[0] | regex_findall('(.*?)\n markerText(.*)')}}"
A example dump of the list looks like:
[[a,b],[c,d],[e,f],[g,h]]
I need to iterate through the parent list, and take the two parts of each sub-list and use them together. I tried with_items, and with_nested but do not get the results I'm looking for.
Using the example above, in one loop pass I need to work with 'a' and 'b'. An example could be item.0 = 'a' and item.1 = 'b'. On the next loop pass, item.0 = 'c' and item.1 = 'd'.
I can't seem to get it correct when it is a list of lists like that.
If I take the list above and just output it, the 'item' iterates through every item in all of the sub-lists.
- debug:
msg: "{{ item }}"
with_items: "{{ nestedList }}"
The result from this looks like:
a
b
c
d
e
f
g
h
How can I iterate through the parent list, and use the items in the sub-lists?

You want to use with_list instead of with_items.
with_items forcefully flattens nested lists, while with_list feeds argument as is.
---
- hosts: localhost
gather_facts: no
vars:
nested_list: [[a,b],[c,d],[e,f],[g,h]]
tasks:
- debug: msg="{{ item[0] }} {{ item[1] }}"
with_list: "{{ nested_list }}"

Related

Ansibe: concatenation of items from with_items

I'm trying to get a variable which will contain comma separated items from with_itmes loop as follow:
- hosts: localhost
connection: local
gather_facts: no
tasks:
- name: set_fact
set_fact:
foo: "{{ foo }}{{ item }},"
with_items:
- "one"
- "two"
- "three"
vars:
foo: ""
- name: Print the var
debug:
var: foo
It works as expected but what I'm getting at the end is trailing comma.
Is there any way to remove it?
There is a join filter that we can use with lists to concatenate list elements with a given character.
If we are passing the list directly to with_items or loop then we can use loop_control to "extend" some more loop information to get ansible_loop.allitems. Then this can be joined with the join filter.
Example:
- set_fact:
foo: "{{ ansible_loop.allitems|join(',') }}"
loop:
- one
- two
- three
loop_control:
extended: true
Otherwise a more straightforward way is to define a variable with list and use join filter on elements of that variable.
Example:
- set_fact:
foo: "{{ mylist|join(',') }}"
vars:
mylist:
- one
- two
- three
No clue if this is correct way to do but it does the job:
- name: Print the var
debug:
msg: "LIST: {{ foo | regex_replace(',$','') }}"

Exclude the three first items of a sorted list

How can I print a sorted list excluding its three first items?
Below is my Ansible playbook:
- set_fact:
filesDel: "{{ filesDel|default({})|
combine({item.NameOfFile: findFiles.files|
sort(attribute='mtime', reverse = true)|
map(attribute='path')|
select('search', item.NameOfFile)|
list}) }}"
with_items:
- "{{ fileList }}"
I tried this, but it is not working
- debug:
msg: "{{ item.value[0:-3] }}"
with_dict:
- "{{ filesDel }}"
loop_control:
label: "{{ item.key }}"
When I removed [0:-3], I do get the whole list of data grouped by file name, for instance:
ok: [142.20.10.15] => (item=fileName.png) => {
"msg": [
"/filePathA/fileName.png.25751.2020-08-31#19:30:59~",
"/filePathB/fileName.png.25752.2020-08-31#19:30:59~",
"/filePathB/fileName.png.25751.2020-08-30#22:30:59~",
"/filePathB/fileName.png.2222.2020-08-31#19:30:59~",
"/filePathB/fileName.png.2222.2020-08-31#19:30:59~",
"/filePathA/fileName.png.2222.2020-08-30#22:30:59~"
]
}
When I add [0:-3], I get:
ok: [142.20.10.15] => (item=fileName.png) => {
"msg": [
"/filePathA/fileName.png.25751.2020-08-31#19:30:59~",
"/filePathB/fileName.png.25752.2020-08-31#19:30:59~",
"/filePathB/fileName.png.25751.2020-08-30#22:30:59~"
]
}
This not correct as those files are the first 3 files.
What I want is to exclude those 3 files, so, I should get:
ok: [142.20.10.15] => (item=fileName.png) => {
"msg": [
"/filePathB/fileName.png.2222.2020-08-31#19:30:59~",
"/filePathB/fileName.png.2222.2020-08-31#19:30:59~",
"/filePathA/fileName.png.2222.2020-08-30#22:30:59~"
]
}
What am I doing wrong here?
Basically, the files are sorted based on the time they were created.
The slicing syntax of Python that you can use in Ansible works like this:
[start:end:step]
For reference, see https://docs.python.org/3/library/functions.html#slice
In this syntax, if you use a negative value, the reference is not the beginning of the list but the end.
Still, in this syntax, any element can be left empty, where [:] would actually means from the beginning to the end of the list.
So your actual trial [0:-3] means that you want
the items from the first element of the list (start: 0)
until the end - 3 of the list (end: -3)
If you want all the element except the three first then, you need
the items from the third element of the list (start: 3)
until the end of the list (end: – so it stays empty)
So your correct slice notation is [3:].
Given the task:
- debug:
msg: "{{ filesDel[3:] }}"
vars:
filesDel:
- "/filePathA/fileName.png.25751.2020-08-31#19:30:59~"
- "/filePathB/fileName.png.25752.2020-08-31#19:30:59~"
- "/filePathB/fileName.png.25751.2020-08-30#22:30:59~"
- "/filePathB/fileName.png.2222.2020-08-31#19:30:59~"
- "/filePathB/fileName.png.2222.2020-08-31#19:30:59~"
- "/filePathA/fileName.png.2222.2020-08-30#22:30:59~"
This yields the list:
- /filePathB/fileName.png.2222.2020-08-31#19:30:59~
- /filePathB/fileName.png.2222.2020-08-31#19:30:59~
- /filePathA/fileName.png.2222.2020-08-30#22:30:59~

Double with_items loop in ansible

I want to create a double loop in ansible.
I have one things like this :
userslist:
- name: user1
primary : user1-group
groups :
- group1
- group2
- name: user2
primary : user2-group
groups :
- group3
- group4
- name : Creating Secondary group
group :
name : "{{ item.groups }}"
state: present
with_items: "{{ userslist}}"
Is it possible for each users create each secondary group ?
I think for this i need to do a second with_items loop but i don't know how
There are two ways to make a nested (double) loop in Ansible.
with_nested. It allows you to have an inner iteration for object you iterate in the outer loop. Examples and explanation are provided in the official documentation: https://docs.ansible.com/ansible/2.5/plugins/lookup/nested.html
using with_items together with include_tasks. This is a complicated yet powerful construction. In theory there is no limit (except for the stack depth) on how nested this construction can be.
It requires to put inner code into a separate tasklist. I'll show it with outer.yaml and inner.yaml, outer perform loop over a list, and inner perform a loop over item (loop variable) of the outer loop.
outer.yaml:
- name: Loop over foo
include_tasks: inner.yaml
with_items: '{{ foo }}'
loop_control:
loop_var: inner_var_name
vars:
foo:
- [1, 2, 3]
- [a, b, c]
inner.yaml:
- name: Performing operation one
debug: msg="Action one for {{ item }}"
with_items: '{{ inner_var_name }}'
- name: Performing operation two
debug: msg="Action two for {{item}}"
with_items: '{{ inner_var_name }}'
The key advantage of this method is that inner.yaml can contain any number of statements, all of which will be processed in a loop from outer.yaml.
One important thing: all include things require a bit of caution with anything related to passing values (set_fact, register, etc) from included code. In is rather tricky and non-obvious, so my advice is never use variables set in include code outside of that inclusion.
I do this and it's work very well
---
- hosts: all
become: yes
vars:
userslist:
- name: user1
primary : user1-group
groups :
- group1
- group2
- name: user2
primary : user2-group
groups :
- group3
- group4
tasks:
- name: Creating Secondary group
group:
name="{{ item.1 }}"
with_subelements:
- '{{ userslist }}'
- groups

Ansible - Map an array of objects to a different array of objects

Is there a way to map an array of objects in Ansible Playbook to a different array of objects? Let's say we have a source array being:
arr:
- value: a
- value: b
- value: c
And what we want is to get a different array based on objects in the first array, let's say:
arr2:
- const: 1
var: a
- const: 1
var: b
- const: 1
var: c
This would be doable by an element template of:
const: 1
var: {{ value }}
Is there a way to apply such a template to every element in an array? I haven't found an appropriate map filter, as lookup('template', ...) cannot be used inside map.
Based on your answer (I must say it opened my eyes and I can't find words that tell the infinite gratitude I feel) I worked out what I think is a slightly more elegant solution.
I try to avoid the set_facts module because the result will have a rather high precedence. I prefer to stick to role defaults and host and group inventory variables.
Also, I am more used to jinja2 templating than Ansible filters.
- hosts: localhost
gather_facts: no
vars:
arr:
- value: a
- value: b
- value: c
arr2: "{{ lookup('template', 'template.yaml.j2') | from_yaml }}"
tasks:
- debug:
var: "arr2"
And the very template.yaml.j2 file will contain the iteration:
{% for item in arr %}
- const: 1
var: {{ item.value }}
{% endfor %}
This opens the door for really crazy variable manipulation while keeping the playbook pretty simple.
Hope it helps somebody as much as it helped me!
As Konstantin Suvorov mentioned in the comment it can be done using recursive array building. This is how I did it:
#role test
---
- hosts: localhost
gather_facts: no
vars:
arr:
- value: a
- value: b
- value: c
tasks:
- set_fact:
arr2: "{{ (arr2 | default([])) + [ lookup('template', 'template.yaml.j2') | from_yaml ] }}"
with_items: "{{ arr }}"
- debug:
msg: "{{ arr2 }}"
#template.yaml.j2
const: 1
var: {{ item.value }}

How to loop over a dict and a list together in ansible?

Say I have two variable like these:
dict:
key1: val1
key2: val2
list:
- item1
- item2
Can I loop over these two variables like below?
- shell: echo {{ item.0.key }} {{ item.1 }}
with_dict: "{{ dict }}"
with_items: "{{ list }}"
I have no idea to loop over these two variables together and don't wanna change the data type of the variables. Is there any way to achieve this kind of loop in ansible?
The combine filter that is introduced in Ansible 2.0 appears to be the closest thing to what you need, but I don't know if it will merge a dict & a list or just two dicts.
Your might need to write your own custom lookup plugin in order to merge these two different variable types.

Resources