Updated March 28, 2023
Introduction to Ansible Loop
Ansible loop is used to repeat any task or a part of code multiple times in an Ansible-playbook. It includes the creation of multiple users using the user module, installing multiple packages using apt or yum module or changing permissions on several files or folders using the file module. There are two keywords used in Ansible to create a loop:
- loop
- with_<look_up>
Syntax:
- name: add multiple users
user:
name: “{{ item }}”
state: present
group: “<group_name>”
loop:
- <user_name>
- <user_name>
Flowchart
Let’s see the flowchart as given below:
How to Use Loop Keyword with Examples?
It is added in Ansible 2.5 however it is not a full replacement of with_<look_up> but it is recommended to use loop keyword to iterate a task in most of the scenarios.
Standard Loops
- Iterating Over a Simple List
- Iterating Over a List of Hashes
- Iterating Over a Dictionary
1. Iterating Over a Simple List
We can use a simple list in a loop if we have a list of users, files, etc.
Code:
- name: delete multiple files from a location
file:
path: ‘/home/ansible/{{ item }}’
state: absent
loop:
- file1
- file2
Output:
Code Explanation: In the above example, when we run the playbook, it will first pick “file” as input and replace the “item” keyword from the path, once file1 is deleted it will pick “file2” as input and delete “file2”.
loop: “{{ list_of_files}}”
We can even pass the list directly to a parameter for some plugins like yum and apt etc. Sometimes passing the list to a parameter is better than using the loop.
2. Iterating Over a List of Hashes
Sometimes we have a situation like we have to add multiples users to different-different groups, not in a single group. In that case, we have a list of key-value pairs called hashes. We can iterate over a list of hashes using the loop as well.
Code:
- name: add multiple users
user:
name: “{{ item.name }}”
state: present
group: “{{item.groups}}”
loop:
- { name: ‘user1’, groups: ‘SRE’ }
- { name: ‘user2’, groups: ‘dbadmin’ }
Output:
Code Explanation: In the above example, we have two users and we want to add those two users in different groups. So we have mentioned user name as well as respective group and defined the name using a key in the task that is {{item.name}} and so on. The above playbook will add ‘user1’ to ‘SRE’ group and ‘user2’ to ‘dbadmin’.
3. Iterating Over a Dictionary
We can loop over a dictionary however we have to use dict2items Dict filter to turn a dictionary into a list of items.
Code:
vars:
tag:
Environment: QA
Application: Cart
Another: “{{ doesnotexist | default() }}”
tasks:
- name: create a tag dictionary of non-empty tags to set facts
set_fact:
tags_dict: “{{ (tags_dict|default({{}}))|combine({item.key: item.value})}}”
loop: “{{ tags | dict2items }}”
when: item.value !=””
Output:
Code Explanation: In the above example, we have been looping through dictionary ‘tags’ and dict2items will first convert the dictionary into a list as below:
- key: Environment
value: Cart
- key: Application
value: Cart
Complex Loops
- Iterating Over Nested Lists
- Retrying a Task until a Condition Is Met
- Looping Over Inventory
1. Iterating Over Nested Lists
If we have a list that is a part of another list that becomes a nested list. We have to use Jinja2 expression to iterate over nested lists. Let’s understand it with an example if we have to add multiple users to multiple groups. In the below example, we have two users and both users have to be added to both the groups ‘sre’ and ‘dbadmin’.
Code:
- name: add multiple users to multiple groups
user:
name: "{{ item[0] }}"
state: present
group: “{{ item[1] }}”
loop: "{{ ['user5', 'user6'] |product(['sre', ‘dbadmin’])|list }}"
Output:
2. Retrying a Task until a Condition is Met
In some scenarios, we have to stop the loop once a certain condition met. We use ‘until’ keyword to stop a loop as below:
Code:
- shell: ls /root | grep “test”
register: result
until: result.rc == 0
retries: 2
delay: 1
Output:
Code Explanation: In the above example, the shell module will run until the return code of the shell module does not return 0 or the task has been retried 2 times with a delay of 1 sec. In the first snapshot, the play failed because rc is 1 because there is no file or folder with name test2 in the root folder but when ran the playbook after creating that folder in the root folder, it got passed. It totally depends upon the scenario. The default value is 3 times and 5 seconds for retries and delays respectively.
3. Looping Over Inventory
If we have items in an inventory and you want to iterate over that inventory or a subset of that inventory, we can use “groups” variable and if we want to iterate on items that are part of current play then we need to use ‘ansible_play_batch’ as shown below:
Code:
# to show the hosts defined under webservers group in the inventory
- debug:
msg: "{{ item }}"
loop: "{{ groups['webservers'] }}"
# to show all the hosts in the current play
- debug:
msg: "{{ item }}"
loop: "{{ ansible_play_batch }}"
Output:
Adding Controls to Loops
- Limit Loop Output
- Pause a Loop
- Track Progress of A Loop
1. Limiting Loop Output
If we want to limit the output of the Ansible-playbook which is generating a huge amount of output on the console, we can use ‘label’ directive with ‘loop_control’. For example, we have to create multiple servers with some standard configuration and ansible-playbook is going to output all details with its name however we want to keep our output clean and only interested in the name of the server to be displayed as a console output. Here is another example:
Code:
- name: add multiple users
user:
name: “{{ item.name }}”
state: present
group: “{{item.groups}}”
loop:
- { name: ‘user1’, groups: ‘SRE’ }
- { name: ‘user2’, groups: ‘dbadmin’ }
loop_control:
label: "{{ item.name }}"
Code Explanation: We have an example that is used in the explanation of ‘Iterate over a list of hashes’ except added loop_control and you can see the difference as it is only showing users name, not the group name in the output.
2. Pause a Loop
In some scenarios, we have to give some time for the first command to complete before executing the next one. We use the ‘pause’ directive with loop_control. It is like using sleep in some programming language like PowerShell.
Code:
- name: pause for 5 secs after each file creation
digital_ocean:
name: "{{ item }}"
state: present
loop:
- file3.txt
- file4.txt
loop_control:
pause: 5
Output:
3. Track Progress of A Loop
If we want to curiously know how many items have been executed in a loop, we can use ‘index_var’ directive with ‘loop_control’.For example:
Code:
- name: count the fruits
debug:
msg: "{{ item }} with index {{ my_idx }}"
loop:
- kiwi
- papaya
- pear
loop_control:
index_var: my_id
Output:
Conclusion – Ansible Loop
The loop is available after Ansible version 2.5 or higher and it is recommended to use loop however it is not a full replacement of with_<look>. Syntax of ‘with_<lookup> is still valid. Also sometimes it is better to directly pass the list to a parameter.
Recommended Articles
This is a guide to Ansible Loop. Here we discuss the Introduction and how to use a loop keyword? along with different examples and its code implementation. You may also have a look at the following articles to learn more –