What is a Playbook? #
If the inventory defines who we manage in the infrastructure, then the Playbook is the component that defines what automation actions we need to perform against them. Playbooks act as the heart of the Ansible automation ecosystem. Written in the declarative YAML markup language, playbooks let you document a series of ordered workflows — from system package installation, configuration editing, to service lifecycle management — to be executed repeatedly with consistent results. This article covers the basic concept of declarative automation, compares ad-hoc commands with playbooks, dissects playbook file anatomy, explains the main components of the play block, and guides you through execution and result report tracing.
The Declarative Concept #
One pillar of Ansible playbook writing philosophy that sets it apart from traditional bash scripts is the Declarative Approach. In imperative programming (like shell scripts), you write how the technical steps should be executed procedurally. You have to check whether a directory already exists, write if-else logic to check package status, and handle errors manually.
In contrast, the declarative approach in Ansible playbooks requires you to focus on defining the desired end state (desired state or what), not the technical procedure to reach it.
Here’s a visualization of how Ansible processes the declarative playbook state from the physical file to runtime execution on target servers:
flowchart TD
A["YAML Playbook (site.yml)"] -->|"1. Parse & Syntax Validation"| B["Ansible Playbook Executor"]
B -->|"2. Read Inventory (hosts.ini)"| C["Host Mapping (webservers, dbservers)"]
C -->|"3. Assemble Variables & Precedence"| D["HostVars Database (Memory)"]
D -->|"4. Execute Play 1 (Linear)"| E["Managed Node Batch 1"]
D -->|"5. Execute Play 2 (Linear)"| F["Managed Node Batch 2"]
E -->|"6. Collect Output & Recap"| G["Result Report in Terminal (PLAY RECAP)"]
F -->|"6. Collect Output & Recap"| GThe Ansible Engine intelligently detects the target server’s current actual state, compares it with the end state you want in the playbook, and only takes change actions if the actual state doesn’t match the end state. This principle is what delivers the reliability of Idempotency.
Ad-hoc Commands vs Playbooks Comparison #
Ansible provides two execution methods: Ad-hoc Commands (Quick Commands) and Playbooks. You should choose the most efficient method based on your operational task context.
1. Ad-hoc Commands (Single Action Commands) #
Single commands run directly from the command-line terminal without being saved into an automation file. This method is great for quick, one-time maintenance tasks.
- Use Cases: Checking target server memory capacity, doing a quick reboot, or copying one emergency file to managed nodes.
- Command Examples:
# Check disk capacity on servers in the webservers group ansible webservers -m command -a "df -h" -u ubuntu # Install the curl package on all managed nodes in parallel ansible all -m apt -a "name=curl state=present" --become
2. Playbooks (Structured Runbooks) #
YAML-format text files that store the entire series of complex automation workflows in a structured way.
- Use Cases: Provisioning a new server from scratch, multi-tier application release deployment, database cluster configuration, or server security hardening.
- Command Example:
# Run the entire structured configuration workflow ansible-playbook -i inventory/production/hosts.ini playbooks/site.yml
The table below summarizes the comparative attributes between Ad-hoc and Playbook:
| Comparison Attribute | Ad-hoc Commands | Playbooks |
|---|---|---|
| Work Structure | Single, linear action | Multi-action, multi-host, hierarchical |
| Portability | Not stored, one-time use | Stored in Git, highly reusable |
| Readability | Hard to understand if arguments are long | Declarative, structured, easy to audit |
| Handlers Support | Not supported | Natively supported (event triggers) |
| Logic Storage | Very limited | Very powerful (loops, conditionals, blocks) |
Playbook File Structure Anatomy #
A playbook file consists of one or more Play blocks (Scenarios). A single Play maps a set of target servers (from the inventory) with the series of tasks (Tasks) to be executed by Ansible modules.
Here’s the visual anatomy of an industry-standard playbook file:
# File: playbooks/setup-webserver.yml
---
# The triple dash marks the start of the YAML document
# ==================== PLAY 1: Web Tier Setup ====================
- name: Configure the Nginx Web Server
hosts: webservers
become: true
vars:
http_port: 80
app_directory: /var/www/my-app
tasks:
- name: Install the Nginx package
apt:
name: nginx
state: present
- name: Copy the virtual host configuration file
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/default
notify: Trigger Nginx Restart
- name: Ensure the Nginx service is active and enabled at boot
systemd:
name: nginx
state: started
enabled: true
handlers:
- name: Trigger Nginx Restart
systemd:
name: nginx
state: restarted
# ==================== PLAY 2: Database Tier Setup ====================
- name: Configure the PostgreSQL Database Server
hosts: dbservers
become: true
tasks:
- name: Install the PostgreSQL package
apt:
name: postgresql
state: present
Structural Element Explanation: #
- name:(Play Level): The global scenario explanation label. This text output appears in your terminal console to make workflow review easier.hosts:: Points to the target servers (in the example above, thewebserversgroup in Play 1 and thedbserversgroup in Play 2).tasks:: The list of declarative work instructions executed sequentially from top to bottom.handlers:: Special tasks acting as signal receivers (event listeners). These tasks don’t run unless a task in thetasks:section produces achangedstatus and sends anotify:signal whose name matches that handler.
Main Components of the Play Block #
When composing a Play block, you can use Ansible’s built-in control parameters to manage the automation flow:
1. hosts (Target Filtering) #
Determines the execution target servers. You can use flexible writing patterns:
hosts: webservers:dbservers # Group union (webservers OR dbservers)
hosts: webservers:&jakarta # Group intersection (webservers that are also members of the jakarta group)
hosts: all:!bastion # All servers in the inventory EXCEPT the bastion server
2. become (Privilege Escalation) #
Controls whether Ansible should run all tasks under this play with administrative (root) privileges:
become: true # Enable escalation
become_method: sudo # Uses the sudo command (Linux default)
become_user: root # Becomes the root user (default)
3. vars (Play Local Variables) #
Defines local configuration parameters that only apply within the scope of this play block. These variables have higher precedence than inventory variables in group_vars/all.yml.
4. gather_facts (Target Data Collection) #
By default, before running the first task, Ansible runs an implicit task called Gathering Facts to detect detailed target server information (like IP addresses, OS type, remaining RAM, disk capacity).
gather_facts: false
If your playbook doesn’t need any target hardware information variables at all (for example, your playbook only triggers external APIs or performs reboots), you are strongly recommended to set gather_facts: false to significantly cut playbook execution duration (saving SSH handshake data negotiation time).
Complete CLI Execution Guide #
You use the ansible-playbook binary command to run your playbook scenario files. Ansible provides advanced CLI arguments to help with safe execution control.
Here’s the list of execution commands every automation developer must master:
1. Running a Standard Playbook #
ansible-playbook -i inventory/production/hosts.ini playbooks/setup-webserver.yml
2. Dynamically Limiting Targets (Limit) #
If the playbook says hosts: webservers but you only want to test execution against the prod-web-01 server without changing the physical playbook code:
ansible-playbook -i inventory/production/hosts.ini playbooks/setup-webserver.yml --limit "prod-web-01.unisbadri.com"
3. Running a Simulation Test (Dry Run) #
Using the --check option (or dry run) makes Ansible process the playbook flow and compare system status without sending any modification commands to target servers. Very important before applying changes on production servers:
ansible-playbook -i inventory/production/hosts.ini playbooks/setup-webserver.yml --check
4. Showing Code Change Details (Diff Mode) #
The --diff option displays the system configuration file changes visually (similar to git diff) before and after execution. Great when combined with check mode:
ansible-playbook -i inventory/production/hosts.ini playbooks/setup-webserver.yml --check --diff
5. Resuming Execution from a Specific Task #
If your playbook execution fails mid-way at task number 10 due to a network problem, after fixing the issue, you can resume execution directly from the failed task without repeating tasks 1 through 9:
ansible-playbook -i inventory/production/hosts.ini playbooks/setup-webserver.yml --start-at-task "Copy the virtual host configuration file"
Understanding Result Reports and Play Recap #
When a playbook executes, Ansible prints an interactive report to the terminal screen. At the end of execution, Ansible presents a summary report called PLAY RECAP:
PLAY RECAP *****************************************************************************************
prod-web-01.unisbadri.com : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0
prod-web-02.unisbadri.com : ok=3 changed=0 unreachable=0 failed=0 skipped=1 rescued=0
prod-db-01.unisbadri.com : ok=2 changed=0 unreachable=1 failed=0 skipped=0 rescued=0
Report Status Explanation: #
ok: The number of tasks that ran successfully without requiring modification actions because the target server was already in the desired end state (idempotency satisfied).changed: The number of tasks that successfully made system modification changes to match the desired end state.unreachable: Indicates Ansible failed to initiate an SSH connection to the target server (network problems, wrong user, closed port, or rejected private key).failed: The number of tasks that failed to execute (script crashes, wrong module arguments, missing package dependencies). If a host has afailedstatus, Ansible by default stops all subsequent tasks for that host.skipped: The number of tasks skipped because they didn’t meet thewhenconditional parameters.
Playbooks as Living Documentation #
In the modern infrastructure engineering era, one of the main added values of playbooks is their function as Executable Documentation.
In conventional server administration, server installation guides are recorded in static text files or internal company wiki pages. Those wikis are often inaccurate because over time, the actual server system configuration keeps changing without anyone updating the wiki notes.
Ansible playbooks solve this problem. Because playbooks are written in declarative YAML with communicative descriptive - name: parameters, playbooks automatically represent the actual blueprint of your infrastructure that anyone can read, while also being runnable at any time to ensure server state compliance.
# File: playbooks/audit-hardening.yml
# This file acts as an audit file and security compliance enforcer
---
- name: Audit and Enforce SSH Security Compliance
hosts: all
become: true
tasks:
- name: Ensure root SSH access is closed
lineinfile:
path: /etc/ssh/sshd_config
regexp: "^PermitRootLogin"
line: "PermitRootLogin no"
state: present
notify: Restart SSH Service
- name: Ensure SSH password authentication is disabled
lineinfile:
path: /etc/ssh/sshd_config
regexp: "^PasswordAuthentication"
line: "PasswordAuthentication no"
state: present
notify: Restart SSH Service
handlers:
- name: Restart SSH Service
systemd:
name: sshd
state: restarted
Sharing this file to the company Git repository lets the security audit team review the SSH security status of all servers just by reading the code above, without having to manually log into hundreds of servers.
Summary #
- Declarative End State — Ansible playbooks focus on defining the desired end state, letting the engine handle the idempotent workflow details automatically.
- Work Efficiency — Use ad-hoc commands only for quick one-time diagnostic actions; use playbooks for structured, reusable configuration automation.
- Central Anatomy — Playbooks consist of Play blocks that map target servers (inventory) with automation instructions (tasks) and follow-up action triggers (handlers).
- gather_facts Time Savings — Disable target fact gathering (
gather_facts: false) if your playbook workflow doesn’t need to read server hardware parameters.- Advanced CLI Control — Get into the habit of running check mode simulations (
--check --diff) before rolling automation releases to production environments to detect potential conflicts.- PLAY RECAP Evaluation — Monitor the final recap output diligently to track target host health status, especially if
failedorunreachableindicators are detected.- Integrated Documentation — Write descriptive task name parameters (
- name:) that explain the task’s functional purpose, not just the terminal command being run.