Ansible 101: #012 - Loops 2

Hello everyone! After a long break, I finally got back to blogging. As promised, here is the next part of the Ansible Starter Guide, which will give you more information about using loops.

Dictionaries

But before we return to our loops, I would like to introduce you to dictionaries, which were only briefly mentioned in Part 5. Simply put, dictionaries are lists whose elements can have multiple fields. A simple example:

people:
  - firstName: Ronald
    surName: Weasley
    age: 21
  - firstName: Gilderoy
    surName: Lockhart
    age: 40
  - firstName: Albus
    surName: Dumbledore
    age: 99

As in a simple "list", the hyphen marks the beginning of a new item. In this example, each of the two items has the fields "name", "last name", and "age".

We can also use dictionaries in loops. For example, if we wanted to print the information defined in our example, the playbook would look like this:

- hosts: localhost
  vars:
    people:
      - firstName: Gilderoy
        surName: Lockhard
        age: 43
      - firstName: Minerva
        surName: McGonnagal
        age: 67
      - firstName: Albus
        surName: Dumbledore
        age: 99
       
  tasks:
    - name: list people
      ansible.builtin.debug:
        msg: "First Name: {{ item.firstName }}, Surname: {{ item.surName }}, Age: {{ item.age }}"
      loop: "{{ people }}"
PLAY [localhost] ***********************************************************************************
TASK [Gathering Facts] ***********************************************************************************
ok: [localhost]

TASK [list people] ***********************************************************************************
ok: [localhost] => (item={'firstName': 'Gilderoy', 'surName': 'Lockhard', 'age': 43}) => {
    "msg": "First Name: Gilderoy, surName: Lockhard, Age: 43"
}
ok: [localhost] => (item={'firstName': 'Minerva', 'surName': 'McGonnagal', 'age': 67}) => {
    "msg": "First Name: Minerva, surName: McGonnagal, Age: 67"
}
ok: [localhost] => (item={'firstName': 'Albus', 'surName': 'Dumbledore', 'age': 99}) => {
    "msg": "First Name: Albus, surName: Dumbledore, Age: 99"
}

PLAY RECAP ***********************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

If we use a dictionary as a loop variable, we can access the individual fields within the task with "{{ item.FIELDNAME }}}".

Loops and conditionals

If we use a loop and a condition in the same task, the condition will be validated for each loop item.

- hosts: localhost
  vars:
    people:
      - firstName: Gilderoy
        surName: Lockhard
        age: 43
      - firstName: Minerva
        surName: McGonnagal
        age: 67
      - firstName: Albus
        surName: Dumbledore
        age: 99

   tasks:
    - name: list people oder than 90
      ansible.builtin.debug:
        msg: "{{ item.firstName }} is older than 90"
      loop: "{{ people }}"
      when: "item.age > 90"
PLAY [localhost] **************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ********************************************************************************************************************************************************************************************
ok: [localhost]

TASK [list people oder than 90] ***********************************************************************************************************************************************************************************
skipping: [localhost] => (item={'firstName': 'Gilderoy', 'surName': 'Lockhard', 'age': 43}) 
skipping: [localhost] => (item={'firstName': 'Minerva', 'surName': 'McGonnagal', 'age': 67}) 
ok: [localhost] => (item={'firstName': 'Albus', 'surName': 'Dumbledore', 'age': 99}) => {
    "msg": "Albus: is older than 90"
}

PLAY RECAP ********************************************************************************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

As you can see, two characters (Gilderoy, Minerva) have been skipped because they do not meet the condition (age > 90). Only good old Albus is over 90 :)

Loop control

In some cases it is necessary to control the behavior of loops. For example, you can set the name of the index variable (default is "item"). This is interesting, for example, if we want to use a loop inside another loop.


Changing the name of the index variable


A loop inside another loop? Huh? Who would do that? In some cases, it may even make sense. For example, here we have a playbook that integrates a task file for multiple elements:

- hosts: all
  tasks:
    - name: include tasks for every list item
      include: my-tasks.yml
      loop:
        - "A"
        - "B"
        - "C"

For example, the my-tasks.yml file might look like this in its simplest form:

- name: print item
  ansible.builtin.debug:
    msg: "{{ item }}"

During execution, the my-tasks file is included for each item listed in the loop in the playbook. The content of the loop variable is output to the my-tasks.yml file.

However, if we now want to use another task with a loop in my-tasks.yml, we have a small problem: the "item" variable is already used by the "outer" loop. This can be solved with loop_control:

- name: print item
  ansible.builtin.debug:
    msg: "{{ item }}"
    
 - name: another loop
   ansible.builtin.debug:
     msg: "{{ item_2 }}"
   loop:
     - "D"
     - "E"
     - "F"
   loop_control:
     index_var: item_2

Pause between items

We can alos use the loop control to specify, for example how long ansible should pause between processing each element:

- hosts: localhost
  tasks:
    - name: include tasks for every list item
      ansible.builtin.debug: 
        msg: "{{ item }}"
      loop:
        - "A"
        - "B"
        - "C"
      loop_control:
        pause: 5


So that's it for now. See you then!

Mow