When you're working with Ansible, it's inevitable that you'll deal with lists and dictionaries. After all, they are all part of YAML, which administrators use to create Ansible playbooks. In addition, Ansible uses lists and dictionaries to exchange data within processes and with third parties.
This article covers analyzing and using the data in lists and dictionaries, which is crucial for anything you want to do with Ansible.
[ Get started with IT automation with the Ansible Automation Platform beginner's guide. ]
What are lists?
Lists are the equivalent of an array, something used in many real programming languages (which Ansible is not).
The term "list" is self-explanatory, but here are some ways to represent lists:
vars:
bands:
- The Beatles
- Led Zeppelin
- The Police
- Rush
bands2: ['The Beatles', 'Led Zeppelin', 'The Police', 'Rush']
The values bands and bands2 are equivalent.
Lists are indexed by numbers (starting with zero). So if I want to use the first entry, bands, I use bands[0]. The second element is bands[1] and so forth. Later on, I will discuss methods to inspect, compare, and loop through lists.
[ Learn how to automate everything with Ansible Automation Platform. ]
What are dictionaries?
Dictionaries are the equivalent of hashes. They differ from a list because they are keyed using a string, not a number.
Here is one way to define a simple dictionary:
vars:
rockers:
drums: John Bonham
bass: John Paul Jones
guitar: Jimmy Page
vocals: Robert Plant
If I want to point to a specific entry, I can use the bracket notation rockers['drums'] to get the "John Bonham" string.
In some places, you may find dot notation, like rockers.drums, but this is not recommended. According to the Ansible documentation, "dot notation can cause problems because some keys collide with attributes and methods of Python dictionaries."
Work with lists
The following playbook contains two predefined hardcoded lists.
In more realistic scenarios, lists would come either from group_vars or from calls to Ansible modules.
---
- name: Lists
hosts: localhost
gather_facts: no
vars:
bands:
- The Beatles
- Led Zeppelin
- The Police
- Rush
bands2: ['The Beatles', 'Led Zeppelin', 'The Police', 'Rush']
tasks:
- name: T01 - List bands 1
ansible.builtin.debug:
msg: "{{ bands }}"
- name: T02 - List bands 2
ansible.builtin.debug:
msg: "{{ bands2 }}"
- name: T03 - Print specific element
ansible.builtin.debug:
msg: "{{ bands[0] }}"
- name: T04 - Process list using a loop
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ bands }}"
- name: T05 - Add item to bands2
ansible.builtin.set_fact:
bands2: "{{ bands2 + ['Rolling Stones'] }}"
- name: T06 - Difference between bands2 and bands
ansible.builtin.debug:
msg: "{{ bands2 | difference(bands) }}"
- name: T07 - Show the data type of a list
ansible.builtin.debug:
msg: "{{ bands | type_debug }}"
...
This is the result of running this playbook:
PLAY [Lists] ******************************************************************
TASK [T01 - List bands 1] *****************************************************
ok: [localhost] => {
"msg": [
"The Beatles",
"Led Zeppelin",
"The Police",
"Rush"
]
}
TASK [T02 - List bands 2] *****************************************************
ok: [localhost] => {
"msg": [
"The Beatles",
"Led Zeppelin",
"The Police",
"Rush"
]
}
TASK [T03 - Print specific element] *******************************************
ok: [localhost] => {
"msg": "The Beatles"
}
TASK [T04 - Process list using a loop] ****************************************
ok: [localhost] => (item=The Beatles) => {
"msg": "The Beatles"
}
ok: [localhost] => (item=Led Zeppelin) => {
"msg": "Led Zeppelin"
}
ok: [localhost] => (item=The Police) => {
"msg": "The Police"
}
ok: [localhost] => (item=Rush) => {
"msg": "Rush"
}
TASK [T05 - Add item to bands2] ***********************************************
ok: [localhost]
TASK [T06 - Difference between bands2 and bands] ******************************
ok: [localhost] => {
"msg": [
"Rolling Stones"
]
}
TASK [T07 - Show the data type of a list] *************************************
ok: [localhost] => {
"msg": "list"
}
PLAY RECAP ********************************************************************
localhost : ok=7 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
In the output above:
- T01 - List bands 1 — Notice that the list is delimited by [ and ], and elements are separated by , .
- T02 - List bands 2 — It's the same for the second list (just to show they have the same content).
- T03 - Print-specific element — This uses the number zero as the index.
- T04 - Process list using a loop — Notice that each item is one element of the list.
- T05 - Add item to bands2
- T06 - Difference between bands2 and bands — This uses the filter difference to compare the lists.
- T07 - Show the data type of a list — You can inspect the output visually, as mentioned in task T01. However, in some situations, it may be useful to validate that a variable contains the type of data you expect and even use that information to handle the data.
[ Want to test your sysadmin skills? Take a skills assessment today. ]
Work with dictionaries
Above, I demonstrated a simple dictionary.
In real life, lists and dictionaries commonly appear combined, so here's a data structure that's a little more elaborate:
---
- name: Dictionaries
hosts: localhost
gather_facts: no
vars:
bands:
- name: The Beatles
drums: Ringo Star
bass: Paul McCartney
guitar:
- George Harrison
- John Lennon
vocals:
- John Lennon
- Paul McCartney
- George Harrison
- Ringo Star
- name: The Police
drums: Stewart Copeland
bass: Sting
guitar: Andy Summers
vocals: Sting
- name: Rush
drums: Neil Peart
bass: Geddy Lee
guitar: Alex Lifeson
vocals: Geddy Lee
- name: Led Zeppelin
drums: John Bonham
bass: John Paul Jones
guitar: Jimmy Page
vocals: Robert Plant
tasks:
- name: T01 - List bands
ansible.builtin.debug:
msg: "{{ bands }}"
- name: T02 - Select element based on band name
ansible.builtin.debug:
msg: "{{ bands | selectattr('name','equalto','The Beatles') }}"
- name: T03 - Show data types
ansible.builtin.debug:
msg:
- "bands............. is of type {{ bands | type_debug }}"
- ""
- "bands[0].......... is of type {{ bands[0] | type_debug }}"
- " name ====> {{ bands[0]['name'] }}"
- ""
- "bands[0]['guitar'] is of type {{ bands[0]['guitar'] | type_debug }}"
- " guitar ==> {{ bands[0]['guitar'] }}"
- ""
- "bands[1]['guitar'] is of type {{ bands[1]['guitar'] | type_debug }}"
- " guitar ==> {{ bands[1]['guitar'] }}"
...
Here is the result of running this playbook:
PLAY [Dictionaries] ************************************************************
TASK [T01 - List bands] ********************************************************
ok: [localhost] => {
"msg": [
{
"bass": "Paul McCartney",
"drums": "Ringo Star",
"guitar": [
"George Harrison",
"John Lennon"
],
"name": "The Beatles",
"vocals": [
"John Lennon",
"Paul McCartney",
"George Harrison",
"Ringo Star"
]
},
{
"bass": "Sting",
"drums": "Stewart Copeland",
"guitar": "Andy Summers",
"name": "The Police",
"vocals": "Sting"
},
{
"bass": "Geddy Lee",
"drums": "Neil Peart",
"guitar": "Alex Lifeson",
"name": "Rush",
"vocals": "Geddy Lee"
},
{
"bass": "John Paul Jones",
"drums": "John Bonham",
"guitar": "Jimmy Page",
"name": "Led Zeppelin",
"vocals": "Robert Plant"
}
]
}
TASK [T02 - Select element based on band name] *********************************
ok: [localhost] => {
"msg": [
{
"bass": "Paul McCartney",
"drums": "Ringo Star",
"guitar": [
"George Harrison",
"John Lennon"
],
"name": "The Beatles",
"vocals": [
"John Lennon",
"Paul McCartney",
"George Harrison",
"Ringo Star"
]
}
]
}
TASK [T03 - Show data types] ***************************************************
ok: [localhost] => {
"msg": [
"bands............. is of type list",
"",
"bands[0].......... is of type dict",
" name ====> The Beatles",
"",
"bands[0]['guitar'] is of type list",
" guitar ==> ['George Harrison', 'John Lennon']",
"",
"bands[1]['guitar'] is of type AnsibleUnicode",
" guitar ==> Andy Summers"
]
}
PLAY RECAP *********************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Analysis of the above output:
- T01 - List bands — The highest level of this data structure is a list, which is indicated by the opening [ after "msg:" and closed by a matching ] two lines after "Robert Plant". Then at the second level, each member of this list is now a dictionary, delimited by { and }. This means each of the "bands" in the list is a dictionary.
- T02 - Select an element based on band name — From all the elements in the list, select only the dictionary where name='The Beatles'.
- T03 - Show data types — This is a sample of some of the elements and their data types.
In the data structure that I used here, some of the bands may have guitar or vocals as a single element ("AnsibleUnicode" in the output) or a list. If this were a real-life scenario, I would need to handle the situation accordingly.
I could also define the first level as a dictionary, like this:
---
- name: Dictionaries
hosts: localhost
gather_facts: no
vars:
bands:
The Beatles:
drums: Ringo Star
bass: Paul McCartney
guitar:
- George Harrison
- John Lennon
vocals:
- John Lennon
- Paul McCartney
- George Harrison
- Ringo Star
The Police:
drums: Stewart Copeland
bass: Sting
guitar: Andy Summers
vocals: Sting
Rush:
drums: Neil Peart
bass: Geddy Lee
guitar: Alex Lifeson
vocals: Geddy Lee
Led Zeppelin:
drums: John Bonham
bass: John Paul Jones
guitar: Jimmy Page
vocals: Robert Plant
tasks:
- name: T01 - List bands
ansible.builtin.debug:
msg: "{{ bands }}"
- name: T02 - Select element based on band name
ansible.builtin.debug:
msg: "{{ bands['The Beatles'] }}"
- name: T03 - Show keys of highest level dictionary
ansible.builtin.debug:
msg: "{{ bands.keys() }}"
...
In this case, printing the full variable would result in the following:
TASK [T01 - List bands] ********************************************************
ok: [localhost] => {
"msg": {
"Led Zeppelin": {
"bass": "John Paul Jones",
"drums": "John Bonham",
"guitar": "Jimmy Page",
"vocals": "Robert Plant"
},
"Rush": {
"bass": "Geddy Lee",
"drums": "Neil Peart",
"guitar": "Alex Lifeson",
"vocals": "Geddy Lee"
},
"The Beatles": {
"bass": "Paul McCartney",
"drums": "Ringo Star",
"guitar": [
"George Harrison",
"John Lennon"
],
"vocals": [
"John Lennon",
"Paul McCartney",
"George Harrison",
"Ringo Star"
]
},
"The Police": {
"bass": "Sting",
"drums": "Stewart Copeland",
"guitar": "Andy Summers",
"vocals": "Sting"
}
}
}
TASK [T02 - Select element based on band name] *********************************
ok: [localhost] => {
"msg": {
"bass": "Paul McCartney",
"drums": "Ringo Star",
"guitar": [
"George Harrison",
"John Lennon"
],
"vocals": [
"John Lennon",
"Paul McCartney",
"George Harrison",
"Ringo Star"
]
}
}
TASK [T03 - Show keys of highest level dictionary] *****************************
ok: [localhost] => {
"msg": [
"The Beatles",
"The Police",
"Rush",
"Led Zeppelin"
]
}
PLAY RECAP *********************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Notice there are no [ and ] at the first level in this final output because this is now a dictionary.
With a data structure like this, you could use the function "keys()" as shown in T03 if you just need to list the keys. You could also use the filter dict2item to run a loop through the dictionary.
Wrap up
From my experience, the important things to consider when dealing with lists, dictionaries, and combinations in real-life scenarios are:
- To understand the data structure: What is the data type for each part of the data?
- What is your strategy for using that data?
- Do you need to process all elements using a loop?
- Do you care only about certain elements (like the first element of a list or the element with the key "xyz" in your dictionary)?
- Are there elements in the data that are optional? Can a list be empty?
Have fun with your lists and dictionaries! (And sorry if I did not mention your favorite band.)
[ Learn how to manage your Linux environment for success. ]
Sobre el autor
Roberto Nozaki (RHCSA/RHCE/RHCA) is an Automation Principal Consultant at Red Hat Canada where he specializes in IT automation with Ansible. He has experience in the financial, retail, and telecommunications sectors, having performed different roles in his career, from programming in mainframe environments to delivering IBM/Tivoli and Netcool products as a pre-sales and post-sales consultant.
Roberto has been a computer and software programming enthusiast for over 35 years. He is currently interested in hacking what he considers to be the ultimate hardware and software: our bodies and our minds.
Roberto lives in Toronto, and when he is not studying and working with Linux and Ansible, he likes to meditate, play the electric guitar, and research neuroscience, altered states of consciousness, biohacking, and spirituality.
Más como éste
A 5-step playbook for unified automation and AI
AI ambitions meet automation reality: The case for a unified automation platform
Technically Speaking | Taming AI agents with observability
Press Start | Command Line Heroes
Navegar por canal
Automatización
Las últimas novedades en la automatización de la TI para los equipos, la tecnología y los entornos
Inteligencia artificial
Descubra las actualizaciones en las plataformas que permiten a los clientes ejecutar cargas de trabajo de inteligecia artificial en cualquier lugar
Nube híbrida abierta
Vea como construimos un futuro flexible con la nube híbrida
Seguridad
Vea las últimas novedades sobre cómo reducimos los riesgos en entornos y tecnologías
Edge computing
Conozca las actualizaciones en las plataformas que simplifican las operaciones en el edge
Infraestructura
Vea las últimas novedades sobre la plataforma Linux empresarial líder en el mundo
Aplicaciones
Conozca nuestras soluciones para abordar los desafíos más complejos de las aplicaciones
Virtualización
El futuro de la virtualización empresarial para tus cargas de trabajo locales o en la nube