Anatomy of an Ad-hoc Command
Every ad-hoc command has the same shape:
BASH — ad-hoc command shape
ansible <host-pattern> -m <module-name> -a "<module-args>" [options]
# └─────┬─────┘ └────┬─────┘ └────────┬────────┘
# │ │ │
# │ │ └─ key=value pairs the module accepts
# │ └─ which Ansible module to run
# └─ which hosts from inventory (group, single host, all, or a pattern)
For example: ansible databases -m ansible.builtin.ping runs the ping module
against every host in the databases group from your inventory.
The 12 Most-Useful Modules for Ad-hoc Use
| Module | What it does | One-line example |
|---|---|---|
ping | Verifies SSH + Python work | -m ping |
setup | Dumps all gathered facts | -m setup -a "filter=ansible_distribution*" |
command | Runs a binary (no shell) | -m command -a "uptime" |
shell | Runs through /bin/sh (pipes, redirects) | -m shell -a "ps -ef | grep mysql" |
copy | Pushes a file from control node | -m copy -a "src=/etc/motd dest=/etc/motd" |
fetch | Pulls a file from managed node | -m fetch -a "src=/etc/hostname dest=./tmp" |
file | Sets file/dir permissions, ownership | -m file -a "path=/var/log mode=0755" |
service | Start / stop / restart a service | -m service -a "name=nginx state=restarted" |
systemd | Same but systemd-aware | -m systemd -a "name=mysqld state=started enabled=true" |
dnf | Install / remove RPM packages | -m dnf -a "name=htop state=present" |
user | Create / modify a Linux user | -m user -a "name=appuser state=present" |
uri | HTTP request from the remote host | -m uri -a "url=https://example.com" |
Real-World Recipes
BASH — ad-hoc recipes you'll use weekly
# 1. Are all my db hosts reachable?
ansible databases -m ping
# 2. What kernel is each host running?
ansible all -m shell -a "uname -r"
# 3. How much disk free on / for every host?
ansible all -m shell -a "df -h / | tail -1"
# 4. Restart MySQL on a single node
ansible db1.example.com -m systemd -a "name=mysqld state=restarted" --become
# 5. Push a new my.cnf to all MySQL primaries
ansible mysql_primary -m copy \
-a "src=./files/my.cnf dest=/etc/my.cnf owner=root mode=0644 backup=yes" \
--become
# 6. Install htop everywhere
ansible all -m dnf -a "name=htop state=present" --become
# 7. Create the deploy user on a fresh box
ansible newbox -m user \
-a "name=deploy shell=/bin/bash groups=wheel append=true" \
--become
# 8. Run a SQL one-liner on every primary
ansible mysql_primary -m community.mysql.mysql_query \
-a "login_user=root login_password=secret query='SHOW MASTER STATUS'"
# 9. Tail the last 50 lines of a log
ansible web -m shell -a "tail -50 /var/log/nginx/error.log" --become
# 10. Reboot a host and wait for it to come back
ansible db2.example.com -m reboot --become
Quoting Rules — The Single Biggest Beginner Trap
Ansible's -a argument is parsed as a string of key=value pairs. If a value
contains spaces, quote it. If it contains both spaces and quotes, escape the inner
ones. Examples:
BASH — quoting
# Single key=value, no spaces — no quotes needed
ansible all -m ping
# Spaces in a value — wrap the whole thing in single quotes
ansible all -m shell -a 'echo Hello World'
# A nested quote — escape with backslash, or use single + double mix
ansible all -m shell -a 'mysql -e "SELECT VERSION()"'
# When using YAML-style args (clearer for complex calls):
ansible all -m copy -a "src=hello.txt dest=/tmp/hello.txt mode='0644'"
Limit Targets, Verbosity, and Dry-Run
| Flag | What it does |
|---|---|
--limit pattern | Restricts the run to a subset of the host pattern |
--check | Dry-run — show what would change, without making changes (most modules support it) |
--diff | Show the line-level diff for files / templates that would change |
-v / -vv / -vvv | Increase verbosity (last one prints the SSH commands themselves) |
-K | Prompt for the sudo password (useful when NOPASSWD is not configured) |
-u username | Override the SSH connection user for this command |
✅ Tip: Use
--check --diff together as your default safety net. --check simulates without making changes; --diff shows you exactly what would change. If both come back clean, you can run for real.