Conditionals and loops

Ansible · Practical · 10 min

One playbook must handle different OSes, environments and lists of users, packages or sites.

when takes a raw Jinja2 expression (no {{ }}) evaluated per host:

  • when: ansible_facts['os_family'] == "Debian"
  • when: result.rc != 0, when: feature_enabled | bool
  • A list means all must be true: when: [a, b].

Since ansible-core 2.19, conditionals must evaluate to an actual boolean. A bare string variable like when: some_var where some_var is "yes" is an error; convert explicitly with | bool or compare with == "yes".

loop repeats a task over a list; the current item is item:

  • loop: [nginx, git, htop]
  • over a list of dicts: item.name, item.groups
  • over a dict: loop: "{{ users | dict2items }}" gives item.key and item.value
  • nested data: subelements, or product for combinations.

loop_control refines it: label: "{{ item.name }}" keeps output readable, loop_var: user avoids clashes in nested includes, pause waits between iterations.

when is evaluated per item when combined with loop, so you can skip some items. Registering a looped task gives result.results, a list with one entry per item.

Many modules accept a list directly (apt: name: [nginx, git]), which is faster than looping because it runs once.

Example

- name: Users and packages
  hosts: localhost
  connection: local
  vars:
    users:
      - {name: ana, admin: true}
      - {name: bo, admin: false}
    features: {gzip: "yes", http3: "no"}
  tasks:
    - name: Show admins only
      ansible.builtin.debug:
        msg: "{{ item.name }} is an admin"
      loop: "{{ users }}"
      loop_control: {label: "{{ item.name }}"}
      when: item.admin

    - name: Enabled features
      ansible.builtin.debug:
        msg: "feature {{ item.key }} on"
      loop: "{{ features | dict2items }}"
      when: item.value | bool          # explicit boolean conversion
# ok: [localhost] => (item=ana) => msg: ana is an admin
# skipping: [localhost] => (item=bo)

Remember

  • when is raw Jinja2 and must yield a real boolean
  • loop with item; dict2items for dicts; loop_control label
  • Pass lists directly to modules that accept them

Watch out for

  • Wrapping when expressions in {{ }}, which triggers warnings and can break evaluation.

Check yourself

  1. Why does when: item.value | bool make sense for values like "yes" and "no"?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.ansible.com/ansible/latest/playbook_guide/playbooks_loops.html