While you are working with Ansible, you will often come across recurring or repeating tasks, like installing packages, creating users, updating configuration files, or other similar tasks. Therefore, instead of writing separate tasks for each action, an Ansible loop can help perform the action easily.
Ansible loops help reduce code duplication, improve code-readability, and make your playbooks much more scalable and usable. Whether you’re looping over simple lists or complex dictionaries, Ansible provides multiple loop options to handle different use cases.
In this guide, we will walk through the basics of using an Ansible loop, explore common commands and types, troubleshoot issues, and learn how to write cleaner automation scripts.
What Are Ansible Loops?
Ansible loops allow you to run a task multiple times with different variables. Instead of repeating the same task each time, you can just define a task using Ansible loop through the values.
Ansible looping is useful for:
- Installing a list of packages
- Creating multiple users or directories
- Running tasks with different variables and configurations
They make your Ansible playbooks better, more manageable, cleaner, and easily scalable in dynamic environments.
Get exclusive access to all things tech-savvy, and be the first to receive
the latest updates directly in your inbox.
Basic Ansible Loop Syntax
The most common Ansible loop syntax in the modern environment is by using the loop keyword.
Example:
– name: Install multiple packages
apt:
name: “{{ item }}”
state: present
loop:
– git
– curl
– vim

In this example, Ansible runs the apt module three times—once for each package. The item variable refers to each value in the list during the loop.
Working with Lists in Ansible Loops
You can Ansible loop through a lot of items like different strings, paths, or numbers. This is an ideal situation for when you need to create repetitive tasks that only require one variable per item.
Example: Creating multiple directories
– name: Create multiple directories
file:
path: “/opt/{{ item }}”
state: directory
loop:
– app1
– app2
– app3
Each loop iteration will create /opt/app1, /opt/app2, and /opt/app3.
Working with Dictionaries (Key-Value Pairs)
If you need to pass multiple different variables for all items, you can also use a dictionary, which is also called a map or key-value pair. This allows you to reference multiple fields in the Ansible loop iteration.
Example: Creating users with different attributes
– name: Create users with specific groups
user:
name: “{{ item.name }}”
groups: “{{ item.groups }}”
state: present
loop:
– { name: ‘alice’, groups: ‘admin’ }
– { name: ‘bob’, groups: ‘dev’ }
Here, each item is a dictionary with its own name and groups. This is useful when your task needs more than one input per item.
Related Article: Ansible URI Module Guide: Automate HTTP Requests in Your Playbooks
With_items vs Ansible Loop
Ansible originally supported the loops using different keywords like with_items, with_dict, and with_nested. While you can still work with these, they are now considered the legacy syntax.
The modern Ansible platform recommends the users to use loop keywords, which is more consistent and easy to read.
Example using with_items (legacy):
– name: Install packages (legacy style)
apt:
name: “{{ item }}”
state: present
with_items:
– nginx
– mysql-server
Same task using loop (recommended):
– name: Install packages (modern style)
apt:
name: “{{ item }}”
state: present
loop:
– nginx
– mysql-server
Ansible Loop Control Options
You can customize an Ansible loop to behave how you want it to. These parameters are defined under the loop_control directive and help you control your loop and make it readable.
index_var – Track loop iteration number:
– name: Add users with index
user:
name: “user{{ item }}”
loop: [1, 2, 3]
loop_control:
index_var: user_index
You can then use {{ user_index }} inside your task for numbering or conditional logic.
label – Change how loop items appear in output:
loop_control:
label: “{{ item.name }}”
This is useful when looping over dictionaries and you want to display meaningful labels in the output logs.
Pause between iterations (not directly part of loop_control, but common):
– name: Pause between loops
shell: “echo Task for {{ item }}”
loop: [one, two, three]
pause:
seconds: 5
Registering Ansible Loop Results
When you need to run a repeatable task in an Ansible loop, you might want to capture the results of all iterations to later save in your Ansible playbook.
To do so automatically, you can use the register keyword with the loop task to store all the iterations.
Example:
– name: Ping multiple hosts
ping:
loop: “{{ groups[‘webservers’] }}”
register: ping_results
This creates a variable ping_results that stores a list of result objects (one per item in the loop). You can then access:
ping_results.results[0].item
ping_results.results[0].rc
ping_results.results[0].failed
This is useful for conditional tasks, debugging, or summarizing results.
Types of Loops in Ansible
There are 5 different types of Ansible loops, out of which two are the most important ones with different types of data and logic.
- Simple loops – Iterating over a basic list
- Dictionary loops – Looping through key-value pairs
- Nested loops – Looping within another loop
- Conditional loops – Using conditions to run specific loop items
- Sequential loops with counters – Tracking loop index with index_var
Nested Loops in Ansible
Nested loops allow you to loop over two or more variables in combination. For example, if you need to assign multiple user groups or setting permissions for multiple files.
Example using subelements:
– name: Add users to multiple groups
user:
name: “{{ item.0 }}”
groups: “{{ item.1 }}”
append: yes
with_subelements:
–
– [ “alice”, “bob” ]
– [ “admin”, “dev” ]
Better alternative using loop and cartesian product:
– name: Assign roles to users
debug:
msg: “User {{ item.0 }} gets role {{ item.1 }}”
loop: “{{ [‘alice’, ‘bob’] | product([‘admin’, ‘dev’]) | list }}”
This will run the task for all combinations:
- alice – admin
- alice – dev
- bob – admin
- bob – dev
Use nested loops when you need to pair or mix multiple lists.
Conditional Ansible Loops with when
Conditional Ansible loops help you run a looped task for certain items. Ansible allows you to combine loops with the when condition when you need to run a task based on specific items or values.
Example: Create only users with active status
– name: Create active users
user:
name: “{{ item.name }}”
state: present
loop:
– { name: ‘alice’, active: true }
– { name: ‘bob’, active: false }
when: item.active == true
This will only create the user alice, since Bob is marked inactive.
Example: Install only missing packages
– name: Install only packages not yet installed
apt:
name: “{{ item }}”
state: present
loop: “{{ my_packages }}”
when: item not in installed_packages
Conditional loops give you more flexibility and help avoid unnecessary tasks or errors.
Best Practices to Work with Ansible Loop
Using an Ansible loop simplifies your playbooks, but following the best practices will ensure that the automation stays easily manageable.
- Use the new loop syntax instead of older styles like with_items or with_dict to increase the readability.
- Keep your logic simple to avoid over complexity.
- Use loop_control.label to increase the readability of your task output, especially when you are working with dictionaries.
- Register the Ansible loop results with a register if you plan to use them in later tasks.
- Combine the Ansible loop with when to run tasks only under specific conditions.
- Do not hardcode variables.
- Structure the loop data clearly.
Troubleshooting Guide For an Ansible Loop
Issue | Possible Cause | Suggested Fix |
item is undefined | Loop variable not correctly referenced | Ensure you’re using {{ item }} or {{ item.key }} properly |
Task runs for all items, even when when is used | Incorrect or missing condition in loop | Use when: item.condition == true or item.key == value syntax |
Unexpected loop results | Data structure mismatch (e.g., using dict as list) | Double-check your loop input type (list vs dictionary) |
Task output is messy or unclear | No label specified in loop_control | Use loop_control: label: “{{ item.name }}”for better output |
Old with_items not behaving as expected | Using legacy syntax with newer modules | Replace with_items with loop for modern compatibility |
Cannot access nested keys | Incorrect variable path | Use {{ item[‘key’] }} or {{ item.key }}depending on your structure |
Conclusion – Running a Successful Ansible Loop
Loops in Ansible are an essential feature for automating repetitive tasks manageably and efficiently. By mastering the logic, you can write cleaner playbooks and make your code easy to understand for others and yourself!
What is the difference between loop
and with_items
in Ansible?
loop
is the modern, preferred syntax that works across different modules. with_items
is the older method and may not support newer features like loop_control
.
Can I capture the results of a loop in Ansible?
Yes. Use the register
keyword to store the output of each loop iteration for further processing or decision-making.
Are nested loops supported in Ansible?
Yes. You can nest loops using the product
filter or with_nested
to iterate over combinations of multiple lists.