How to Use Loops in Ansible Playbook

Channel: Linux
Abstract: '{{ item.uid }}'        - item.required == True

In the course of executing tasks in Ansible playbook, you might come across some tasks that are repetitive in nature. These are tasks that require you to create multiple plays, something which can be quite tedious. As with any programming language, loops in Ansible provide an easier way of executing repetitive tasks using fewer lines of code in a playbook.

When creating loops, Ansible provides these two directives: loop and with_*  keyword.

The loop keyword was recently added to Ansible 2.5. The loop keyword is usually used to create simple and standard loops that iterate through several items.

The with_*   keyword is used with a number of lookup plugins when iterating through values. Lookup plugins enable Ansible to access information from external sources such as external data stores, filesystems etc. The with_*  lookup is still very much in use and has not yet been deprecated.

Let’s now have a look at how you can implement Loops in Ansible.

Iterating over simple loops 

Consider a Playbook that adds a new user on the target system using the user module as shown:

---
- hosts: ubuntu_webserver
  tasks:
    - name: Create new user john
      user:
        name: john
        state: present

This looks okay. But what if we have multiple users to add to the target system? How would we go about that? One way would be to duplicate the tasks as shown in the example below where we are adding 3 users.

---
- hosts: ubuntu_webserver
  tasks:
    - name: Create new user john
      user:
        name: john
        state: present

    - name: Create new user mike
      user:
        name: mike
        state: present

    - name: Create new user andrew
      user:
        name: andrew
        state: present

As you can seem this calls for a lot of duplication and repetition.

To make things easier, the same playbook can be written using the loop directive. The loop directive executes the same task multiple times. It  stores the value of each item in a variable called item.So, instead of specifying the names of the users to be added, simply specify a variable called item enclosed between double curly braces as shown.

 name: ‘{{ item }}’

Therefore, each of the items within the loop will be referenced by the variable.

As you can see, our playbook is now much more organized with unnecessary duplication/repetition.

$ vi create_users.yaml
---
- hosts: ubuntu_webserver
  tasks:
    - name: Create new users
      user:
        name: '{{ item }}'
        state: present
      loop:
        - john
        - mike
        - andrew

Also, you can use the with_items directive instead of the loop directive. Both will yield the same result.

---
- hosts: ubuntu_webserver
  become: yes
  tasks:
    - name: Create new users
      user:
        name: '{{ item }}'
        state: present

      with_items:
        - john
        - mike
        - andrew

You can now run the playbook to create the users using the ansible-playbook command as shown below:

$ ansible-playbook -i inventory.txt create_users.yaml

Iterating over a list of Dictionaries

In the first example, we looked at a simple standard loop where the array was a list of string values representing users to be added to the remote target.

But what if we need to add the uid to the loop such that each item now has two values: the username and uid. How would you pass 2 values in an array?

In this scenario, you will have to pass an array of dictionaries, each with 2 key-value pairs as shown. The keys would be the name and uid whilst the values will be the username and the ID of each user.

Under the ‘tasks‘ section, you can no longer define the variable item as before. Since we have 2 values, this will translate into two variables: item.name & item.uid.

The complete playbook is shown below:

$ vi create_users.yaml
---
- hosts: ubuntu_webserver
  become: yes
  tasks:
    - name: Create new users
      user:
        name: '{{ item.name }}'
        uid: '{{ item.uid }}'
        state: present

      loop:
        - name: john
          uid: 1020
        - name: mike
          uid: 1030
        - name: andrew
          uid: 1040

The array of dictionaries can also be represented in a JSON format.

loop:
  - { name: john , uid: 1020 }
  - { name: mike , uid: 1030 }
  - { name: andrew , uid: 1040}

When executed, you will get the following output:

$ ansible-playbook -i inventory.txt create_users.yaml

Ansible Loops with indexes

Occasionally, you may want to keep track of the index values within your array of items. For this, use the ‘with indexed_items‘ lookup. The index value begins from 0 whilst The loop index begins from item.0 and the value from item.1

Consider the playbook below:

$ vi indexes.yaml
---
- hosts: ubuntu_webserver
  tasks:
  - name: Indexes with Ansible loop
    debug:
      msg: "The car at {{ item.0 }} is {{ item.1 }}"
    with_indexed_items:
      - "Nissan"
      - "Mercedes"
      - "Toyota"
      - "Mazda"
      - "BMW"

When executed, the playbook displays the index value of each item in the array list.

$ ansible-playbook -i inventory.txt indexes.yaml

Conditionals in Ansible Loops

In Ansible loops you can use the conditional statement when to control the looping based on the nature of variables or system facts. Consider the playbook below where we have a list of packages that need to be installed.

We have specified an array called ‘packages‘ that contains a list of packages that need to be installed. Each of the items on the array contains the name of the package to be installed and the property called ‘required‘ which is set to ‘True‘ for 2 packages and ‘False‘ for one package.

$ vi install-packages.yaml
---
- name: Install software
  become: yes
  hosts: all
  vars:
    packages:
      - name: neofetch
        required: True

      - name: cpu-checker
        required: True

      - name: screenfetch
        required: False
  tasks:
    - name: Install "{{ item.name }}" on Ubuntu
      apt:
        name: "{{ item.name }}"
        state: present

      when:
        - item.required == True
        - ansible_facts['distribution'] =="Ubuntu"

      loop: "{{ packages }}"

The ‘when‘ conditional statement seeks to install the software packages with the property ‘required‘ set to ‘True’ on target systems which are Ubuntu distros. Below is the output of the playbook when executed.

The verbose output clearly shows the packages that are being installed and the one which is ignored based on the conditional statement.

And this brings us to the end of this topic. We do hope that you have a decent understanding of Loops in Ansible playbooks. Feel free to reach out for any clarification.

Also Read : How to Use Jinja2 Template in Ansible Playbook

Ref From: linuxtechi

Related articles