Ansible Lesson 8 – Ansible Ad-hoc Commands | Dataplexa
Section I · Lesson 8

Ansible Ad-hoc Commands

In this lesson

Ad-hoc syntax Common use cases Useful modules Flags & options Ad-hoc vs playbooks

An ad-hoc command is a single Ansible task executed directly from the command line — no playbook file required. It uses the ansible binary (not ansible-playbook) and runs one module against one or more hosts in a single invocation. Ad-hoc commands exist for speed: when you need to check a fact, restart a service, copy a file, or run a quick fix across your fleet right now, writing a full playbook is overkill. Ad-hoc commands are Ansible's answer to the SSH-into-every-server-manually problem — they give you the reach of automation with the immediacy of a shell command.

Anatomy of an Ad-hoc Command

Every ad-hoc command follows the same structure. Understanding each part makes it easy to construct any command on the fly without looking up the syntax.

ansible webservers -i inventory.ini -m ansible.builtin.apt -a "name=nginx" Binary Target pattern Inventory file Module name Module args ansible group or host -i flag -m flag -a flag

Key Flags and Options

The ansible command accepts several flags that you will use in almost every ad-hoc invocation. Many of these mirror settings in ansible.cfg — passing them on the command line overrides the config file for that single run.

Flag — purpose — example

-m Module to run. Defaults to ansible.builtin.command if omitted — but always specify it explicitly for clarity. Example: -m ansible.builtin.ping
-a Module arguments as a quoted string. Key=value pairs separated by spaces. Example: -a "name=nginx state=present"
-i Inventory file path. Can be omitted if inventory is set in ansible.cfg. Example: -i inventory.ini
-u Remote user to connect as. Overrides remote_user in config. Example: -u ansible
-b Enable privilege escalation (become). Equivalent to become: true in a playbook. Required for tasks that need root. Example: -b
-f Number of parallel forks for this run. Overrides the forks setting in config. Example: -f 20
-v / -vvv Verbosity level. -v shows task results; -vvv shows SSH debug output. Invaluable for diagnosing connection and module errors.

The Walkie-Talkie Analogy

Ad-hoc commands are like a walkie-talkie — instant, direct, and perfect for quick coordination. Playbooks are like a written operations manual — thorough, repeatable, and the right tool when precision and auditability matter. You would not write a manual to ask someone to check whether a door is locked. But you also would not use a walkie-talkie to coordinate a multi-stage building evacuation. Use the right tool for the scale of the task.

Common Use Cases

The following examples cover the scenarios where engineers reach for ad-hoc commands most often. Each builds on the same inventory from Lesson 7. Run these against your own environment as you read.

Use Case 1

Connectivity check — ping all hosts

# Verify all hosts in inventory are reachable and have Python available
ansible all -i inventory.ini -m ansible.builtin.ping
web01.example.com | SUCCESS => {"changed": false, "ping": "pong"}
web02.example.com | SUCCESS => {"changed": false, "ping": "pong"}
db01.example.com  | SUCCESS => {"changed": false, "ping": "pong"}
db02.example.com  | SUCCESS => {"changed": false, "ping": "pong"}

Use Case 2

Check free disk space across the fleet

# Run 'df -h' on every host — useful for a quick storage health check
# ansible.builtin.command does not use a shell — safe for simple commands
ansible all -i inventory.ini -m ansible.builtin.command -a "df -h"

Use Case 3

Install a package on a group of hosts

# Install htop on all webservers — requires -b for root privilege
ansible webservers -i inventory.ini -m ansible.builtin.package \
  -a "name=htop state=present" -b
web01.example.com | CHANGED => {
    "changed": true,
    "msg": "1 package(s) installed."
}
web02.example.com | SUCCESS => {
    "changed": false,
    "msg": "Nothing to do"   <-- already installed on web02
}

Use Case 4

Restart a service on all web servers

# Restart nginx on every host in the webservers group
# state=restarted always restarts; state=reloaded only reloads config
ansible webservers -i inventory.ini -m ansible.builtin.service \
  -a "name=nginx state=restarted" -b

Use Case 5

Copy a file to all hosts

# Push a local config file to every server in the production group
# mode sets file permissions; owner/group set ownership
ansible production -i inventory.ini -m ansible.builtin.copy \
  -a "src=./motd.txt dest=/etc/motd mode=0644 owner=root group=root" -b

Use Case 6

Gather facts about a single host

# Collect and display all system facts from web01
# ansible.builtin.setup is the facts-gathering module — used implicitly at the
# start of every playbook run. Here we call it explicitly to inspect a single host.
ansible web01.example.com -i inventory.ini -m ansible.builtin.setup
web01.example.com | SUCCESS => {
    "ansible_facts": {
        "ansible_distribution": "Ubuntu",
        "ansible_distribution_version": "22.04",
        "ansible_hostname": "web01",
        "ansible_memtotal_mb": 4096,
        "ansible_processor_vcpus": 2,
        "ansible_default_ipv4": {
            "address": "192.168.1.10"
        }
        ... (hundreds more facts)
    }
}

Targeting with Patterns

The second argument in every ad-hoc command is a host pattern that tells Ansible which hosts from the inventory to target. Patterns are more powerful than just a group name — they support wildcards, exclusions, intersections, and combinations.

All hosts

all

Targets every host in the inventory. Equivalent to the implicit all group.

Group name

webservers

Targets every host in the named group, including hosts in any child groups.

Wildcard

web*.example.com

Targets all hosts whose name matches the wildcard pattern. Useful for naming conventions.

Exclusion

all:!databases

Targets all hosts except those in the databases group. The ! prefix negates a group or host.

Intersection

production:&webservers

Targets only hosts that belong to both groups. The & operator produces the intersection.

Single host

web01.example.com

Targets exactly one host by its inventory name or IP address. Useful for per-host operations.

Ad-hoc Commands vs Playbooks

Knowing when to use an ad-hoc command and when to write a playbook is one of the first judgement calls you develop as an Ansible practitioner. The distinction is not complexity — it is repeatability and auditability.

Use ad-hoc when…
The task will not be repeated
You need an answer right now — system check, service status, disk usage
It is a one-off emergency fix during an incident
You are exploring what a module does before adding it to a playbook
Use a playbook when…
The task needs to run again — on new servers, after a rebuild, in CI/CD
Multiple tasks must run in a specific order with dependencies between them
The change needs to be reviewed, version controlled, and auditable
The task involves conditionals, loops, variables, or handlers

Avoid ansible.builtin.shell for Ad-hoc Commands Unless Necessary

It is tempting to use ansible.builtin.shell for everything because it accepts any bash command. But shell is not idempotent — running it twice can produce different results or errors. Always prefer a dedicated module (ansible.builtin.package, ansible.builtin.service, ansible.builtin.copy) when one exists. Reserve ansible.builtin.shell for genuinely complex commands that no module covers — and document why.

Key Takeaways

Ad-hoc commands use the ansible binary — not ansible-playbook. The structure is always: ansible <pattern> -m <module> -a "<args>".
Host patterns are flexible — target a single host, a group, a wildcard, an intersection, or an exclusion directly from the command line without modifying the inventory.
Use -b for tasks requiring root — any module that installs packages, modifies system files, or manages services needs privilege escalation via the -b flag.
Ad-hoc commands are for one-off tasks — if you find yourself running the same ad-hoc command more than once, it belongs in a playbook where it is version-controlled and repeatable.
Prefer dedicated modules over shell — idempotency is the goal. Use ansible.builtin.package, service, and copy over raw shell commands wherever a module exists.

Teacher's Note

Run every example in this lesson against your own lab environment today — ad-hoc commands are where muscle memory for Ansible's syntax begins, and the best way to build it is repetition at the terminal.

Practice Questions

1. Which flag enables privilege escalation (sudo) for a single ad-hoc command?



2. Which Ansible module collects and returns system facts from a managed node?



3. Write the host pattern that targets all hosts in the inventory except those in the databases group.



Quiz

1. What is the key difference between ansible.builtin.command and ansible.builtin.shell?


2. You run an ad-hoc package install command and see SUCCESS with "changed": false. What does this mean?


3. When should an ad-hoc command be converted into a playbook?


Up Next · Lesson 9

Ansible Modules Overview

A comprehensive tour of Ansible's module library — how modules are organised, the most important ones to know, and how to find documentation for any module instantly.