1. The Structure: Playbook → Play → Task → Module
Everything nests like Russian dolls. A playbook contains one or more plays. Each play targets a group of hosts and contains tasks. Each task calls exactly one module.
🍕 Analogy: Playbook = cookbook, Play = one recipe ("pizza for the whole class"), Task = one step ("knead the dough"), Module = the kitchen tool that does that step (the oven, the mixer). You don't build an oven each time — you reuse it with different settings.
2. A Real Playbook, Fully Labeled
Every colored zone on the left is explained on the right — same colors:
3. YAML Rules — The 4 That Matter
| Rule | Example | Why it matters |
|---|---|---|
| Indentation = structure (2 spaces, never tabs) | tasks: then two spaces before - name: | Wrong indent = wrong nesting = confusing errors |
A dash - means "list item" | - name: Install nginx | Plays and tasks are LISTS — each starts with - |
key: value pairs | state: present | Everything is key–value; note the space after : |
--- starts the file | First line of the playbook | Marks the start of a YAML document (convention) |
⚠️ #1 student mistake: mixing tabs and spaces. YAML forbids tabs for indentation. Configure your editor to insert 2 spaces per Tab press.
4. Modules = Ansible's Toolbox
There are thousands of modules; you'll use these constantly:
| Module | What it does | Mini example |
|---|---|---|
apt / yum / dnf | Install/remove packages | apt: name=nginx state=present |
copy | Copy a file to the node | copy: src=app.conf dest=/etc/app.conf |
template | Copy a file with variables filled in (Jinja2) | template: src=web.conf.j2 dest=/etc/web.conf |
service / systemd | Start/stop/enable services | service: name=nginx state=restarted |
file | Create dirs, set permissions, delete files | file: path=/opt/app state=directory |
user | Manage user accounts | user: name=deploy state=present |
command / shell | Run a raw command (last resort — not idempotent!) | shell: /opt/app/migrate.sh |
debug | Print a message/variable (great for learning) | debug: msg="Port is {{ http_port }}" |
✅ Golden rule: always prefer a real module over
shell/command. Modules are idempotent and report changed correctly; raw shell commands run blindly every time.5. Reading the Output
When you run ansible-playbook site.yml, each task prints one of three results per host: