Playbook Structure, Tasks & Idempotent System Management Modules

Master YAML playbook structure, privilege escalation (become), core system modules (package, service, template, user, file), and the Idempotency state machine.

intermediate 25 min lesson hands-on task included

An Ansible Playbook is an ordered list of tasks written in human-readable YAML format. While an Ad-Hoc command runs a single task, a Playbook maps host groups to a series of automated steps that bring systems into a desired end state.

Understanding task execution mechanics, privilege escalation (become), and the Idempotency State Machine is essential for building production-grade infrastructure automation.


Topic 1: Playbook Anatomy & YAML Structure

PLAYBOOK EXECUTION LIFECYCLE & IDEMPOTENCY STATE MACHINE PLAYBOOK PIPELINE: site.yml 1. Gather Facts ansible_facts Evaluates desired state 2. Task 1: Package apt / yum state=present Evaluates desired state 3. Task 2: Config template / copy Evaluates desired state 4. Task 3: Service systemd state=started Evaluates desired state IDEMPOTENCY STATES: DESIRED VS CURRENT SYSTEM STATE ok: [web1] Target system ALREADY matches desired state. No action taken. changed: [web1] Target system modified to match desired state. Triggers Handlers! failed: [web1] Task error encountered. Playbook stops for this host unless rescued. DRY RUN & VERIFICATION FLAGS `ansible-playbook --check --diff site.yml` shows exact line-by-line configuration changes without modifying target hosts.
Playbook Execution Lifecycle: Evaluating task desired state against target host current state (OK vs Changed vs Failed).

Every playbook starts with --- (the YAML document start marker) and contains one or more Plays. Each Play defines:

  • name: A descriptive title for the play.
  • hosts: The target group or pattern from your inventory (webservers, all, dbservers).
  • become: Whether to escalate privileges (true/false).
  • vars: Play-level variables.
  • tasks: The sequential array of task modules to execute.
---
# Play 1: Configure Web Application Tier
- name: Deploy Web Application Server Stack
  hosts: webservers
  become: true                    # Escalate to root via sudo
  become_method: sudo
  become_user: root
  vars:
    app_port: 8080
    app_version: "2.4.1"

  tasks:
    - name: Ensure Apache package is installed
      ansible.builtin.package:
        name: httpd
        state: present

    - name: Ensure Apache web service is enabled and started
      ansible.builtin.service:
        name: httpd
        state: started
        enabled: true

Topic 2: Privilege Escalation (become, become_user, become_method)

Ansible typically connects to managed nodes as a standard unprivileged user (e.g., ubuntu or ec2-user). Tasks requiring administrative privileges (installing packages, modifying /etc/, restarting services) escalate privileges using become:

  • become: true: Tells Ansible to run the task or play with elevated privileges.
  • become_method: sudo: The privilege escalation tool to use (sudo, su, pbrun, doas).
  • become_user: root: The target user account to escalate into (defaults to root).
  • become_flags: '-H -S': Optional custom flags passed to the sudo binary.
# Specifying become at the task level rather than play level
- name: Non-root task executed as standard user
  ansible.builtin.command: whoami
  register: user_output

- name: System task requiring elevated root privileges
  ansible.builtin.package:
    name: htop
    state: present
  become: true                    # Task-level privilege escalation

[!WARNING] Root Login Security: Never configure Ansible to SSH directly into servers as root! The Red Hat security standard is to SSH as a dedicated user (e.g., ansible) using SSH keys, and use become: true with passwordless sudo rules (ansible ALL=(ALL) NOPASSWD: ALL).


Topic 3: The Idempotency State Machine (ok vs changed vs failed)

The cornerstone of Ansible’s philosophy is Idempotency. An idempotent playbook brings a system to the desired end state regardless of its starting state, without making unnecessary modifications.

When Ansible executes a task module against a target node, it evaluates system state and returns one of three core statuses:

                  ┌───────────────────────────────────────────┐
                  │          EXECUTE TASK MODULE              │
                  └─────────────────────┬─────────────────────┘
                                        │
                         Is system already in desired state?
                                        │
                    ┌───────────────────┴───────────────────┐
                    │                                       │
                  YES                                      NO
                    │                                       │
            ┌───────┴───────┐                       ┌───────┴───────┐
            │   ok: [host]  │                       │ changed:[host]│
            │ (No action    │                       │ (State updated│
            │  taken)       │                       │  to match)    │
            └───────────────┘                       └───────┬───────┘
                                                            │
                                                   Did task encounter
                                                   a non-zero error?
                                                            │
                                                    ┌───────┴───────┐
                                                    │ failed:[host] │
                                                    │ (Playbook     │
                                                    │  halts)       │
                                                    └───────────────┘
  1. ok: [host]: The managed node ALREADY matches the desired state defined in the task. Ansible takes no action.
  2. changed: [host]: The managed node DIFFERED from the desired state. Ansible executed changes to align it with the playbook. (This triggers Handlers!).
  3. failed: [host]: The task encountered an error (non-zero return code or missing file). Ansible halts playbook execution for this host immediately.

Topic 4: Comprehensive System Management Modules Reference

Ansible includes hundreds of built-in modules in the ansible.builtin namespace. Below is the production reference guide for core system modules:

1. Package Management (ansible.builtin.package)

Auto-detects the underlying package manager (apt on Ubuntu/Debian, dnf/yum on RHEL/CentOS, apk on Alpine) so playbooks remain OS-agnostic:

- name: Ensure security packages are present
  ansible.builtin.package:
    name:
      - curl
      - ufw
      - fail2ban
    state: present                # Options: present, absent, latest

2. Service Management (ansible.builtin.service / systemd)

Controls systemd services and boot init scripts:

- name: Ensure Nginx service is enabled and started
  ansible.builtin.service:
    name: nginx
    state: started                # Options: started, stopped, restarted, reloaded
    enabled: true                 # Options: true (start at boot), false

3. File & Directory Management (ansible.builtin.file)

Manages file properties, directory trees, and symbolic links:

- name: Create app logs directory with explicit permissions
  ansible.builtin.file:
    path: /var/log/myapp
    state: directory              # Options: directory, file, link, absent, touch
    owner: appuser
    group: www-data
    mode: "0755"

- name: Create symbolic link
  ansible.builtin.file:
    src: /opt/myapp/releases/v2.4
    dest: /opt/myapp/current
    state: link
    force: yes

4. File Copying (ansible.builtin.copy & fetch)

Transfers static files from Control Node to Managed Nodes (copy), or pulls log files from Managed Nodes back to Control Node (fetch):

- name: Copy static index.html to web root with backup
  ansible.builtin.copy:
    src: files/index.html
    dest: /var/www/html/index.html
    owner: root
    group: root
    mode: "0644"
    backup: yes                   # Creates timestamped backup if file changes!

- name: Fetch diagnostic log file back to control node
  ansible.builtin.fetch:
    src: /var/log/nginx/error.log
    dest: ./fetched_logs/
    flat: yes

5. User & Group Management (ansible.builtin.user & group)

Manages Linux user accounts, SSH keys, and group memberships:

- name: Create deployment group
  ansible.builtin.group:
    name: deployers
    gid: 1500
    state: present

- name: Create deployer user account
  ansible.builtin.user:
    name: deployer
    uid: 1500
    group: deployers
    groups: sudo
    shell: /bin/bash
    generate_ssh_key: yes
    ssh_key_bits: 4096
    state: present

Topic 5: Descriptive Task Naming Standards

Red Hat best practice strictly mandates giving every single task a clear, human-meaningful name:.

# BAD (Uninformative logging)
- package: name=nginx state=latest
- service: name=nginx state=started

# GOOD (Self-documenting audit logs)
- name: Ensure Nginx package is updated to latest security release
  ansible.builtin.package:
    name: nginx
    state: latest

- name: Ensure Nginx service is running and configured for auto-restart
  ansible.builtin.service:
    name: nginx
    state: started
    enabled: true

Common mistake: Reaching for shell or command because it is the syntax you already know. You lose idempotency, --check mode and the change reporting that makes a run reviewable — every task reports changed on every run, so nobody can tell what a play actually did. Use the module, and if you must shell out, set creates/removes.