Ansible Course
Ansible Ad-hoc Commands
In this lesson
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.
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
Targets every
host in the inventory. Equivalent to the implicit all group.
webservers
Targets every host in the named group, including hosts in any child groups.
web*.example.com
Targets all hosts whose name matches the wildcard pattern. Useful for naming conventions.
all:!databases
Targets all
hosts except those in the databases group. The !
prefix negates a group or host.
production:&webservers
Targets only
hosts that belong to both groups. The & operator
produces the intersection.
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.
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
ansible binary — not
ansible-playbook. The structure is always:
ansible <pattern> -m <module> -a "<args>".
-b for tasks requiring root — any module that
installs packages, modifies system files, or manages services needs privilege
escalation via the -b flag.
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.