Ansible - Architecture & Core Concepts
Chapter 1 — Architecture & Core Concepts
1. Theory
Ansible's architecture has four core pieces: the control node (where Ansible runs), the inventory (host list + grouping), modules (idempotent units of work — e.g. "ensure package X is installed"), and playbooks (ordered lists of plays/tasks declaring desired state). Idempotency is the central design principle: running the same playbook twice should produce the same end state without duplicating side effects.
2. Internal Working
A playbook run compiles into an internal task list per host, executed largely in
parallel across hosts (default 5 forks) but sequentially task-by-task within a single
host, unless a strategy like free is used. Each module checks current state before
acting — e.g. the package module queries whether the package is already installed and
reports changed: false if nothing needed to happen.
3. Diagram
Inventory ──▶ Playbook ──▶ Play (hosts: web) ──▶ Task 1 ──▶ Task 2 ──▶ Task 3
│
▼
┌─────────────────────────────┐
│ For each host (parallel, │
│ default forks=5): │
│ run Task 1 → check/change │
│ run Task 2 → check/change │
└─────────────────────────────┘
4. Commands
ansible-doc -l | grep package # list modules matching "package"
ansible-doc apt # full docs for a specific module
ansible-playbook site.yml -vvv # verbose, shows exact module invocation
ansible-playbook site.yml --check # dry-run, no changes applied
ansible-playbook site.yml --diff # show file content diffs
5. Code Examples
# site.yml — a minimal playbook demonstrating idempotency
- name: Ensure nginx is installed and running
hosts: web
become: true
tasks:
- name: Install nginx
apt:
name: nginx
state: present
register: install_result
- name: Show whether anything changed
debug:
msg: "Changed: {{ install_result.changed }}"
- name: Ensure nginx service is running
service:
name: nginx
state: started
enabled: true
Running this twice: the first run shows changed: true for install, the second shows
changed: false — proof of idempotency.
6. Real-world Example
A new engineer joins and needs their laptop's dev environment to match everyone else's.
Instead of a wiki page of manual steps, they run the team's existing bootstrap.yml
playbook against localhost — the same playbook used for servers — and get an identical,
reproducible setup in minutes.
7. Production Example
Production Ansible runs are typically triggered from CI/CD (not laptops), against a
locked inventory, with --check --diff run first as a required pipeline gate so a human
reviews the plan before real execution — mirroring how Terraform plan/apply is treated.
8. Common Mistakes
- Assuming all tasks run across all hosts simultaneously — within one host, tasks are strictly sequential; only the host-level parallelism is concurrent.
- Writing tasks that aren't actually idempotent (e.g.
shell: echo "x" >> file.confappends every run) and being surprised when repeated runs change more than intended. - Treating
changed: falseoutput as "nothing happened" without realizing it means the module determined the desired state already existed.
9. Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
Task shows changed: true every run |
Non-idempotent module usage (e.g. raw shell/command) |
Use a purpose-built module (lineinfile, copy, template) or add creates/removes guards |
| Playbook hangs on one host | Forks limit + a slow/unreachable host blocking a batch | Increase forks, or use strategy: free |
| Wrong module version behavior | Multiple Ansible versions installed | Check ansible --version and which ansible for conflicts |
10. Security Notes
become: truegives task-level root — treat playbooks withbecomethe same way you'd treat sudo access itself: reviewed, version-controlled, and access-restricted.- Idempotent modules reduce security risk indirectly: fewer surprise side-effects means fewer unreviewed state changes creeping into infrastructure over time.
11. Interview Questions
- What makes a module idempotent? It checks current state before acting and only changes what's needed to reach the declared state.
- What's the default parallelism model? Host-level parallel (default 5 forks), task-level sequential per host.
- What does
--checkdo? Simulates the run without applying changes (dry-run), though not all modules support it perfectly. - Why prefer
package/copy/templateover rawshell/command? They're idempotent and structured;shell/commandare imperative and re-run every time. - What is a "play" vs a "task" vs a "playbook"? Playbook = file containing one or more plays; play = hosts + tasks; task = single module invocation.
12. Hands-on Lab
- Write the nginx playbook above targeting a local container.
- Run it once, note
changed: true. - Run it again, confirm
changed: falsefor the install task. - Replace the
apttask withshell: apt-get install -y nginxand observe it reportschanged: trueon every run — demonstrating non-idempotent behavior.
13. Hacks
ansible-playbook site.yml --list-taskspreviews exactly what will run without executing anything.register+debugis the fastest way to inspect a module's raw JSON return value while learning a new module.
14. Workarounds
- For genuinely one-off imperative commands with no matching module, use
command/shellwith acreates:orunless:-style guard (viawhenon a priorstatcheck) to restore idempotency manually.
15. Exercises
- Convert a
shell: touch /tmp/markertask into an idempotent equivalent usingfile. - Time a playbook run with
forks=5vsforks=20against 20 local containers and compare.
16. Quiz
- What are the four core architectural pieces of Ansible?
- Default fork count?
- What flag previews planned changes without applying them?
- True/False: tasks within a single host run in parallel by default.
- Name one module that's a safer idempotent alternative to
shellfor installing packages.
Answer Key
- Control node, inventory, modules, playbooks
- 5
--check- False
package/apt/yum(any package manager module)