# Must-Know Ansible Commands and Core Concepts

Date: 2025-11-06

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.

<!--more-->

## 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

```shell
# 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`:

```shell
# 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.

```shell
# 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.

```yaml
---
- 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: "{{ ansible_distribution_release }}-security"
      tags: [security]

    - name: Full dist-upgrade
      ansible.builtin.apt:
        upgrade: dist
      tags: [full-upgrade]

    - name: Restart services
      ansible.builtin.service:
        name: "{{ item }}"
        state: restarted
      loop:
        - nginx
        - ssh
      tags: [restart]
```

Run security updates only:

```shell
ansible-playbook -i inventory.ini patch.yml --tags security
```

Skip the restart step:

```shell
ansible-playbook -i inventory.ini patch.yml --skip-tags restart
```

---

## Practical Reference: Common Patching Commands

```shell
# 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](/ansible/devops/2025/11/03/ansible-patch-management-getting-started.html)

**Next in the series:** [Ansible Architecture and Best Practices](/ansible/devops/2025/11/10/ansible-architecture-best-practices.html) — roles, directory structure, idempotency, Ansible Vault, and how poor variable handling leaks credentials into CI/CD logs.
