Ansible¶
Tool · prereq 03 (Containers/Podman) · ~1.5–2 days (4 submodules: inventory/vars, roles/collections, templating/handlers, vault/secrets)
A single host is easy to configure by hand — SSH in, install a package, edit a file, restart the service. A fleet isn't: the same steps repeated across ten or a hundred hosts, days apart, by whoever's on call — and configuration drifts the moment one host gets patched and another doesn't.
Ansible replaces that by-hand repetition with a playbook: declare the desired state once, push it over SSH with no agent to install on the target, and run it as many times as you want. A host already in that state reports no change — that's idempotency, and it's what makes "just run it again" a safe default instead of a gamble.
TL;DR — in 30 seconds:
- Ansible is declarative and agentless: no daemon on the target — it SSHes in, runs a one-shot Python payload, and removes it; the control node holds all the brains.
- Idempotency is the core guarantee: a converged host reports no changes on a second run, which is what makes "just run the playbook again" safe instead of risky.
- A playbook binds hosts to tasks (each task = one module call); a handler fires only when a task reports changed, and secrets live encrypted in Ansible Vault.
Learning objectives¶
By the end, the junior can:
- Write an inventory and an idempotent playbook.
- Explain idempotency and why a second run is (mostly) green.
- Use handlers and keep secrets in Ansible Vault.
Pair, Do and Prove run in Claude Code, with the onboarding-tutor skill. Click a button to copy the exact prompt, then paste it into Claude Code in this repo.
1. Learn · acquire the concept¶
Read: the ansible course — focus §4 (static inventory), §6 (first playbook), §7 (common modules), §11 (handlers), §12 (idempotency), §15 (Vault). Skip for now: §14 (roles) and the AWS-static patterns — revisit when you operate a real fleet. Glossary (beat 5): the module’s Anki deck. Read next: Ansible documentation (official docs — full module index and the playbook/inventory reference).
Check yourself — you run the same playbook twice against a host that's already correctly configured. What should the second run report, and why does that matter?
All ok, zero changed — idempotency means Ansible detects the host already matches the desired state
and skips re-applying it. If a second run keeps reporting changed on the same task, that task isn't
actually idempotent, almost always because it's a command/shell call standing in for a real module.
Check yourself — why doesn't Ansible need an agent installed on every target host, unlike Chef or Puppet?
It's a push model over SSH — the control node connects, copies a one-shot Python payload into
/tmp, executes it, and removes it. The target only needs SSH and Python; nothing is installed or
registered on it ahead of time.
2. Pair · passive → active¶
Drive the onboarding-tutor skill:
- "Explain idempotency with a concrete Ansible task example."
- "3-line mental model of playbook → task → module."
- "Quiz me on handler vs task before I write anything."
Common gotchas
- Using
command:/shell:where a real module exists. Fix: check first — a proper module reports changed/ok correctly and is idempotent for free;command/shellalways reports changed unless you addcreates:/changed_when:yourself. - Assuming a handler fires immediately when its task runs. Fix: a handler only fires when the
notifying task reports
changed, and only once at the end of the play regardless of how many tasks notify it. - Committing an unencrypted secret because "it's just a lab." Fix: Vault-encrypt anything secret before it's committed — once a plaintext secret is in git history, rotating the value doesn't erase it from history.
- Skipping
--check --diffbefore applying to a host you don't fully control. Fix: a dry run shows exactly what would change; skipping it on a shared fleet is how "it worked on my host" surprises production.
Done when: you can explain why re-running a good playbook is safe, unprompted.
3. Do · produce an artifact¶
Exercise — configure a host idempotently:
Target a container or a local VM (not a real server).
- Install Ansible first (course §3):
pip install 'ansible-core>=2.16'. - Write an inventory with your target.
- Write a playbook that installs a package, drops a templated config file, and restarts the service via a handler (only when the config changes).
- Run it. Then run it again — the second run should report no changes (except when you actually change the template).
Done when: the playbook converges, and a second run is green.
4. Prove · understanding gate¶
Mandatory. Pass bar in the Socratic gate.
- Walkthrough: Explain each task and why it's idempotent. When does your handler fire, and why not every run?
- Alternative:
command/shellvs a proper module — why prefer the module? What breaks idempotency? - Failure & debug: The second run keeps reporting "changed" on one task. What's likely wrong and how do you find it (
--check,--diff)? - Provenance: Which tasks did the agent write? How did you verify idempotency rather than assuming it?
Pass bar: you can defend idempotency and handler behaviour. A playbook that's "changed" every run and you can't explain why = back to Pair.
5. Retain · fight forgetting¶
Study the module deck SE Onboarding::08 Ansible — its 9 theme subdecks let you drill one area at a time. It syncs to your Anki automatically on pull (anki-sync).
Done when: you can recall the deck's key terms — playbook, inventory, module/task, facts & templating, idempotence — and have added a card for anything the gate exposed as weak.
Key takeaways¶
- Ansible is agentless and push-based: the control node SSHes in, runs a one-shot Python payload, and removes it — nothing is installed or registered on the target ahead of time.
- Idempotency is the whole safety model: a converged host reports zero
changedon a second run; if a task keeps reportingchanged, it isn't actually idempotent. - A playbook binds hosts to tasks, and each task is one module call. Prefer a real module over
command/shell— a module reports changed/ok correctly and is idempotent for free, whilecommand/shellalways reports changed unless you addcreates:/changed_when:yourself. - A handler fires only when its notifying task reports changed, and only once at the end of the play — not immediately when the task runs, and not once per notifying task.
- Secrets are Vault-encrypted before they're committed, not after. Once a plaintext secret is in git history, rotating the value doesn't erase it from history.
--check --diffis the dry run that catches a bad playbook before it touches a shared host — skipping it on a fleet you don't fully control is how "it worked on my host" becomes a production surprise.
Go deeper — curated resources¶
Hand-picked external material to take this topic further — the best books, courses, talks, and writing. Cost is tagged Free, Free online (full text/course free on the web), or Paid.
- Book · Ansible for DevOps — Jeff Geerling. The practical standard; the full manuscript reads free online under CC BY-SA, with a polished ebook/paperback for sale. Free online.
- Video · Ansible 101 — Jeff Geerling. The best free video series, walking through the book in live tutorial form. Free.
- Docs · Ansible Documentation — Red Hat / Ansible community. The full module index plus playbook and inventory reference. Free.
- Repo · Ansible Galaxy — Ansible community. Community-published roles and collections you can pull into a playbook. Free.
Gate log (mentor fills on pass)¶
- Date passed: / Passed by: / checked by: / Weak spots: