Ad-Hoc Commands: Ansible Without a Playbook

Scenario: At 02:10 someone needs to know the uptime and free disk of forty machines, right now. Writing a playbook first would be absurd.

New words, in plain English

The ansible command (as opposed to ansible-playbook) runs one module against a host pattern immediately. It is the right tool for investigation and for genuinely one-off actions, and the wrong tool for anything you will ever want to repeat, review or explain.

The canonical first command is ansible all -m ping. It is worth understanding what a successful pong actually proves: the host resolved, SSH connected, authentication succeeded, a Python interpreter was found, a module was copied, executed and cleaned up. That is nearly the entire Ansible pipeline verified in one line - which is why it is the first thing to run when anything is behaving strangely.

Ad-hoc commands accept the same connection flags as playbooks: -i for inventory, -u for the user, --limit, -o for one-line output.

Analogy: Ad-hoc commands are a phone call; playbooks are a signed contract. The phone call is faster and perfectly appropriate for 'are you there?'. You would not run a production release over one.

A worked example

# Is everything reachable and runnable?
ansible all -m ping

# Gather one fact from a group, one line per host
ansible webservers -m setup -a 'filter=ansible_distribution' -o

# Run a command (note: no shell features - no pipes, no redirects)
ansible production -m command -a 'uptime' -o

# When you genuinely need shell features, use the shell module
ansible production -m shell -a 'df -h / | tail -1' -o

The distinction between command and shell shows up here for the first time and never goes away. command executes a binary directly with arguments - no shell involved, so no pipes, no redirection, no globbing, no environment-variable expansion, and importantly no shell-injection surface. shell runs the string through /bin/sh, which gives you all of those features and all of their risks.

Prefer command unless you specifically need a shell feature. And prefer a real module over either: ansible.builtin.file over command: mkdir, ansible.builtin.copy over shell: echo >>. Modules check before they act; raw commands do not.

Tip: Add -o to ad-hoc commands for one compact line per host. Across forty machines it turns an unreadable wall of JSON into something you can actually scan.
Goal: Put this to work in the ansible-playbook-syntax lab. Open /labs/ansible, pick ansible-playbook-syntax, and fix the real broken project - Ansible really does SSH into four managed hosts and converge them.