Installation and Your First Ad-hoc Commands

K
Kai··4 min read

This article sets up the practice environment and runs the first Ansible commands. By the end you'll be able to drive a remote server with ad-hoc commands — quickly running a single module, no playbook needed yet.

The series' hands-on code is at github.com/nghiadaulau/ansible-series. Clone it to follow along.

Install Ansible (control node)

Ansible only needs to be installed on one control machine (recall Article 1 — agentless). Install it with pip (the most common way, always the latest):

pip install ansible          # full edition (includes collections)
# or leaner: pip install ansible-core
ansible --version            # check
ansible [core 2.19.x]
  python version = 3.11...

(Alternatives: brew install ansible on macOS, or your distro's package manager — recall Linux series Article 11. On Windows, install inside WSL since Ansible doesn't run natively on Windows.)

Prepare a host to manage

We need a target machine. This article uses an EC2 Amazon Linux instance (recall AWS series Article 2 for how to create one) — but any Linux machine you can SSH into works. The host's requirements (Article 1): SSH-able + has Python (Amazon Linux 2023 ships python3).

You'll need: the host's public IP address, the login user (ec2-user for Amazon Linux), and the SSH key (a .pem file). Remember to tighten the Security Group to open SSH only to your IP (AWS series Article 3).

Configuration: ansible.cfg and inventory

Create a project directory, then two files.

ansible.cfg — the default configuration for this directory:

[defaults]
inventory = inventory.ini
host_key_checking = False
remote_user = ec2-user
private_key_file = ansible-lab.pem
interpreter_python = /usr/bin/python3

inventory.ini — the host list (Article 3 goes deeper):

[web]
lab ansible_host=203.0.113.10

Here lab is a friendly name for the host, and [web] is a group. host_key_checking = False is handy for a lab (skips the host-key confirmation prompt). Don't commit the .pem file to Git — add it to .gitignore.

Ad-hoc commands: run a single module quickly

An ad-hoc command has the structure:

ansible <pattern> -m <module> -a "<parameters>"
        └───┬───┘  └────┬───┘  └─────┬──────┘
       host/group    module     parameters

<pattern> is the host/group to apply to (all, web, lab...). This is how you quickly try out an operation without a playbook.

ping: check connectivity

ansible all -m ping
lab | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

pong means Ansible SSH'd in, found Python, and ran the module successfully (the exact lifecycle from Article 1). "changed": false because ping changes nothing — note this changed field, it's the spirit of idempotency (Article 5).

Note: Ansible's ping module is not the ICMP ping command. It checks "SSH + Python + module runs" — i.e. it verifies Ansible can manage the host.

command and shell: run commands

ansible all -m command -a "uptime"
lab | CHANGED | rc=0 >>
 10:16:19 up 3 min,  1 user,  load average: 0.29, 0.12

command is the default module (ansible all -a "uptime" also works). But command does not support pipes, redirects, or shell variables. When you need those, use shell:

ansible all -m shell -a "cat /etc/os-release | grep PRETTY_NAME"
lab | CHANGED | rc=0 >>
PRETTY_NAME="Amazon Linux 2023..."

Rule: prefer command (safer, doesn't go through a shell); only use shell when you need pipes/redirects. And even better: use a dedicated module (apt, copy, service...) instead of command/shell whenever one exists — because they're idempotent, whereas command/shell always report CHANGED (Ansible can't tell whether an arbitrary command changed anything).

become: run as root

Many tasks need sudo (recall Linux series Article 12). Add -b (become):

ansible all -b -m command -a "whoami"
lab | CHANGED | rc=0 >>
root

-b elevates to root (via sudo) to run. You'll use become for nearly every admin task (installing packages, editing system files).

setup: gather facts

The setup module gathers facts — information about the host (OS, IP, RAM, CPU...):

ansible all -m setup -a "filter=ansible_distribution*"
"ansible_distribution": "Amazon",
"ansible_distribution_version": "2023",

Facts are extremely useful: you can write tasks that vary by OS, version, and so on. We dig into facts in Article 6.

When to use ad-hoc, when to use a playbook

Ad-hoc fits one-off, quick jobs: checking state, restarting a single service across many machines, viewing facts. For repeatable, multi-step, save-worthy work, write a playbook (Article 4) — that's where Ansible's real power lies.

🧹 Note

The lab EC2 stays around throughout the series for practice; we terminate it in Article 16. If you want to stop partway through, remember to terminate it to avoid running up a bill (AWS series Article 2).

Wrap-up

Install Ansible on the control node (pip/brew/package), prepare a host that's SSH-able + has Python, then declare it in ansible.cfg and inventory.ini. An ad-hoc command (ansible <pattern> -m <module> -a "<args>") quickly runs one module: ping (verify the host is manageable), command/shell (run a command — prefer command), -b/become (root), setup (facts). Prefer dedicated modules because they're idempotent. Ad-hoc is for one-offs; for repeatable work use a playbook.

Before writing a playbook, we need to understand the inventory well — how Ansible knows which machines to manage and how to group them. That's Article 3.