This is one anti-pattern with two ends, and the second follows from the
first with enough reliability that they belong in the same lesson.
The first end: no lint, no --syntax-check, no check-mode run, no
Molecule scenario, no review. Production is the first test environment.
The second end, which is where it goes: the automation has broken
things often enough that nobody wants to run it, so hosts get fixed by
hand. That is drift with extra steps, plus a repository that now lies
about the estate.
End one: production as the first test environment
Read-only / Safethe five layers, cheapest first - each takes seconds— READ-ONLY: all five only read or simulate. The first four require no infrastructure at all.
# 1. Parse. Catches YAML errors, undefined roles, bad task keywords.
ansible-playbook site.yml --syntax-check
# 2. Lint. Catches anti-patterns from this whole part, by rule.
ansible-lint --profile production
# 3. Inventory resolution. Proves the targeting is what you think.
ansible-inventory -i inventory/ --graph
ansible-playbook site.yml --limit app_prod --list-hosts
# 4. Check mode with diff, against a canary.
ansible-playbook site.yml --limit canary --check --diff
# 5. Idempotence. The second run should report changed=0.
ansible-playbook site.yml --limit canary
ansible-playbook site.yml --limit canary
Five commands. Nothing to install beyond ansible-lint, no test
infrastructure, no container runtime. Every one of them catches a class
of defect that would otherwise be caught by a managed node.
Molecule sits above these: a role tested in a container, converged,
verified and converged again, without a managed node. Part XXVI covers
it. It is the most valuable layer and the most expensive one, which is
why it belongs after the five above rather than instead of them.
The specific omission: no validation of what gets written
Configuration changethe difference validate: makes— CONFIGURATION: writes a config file. With validate, a file that fails the check is never installed - the module writes to a temporary path, runs the validator, and only then moves it into place.
# Without: a broken config is written, then the handler restarts
# the service into it.
- name: Install the nginx configuration
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Restart nginx
# With: the file is validated before it replaces the live one.
- name: Install the nginx configuration
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
validate: 'nginx -t -c %s'
notify: Restart nginx
validate: is a per-task control that costs one line and converts
“write a broken config and restart into it” into “fail before touching
the live file”. Combined with serial, the failure stops at host one.
Part XVII covers the render-check-activate pattern in full.
End two: automation nobody dares run
The endpoint has a recognisable set of symptoms. If several of these are
familiar, this is where the repository is.
The playbook is run only by one person, who "knows how it behaves".
Runs are always scoped with --limit to a handful of hosts, never to a group, because a wider run is felt to be risky.
There is a step in the runbook that says "check with the team before running site.yml".
Hosts are being fixed by hand during incidents, with the intention of putting it in the playbook afterwards - which happens sometimes.
Nobody has run the full playbook against the full fleet in more than six months.
A new engineer is told which playbooks are safe and which are not, verbally.
Getting out
The instinct is to run the playbook against the fleet and let it
converge. That is the one move guaranteed to confirm everyone’s fears.
Measure the divergence before changing anything. --check --diff against one host per group, read the diffs, and write down what has drifted. This is read-only and it is the whole basis for what follows.
Triage each difference: a manual fix that should be in the repository, a manual fix that was wrong, or a genuine per-host exception that belongs in inventory as data.
Codify the first category. Every manual fix that should be automation becomes a commit. The repository starts describing reality again, one difference at a time.
Declare the third category. Exceptions belong in group_vars or a documented exception group, with a reason and a review date, per Part XXXVI.
Only then converge, and do it with staging: one canary host, then a small group, then the tier. The first honest run after a long gap is the most dangerous run in this course, and it should be treated like a migration.
Add the five testing layers before the second convergence, so the gap does not reopen.
The order matters. Codifying before converging is what turns a
frightening fleet-wide change into a series of reviewable commits whose
effect is already known, because the diff was read first.
The part, as a review checklist
Each anti-pattern with the incident class it produces. This is the
summary artefact — usable directly on a pull request or a repository
review.
Irrevocable leak, remediated by fleet-wide rotation
Part XXI
No limit, no canary
Fleet-wide outage with no healthy population left
Parts XXX, XXXI
ignore_errors everywhere
False assurance — success reported during an outage
Part XXIII
Inaccurate changed
Restart storms, then silent drift
Parts XII, XVI
Inventory logic in when
Unreviewable targeting; check mode generalises to nothing
Parts V, XV
latest everywhere
Unreproducible estate; Part L becomes unachievable
Parts XXVII, XL
Nobody tests, nobody dares run
Silent drift and a repository that lies
Parts XXV, XXVI
Five distinct incident classes across eight anti-patterns: false
assurance, irrevocable leak, fleet-wide outage,
unreproducible estate, and silent drift. Ranked by blast radius,
the leak and the outage come first — but false assurance is the one that
enables the others, because it is what stops you noticing.
Knowledge check
Knowledge check · 5 questions
Q1. A team has no testing at all and limited time. Which layers give the most defect reduction for the least effort?
Q2. Which failures follow from a repository that no longer describes the estate because hosts have been fixed by hand? Select all that apply.
Q3. The right first move for a long-unrun playbook is to run it against the fleet so the estate converges back to the repository.
Q4. Why does untested automation reliably become feared automation rather than staying merely untested?
Q5. An estate is too diverged for a full reconciliation to be practical. Which approach is most likely to succeed?
Passing score: 75%. Answers are checked in this browser.