Skip to content

Ansible Loops

Overview

Loops in Ansible allow you to perform tasks repeatedly with different values. They're essential for efficient automation and reducing playbook complexity.

Basic Loops

Standard Loop

# Simple list loop
- name: Create multiple users
  ansible.builtin.user:
    name: "{{ item }}"
    state: present
  loop:
    - john
    - jane
    - bob

# Dictionary loop
- name: Configure user details
  ansible.builtin.user:
    name: "{{ item.name }}"
    groups: "{{ item.groups }}"
    shell: "{{ item.shell }}"
  loop:
    - { name: 'john', groups: 'admin', shell: '/bin/bash' }
    - { name: 'jane', groups: 'dev', shell: '/bin/zsh' }

Loop with Index

# Using loop_control
- name: Create numbered files
  ansible.builtin.copy:
    content: "File content"
    dest: "/tmp/file{{ ansible_loop.index }}.txt"
  loop:
    - alpha
    - beta
    - gamma
  loop_control:
    index_var: ansible_loop.index

Advanced Loop Techniques

Nested Loops

# Using nested items
- name: Configure virtual hosts
  ansible.builtin.template:
    src: vhost.conf.j2
    dest: "/etc/nginx/sites-available/{{ item.0.domain }}_{{ item.1 }}.conf"
  loop: "{{ domains | product(environments) | list }}"
  vars:
    domains:
      - { domain: 'example.com', root: '/var/www/example' }
      - { domain: 'test.com', root: '/var/www/test' }
    environments:
      - dev
      - staging
      - prod

Loop Controls

# Loop with label
- name: Install packages with status
  ansible.builtin.apt:
    name: "{{ item }}"
    state: present
  loop:
    - nginx
    - postgresql
    - redis-server
  loop_control:
    label: "Installing {{ item }}"
    pause: 2  # 2 second pause between iterations

# Loop with index
- name: Create sequential files
  ansible.builtin.copy:
    content: "Content for file {{ ansible_loop.index }}"
    dest: "/tmp/file{{ ansible_loop.index }}.txt"
  loop:
    - first
    - second
    - third
  loop_control:
    index_var: ansible_loop.index

Conditional Loops

# Loop with conditionals
- name: Start services if enabled
  ansible.builtin.service:
    name: "{{ item.name }}"
    state: started
  loop: "{{ services }}"
  when: item.enabled | bool
  vars:
    services:
      - { name: 'nginx', enabled: true }
      - { name: 'mysql', enabled: false }
      - { name: 'redis', enabled: true }

Loop with Filters

Filtering Lists

# Select specific items
- name: Process active users
  debug:
    msg: "Processing user {{ item }}"
  loop: "{{ users | selectattr('active', 'eq', true) | list }}"
  vars:
    users:
      - { name: 'john', active: true }
      - { name: 'jane', active: false }
      - { name: 'bob', active: true }

Transform and Loop

# Map and loop
- name: Display uppercase names
  debug:
    msg: "Name: {{ item }}"
  loop: "{{ users | map(attribute='name') | map('upper') | list }}"

Error Handling in Loops

Continue on Error

# Ignore failures
- name: Install packages with failure handling
  ansible.builtin.apt:
    name: "{{ item }}"
    state: present
  loop:
    - package1
    - package2
  ignore_errors: yes

Retry Logic

# Retry failed operations
- name: Attempt operations with retry
  ansible.builtin.command: "{{ item }}"
  loop:
    - "service1 start"
    - "service2 start"
  register: result
  until: result.rc == 0
  retries: 3
  delay: 5

Best Practices

Performance Optimization

# Use with_items for large lists
- name: Process large lists efficiently
  debug:
    msg: "Processing {{ item }}"
  with_items: "{{ large_list }}"

# Parallel execution
- name: Run tasks in parallel
  command: "long_running_task.sh"
  async: 3600
  poll: 0
  loop: "{{ tasks }}"
  register: async_results

- name: Wait for completion
  async_status:
    jid: "{{ async_result_item.ansible_job_id }}"
  loop: "{{ async_results.results }}"
  loop_control:
    loop_var: "async_result_item"
  register: async_poll_results
  until: async_poll_results.finished
  retries: 30

Loop Documentation

# Document complex loops
- name: Complex loop operation
  vars:
    loop_description: |
      This loop:
      1. Filters active items
      2. Transforms data
      3. Processes results
  debug:
    msg: "{{ loop_description }}"
  loop: "{{ complex_data | selectattr('active', 'eq', true) | map(attribute='name') | list }}"