What is a Playbook?
If Ad-Hoc commands are the Ansible equivalent of typing a single command into a terminal, Playbooks are the equivalent of writing a robust, complex bash script.
A Playbook is a YAML file that declares your desired infrastructure state. It contains an ordered list of tasks mapping to groups of hosts in your inventory.
Why Playbooks?
- Version Control: You commit playbooks to Git. Your entire infrastructure becomes documented and reviewable.
- Idempotence: Playbooks can be run 100 times safely. Ansible will only make changes when the actual state differs from the playbook's desired state.
- Complexity: Playbooks support variables, loops, conditionals, and error handling.
Anatomy of a Playbook
A playbook is a list of dictionaries in YAML. Each primary dictionary is called a "Play".
A Play maps a group of hosts (e.g., webservers) to a list of tasks (e.g., Install Nginx, Start Nginx). A single playbook file can contain multiple plays.
Here is a classic, introductory playbook (webserver.yml) that installs and configures Nginx:
---
# This is a Play!
- name: Configure Web Servers # A human-readable description of the play
hosts: webservers # Which group from the inventory to target
become: yes # Use sudo privilege escalation
# The list of actions to perform
tasks:
- name: Ensure Nginx is installed
apt: # The module being used
name: nginx # Module parameter
state: present # Desired state
update_cache: yes # Run apt-get update before installing
- name: Start and enable the Nginx service
service: # Another module
name: nginx
state: started # Ensure it is currently running
enabled: yes # Ensure it starts automatically on bootThe --- Header
YAML files traditionally begin with three dashes ---. It indicates the start of the YAML document. While technically optional in modern Ansible, it is considered a strict best practice to include it.
Running a Playbook
To execute a playbook, you use the ansible-playbook CLI command, providing your inventory file and the playbook file.
ansible-playbook -i inventory.ini webserver.ymlReading the Output
When you run a playbook, Ansible logs its progress to the terminal. The output is highly structured and color-coded.
PLAY [Configure Web Servers] **********************************************************
TASK [Gathering Facts] ****************************************************************
ok: [web1.example.com]
ok: [web2.example.com]
TASK [Ensure Nginx is installed] ******************************************************
changed: [web1.example.com] ← Yellow (State was changed!)
ok: [web2.example.com] ← Green (Nginx was already installed!)
TASK [Start and enable the Nginx service] *********************************************
ok: [web1.example.com]
ok: [web2.example.com]
PLAY RECAP ****************************************************************************
web1.example.com : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
web2.example.com : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 Key takeaways from the output:
- Gathering Facts: Before running your tasks, Ansible implicitly runs a setup module to discover variables about your target machines (OS version, IP addresses, disk space). We will discuss these "Facts" in a later tutorial.
- ok (Green): The target was already in the desired state. Ansible did nothing.
- changed (Yellow): The target was not in the desired state. Ansible made a modification to fix it.
- failed (Red): The module crashed or the operation was invalid. The playbook halts for that specific server.
Multi-Play Playbooks
You do not need to write a separate YAML file for every tier of your infrastructure. You can chain multiple Plays sequentially in a single playbook.
---
# Play 1: Configure Databases
- name: Setup Database Tier
hosts: databases
become: yes
tasks:
- name: Install PostgreSQL
apt:
name: postgresql
state: present
# Play 2: Configure Web Servers
- name: Setup Web Tier
hosts: webservers
become: yes
tasks:
- name: Install Nginx
apt:
name: nginx
state: present
- name: Write Web Config
copy:
src: /local/path/nginx.conf
dest: /etc/nginx/nginx.confAnsible executes top-to-bottom. It will completely finish the database play on all database servers before it begins the web tier play.
Dry Runs (Check Mode)
What if you want to know what a playbook would change, without actually making the changes? This is critical for auditing changes before deploying to production.
Use the --check flag (often combined with --diff to show exact line changes in files):
ansible-playbook -i inventory.ini webserver.yml --check --diffIf a task is marked changed in Check Mode, it means the playbook would have modified the system if run normally.
Playbook Best Practices
- Always name your plays and tasks. A task without a
name:attribute will just output the module name (e.g.,TASK [apt]). This makes debugging logs a nightmare. Give them descriptive, human-readable names (name: Install Nginx web server). - Use YAML dictionary formatting.
# ❌ Bad (Inline syntax string - hard to read) - apt: "name=nginx state=present update_cache=yes" # ✅ Good (Standard YAML syntax - easy to read and version control) - apt: name: nginx state: present update_cache: yes - Commit to Git. Your
inventory.iniandplaybook.ymlfiles should be version-controlled together in a repository.