Composing and Ordering Roles
Scenario: A play lists awebapprole before acommonrole. The web application is configured against a hostname and timezone thatcommonhas not set yet.
New words, in plain English
roles:section - roles listed here run BEFORE the play'stasks:section.pre_tasks/post_tasks- tasks that run before all roles, and after all tasks, respectively.import_role- include a role at parse time - static.include_role- include a role at run time - dynamic, so it can be looped or conditionally chosen.
Execution order inside a play is fixed and worth memorising, because a surprising amount of debugging comes down to not knowing it:
pre_tasks- handlers notified by
pre_tasks(flushed here) roles:- in the order listedtasks:- handlers notified by roles or tasks
post_tasks- handlers notified by
post_tasks
So roles always run before the play's own tasks:, no matter where the tasks: block appears in the file. And roles run in listed order, which is why common belongs before webapp.
pre_tasks is the natural home for validation - the assertions that decide whether this run should proceed at all - because it runs before any role has touched anything. post_tasks is the natural home for verification and notification.
For including roles from inside a task list there are two forms. import_role is static: resolved when the playbook is parsed, so its tasks are visible to --list-tasks and tags apply to them individually, but it cannot be looped or chosen by a run-time value. include_role is dynamic: resolved when reached, so it can be looped or selected conditionally, at the cost of being invisible until it runs.
Analogy: The play is a construction schedule. Foundations before walls, walls before wiring. pre_tasks is the site inspection that can call the whole thing off before a single brick is laid.
A worked example
- name: Configure the web tier
hosts: webservers
pre_tasks:
- name: Refuse to run without a valid release
ansible.builtin.assert:
that:
- app_version is defined
- app_version is match('^\d+\.\d+\.\d+