AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible Playbook: Write and Run Your First Playbook (Complete Guide)

By Luca Berton · Published 2024-01-01 · Category: installation

How to write and run Ansible playbooks. Complete guide to playbook structure, plays, tasks, variables, handlers, loops, conditionals, and best practices.

Ansible Playbook: Write and Run Your First Playbook (Complete Guide)

An Ansible playbook is a YAML file that defines automation tasks to run on remote hosts. Playbooks are the core of Ansible — they describe the desired state of your infrastructure in a human-readable format.

See also: Ansible Variables: Define, Use, and Override Variables (Complete Guide)

Playbook Structure

---
# A playbook contains one or more plays
- name: Configure web servers        # Play 1
  hosts: webservers                   # Target hosts
  become: true                        # Run as root
  vars:                               # Variables
    http_port: 80

  tasks:                              # List of tasks
    - name: Install nginx
      ansible.builtin.apt:
        name: nginx
        state: present

    - name: Start nginx
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: true

  handlers:                           # Run only when notified
    - name: restart nginx
      ansible.builtin.service:
        name: nginx
        state: restarted

- name: Configure databases           # Play 2
  hosts: databases
  become: true
  tasks:
    - name: Install PostgreSQL
      ansible.builtin.apt:
        name: postgresql
        state: present

Run a Playbook

# Basic run
ansible-playbook playbook.yml

# Specify inventory
ansible-playbook -i inventory.ini playbook.yml

# Limit to specific hosts
ansible-playbook playbook.yml --limit webserver01

# Check mode (dry run)
ansible-playbook playbook.yml --check --diff

# Extra variables
ansible-playbook playbook.yml -e "http_port=8080"

# Verbose output
ansible-playbook playbook.yml -vvv

# Start at a specific task
ansible-playbook playbook.yml --start-at-task="Install nginx"

# Step through tasks one by one
ansible-playbook playbook.yml --step

# List tasks without running
ansible-playbook playbook.yml --list-tasks

# List hosts that would be affected
ansible-playbook playbook.yml --list-hosts

See also: Configuring Ansible for VMware: Complete Setup Guide & Playbook

Variables

- hosts: webservers
  become: true
  vars:
    app_name: mywebapp
    app_port: 8080
    app_user: deploy
    packages:
      - nginx
      - python3
      - python3-pip

  tasks:
    - name: Install packages
      ansible.builtin.apt:
        name: "{{ packages }}"
        state: present

    - name: Create app user
      ansible.builtin.user:
        name: "{{ app_user }}"
        shell: /bin/bash

    - name: Deploy config
      ansible.builtin.template:
        src: app.conf.j2
        dest: "/etc/{{ app_name }}/config.yml"

Variable Files

- hosts: webservers
  vars_files:
    - vars/common.yml
    - "vars/{{ ansible_os_family }}.yml"

Register Task Output

- name: Check disk space
  ansible.builtin.command: df -h /
  register: disk_result
  changed_when: false

- name: Show disk space
  ansible.builtin.debug:
    var: disk_result.stdout_lines

Conditionals

- name: Install on Debian
  ansible.builtin.apt:
    name: nginx
    state: present
  when: ansible_os_family == "Debian"

- name: Install on RedHat
  ansible.builtin.dnf:
    name: nginx
    state: present
  when: ansible_os_family == "RedHat"

# Multiple conditions
- name: Install only on Ubuntu 24.04+
  ansible.builtin.apt:
    name: nginx
  when:
    - ansible_distribution == "Ubuntu"
    - ansible_distribution_major_version | int >= 24

See also: Creating Custom Ansible Plugins to Fetch API Data Easily

Loops

# Simple loop
- name: Create multiple users
  ansible.builtin.user:
    name: "{{ item }}"
    state: present
  loop:
    - alice
    - bob
    - charlie

# Loop with dictionaries
- name: Create users with groups
  ansible.builtin.user:
    name: "{{ item.name }}"
    groups: "{{ item.groups }}"
  loop:
    - { name: alice, groups: "sudo,docker" }
    - { name: bob, groups: "developers" }

# Loop with index
- name: Show index
  ansible.builtin.debug:
    msg: "{{ idx }}: {{ item }}"
  loop: ["web", "db", "cache"]
  loop_control:
    index_var: idx

Handlers

- hosts: webservers
  become: true
  tasks:
    - name: Update nginx config
      ansible.builtin.template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify:
        - validate nginx
        - restart nginx

  handlers:
    - name: validate nginx
      ansible.builtin.command: nginx -t
      listen: "validate nginx"

    - name: restart nginx
      ansible.builtin.service:
        name: nginx
        state: restarted

Error Handling

- name: Try risky operation
  block:
    - name: Attempt deployment
      ansible.builtin.command: /opt/deploy.sh
      
    - name: Verify deployment
      ansible.builtin.uri:
        url: http://localhost:8080/health
        status_code: 200
  rescue:
    - name: Rollback on failure
      ansible.builtin.command: /opt/rollback.sh
      
    - name: Alert team
      ansible.builtin.debug:
        msg: "Deployment failed on {{ inventory_hostname }}, rolled back"
  always:
    - name: Clean up temp files
      ansible.builtin.file:
        path: /tmp/deploy-artifacts
        state: absent

Tags

- hosts: webservers
  tasks:
    - name: Install packages
      ansible.builtin.apt:
        name: nginx
      tags: [install, packages]

    - name: Deploy config
      ansible.builtin.template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      tags: [config]

    - name: Start service
      ansible.builtin.service:
        name: nginx
        state: started
      tags: [service]
# Run only tagged tasks
ansible-playbook site.yml --tags "config"

# Skip tagged tasks
ansible-playbook site.yml --skip-tags "install"

Include and Import

# Static import (parsed at playbook load time)
- name: Configure webservers
  hosts: webservers
  tasks:
    - ansible.builtin.import_tasks: tasks/install.yml
    - ansible.builtin.import_tasks: tasks/configure.yml

# Dynamic include (parsed at runtime — supports loops/when)
- name: Include OS-specific tasks
  ansible.builtin.include_tasks: "tasks/{{ ansible_os_family }}.yml"

Complete Example: Web App Deployment

---
- name: Deploy web application
  hosts: webservers
  become: true
  vars:
    app_version: "2.1.0"
    app_user: deploy
    app_dir: /opt/webapp

  pre_tasks:
    - name: Update apt cache
      ansible.builtin.apt:
        update_cache: true
        cache_valid_time: 3600

  tasks:
    - name: Install dependencies
      ansible.builtin.apt:
        name:
          - nginx
          - python3
          - python3-venv
        state: present

    - name: Create app user
      ansible.builtin.user:
        name: "{{ app_user }}"
        system: true
        create_home: false

    - name: Create app directory
      ansible.builtin.file:
        path: "{{ app_dir }}"
        state: directory
        owner: "{{ app_user }}"
        mode: '0755'

    - name: Deploy application
      ansible.builtin.unarchive:
        src: "https://releases.example.com/webapp-{{ app_version }}.tar.gz"
        dest: "{{ app_dir }}"
        remote_src: true
        owner: "{{ app_user }}"
      notify: restart webapp

    - name: Deploy nginx config
      ansible.builtin.template:
        src: templates/nginx-webapp.conf.j2
        dest: /etc/nginx/sites-available/webapp.conf
      notify: reload nginx

    - name: Enable site
      ansible.builtin.file:
        src: /etc/nginx/sites-available/webapp.conf
        dest: /etc/nginx/sites-enabled/webapp.conf
        state: link
      notify: reload nginx

  handlers:
    - name: restart webapp
      ansible.builtin.systemd:
        name: webapp
        state: restarted

    - name: reload nginx
      ansible.builtin.service:
        name: nginx
        state: reloaded

  post_tasks:
    - name: Verify app is responding
      ansible.builtin.uri:
        url: "http://localhost:8080/health"
        status_code: 200
      retries: 5
      delay: 3

FAQ

What is an Ansible playbook?

An Ansible playbook is a YAML file containing one or more plays. Each play maps a group of hosts to tasks that define the desired state. Playbooks are the primary way to automate configuration, deployment, and orchestration with Ansible.

How do I run an Ansible playbook?

Use ansible-playbook playbook.yml. Add -i inventory.ini to specify an inventory, --check for dry run, -e "var=value" for extra variables, and -v for verbose output.

What is the difference between import_tasks and include_tasks?

import_tasks is static — parsed at load time, supports tags, but can't use loops or runtime variables. include_tasks is dynamic — parsed at runtime, supports loops and conditionals, but tags don't propagate the same way.

How do I pass variables to a playbook?

Use -e on the command line (-e "version=2.0"), define in vars: section, load from vars_files:, or set in inventory. Variables from -e have the highest precedence.

What is check mode in Ansible?

Check mode (--check) is a dry run — Ansible reports what would change without making actual changes. Add --diff to see the exact differences. Not all modules support check mode.

Conclusion

Playbooks are the heart of Ansible automation. Master the structure — plays, tasks, variables, handlers, conditionals, and loops — and you can automate any infrastructure task reliably and repeatably.

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home