Operations 37 min read

Master Ansible Playbooks: From Basics to Advanced Variable Management

This guide explains what an Ansible playbook is, its YAML syntax, how to write tasks, use inventory limits, manage variables through extra‑vars, vars files, host/group vars, custom facts, control flow with when/changed_when/failed_when, delegate tasks, prompt users, apply tags, and handle errors with blocks and rescue/always sections.

Liangxu Linux
Liangxu Linux
Liangxu Linux
Master Ansible Playbooks: From Basics to Advanced Variable Management

What is an Ansible Playbook?

Fundamentally, a playbook is similar to a shell script: it strings together commands (tasks) with optional conditionals, but unlike a shell script it can run on multiple hosts simultaneously, requiring you to specify the target hosts.

Playbook Syntax

Playbooks use YAML. A file starts with three dashes ( ---) and uses indentation to define blocks. A minimal example from the official documentation looks like this:

---
- hosts: webservers
  vars:
    http_port: 80
    max_clients: 200
  remote_user: root
  tasks:
  - name: ensure apache is at the latest version
    yum: pkg=httpd state=latest
  - name: write the apache config file
    template: src=/srv/httpd.j2 dest=/etc/httpd.conf

Indentation works like Python to delimit code blocks.

Simple Example: Check MySQL Status

---
- hosts: all
  remote_user: root
  gather_facts: no
  tasks:
  - name: check the mysql status
    service: name=mysqld state=running

Running the playbook with ansible-playbook test.yml produces output showing each host’s result.

Limiting Execution to Specific Hosts

Use the --limit option to run a playbook only on a subset of hosts defined in the inventory:

[root@test2 playbook]# ansible-playbook test.yml --limit dbservers

Listing Hosts in a Playbook

[root@test2 playbook]# ansible-playbook test.yml --list-hosts

Other useful command‑line options include: --inventory=path – specify an inventory file (default /etc/ansible/hosts). -v, -vvv, -vvvv – increase verbosity. --extra-vars=vars – define variables on the command line. --forks – set the number of parallel processes (default 5). --connection=type – choose the remote connection method (default ssh). --check – run in dry‑run mode; tasks are evaluated but not executed.

Handlers

Handlers let you trigger actions (e.g., service restarts) only when a task reports a change. Define them with a handlers: section and invoke them via notify:

handlers:
  - name: restart memcached
    service: name=memcached state=restarted
  - name: restart apache
    service: name=apache state=restarted

- name: template configuration file
  template: src=template.j2 dest=/etc/foo.conf
  notify:
    - restart memcached
    - restart apache

Key points:

Handlers run only if the notifying task actually executes.

They run once at the end of each play unless you use meta: flush_handlers.

If a play fails before reaching a handler, you can force execution with --force-handlers.

Variables

Variables can be supplied in several ways:

Extra vars on the command line: ansible-playbook test.yml --extra-vars "test_var=test".

vars block inside the playbook:

---
- hosts: all
  remote_user: root
  gather_facts: no
  vars:
    test_var: Hello World
  tasks:
  - name: test playbook variables
    command: echo {{ test_var }}

vars_files to include an external YAML file.

---
- hosts: all
  remote_user: root
  gather_facts: no
  vars_files:
    - vars.yml
  tasks:
  - name: test playbook variables
    command: echo {{ test_var }}

The vars.yml file simply contains the variable definitions.

Inventory variables can be defined per host or per group directly in the /etc/ansible/hosts file, e.g., 10.0.102.212 test_var=212.

For larger inventories, use host_vars/ and group_vars/ directories. Each file is named after a host or group and contains YAML variable definitions.

# Example host_vars/10.0.102.162
---
test_var: 162

Group variables apply to all hosts in the group:

# group_vars/all
---
test_group_var: from group

Using Hostvars and Built‑in Facts

You can access any host’s variables with hostvars['host1']['admin_user']. Common built‑in facts include groups, group_names, inventory_hostname, inventory_hostname_short, and play_hosts.

Registered Variables

Use register to capture a task’s result (stdout, stderr, changed, delta) for later use:

- name: test the register variables
  shell: uptime
  register: results

- name: print the register result
  debug: msg="{{ results.stdout }}"

Control Flow

when statements let tasks run only when a condition is true, e.g., stopping MySQL on a specific IP:

- name: shut down the db server
  service: name=mysqld state=stopped
  when: ansible_eth0.ipv4.address == "10.0.102.162"

Additional conditional keywords are changed_when and failed_when.

Task Delegation

Use delegate_to to run a task on a different host than the one currently being processed, or local_action to run it on the control machine:

- name: stop the db server
  service: name=mysqld state=stopped
  delegate_to: 10.0.102.162

- name: create a local test file
  local_action: shell touch test1111

Waiting for Services

Use the wait_for module to pause execution until a port becomes reachable:

- name: wait for webserver to start
  local_action:
    module: wait_for
    host: webserver1
    port: 80
    delay: 10
    timeout: 300
    state: started

Interactive Prompts

The vars_prompt keyword asks the user for input at runtime:

---
- hosts: all
  remote_user: root
  vars_prompt:
    - name: share_user
      prompt: "what is your network username?"
      private: no
    - name: share_pass
      prompt: "what is your network password"
      private: no

Collected values can be referenced later with {{ share_user }} and {{ share_pass }}.

Tags

Tags let you run or skip specific tasks or roles. Define them with a tags: list on a play, role, or task, then invoke with --tags or --skip-tags on the command line.

Blocks, Rescue, and Always

Blocks group related tasks and allow error handling:

- block:
    - name: risky command
      script: monitoring-connect.sh
  rescue:
    - debug: msg="There was an error in the block"
  always:
    - debug: msg="This always executes"

If any task inside the block fails, the rescue section runs; the always section runs regardless of success or failure.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DevOpsVariablesPlaybookHandlers
Liangxu Linux
Written by

Liangxu Linux

Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.