Ansible offers a robust feature to iterate over tasks using loops. Loops in Ansible simplify repetitive tasks, improve code readability, and reduce redundancy. Loops in Ansible allow you to run the same task multiple times with different inputs. They are useful when:
- Installing multiple packages
- Creating multiple users
- Copying a set of files to a target location
Ansible provides two primary ways to define loops: with_items and the modern loop keyword.
This guide will cover how to use with_items, loop, loop control, with_nested, and block loops in Ansible playbooks with practical examples.
Table of Contents
Using with_items in Ansible
The with_items construct is one of the older but widely used methods for creating loops in Ansible. It works by iterating over a list of items and performing the specified task for each item.
Here is an example playbook:
- name: Install multiple packages
hosts: webservers
tasks:
- name: Install multiple packages
apt:
name: "{{ item }}"
state: present
with_items:
- nginx
- curl
- git
In the above example, with_items loop iterates over a list of packages: nginx, curl, and git. These packages will be installed sequentially with apt module on the webservers group.
Using the loop Keyword in Ansible
The loop keyword is a more modern alternative to with_items. It provides better readability and supports advanced features like dictionaries and nested loops.
Here is an example playbook to add multiple users on remote hosts.
- name: Add multiple users
hosts: webservers
tasks:
- name: Create multiple users
user:
name: "{{ item.name }}"
shell: "{{ item.shell }}"
state: present
loop:
- { name: 'alice', shell: '/bin/bash' }
- { name: 'bob', shell: '/bin/zsh' }
The above playbook iterates through a list of users and creates them using the user module.
Controlling Loops with loop_control
Ansible’s loop_control allows you to customize how loops behave. You can control variables like index and label formatting for better debugging and reporting.
Here’s an example:
- name: Create numbered directories
hosts: webservers
tasks:
- name: Create directories with index
file:
path: "/tmp/directory_{{ item }}"
state: directory
loop: "{{ range(1, 4) | list }}"
loop_control:
index_var: index
Explanation:
- range(1, 4) generates numbers from 1 to 3.
- index_var stores the current index for use in tasks.
This playbook creates directories (/tmp/directory_1, /tmp/directory_2, etc.) and assigns a custom index for each iteration using loop_control.
Working with Ansible Nested Loops
Nested loops allow tasks to iterate over multiple lists simultaneously. This is useful when dealing with matrix-like configurations.
Configuring services for multiple hosts:
- name: Configure services on hosts
hosts: webservers
tasks:
- name: Configure services with nested loops
debug:
msg: "Configuring {{ item.0 }} on {{ item.1 }}"
with_nested:
- ["nginx", "apache"]
- ["host1", "host2"]
This playbook configures services (nginx, apache) on multiple hosts (host1, host2) using a nested loop to iterate over both lists.
Using Block Loops in Ansible
Sometimes, you want to apply a set of tasks (a block) repeatedly. Block loops let you loop over multiple tasks as a group.
Example playbook:
- name: Manage files with a block loop
hosts: webservers
tasks:
- block:
- name: Create file
file:
path: "/tmp/{{ item }}"
state: touch
- name: Add content to file
lineinfile:
path: "/tmp/{{ item }}"
line: "Managed by Ansible"
loop:
- file1.txt
- file2.txt
In this example:
- File module ensures the specified files (file1.txt and file2.txt) are created in the /tmp directory.
- Lineinfile module adds the line “Managed by Ansible” to each file, ensuring it exists without duplication.
- Loop iterates over the list of file names (file1.txt and file2.txt) and applies the tasks in the block to each file.
Error Handling in Loop
When running loops in Ansible, it’s important to handle errors gracefully, especially in cases where one failed iteration shouldn’t stop the execution of subsequent tasks.
You can use the ignore_errors: yes directive to ensure that a failed iteration does not stop the playbook.
Example: installing packages with ignored errors
- name: Install multiple packages ignoring errors
hosts: webservers
tasks:
- name: Install packages
apt:
name: "{{ item }}"
state: present
loop:
- valid_package
- invalid_package # This will cause an error
- another_valid_package
ignore_errors: yes
If invalid_package fails to install, Ansible will skip the error and proceed with the next item in the loop.
Conclusion
Ansible loops are essential for simplifying repetitive tasks and enhancing playbook efficiency. They help automate actions like installing multiple packages, creating users, or configuring services across multiple hosts.
FAQs
1. How is the loop keyword different from with_items?
The loop keyword is a modern alternative to with_items, offering better readability and support for advanced features like dictionaries.
2. Why should I use loops in Ansible?
Loops save time, reduce repetitive code, and make your playbooks more efficient and easier to maintain.
3. How do I handle errors in loops?
You can use ignore_errors: yes or rescue blocks within a task to handle errors during loop iterations without stopping the playbook.