Sign in
← Ansible
13 min · easy · Lesson 2 of 12

Inventory and ad-hoc commands

Inventory in ini and yaml, host groups, ad-hoc commands with -m, the ping/command/shell/copy modules.

Why

Before Ansible can do anything, it needs to know which hosts to work on and how to connect to them. Inventory describes that. The second half of the lesson is about ad-hoc commands: one action on a group of machines without a playbook. People use them to check connectivity, look at the state of a fleet, and make one-off changes.

Inventory: the list of hosts

Inventory is a file listing hosts, their groups, and connection parameters.

The default path is /etc/ansible/hosts. In real projects the inventory lives in the repository next to the playbook, and is passed with the -i flag.

ini format

The most common and simplest option.

# inventory.ini

[web]
web-1.example.com
web-2.example.com ansible_user=deploy

[db]
db-1.example.com ansible_host=10.0.0.10 ansible_port=2222

[web:vars]
ansible_user=ubuntu

What's going on here:

Look at web-2.example.com. The web group sets the user to ubuntu, but the host itself sets deploy. A host variable wins over a group variable, so Ansible connects to web-2 as deploy, and to web-1 as ubuntu.

The same inventory in yaml

# inventory.yml
all:
  children:
    web:
      vars:
        ansible_user: ubuntu
      hosts:
        web-1.example.com: {}
        web-2.example.com:
          ansible_user: deploy
    db:
      hosts:
        db-1.example.com:
          ansible_host: 10.0.0.10
          ansible_port: 2222

Ansible reads both files into the same structure. yaml is convenient when the inventory is generated by a program, or has a lot of nested groups. ini is simpler to read and edit by hand.

Checking what Ansible parsed

Before running anything on hosts, it helps to see the inventory the way Ansible sees it:

ansible-inventory -i inventory.ini --graph
@all:
  |--@ungrouped:
  |--@web:
  |  |--web-1.example.com
  |  |--web-2.example.com
  |--@db:
  |  |--db-1.example.com

The final variables for one host, with groups already accounted for:

ansible-inventory -i inventory.ini --host web-2.example.com
{
    "ansible_user": "deploy"
}

Special groups

Ansible always adds two groups on its own:

GroupWhat it includes
allevery host in the inventory
ungroupedhosts that don't fall into any explicit group

The all pattern in a command means exactly this group.

Ad-hoc commands

Ad-hoc is a one-off action without a playbook. Good for checking connectivity, quickly running a command on a fleet of machines, or making a one-time change.

Syntax:

ansible <pattern> -i inventory.ini -m <module> -a "<args>"

The pattern picks which hosts to work on:

Put a pattern with ! in single quotes. In an interactive bash shell, an exclamation mark triggers history expansion, and without quotes the command breaks.

If -m isn't given, Ansible uses the command module. So ansible web -a "uptime" and ansible web -m command -a "uptime" do the same thing.

Everything happens through modules

Ansible almost never sends a raw command to a host. It calls modules: small programs that get copied to the managed node and run there by Python. Each module does one thing and reports whether it changed anything (CHANGED) or everything was already fine (SUCCESS).

Basic modules:

ModuleWhat it does
pingChecks SSH access and a working Python on the host. Not ICMP.
commandRuns a command without a shell.
shellRuns a command through /bin/sh: pipes and redirects work.
copyCopies a file from the control node to the managed node.
aptManages packages on Debian/Ubuntu.
dnfThe same for RHEL/Rocky/AlmaLinux/Fedora. The old name yum is redirected to dnf in ansible-core.
serviceManages services through any init system (systemd, OpenRC, SysV). There's a separate systemd module for systemd specifics (daemon-reload, masks).
userCreates and modifies users.

command and shell

command runs a program directly, without a shell. Pipes (|), redirects (>), ;, and && don't work: they're passed to the program as plain arguments. The module expands $HOME-style environment variables itself, without a shell.

shell is for when you can't do without a shell: pipes (ps aux | grep nginx), redirects (> /tmp/out), globs (*.conf). But if user input ends up in the arguments, there's a risk of injection.

Rule: command by default, shell only when you need shell features.

Ad-hoc command examples

Check connectivity with every host:

ansible all -i inventory.ini -m ping

Get the uptime on the web group:

ansible web -i inventory.ini -m command -a "uptime"

Install a package with escalated privileges:

ansible all -i inventory.ini -m apt -a "name=nginx state=present" -b

Copy a file to every web server:

ansible web -i inventory.ini -m copy -a "src=./nginx.conf dest=/etc/nginx/nginx.conf" -b

Restart a service:

ansible web -i inventory.ini -m service -a "name=nginx state=restarted" -b

become: privilege escalation

Many operations need root: installing packages, writing to /etc, managing services. The -b flag (or --become) tells Ansible to run the module through sudo.

ansible all -i inventory.ini -m apt -a "name=vim state=present" -b

By default, become uses sudo. Another mechanism (su, doas, and so on) is set with the --become-method flag. If sudo on the host asks for a password, add -K: Ansible will ask for it once before running.

In a playbook, become is set at the play level or on an individual task. That's more convenient than remembering the flag for every ad-hoc command.

Common mistakes

What you'll need for the tasks

Self-check

Type the command or key line. Case and spaces don't matter.

1. Check connectivity to all hosts in inventory.ini using the ping module.
2. Run the `uptime` command on the web group using the command module.
3. Install the nginx package using the apt module with privilege escalation (become) on all hosts.

Done reading?

Mark the lesson as read to move your track progress forward.

Go through it with an AI agent

The agent explains anything unclear in plain words, gives you 3 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.

Get access for $49

Tell the agent: use MCP izi, give me lesson ansible-basics-02-inventory-adhoc