G
GuideDevOps
Lesson 5 of 14

Playbooks

Part of the Ansible tutorial series.

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?

  1. Version Control: You commit playbooks to Git. Your entire infrastructure becomes documented and reviewable.
  2. Idempotence: Playbooks can be run 100 times safely. Ansible will only make changes when the actual state differs from the playbook's desired state.
  3. 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 boot

The --- 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.yml

Reading 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:

  1. 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.
  2. ok (Green): The target was already in the desired state. Ansible did nothing.
  3. changed (Yellow): The target was not in the desired state. Ansible made a modification to fix it.
  4. 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.conf

Ansible 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 --diff

If a task is marked changed in Check Mode, it means the playbook would have modified the system if run normally.


Playbook Best Practices

  1. 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).
  2. 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
  3. Commit to Git. Your inventory.ini and playbook.yml files should be version-controlled together in a repository.