git clone --filter=blob:none --sparse https://github.com/calmict/book_labs.git cd book_labs git sparse-checkout set ansible/ed1
The manual opens with a promise: to become the conductor of your infrastructure orchestra. But before you raise the baton you have to understand *why* the hand-written score — the Bash script — crumbles when the players are not three but three thousand. In this first lab we will not use Ansible yet: we install it in chapter 6. Here you feel with your own hands the *problem* Ansible exists to solve, so the rest of the manual has a reason to exist.
Three "servers" (three containers), one state you want to keep identical across all of them, and a script that tries to enforce it. The script will work. Then you re-run it, and the first crack opens.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap01/solution ./run.sh
The conductor of the orchestra does not implant a chip in every musician's brain: he speaks, and they — who already know how to read music — play. Ansible works the same way. It does not install an *agent* that lives on the machine: it visits over SSH, has it do one thing using the Python that is already there, and leaves. This is called agentless, and it is the heart of the architecture.
In this lab you become the messenger: you will reproduce by hand, over SSH, the journey Ansible automates for every task. We will not use Ansible yet (you install it in chapter 6): the point is exactly to show that nothing *of its own* is needed on the target — only SSH and Python.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap02/solution ./run.sh
In chapter 2 the script handed you a key and you got onto the managed node Ansible travels on. At its heart is asymmetric cryptography and one golden rule: the private key never leaves the control node; only its public half travels. We will build a small world — an exposed bastion and a target locked in a segregated network — and cross it by hand.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap03/solution ./run.sh
YAML is the score you will write every playbook and every inventory on. It looks trivial — and that is exactly the trap: a value written the way you mean it can be 1.2, 22:30 becomes 1350. In this lab you learn the anatomy of YAML and, above all, how not to let the score lie to you. No containers, no Ansible yet: just YAML files and the very parser Ansible uses under the hood (PyYAML).
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap04/solution ./run.sh
In chapter 1 you saw the difference between "re-runnable" and "convergent". Now we take it to the core: idempotence. A light switch is idempotent — you flip it to ON; if it is already ON, nothing happens, and the room is lit either way. A doorbell is not: every press rings again. Ansible is made of switches: you tell it the desired *state*, it acts only if needed, and it tells you the colour of the change. Before installing it (chapter 6), you build it in miniature with your own hands: a tiny idempotent engine in bash that reports the colours and can do a "dry run".
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap05/solution ./run.sh
For five chapters you studied the score without ever raising the baton. Now you pick it up: you install Ansible. But like a good conductor you do not dirty the stage — you install it in an isolated environment (a virtualenv), so you never touch the system Python. Then you tune the players: you prepare the nodes you will configure in the coming chapters.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap06/solution ./run.sh
The conductor has the baton (chapter 6); now comes the orchestra rulebook: ansible.cfg, the file where the rules of how Ansible works live — how many players in parallel, which key to enter with, whether to ask for permission. The most important thing to understand is not *what* is written in it, but which copy of the rulebook gets read: Ansible looks at four music stands in a fixed order and uses the first one it finds, in its entirety — no merging. And there is a security trap: if the stand is in a room anyone can write in, Ansible refuses to read it.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap07/solution ./run.sh
The conductor has the baton (ch. 6) and the rulebook (ch. 7), but does not yet know their addresses, the groups they play in. It is the file that turns "a container on port 2281" into web1, and "web1 and web2" into web — so from here on you say *ansible web* and not a list of IPs. In this lab you write it in INI, verify it with the right tools, and at the end the conductor calls the roll: ping the whole fleet, by name.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap08/solution ./run.sh
The address book answers the roll (ch. 8). Now the conductor gives the first orders — but without writing the full score (that is the playbook, ch. 10). An ad-hoc command is a *cue*: one module, one target, right now, across the whole fleet. Perfect for a one-off (is it up? how much disk? restart that service); wrong for anything to repeat or version — that needs the score. Here you learn the anatomy of the cue, the arsenal of modules, and the crucial difference — which you already sensed in ch. 5 — between a switch module and a doorbell command.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap09/solution ./run.sh
In chapter 9 the conductor gave cues on the fly: one module, one target, right now. Useful, but fleeting — no record, nothing to review, nothing to re-run with confidence. Now you write the score: the playbook, a YAML file that puts the same modules in order, with a name, under version control. This is the heart of Ansible — from here on almost everything is a playbook. You learn the layered structure (play → task → module), write your first playbook line by line, learn to read its output, and rediscover the property that matters most of all: re-running it does no harm (the acid test of ch. 5, now at scale).
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap10/solution ./run.sh
In chapter 10 privilege was a switch flipped in bulk: become: true, and everything ran as root. Now you open that box. become is not "just be root": it is asking the building's caretaker for the keys — on Linux, almost always sudo. The caretaker has a rulebook (the sudoers file): it decides *who* may take *which* key, and whether they must first show ID (the password). This chapter — the first of the Intermediate tier — shows you the anatomy of become, the rulebook under the hood, the three answers to the password, how to become a user *other* than root, and the golden rules for not leaving the master key lying around.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap11/solution ./run.sh
The score of chapter 10 was rigid: /etc/motd, port 80, those values written inside the playbook. But web1 and web2 are not identical — different ports, different limits — and rewriting the playbook for each would be chapter 1's crack returning. Variables are the pencil annotations on the score: a value with a name, written once and reused everywhere, that can arrive from many sources — the group, the single host, the command line, the facts Ansible discovers on its own. In this chapter you see what shape they take (the types), how to use them (Jinja2's double braces), where they live, how to capture a result on the fly, and how to keep them tidy.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap12/solution ./run.sh
In chapter 12 you saw the command line beat group_vars, almost without thinking. It was not magic: it was precedence. Ansible lets you define a variable in *many* places — a huge convenience — but the price is that, when two places declare the same name with different values, one must win. Ansible has a rigid chain of command: 22 levels, from the weakest (role defaults) to the strongest (-e). This chapter does not have you build infrastructure: it has you *investigate*. You provoke real clashes between variables, watch who wins, learn the three principles that explain almost everything, the two pitfalls that surprise everyone, and how to design so as never to fight.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap13/solution ./run.sh
You change a service's configuration: now it must be reloaded to read it. But reloading it on *every* run of the playbook — even when you touched nothing — is waste and risk: needless interruptions, dropped connections, for nothing. You want to reload it only if the config really changed. Ansible solves this with the notify / handler pair: a task leaves a *recall*, and at the end of the rehearsal — only if that task reported changed — the handler fires. It is the changed colour of chapter 5 that stops being a mere signal and becomes a trigger.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap14/solution ./run.sh
So far every task did one thing, once, always. But reality adapts: dev and prod are not the same, some features are optional, some actions must be repeated over twenty items. You need two new powers. when gives a task the ability to *decide*: act only *if* a condition is true. loop gives it the ability to *repeat*: one task, many items. With these two — and the braces trap that comes with them — a playbook stops being a fixed list and becomes an intelligent procedure.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap15/solution ./run.sh
The playbook of chapter 15 can decide and repeat — but it has grown: tasks, variables, templates, handlers, all piled into a single file. Tomorrow a second project will want the same app: do you copy and paste? The role is the answer. It is a section of the orchestra: a self-contained block, holding its own tasks, its own files, its own default tuning, that the conductor calls by name — and that you can reuse in any concert. In this chapter you turn that bloated playbook into a clean role, and the playbook shrinks back to three lines.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap16/solution ./run.sh
The role of chapter 16 is yours, written at home. But thousands of people have already written and shared roles and modules for every conceivable task: managing firewalls, databases, cloud providers, system services. You do not have to re-compose what already exists — you can draw on the shared repertoire. The place is Ansible Galaxy; the unit of distribution is the collection; and the way to cite each piece precisely is the fully qualified name, the FQCN. This chapter — the last of the Intermediate tier — teaches you to stand on the shoulders of giants without losing reproducibility.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap17/solution ./run.sh
In chapter 11 you asked the caretaker for the keys: become, and for the node whose sudo needs a password you made it work — but you wrote that password *in clear text* in the inventory, with the promise "one day we will encrypt it". Today is that day. A playbook ends up in a Git repository, and Git *does not forget*: a password committed in clear stays in the history forever, even if you delete it tomorrow. This chapter — the first of the Advanced tier — opens Ansible's strongbox: Ansible Vault, which encrypts secrets *inside* your files, so the repository stays shareable and the secret stays secret.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap18/solution ./run.sh
In chapter 18 you shut the password in the strongbox. But a paradox remained: the strongbox is encrypted, and its key — the vault passphrase — where does it live? If you write it in a file next to the playbook, you are back at chapter 11's sin: a secret in clear on disk. Chapter 18 moved the problem, it did not remove it. The real solution changes paradigm: the secret is not kept at all — neither in clear nor encrypted — but fetched at runtime from an external service that guards it, hands it to whoever has the right, and never lets it rest with you. That service is a strongroom: in this lab, HashiCorp Vault.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap19/solution ./run.sh
So far you have *passed* data around: a variable here, a list there, a dictionary into a template. But raw data rarely already has the shape you need: you have a list of services and want only the enabled ones; you have a base configuration and a bundle of per-environment changes, and you want them merged; you have a map and want to walk it line by line. Chapter 20 gives you the arranger: Jinja2 in its full form — the filters that transform, the tests that ask, the lookups that fetch — and the .j2 templates that, from the data, write the configuration *by themselves*. You stop writing configs by hand: the config becomes a *function* of the data.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap20/solution ./run.sh
In chapter 8 you wrote the address book by hand: a file with the nodes, one per line. It works as long as the fleet stands still. But in the real world the fleet *moves on its own*: machines that are born when load rises and die when it drops, in the cloud, while you sleep. A hand-written address book is stale the moment you save it. The answer is to flip the mechanism: instead of *listing* the nodes, you have them answer a roll-call — Ansible asks whoever really knows the fleet (the provider) "who is here now?", and builds the inventory *on the spot*. Here the provider is the Docker daemon, and the fleet is containers that come and go; but AWS, Azure, GCP work identically.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap21/solution ./run.sh
You can discover the fleet (ch. 21) and act on it. But a real orchestra plays in an imperfect world: a string snaps mid-concert, a stand falls, a player misses the cue. The question is not *whether* something will go wrong on one of the thousand nodes at the roll-call, but *what the conductor does when it happens*. By default Ansible, faced with an error, stops on that host — prudent, but not enough. This chapter gives you the tools of resilience: recover with block/rescue/always, retry what is slow, redefine what counts as an error, and — when needed — stop everything fast before the disaster spreads.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap22/solution ./run.sh
Chapter 22 taught you to *react* to errors. But the best way to handle an error is not to make it — or at least to catch it *before* you touch production. Before a concert no orchestra walks on stage blind: it re-reads the parts (is there a misprint?), holds a dress rehearsal in an empty theatre (plays everything, no audience), and only then opens the doors. Ansible gives you the same safety net, at three increasingly rich levels: --syntax-check (the quick read), ansible-lint (the expert proofreader), and check mode with --diff (the dress rehearsal that shows you what would change without changing it). Better a red error on your terminal than a silent breakage on a thousand nodes.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap23/solution ./run.sh
Chapter 23 gave you three nets — syntax-check, lint, check mode — but none of them actually *runs* the role against a real system. A clean lint and an empty-theatre rehearsal tell you the playbook is *well written* and what it *would change*, not that the role works: that it starts from scratch, converges, is idempotent, and leaves the system in the right state. That is the wall chapter 23 cannot cross. Molecule tears it down: it stands up a real but throwaway environment (a container), applies the role, checks idempotence and result, and tears it all down — one command up, one command down. It is the rehearsal on a real stage, with the certainty that you can always rebuild it from nothing.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap24/solution ./run.sh
Until now you have orchestrated a handful of nodes, and with a handful every playbook feels fast. But the Cloud Architect tier opens with a different question: what happens when the nodes become a multiplied by a thousand, repeated on every deploy. The bottleneck is no longer *what* the playbook does, but *how* Ansible spreads it across the fleet. This chapter gives you the levers to tighten that time: how many nodes to drive at once (forks), whether to march them in lock-step or let them race (strategies), and how not to pay for work nobody needs (taming facts). You measure them on a real fleet — twelve nodes — and watch them bite: the same rollout drops from ~24 seconds to ~8.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap25/solution ./run.sh
Chapter 25 gave you the speed to serve a thousand nodes. But at that scale one thing has already changed under your feet: it is no longer a person who launches the playbook. With a thousand nodes and many hands on the same code, an ansible-playbook typed by hand from someone's laptop is too fragile — nobody checked the lint, nobody knows which version it starts from, nobody stops an untested change from reaching production on a Friday evening. The answer is a stage machinery: every change passes through version control, crosses a pipeline that validates it on its own (CI), and only an authorised release crosses the gate into production (CD). This chapter builds it: the quality gates on GitHub Actions, the production gate that opens only on a tag, and the pre-commit hooks that run the check before the commit even exists.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap26/solution ./run.sh
Chapter 26 gave you the stage machinery that carries a change to the door of production. But the deploy job was one line of echo, and now the real question arrives: *how* do you update a fleet of a thousand nodes without taking the service down? Applying to everyone at once is an outage — for a few seconds or minutes, every backend is down at the same moment. A conductor does not stop the whole orchestra to let one violinist change a string: they bring sections in and out one at a time, and the music never stops. This is orchestration: the rolling update. This chapter gives you the levers — before you touch it), the choreography pre_tasks/tasks/post_tasks (drain → update → re-enable), and them at work on a web farm where, at every instant, no more than one wave is out of rotation.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap27/solution ./run.sh
Chapter 27 gave you the wave release; chapter 26 the pipeline that launches it. But in both it is still you — or a script — typing a command at a terminal, with inventories, credentials and "who-can-do-what" kept in your head or scattered across files. That works for one person. It does not work for an point the terminal is no longer enough — the way a troupe touring from town to town is no longer enough once the city wants a resident season, with a home, a box office, a company roster and an archive. automation stops being a gesture at a terminal and becomes a service with a console, its own permissions, and its own history. This chapter assembles its core objects — the job template, versioned and validated before they go on stage. Because the Cloud Architect way to run the platform is not "click in the UI": it is GitOps here too.
Fill in the TODOs in the starter files, then run the solution's test:
cd ansible/ed1/cap28/solution ./run.sh