Ansible Loops & Iteration

Loops eliminate the need to write the same task multiple times with different values. Instead of five separate tasks to install five packages, you write one task with a loop over a list of package names. This is the 80/20 feature of Ansible efficiency — loops appear in virtually every production playbook.

Basic Loops with loop

- name: Install required packages
  apt:
    name: "{{ item }}"
    state: present
  loop:
    - nginx
    - curl
    - vim
    - git
    - htop

The loop keyword makes Ansible run the task once for each item in the list. The current item is available as {{ item }}. This is equivalent to writing five separate tasks but dramatically more maintainable.

Looping Over a Variable List

vars:
  web_packages:
    - nginx
    - php-fpm
    - php-mysql
  
tasks:
  - name: Install web packages
    apt:
      name: "{{ item }}"
      state: present
    loop: "{{ web_packages }}"

Keeping the list in a variable (or in group_vars) means you can change the packages without modifying the task. This separation of data and logic is a key maintainability principle.

The apt Module's Built-In List Support

For package installation specifically, the apt and yum modules accept a list of package names directly — more efficient than a loop because they install all packages in one operation:

- name: Install multiple packages efficiently
  apt:
    name:
      - nginx
      - curl
      - vim
    state: present

Use this form for packages; use explicit loops for other modules.

Looping Over Dictionaries

- name: Create multiple users
  user:
    name: "{{ item.name }}"
    shell: "{{ item.shell }}"
    groups: "{{ item.groups }}"
    state: present
  loop:
    - name: alice
      shell: /bin/bash
      groups: sudo,developers
    - name: bob
      shell: /bin/zsh
      groups: developers
    - name: deploy
      shell: /bin/bash
      groups: www-data

When each item is a dictionary, access its fields with dot notation: item.name, item.shell. This pattern is extremely common for user management, creating multiple files with different content, and configuring multiple services with different parameters.

Looping with Index: loop_control

- name: Create numbered config files
  copy:
    content: "worker {{ item.index }}: port {{ item.value }}"
    dest: "/etc/app/worker-{{ item.index }}.conf"
  loop: [8001, 8002, 8003, 8004]
  loop_control:
    index_var: item.index    # Access loop index as item.index
    label: "port {{ item }}" # Customise the task output label

Loops with when

- name: Install optional packages
  apt:
    name: "{{ item.name }}"
    state: present
  loop:
    - { name: nginx, required: true }
    - { name: certbot, required: "{{ ssl_enabled }}" }
    - { name: munin, required: "{{ monitoring_enabled }}" }
  when: item.required | bool

The when condition is evaluated for each loop iteration independently. This lets you combine loops with conditionals to install different packages based on runtime conditions.

Registering Loop Results

- name: Check status of all services
  service_facts:
  
- name: Show status for monitored services
  debug:
    msg: "{{ item }} is {{ ansible_facts.services[item + '.service'].state }}"
  loop:
    - nginx
    - postgresql
    - redis
  when: item + '.service' in ansible_facts.services

Try This: User Management Loop

Write a playbook that creates five user accounts on your web servers using a single task with a loop. Each user should have a specific shell and belong to appropriate groups. Add a second task that creates a home directory structure (bin, logs, config subdirectories) for each user using a nested loop structure. Run the playbook and verify with getent passwd that all users were created.

Summary

Loops eliminate repetitive task definitions and replace them with a single parameterised task. The loop keyword iterates over lists, with the current item available as item. Dictionary items use dot notation for field access. The loop_control option provides index access and output label customisation. Loops combine naturally with when conditionals for selective iteration. For package installation, prefer native list arguments over explicit loops for efficiency.

Leave a Comment