Understanding Ansible’s syntax is not the hard part. The hard part is knowing which mode to use when—and why running the wrong command in production without --check is a patching incident waiting to happen. Operational fluency means knowing your modules, your targeting options, and your safety levers before you touch a single managed node.
Ad-Hoc Commands vs Playbooks
| Ad-Hoc | Playbook | |
|---|---|---|
| Use for | One-off tasks, quick checks, validation | Repeatable, auditable workflows |
| Format | Single CLI command | YAML file |
| Idempotent | Depends on module | Yes, by design |
| Auditable | No | Yes (version-controlled) |
Use ad-hoc commands for investigation and quick fixes. Use playbooks for anything that will run more than once.
Essential Modules for Patching
| Module | Distro | Purpose |
|---|---|---|
ansible.builtin.apt |
Debian / Ubuntu | Update, upgrade, install packages |
ansible.builtin.yum |
RHEL / CentOS 7 | Same for older RHEL |
ansible.builtin.dnf |
RHEL 8+ / Fedora | Same for modern RHEL |
ansible.builtin.package |
Agnostic | Delegates to the host’s native manager |
ansible.builtin.service |
All | Start, stop, restart services post-patch |
ansible.builtin.reboot |
All | Controlled reboot with wait |
For most multi-distro environments, prefer ansible.builtin.package to keep playbooks portable. Drop to apt or dnf only when you need distro-specific flags.
Inventory Targeting
# All hosts in inventory
ansible all -i inventory.ini -m ping
# Single group
ansible webservers -i inventory.ini -m ping
# Multiple groups
ansible 'webservers:dbservers' -i inventory.ini -m ping
# Intersection (hosts in both groups)
ansible 'webservers:&production' -i inventory.ini -m ping
# Exclusion (webservers except those also in maintenance)
ansible 'webservers:!maintenance' -i inventory.ini -m ping
# Single host
ansible web01.example.com -i inventory.ini -m ping
Privilege Escalation
Most patching operations require root. Use become:
# Ad-hoc with become
ansible webservers -i inventory.ini -m apt -a "upgrade=dist update_cache=true" --become
# In a playbook
- name: Full dist-upgrade
hosts: webservers
become: true # applies to all tasks
tasks:
- name: Upgrade all packages
ansible.builtin.apt:
upgrade: dist
update_cache: true
become defaults to sudo. You can specify become_method: su or become_user: root explicitly. Never store the become password in plain text—use Ansible Vault (covered in part three).
Check Mode and Diff Mode
These are your primary safety levers before any production patching run.
# Simulate only — no changes made
ansible-playbook -i inventory.ini patch.yml --check
# Show what would change in files and packages
ansible-playbook -i inventory.ini patch.yml --check --diff
# Enforce check mode inside a specific task (playbook-level)
- name: Upgrade packages (dry run enforced)
ansible.builtin.apt:
upgrade: dist
check_mode: true
Make --check --diff the default for any first run against a new environment. Only remove it when you have reviewed the diff output and confirmed it matches expectations.
Tags for Selective Execution
Tags let you run specific subsets of a playbook without duplicating files.
---
- name: Patch and validate
hosts: all
become: true
tasks:
- name: Update package cache
ansible.builtin.apt:
update_cache: true
tags: [cache, always]
- name: Install security updates only
ansible.builtin.apt:
upgrade: dist
default_release: "-security"
tags: [security]
- name: Full dist-upgrade
ansible.builtin.apt:
upgrade: dist
tags: [full-upgrade]
- name: Restart services
ansible.builtin.service:
name: ""
state: restarted
loop:
- nginx
- ssh
tags: [restart]
Run security updates only:
ansible-playbook -i inventory.ini patch.yml --tags security
Skip the restart step:
ansible-playbook -i inventory.ini patch.yml --skip-tags restart
Practical Reference: Common Patching Commands
# Check what packages are outdated (no changes)
ansible webservers -i inventory.ini -m apt -a "upgrade=dist update_cache=true" --become --check
# Apply security updates to web servers only
ansible webservers -i inventory.ini -m apt -a "upgrade=dist" --become
# Install a specific package version
ansible all -i inventory.ini -m apt -a "name=openssl=3.0.2-0ubuntu1.15 state=present" --become
# Restart a service after manual emergency patch
ansible dbservers -i inventory.ini -m service -a "name=mysql state=restarted" --become
# Reboot all servers in a group with a wait
ansible webservers -i inventory.ini -m reboot -a "reboot_timeout=300" --become
Previous: Getting Started with Ansible for Patch Management
Next in the series: Ansible Architecture and Best Practices — roles, directory structure, idempotency, Ansible Vault, and how poor variable handling leaks credentials into CI/CD logs.
