Make tasks decide and repeat — run a task only when a condition holds with when, and act on every item in a list with loop.
Why: not every task applies to every host. when runs a task only if its expression is true — based on a fact, a variable, or a registered result. Note: inside when you write the variable bare (no {{ }}), because when is already a Jinja2 expression.
- name: OS-specific tasks
hosts: local
tasks:
- name: Install with apt (Debian/Ubuntu only)
ansible.builtin.apt:
name: htop
state: present
become: true
when: ansible_facts['os_family'] == "Debian"Why: loop repeats a task for each item in a list, with item as the current value — far cleaner than copying the task many times. Create several files, install several packages, add several users, all from one task.
- name: Create several files
hosts: local
tasks:
- name: Ensure each file exists
ansible.builtin.file:
path: "/tmp/{{ item }}"
state: touch
loop:
- one.txt
- two.txt
- three.txtWhy: when each iteration needs several values, loop over a list of dictionaries and read item.key. This is the idiomatic way to create users with their groups, or services with their ports — structured data driving one task.
- name: Create users
hosts: local
become: true
tasks:
- name: Add each user
ansible.builtin.user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
loop:
- { name: alice, groups: "sudo" }
- { name: bob, groups: "developers" }Why: loop and when work together — when is evaluated for each item, so you can filter which items act. Here only the packages marked install: true are installed. This pattern drives optional, data-defined configuration.
- name: Install selected packages
hosts: local
become: true
tasks:
- name: Install where requested
ansible.builtin.package:
name: "{{ item.name }}"
state: present
loop:
- { name: git, install: true }
- { name: vim, install: false }
when: item.install