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.

Related Articles

Getting Started with AnsibleAnsible Inventory: Complete GuideAnsible Roles: Organize Your Playbooks

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home