Ansible Loops – Simplify Repetitive Tasks in Playbooks

Ansible Loops Example

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.

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.

About Hitesh Jethva

I am Hitesh Jethva, Founder and Author at Code2DevOps.com. With over 15 years of experience in DevOps and open source technologies, I am passionate about empowering teams through automation, continuous integration, and scalable solutions.

View all posts by Hitesh Jethva