Ansible - Appending to lists and dictionaries
In this blog post I'll show how to add items to lists and dictionaries, when using loops, and across tasks.
Normally when trying to add a new item to the variable, while in the loop, or between tasks, Ansible will ovewrite the values, keeping the result of the last iteration.
For example, let's see what will be the result of running the following playbook:
---
- name: Append to list
hosts: localhost
vars:
cisco:
- CiscoRouter01
- CiscoRouter02
- CiscoRouter03
- CiscoSwitch01
arista:
- AristaSwitch01
- AristaSwitch02
- AristaSwitch03
tasks:
- name: Add Cisco and Airsta devices to the list
set_fact:
devices: "{{ item }}"
with_items:
- "{{ cisco }}"
- "{{ arista }}"
- name: Debug list
debug:
var: devices
verbosity: 0
[przemek@quasar blog]$ ansible-playbook append_list.yml
PLAY [Append to list] ************************************************************************************************************
TASK [Gathering Facts] ***********************************************************************************************************
ok: [localhost]
TASK [Add Cisco and Airsta devices to the list] **********************************************************************************
ok: [localhost] => (item=CiscoRouter01)
ok: [localhost] => (item=CiscoRouter02)
ok: [localhost] => (item=CiscoRouter03)
ok: [localhost] => (item=CiscoSwitch01)
ok: [localhost] => (item=AristaSwitch01)
ok: [localhost] => (item=AristaSwitch02)
ok: [localhost] => (item=AristaSwitch03)
TASK [Debug list] *********************************************************************************************************
ok: [localhost] => {
"devices": "AristaSwitch03"
}
PLAY RECAP ***********************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0
Variable "devices" holds just one value: "AristaSwitch03".
To make Ansible append to the list, instead of ovewriting the last value, we'll make a few modification.
We will initiate "devices" var, assigning an empty list to it. We will also change the assignment expression to make Ansible append to this variable.
Our playbook after modifications:
---
- name: Append to list
hosts: localhost
vars:
devices: []
cisco:
- CiscoRouter01
- CiscoRouter02
- CiscoRouter03
- CiscoSwitch01
arista:
- AristaSwitch01
- AristaSwitch02
- AristaSwitch03
tasks:
- name: Add Cisco and Airsta devices to the list
set_fact:
devices: "{{ devices + [item] }}"
with_items:
- "{{ cisco }}"
- "{{ arista }}"
- name: Debug list
debug:
var: devices
verbosity: 0
Now, when we run this playbook, we will see that all of the items from two separate lists are appended to our newly defined list "devices". Just as we wanted:
[przemek@quasar blog]$ ansible-playbook append_list_v2.yml
PLAY [Append to list] ************************************************************************************************************
TASK [Gathering Facts] ***********************************************************************************************************
ok: [localhost]
TASK [Add Cisco and Airsta devices to the list] **********************************************************************************
ok: [localhost] => (item=CiscoRouter01)
ok: [localhost] => (item=CiscoRouter02)
ok: [localhost] => (item=CiscoRouter03)
ok: [localhost] => (item=CiscoSwitch01)
ok: [localhost] => (item=AristaSwitch01)
ok: [localhost] => (item=AristaSwitch02)
ok: [localhost] => (item=AristaSwitch03)
TASK [Debug list] *********************************************************************************************************
ok: [localhost] => {
"devices": [
"CiscoRouter01",
"CiscoRouter02",
"CiscoRouter03",
"CiscoSwitch01",
"AristaSwitch01",
"AristaSwitch02",
"AristaSwitch03"
]
}
PLAY RECAP ***********************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0
Similar concept applies to combining dictionaries. Here we assign an empty dictionary to the newly initiated variable. In the assignment expression we will need to use filter "combine" to add new key-value pairs to the existing dictionary.
A working example can be found below:
---
- name: Append to dictionary
hosts: localhost
vars:
devices: {}
cisco:
- CiscoRouter01
- CiscoRouter02
- CiscoRouter03
- CiscoSwitch01
arista:
- AristaSwitch01
- AristaSwitch02
- AristaSwitch03
tasks:
- name: Add Cisco devices to the dictionary
set_fact:
devices: "{{ devices | combine({item: 'Cisco'}) }}"
with_items: "{{ cisco }}"
- name: Add Arista devices to the dictionary
set_fact:
devices: "{{ devices | combine({item: 'Arista'}) }}"
with_items: "{{ arista }}"
- name: Debug dictionary
debug:
var: devices
verbosity: 0
And the result will be a dictionary, where we assign a vendor to each of the keys, where keys are device hostnames taken from two separate lists.
[przemek@quasar blog]$ ansible-playbook append_dict_v2.yml
PLAY [Append to dictionary] ******************************************************************************************************
TASK [Gathering Facts] ***********************************************************************************************************
ok: [localhost]
TASK [Add Cisco devices to the dictionary] ***************************************************************************************
ok: [localhost] => (item=CiscoRouter01)
ok: [localhost] => (item=CiscoRouter02)
ok: [localhost] => (item=CiscoRouter03)
ok: [localhost] => (item=CiscoSwitch01)
TASK [Add Arista devices to the dictionary] **************************************************************************************
ok: [localhost] => (item=AristaSwitch01)
ok: [localhost] => (item=AristaSwitch02)
ok: [localhost] => (item=AristaSwitch03)
TASK [Debug dictionary] *********************************************************************************************************
ok: [localhost] => {
"devices": {
"AristaSwitch01": "Arista",
"AristaSwitch02": "Arista",
"AristaSwitch03": "Arista",
"CiscoRouter01": "Cisco",
"CiscoRouter02": "Cisco",
"CiscoRouter03": "Cisco",
"CiscoSwitch01": "Cisco"
}
}
PLAY RECAP ***********************************************************************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0
This is a very handy technique, especially when combining the results from multiple tasks. Instead of working with multiple variables, we can, for instance, assign task outputs to keys in one dictionary. After we're done with our tasks we can pass this dictionary to a Jinja2 template, or just dump its contents on the command line.
You can get full listings of the playbooks from my github repository:
https://github.com/progala/ttl255.com/tree/master/ansible/appending-to-lists-and-dictionaries