Chapter Seven

Control Flow

Deciding which tasks run on which host, looping a task over a list, recovering when a step fails, overriding what counts as failure or change, and carving a playbook into selectable phases. Conditionals, loops, blocks, failure control, tags, and the static-versus-dynamic split between import and include.

6 topics

A playbook with no control flow runs every task on every host, in order, no matter what — which is fine until reality intrudes. Real fleets are heterogeneous, real changes can fail partway, and a real playbook grows to sixty tasks you do not always want to run end to end. This chapter is the machinery that turns a flat task list into something that decides, repeats, recovers, and selects.

Six topics, building on the Larkspur web stack. Conditionals gate a task per host with when; loops run one task over a list; blocks give you try/except/finally; failure control rewrites what Ansible calls a failure or a change; tags let you run just the deploy or just the config; and the import-versus-include split — the most confused pair in Ansible — decides whether content is inlined at parse time or pulled in at run time, which changes how tags, loops, and when behave.

Topics in This Chapter

Topic 39
Conditionals and when
when decides whether a task runs on a given host, evaluated as raw Jinja2 with no braces. The string-versus-bool truthiness trap, definedness guards, and combining conditions without inverting your own logic.
ConceptControl Flow
Topic 40
Loops
Run one task over a list instead of copying it five times. loop versus the legacy with_* family, iterating dicts with dict2items, loop_control for labels and variable renaming, and the .results shape of a looped register.
ConceptControl Flow
Topic 41
Blocks and Error Handling
block, rescue, and always are Ansible's try/except/finally. Grouping tasks under a shared directive, recovering from a mid-deploy failure, and the cleanup that must run on every path — drain, roll back, re-enable.
ConceptControl Flow
Topic 42
Failure Control
Why command and shell need changed_when and failed_when when idempotent modules don't. ignore_errors versus failed_when: false, catching tools that exit 0 on error, and any_errors_fatal versus max_fail_percentage.
Reliability
Topic 43
Tags
Label tasks, blocks, and roles to run or skip parts of a big playbook. --tags and --skip-tags, the special always and never tags, and verifying a selection with --list-tasks before it touches anything.
Workflow
Topic 44
include vs import
The single most confused pair in Ansible. import is static and inlined at parse time; include is dynamic and resolved at run time. How that one difference decides the behavior of tags, loops, and when.
ConceptControl Flow