CoursesAnsibleIntermediate

Handlers, loops & conditionals

React to change; repeat; branch.

Intermediate12 min · lesson 8 of 12

A handler is a task that runs only when notified, and only once at the end of a play — the standard pattern for “restart the service if (and only if) its config changed.” A task that modifies a file calls notify; if that task reports changed, the named handler runs after all tasks complete. If nothing changed, the handler does not run, which keeps reloads from happening needlessly on every play.

play
tasks:
- name: Deploy config
ansible.builtin.template: { src: app.conf.j2, dest: /etc/app/app.conf }
notify: restart app # fires the handler only if the file changed
handlers:
- name: restart app
ansible.builtin.service: { name: app, state: restarted }

Loops and conditionals

Two more everyday constructs. loop repeats a task over a list — install ten packages, create five users — without copy-pasting the task. when gates a task on a condition, most often a fact, so the same play does the right thing on different OSes or only acts when needed. Together with handlers, these cover the bulk of real playbook logic.

play
- name: Install base packages
ansible.builtin.apt: { name: "{{ item }}", state: present }
loop: [git, curl, ufw]
when: ansible_facts['os_family'] == "Debian"
Handlers do not run if the play fails first
Handlers are flushed at the end of a play, so if a later task fails, notified handlers may never run — leaving, say, a new config written but the service not reloaded. For critical cases force a flush with meta: flush_handlers at the right point, and remember that a failed run can leave hosts partway; design plays so a re-run safely completes what the failed one started.