Ansible loops examples and introduction

In a previous article we talked about Ansible, a very useful provisioning free and open source software written in Python, which we can use to automate tasks on multiple machines. We saw how to install it on some of the most used Linux distributions and the basic concepts behind its usage. In this article we focus on how to use loops inside Ansible playbooks in order to perform a single task multiple times with different data.

In this tutorial you will learn:

  • How to use loops inside Ansible playbooks
  • How to loop over a list of items
  • How to loop over a list of hashes
  • How to specify time interval between loop iterations
  • How to keep track of the loop index
Ansible loops examples and introduction
Ansible loops examples and introduction

Software requirements and conventions used

Software Requirements and Linux Command Line Conventions
Category Requirements, Conventions or Software Version Used
System Distribution independent
Software Ansible
Other None
Conventions # – requires given linux-commands to be executed with root privileges either directly as a root user or by use of sudo command
$ – requires given linux-commands to be executed as a regular non-privileged user

Introducing loops

Let’s start with a simple, single task. Suppose we want to be sure a file has a specific set of permissions applied to it. To translate the concept into an Ansible task, we would use the ansible.builtin.file module and write:

- name: Apply permissions
  ansible.builtin.file:
    path: /foo.conf
    mode: '600'

With the task definition above we declared a state: the /foo.conf file must have the 600 permission mode applied to it (its owner should be able to read it and write to it; no privileges should be assigned to its group and the rest of the world). Suppose we want to do the same thing for multiple files; how should we proceed?



Twitter icon Follow LinuxConfig.org on Twitter for the latest tips and tricks about Linux!

Of course writing the exact same task for each file would be a very bad idea, since we would repeat ourselves. The ideal thing would be to use the same task, but with different data. This is a typical case when the right thing to do is to use a loop. Here is what we could write:

- name: Set permissions
  ansible.builtin.file:
    path: "{{ item }}"
    mode: '600'
  loop:
    - /foo.conf
    - /bar.conf
    - /baz.conf

When the task is executed, the following output is returned in the console:

TASK [Apply permissions] *******************************************************
changed: [localhost] => (item=/foo.conf)
changed: [localhost] => (item=/bar.conf)
changed: [localhost] => (item=/baz.conf)

What we did above is a very simple example of a loop in an Ansible playbook. As you can see, we used the loop keyword at the same indentation level of the task name. In this case we provided, using the yaml syntax, a list of paths; then, in the task itself, we used the item variable to reference each one of them. At each iteration this variable will reference one element of the list we specified.

Pretty easy! In this trivial example we assigned the same permissions to all the files in the list; what if we want to assign a different permission mode to each one of them?

Specifying multiple parameters by iterating over a list of hashes

As we said, in the previous example we simple iterated over a list; there may be cases, however, when we need to specify multiple parameters at each iteration. In those cases we want to define and iterate over a list of hashes instead.



Suppose we want to set the permissions of multiple files with the same task, just like we did before, but we want to assign each file a different permission mode. How could we do it? In a case like that, iterating over a simple list would not be enough. What we want to do is to iterate over a list of hashes. Inside each hash we specify the parameters which should be used and their values. Here is an example:

- name: Set permissions
  ansible.builtin.file:
    path: "{{ item.path }}"
    mode: "{{ item.mode }}"
  loop:
    - { path: '/foo.conf', mode: '600' }
    - { path: '/bar.conf', mode: '640' }
    - { path: '/baz.conf', mode: '640' }

Let’s take a look at what we did above. Just like in the previous example we used the loop instruction to create a loop, this time, though, we specified a list of hashes. Inside each hash we used the path and mode keys, and assigned them the appropriate values for each file.

Notice that the key names here are completely arbitrary: they should not necessarily correspond to the parameters used in the task. Inside the task itself, just as before, the value assigned at each iteration of the loop is referenced via the item variable. In this case each item would be one of the hashes we specified; to access the keys in each hash, we use a ., just like we would do to access a property of a Python object, so each time, for example, item.path will reference the value assigned to that key in the hash.

Controlling time between iterations

There could be some cases in which we want to set the amount of time which should pass between the iterations of a loop. How can we do this in a playbook? All we have to do is to use the pause directive inside the loop_control section. Here is a trivial ansible loop example in which each iteration runs 5 seconds after the previous one:

- name: Print message
  ansible.builtin.debug:
    msg: "{{ item }}"
  loop:
    - Hello
    - World
  loop_control:
    pause: 5

Keeping track of the iteration index

Just like we did in the previous example we can use the loop_control section to keep track of the loop iteration count. All we have to do is to use the index_var directive. The variable we specify as value to this directive will contain the index of the current iteration (zero-based). Here is an example:

- name: Print message
  ansible.builtin.debug:
    msg: "The item is {{ item }} and loop index is {{ i }}"
  loop:
    - hello
    - world
  loop_control:
    index_var: i


The task we defined in the above example is very trivial and has not a real use; however, it could be useful to display how the iteration index is increased. If we execute it, we obtain the following output:

TASK [Print message] ***********************************************************
ok: [localhost] => (item=Hello) => {
    "msg": "The item is Hello and loop index is 0"
}
ok: [localhost] => (item=World) => {
    "msg": "The item is World and loop index is 1"
}

Conclusions

In this article we learned the basic usage of loops inside Ansible playbooks and readers were provided some introductory Ansible loops examples. We saw how to iterate over a simple list of items and over a list of hashes, each one containing a set of key-value pairs.

We also saw how to specify how much seconds should pass between each iteration of a loop, and how to keep track of the iteration index in a variable using the loop_control section and, respectively, the pause and index_var directives. Here we barely scratched the surface of what is possible to achieve with loops. For a more in depth knowledge please consult the official Ansible documentation!



Comments and Discussions
Linux Forum