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
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 toroot).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 usebecome: truewith 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) │
└───────────────┘
ok: [host]: The managed node ALREADY matches the desired state defined in the task. Ansible takes no action.changed: [host]: The managed node DIFFERED from the desired state. Ansible executed changes to align it with the playbook. (This triggers Handlers!).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.