If you really want to append to content, you will need to use the set_fact module. But if you just want to use the merged lists it is as easy as this:
{{ list1 + list2 }}
With set_fact it would look like this:
- set_fact:
list_merged: "{{ list1 + list2 }}"
NOTE: If you need to do additional operations on the concatenated lists be sure to group them like so:
- set_fact:
list_merged: "{{ (list1 + list2) | ... }}"
Answer from udondan on Stack OverflowIf you really want to append to content, you will need to use the set_fact module. But if you just want to use the merged lists it is as easy as this:
{{ list1 + list2 }}
With set_fact it would look like this:
- set_fact:
list_merged: "{{ list1 + list2 }}"
NOTE: If you need to do additional operations on the concatenated lists be sure to group them like so:
- set_fact:
list_merged: "{{ (list1 + list2) | ... }}"
The following worked for me with Ansible 2.1.2.0:
- name: Define list of mappings
set_facts:
something:
- name: bla
mode: 1
- name: Append list with additional mapping
set_facts:
something: "{{ something + [{'name': 'blabla', 'mode': 1}] }}"
Appending an item to a list
python - Append a list with loop in Ansible - Stack Overflow
Append a string to a List in Ansible - Stack Overflow
ansible - cannot append into a list in a with_items loop - Stack Overflow
- set_fact:
items_list: "{{ items_list | default(['item1']) + [item] }}"
loop:
- "{{ (True and True) | ternary('item2', None) }}"
- "{{ (True) | ternary('item3', None) }}"
when: item
- debug: var=items_list
or
- set_fact:
items_list: "{{ items_list | default(['item1']) + [item] }}"
loop:
- "item2"
- "item3"
when: >-
(item == "item2" and True and True) or
(item == "item3" and True)
- debug: var=items_list
The use-case is adding an item to a list if some variables are defined. The best structure is a dictionary, e.g.
d1:
item1:
l: []
item2:
l: [condition1, condition3]
item3:
l: [condition4]
If the variable condition4 is defined but others are not the task below
- set_fact:
items_list: "{{ items_list|default([]) + [item.key] }}"
loop: "{{ d1|dict2items }}"
when: _vals|from_yaml is all
vars:
_vals: |
[
{% for i in item.value.l %}
{{ vars[i] is defined }},
{% endfor %}
]
gives
items_list:
- item1
- item3
You can merge two lists in a variable with +.
Say you have a group_vars file with this content:
---
# group_vars/all
pgsql_extensions:
- ext1
- ext2
- ext3
And it's used in a template pgsql.conf.j2 like:
# {{ ansible_managed }}
pgsql_extensions={% for item in pgsql_extensions %}{{ item }}, {% endfor %}
You can then append extensions to the testing database servers like this:
---
# group_vars/testing_db
append_exts:
- ext4
- ext5
pgsql_extensions: "{{ pgsql_extensions + append_exts }}"
When the role is run in any of the testing servers, the aditional extensions will be added.
I'm not sure this works for dictionaries as well, and also be careful with spaces and leaving a dangling comma at the end of the line.
Since Ansible v2.x you can do these:
# use case I: appending to LIST variable:
- name: my appender
set_fact:
my_list_var: '{{my_list_myvar + new_items_list}}'
# use case II: appending to LIST variable one by one:
- name: my appender
set_fact:
my_list_var: '{{my_list_var + [item]}}'
with_items: '{{my_new_items|list}}'
# use case III: appending more keys DICT variable in a "batch":
- name: my appender
set_fact:
my_dict_var: '{{my_dict_var|combine(my_new_keys_in_a_dict)}}'
# use case IV: appending keys DICT variable one by one from tuples
- name: setup list of tuples (for 2.4.x and up
set_fact:
lot: >
[('key1', 'value1',), ('key2', 'value2',), ..., ('keyN', 'valueN',)],
- name: my appender
set_fact:
my_dict_var: '{{my_dict_var|combine({item[0]: item[1]})}}'
with_items: '{{lot}}'
# use case V: appending keys DICT variable one by one from list of dicts (thanks to @ssc)
- name: add new key / value pairs to dict
set_fact:
my_dict_var: "{{ my_dict_var | combine({item.key: item.value}) }}"
with_items:
- { key: 'key01', value: 'value 01' }
- { key: 'key02', value: 'value 03' }
- { key: 'key03', value: 'value 04' }
all the above is documented in: https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#combining-hashes-dictionaries
I just want to do something very simple: I want to store a new list that combines other values. I tried with
port_mapping:
- "10025:25"
- "10143:143"
published_ports: "{{ port_mapping + ["{{ another_port }}:80"] }}"as well as with
published_ports: "{{ mailserver_port_mapping.concat("{{ another_port }}:80") }}"But obviously I'm misunderstanding examples like this.
Maybe it's not as intuitive as I thought.
You can do it like this:
- name: create new list for tunnel
set_fact:
list_tunnel: "{{ list_tunnel | default([]) + ['tunnel.' + item | string] }}"
loop: "{{ range(1,10) | list}}"
- debug:
msg: "{{ list_tunnel }}"
or
- set_fact:
list_tunnel: "{{ result }}"
vars:
prefix: tunnel
a_list: "{{ range(1, 10) | list }}"
result: "{{ [prefix] | product(a_list) | map('join', '.') | list }}"
Create the list you want to add
list_tunnel_add: "{{ ['tunnel']|product(range(1,10))|
map('join','.')|
list }}"
gives
list_tunnel_add:
- tunnel.1
- tunnel.2
- tunnel.3
- tunnel.4
- tunnel.5
- tunnel.6
- tunnel.7
- tunnel.8
- tunnel.9
Then concatenate the lists
list_tunnel_new: "{{ list_tunel + list_tunnel_add }}"
Example of a complete playbook
- hosts: localhost
vars:
list_tunel: []
list_tunnel_add: "{{ ['tunnel']|product(range(1,10))|
map('join','.')|
list }}"
list_tunnel_new: "{{ list_tunel + list_tunnel_add }}"
tasks:
- debug:
var: list_tunnel_new
gives
list_tunnel_new:
- tunnel.1
- tunnel.2
- tunnel.3
- tunnel.4
- tunnel.5
- tunnel.6
- tunnel.7
- tunnel.8
- tunnel.9
Q: "Prepend a string 'delete' in front of each item."
A: Is this what you're looking for?
- hosts: localhost
vars:
info: ['a', 'b', 'c']
tasks:
- set_fact:
payload: "{{ payload|default([]) + ['delete ' ~ item] }}"
loop: "{{ info }}"
- debug:
var: payload
gives (abridged)
payload:
- delete a
- delete b
- delete c
The same result can be achieved without iteration (credit @Romain DEQUIDT)
- hosts: localhost
vars:
info: ['a', 'b', 'c']
payload: "{{ ['delete']|product(info)|map('join', ' ')|list }}"
tasks:
- debug:
var: payload
Q: "Prepend a string 'delete' in front of each output that starts with 'gtm topology'."
A: Use the test search
- hosts: localhost
gather_facts: false
vars:
info: ['a', 'gtm topology', 'c']
tasks:
- set_fact:
payload: "{{ payload|default([]) + ['delete ' ~ item] }}"
loop: "{{ info }}"
when: item is search('^gtm topology')
- debug:
var: payload
gives (abridged)
payload:
- delete gtm topology
The same result can be achieved without iteration
- hosts: localhost
vars:
info: ['a', 'gtm topology', 'c']
info_gtm_topology: "{{ info|select('search', '^gtm topology') }}"
payload: "{{ ['delete']|product(info_gtm_topology)|map('join', ' ')|list }}"
tasks:
- debug:
var: payload
Q: "In addition to the conditions above, process the list till 'test-pool1' is found."
A: Use the test search also to find the index of the last item
hosts: localhost
vars:
info: ['a', 'gtm topology 1', 'c', 'test-pool1', 'gtm topology 2', 'd']
stop_regex: '.*pool1.*'
stop_items: "{{ info|select('search', stop_regex) }}"
stop_index: "{{ info.index(stop_items.0) }}"
tasks:
- set_fact:
payload: "{{ payload|default([]) + ['delete ' ~ item] }}"
loop: "{{ info[:stop_index|int] }}"
when: item is search('^gtm topology')
- debug:
var: payload
gives (abridged)
payload:
- delete gtm topology 1
The same result can be achieved without iteration
- hosts: localhost
vars:
info: ['a', 'gtm topology 1', 'c', 'test-pool1', 'gtm topology 2', 'd']
stop_regex: '.*pool1.*'
stop_items: "{{ info|select('search', stop_regex) }}"
stop_index: "{{ info.index(stop_items.0) }}"
info_gtm_topology: "{{ info[:stop_index|int]|select('search', '^gtm topology') }}"
payload: "{{ ['delete']|product(info_gtm_topology)|map('join', ' ')|list }}"
tasks:
- debug:
var: payload
As a side note, you can create custom filters. See filter_plugins
shell> cat filter_plugins/list_search.py
import re
def list_search(l, x):
r = re.compile(x)
return list(filter(r.match, l))
def list_index(l, x, *i):
if len(i) == 0:
return l.index(x) if x in l else -1
elif len(i) == 1:
return l.index(x, i[0]) if x in l[i[0]:] else -1
else:
return l.index(x, i[0], i[1]) if x in l[i[0]:i[1]] else -1
class FilterModule(object):
''' Ansible filters for operating on lists '''
def filters(self):
return {
'list_index': list_index,
'list_search': list_search,
}
The play below
- hosts: localhost
vars:
info: ['a', 'gtm topology 1', 'c', 'test-pool1', 'gtm topology 2', 'd']
stop_regex: '.*pool1.*'
stop_items: "{{ info|list_search(stop_regex) }}"
stop_index: "{{ info|list_index(stop_items.0) }}"
info_gtm_topology: "{{ info[:stop_index|int]|select('search', '^gtm topology') }}"
payload: "{{ ['delete']|product(info_gtm_topology)|map('join', ' ')|list }}"
tasks:
- debug:
var: payload
gives the same result
payload:
- delete gtm topology 1
You can use the product filter to prefix or suffix items in a list:
---
- hosts: localhost
gather_facts: false
vars:
string: "prepend "
list: ["value1", "value2", "value3"]
tasks:
- name: "prefix"
set_fact:
prefix_list: "{{ ["prefix_"] | product(list) | map('join') }}"
- debug:
msg: "{{ prefix_list }}"
- name: "suffix"
set_fact:
suffix_list: "{{ list | product(["_suffix"]) | map('join') }}"
- debug:
msg: "{{ suffix_list }}"