Part 38 — Playbook Structure and Control Flow¶
Chapter status: outline
This chapter is scoped but not yet written in full prose. The sections below define what each part will cover.
This chapter (the first of three covering what used to be a single "Playbooks" chapter) is about the keywords that decide what runs, on which hosts, in what order, and at what batch size — the structural skeleton of a play, before any individual task's own logic.
Why This Exists¶
- A single flat list of tasks doesn't scale to real infrastructure: you need ordering control (
pre_tasks/post_tasks), rollout control (serial,strategy), and the ability to run a task somewhere other than its "natural" target (delegate_to,run_once). This chapter covers the keywords that make a playbook a controlled rollout instead of a single blind blast to every host.
Problem Statement¶
- Real rollouts need to answer questions a flat task list can't: "run this against 10% of hosts first," "run this one task only once across the whole play," "run this task's action on the load balancer, not the web server it's for." Each has a specific, named keyword — guessing at workarounds instead of knowing them leads to fragile playbooks.
Internal Architecture¶
- How a playbook YAML file compiles into an internal
Playobject, which expands into a list ofTaskobjects executed by the Task Queue Manager — full internals in Volume 3. This chapter stays at the keyword/behavior level.
Step-by-Step Explanation — Keyword Reference¶
hosts: the target pattern for the play (cross-reference to inventory patterns, Part 8).tasks: the ordered list of actions within a play.pre_tasks/post_tasks: tasks that run before/afterroles:regardless of role structure — useful for "always check X before any role runs" logic.serial: batch size for rolling execution — an integer, a percentage, or a stepped list ([1, 5, "100%"]) to canary a rollout before going wide.strategy:linear(default — waits for all hosts to finish each task before moving to the next) vs.free(hosts run independently, no per-task lockstep) vs.debug.delegate_to: running a task's action on a different host than the one it's nominally "for" — the classic example is updating a load balancer's config on behalf of a web server being taken in/out of rotation.run_once: executing a task exactly once across the whole play, regardless of how many hosts are targeted (e.g., triggering a single shared database migration).include_tasks/import_tasks: dynamic (runtime-resolved) vs. static (parse-time-resolved) task inclusion — the distinction changes what--list-tasksand tag filtering can actually see ahead of a run.
Production Best Practices¶
- Using
serialwith a stepped list (a small canary batch, then the rest) for any change that could plausibly break a host, rather than an all-at-once rollout. - Preferring
import_tasks/roles for structure that should be visible to--list-tasksand tag filtering; reaching forinclude_tasksonly when the task list genuinely must be decided at runtime. - Being deliberate about
strategy: free— only using it when hosts are genuinely independent for that play, with no cross-host ordering assumptions.
Common Mistakes¶
- Assuming task execution is host-by-host sequential by default — it's task-by-task across a batch of hosts (the
linearstrategy), which surprises people used to purely imperative scripting. - Forgetting that
delegate_tostill needs its own connection and privilege-escalation considerations on the delegate target — it isn't "free." - Using
include_tasksout of habit whereimport_taskswould give better--list-tasks/tag visibility.
Performance Considerations¶
strategy: freevs.lineartradeoffs at scale, and howserialtrades speed for blast-radius control during rollouts — quantified fully in Volume 6.
Security Considerations¶
delegate_tochanges where a task's credentials and privilege escalation actually apply — audit delegated tasks with the same care as the host they're nominally targeting.
Interview Questions¶
- "What's the difference between
include_tasksandimport_tasks?" - "What does
serialcontrol, and why would you use a stepped list likeserial: [1, 5, "100%"]?" - "When would you use
delegate_to, and what does it not change about a task's execution?"
Hands-On Lab¶
- Write a playbook targeting 4 hosts with
serial: [1, "100%"], and observe (with-v) that only one host runs the first batch before the rest proceed.
Summary¶
- These keywords control the shape of a rollout — order, batching, and where a task's action actually happens — independent of what any individual task does. The next two chapters cover per-task branching/repetition (loops, conditionals, handlers) and failure handling (blocks).
Next¶
Continue to Part 39 — Loops, Conditionals and Handlers.