Chapter 5: Implementing Task Control

Ansible task control cheat sheet

The chapter on one page: the rules to remember, the keywords in one place, and flashcards for quick revision.

The chapter in six sentences

Cheat sheet

Loops

- name: Users exist
  ansible.builtin.user:
    name: "{{ item['name'] }}"
    groups: "{{ item['groups'] }}"
  loop: "{{ users }}"          # quoted: starts with {{
  register: user_results       # results list, one per item

- ansible.builtin.debug:
    msg: "{{ item['item']['name'] }} changed: {{ item['changed'] }}"
  loop: "{{ user_results['results'] }}"

Conditionals

when: ansible_facts['distribution'] == "RedHat"      # string: quoted
when: ansible_facts['memtotal_mb'] >= 2048            # number: not quoted
when: my_var is defined                               # or: is not defined
when: ansible_facts['distribution'] in supported      # list membership
when: flag | bool                                     # string → Boolean
when:                                                 # list = and
  - cond_one
  - cond_two
when: >                                               # grouping
  (a == "x" and b == "y") or c == "z"

Handlers

tasks:
  - name: Config is deployed
    ansible.builtin.copy:
      src: app.conf
      dest: /etc/app.conf
    notify:
      - restart app

handlers:                     # same level as tasks
  - name: restart app         # must match notify exactly
    ansible.builtin.service:
      name: app
      state: restarted

Failure

- hosts: all
  force_handlers: true              # play level
  tasks:
    - name: Check config
      ansible.builtin.command: httpd -t
      register: check
      changed_when: false           # read-only: never "changed"
      failed_when: "'Syntax OK' not in check.stderr"
      ignore_errors: true           # keep going if it fails

    - name: Upgrade
      block:
        - ansible.builtin.command: /usr/local/bin/upgrade
      rescue:
        - ansible.builtin.command: /usr/local/bin/rollback
      always:
        - ansible.builtin.service:
            name: app
            state: restarted
KeywordLevelEffect
loop / with_itemstaskRun the task once per list item
whentask, blockRun only if the condition is true (per item with loops)
registertaskSave the task result (a results list for loops)
notifytaskTrigger handlers if the task reports changed
handlersplayTasks run once at the end of the play, if notified
force_handlersplayRun notified handlers even after a failure
ignore_errorstaskReport failure but continue
failed_whentaskYour own rule for failure
changed_whentaskYour own rule for changed
ansible.builtin.failmoduleFail on purpose with a message
block / rescue / alwaystaskGroup, recover, clean up
Which variable holds the current value in a loop?
`item`
Where does `loop:` go in a task?
At **task level**, aligned with `name` and the module, not under the module.
What does a looping task register?
A dictionary with a **`results` list**, one entry per item.
Do you use `{{ }}` in a `when` condition?
No. `when` is already an expression: `when: my_var == "x"`.
How do you test that a variable exists?
`when: my_var is defined` (or `is not defined`).
Why can `when: flag` be wrong for `flag: "false"`?
A non-empty string is true. Use `when: flag | bool`.
When is a handler notified?
Only when the notifying task reports **changed**.
How many times does a handler run if notified by 3 tasks?
**Once**, after all tasks, in the handlers-section order.
What happens to notified handlers if a later task fails?
They do not run, unless the play sets `force_handlers: true`.
How do you stop a read-only command from reporting changed?
`changed_when: false`
When does `rescue` run? When does `always` run?
rescue: only if the block failed. always: every time.
Which module fails a play on purpose with your message?
`ansible.builtin.fail` with `msg:`