RH294 Ch 6: Loops, Conditions, Handlers and Error Handling
Real playbooks must repeat work, skip tasks that do not apply, restart services only when needed, and survive expected failures.
Loops. loop runs a task once per item; the current item is item. Items can be dictionaries:
yaml - ansible.builtin.user: name: "{{ item.name }}" groups: "{{ item.groups | default(omit) }}" loop: - { name: ops1, groups: wheel } - { name: ops3 }
default(omit) leaves the option out entirely when the key is missing. Many modules, dnf among them, accept a list directly (name: [httpd, modssl]), which is faster than a loop.
Conditionals. when takes a raw Jinja expression, no {{ }}: when: ansiblefacts['distribution'] == 'RedHat', when: "'dev' in groupnames", when: result.rc != 0. A list under when means all conditions must be true. Quote the expression when it starts with a quote character.
Handlers. A task that changes something can notify a handler by name. Handlers run once, at the end of the play, and only if at least one notifying task reported changed.
yaml tasks: - ansible.builtin.copy: src: security.conf dest: /etc/httpd/conf.d/security.conf notify: restart httpd handlers: - name: restart httpd ansible.builtin.service: name: httpd state: restarted
If a later task fails, pending handlers do not run; --force-handlers or forcehandlers: true changes that.
Blocks. block groups tasks; rescue runs only if a task in the block failed, and always runs either way. A failure that is rescued does not fail the play.
Controlling results - ignoreerrors: true carries on after a failed task (it still shows as failed). - failedwhen: "'ERROR' in out.stdout" defines your own failure condition. - changedwhen: false marks read-only commands as unchanged so reruns stay clean.
Common mistakes - Writing when: "{{ x }} == 1": no braces in when. - Misspelling the handler name in notify; the names must match exactly. - Expecting a handler to run right after its task; use meta: flushhandlers if it must run earlier. - Putting rescue at the wrong indentation, so it becomes an unknown task keyword.