Ansible control node & inventory.
Ansible is agentless β one control node pushes Python modules to a whole fleet over SSH, with nothing to install on the targets. Watch the model go from `dnf install ansible-core` to a live ad-hoc command fanning out across grouped hosts.
Ansible control node and inventory animated tutorial. Agentless push architecture over SSH, installing ansible-core, writing INI and YAML inventories, host ranges, groups and group_vars, testing connectivity with the ping module, ad-hoc commands with -m and -a, privilege escalation, and project defaults in ansible.cfg. RHCE EX294 ready.Ansible is agentless: one control node pushes work to a fleet over SSH. Watch the model go from install β inventory β groups β a live ad-hoc command fanning out across hosts.
- Β·Comfortable with SSH and SSH key auth
- Β·Basic Linux shell + package management (dnf)
- Β·Know what YAML looks like (helps, not required)
Install Ansible on a control node, write an inventory with groups, prove connectivity with the ping module, and run ad-hoc commands across a fleet.
pace: 10 minutes
π°οΈ control Β· ansible-corepushes over ssh βπ₯οΈ serveraβ idleπ₯οΈ serverbβ idleπ₯οΈ servercβ idleAgentless push β how Ansible reaches a fleet
Ansible has no agent. The CONTROL NODE opens an SSH connection to each MANAGED NODE, copies a tiny Python module over, runs it, captures the result, and deletes it. Managed nodes need only an SSH server and Python β nothing to install, no daemon to keep alive. That's the whole architecture: push over SSH, run, clean up. Every module is written to be idempotent, so re-running a play changes nothing if the system already matches.
$dnf install ansible-core$ansible --version$ssh servera 'python3 --version'Everything in Ansible flows from one idea: the control node pushes Python over SSH and the managed nodes stay dumb. The inventory names the fleet, groups carve it into targets, the ping module proves the path, and ad-hoc commands let you act now β but the moment a task is worth repeating, it belongs in a playbook so it's idempotent and reviewable.
$dnf install ansible-core$ansible --version$ansible all -m ping$ansible all --list-hosts$ansible GROUP --list-hosts$ansible-inventory --graph$ansible all -m setup$ansible all -a "CMD"$ansible H -m shell -a "a | b"$ansible H -m dnf -a "name=X state=present" -b$ansible H -m service -a "name=X state=started enabled=true" -b$ansible H -m copy -a "src=β¦ dest=β¦"$-i inventory$-u USER -b --become-user=root$./ansible.cfg [defaults]$ansible-config dump --only-changedWhat does a managed node need installed for Ansible to manage it?
`ansible all -m ping` returns SUCCESS β pong. What did that actually prove?
You want to install httpd on the webservers group, just once, right now, as root. Best ad-hoc command?
You run Ansible from a project directory that has its own ./ansible.cfg, and /etc/ansible/ansible.cfg also exists. Which wins?
These aren't graded β they're just for active recall, which is what actually makes the lesson stick.
SSH key authentication
Ansible is only as smooth as your SSH. Set up key-based auth to every managed node and `ansible all -m ping` just works β no passwords, no prompts.
Read this lesson as text6 chapters Β· quiz answers
01Agentless push β how Ansible reaches a fleet
Ansible has no agent. The CONTROL NODE opens an SSH connection to each MANAGED NODE, copies a tiny Python module over, runs it, captures the result, and deletes it. Managed nodes need only an SSH server and Python β nothing to install, no daemon to keep alive. That's the whole architecture: push over SSH, run, clean up. Every module is written to be idempotent, so re-running a play changes nothing if the system already matches.
$ dnf install ansible-coreβ install on the CONTROL node only$ ansible --versionβ confirm + see config path used$ ssh servera 'python3 --version'β managed node just needs ssh + python
02The inventory β who you manage
An inventory lists the managed hosts. The default is /etc/ansible/hosts, but real projects keep a project-local file and pass it with
-i inventory. It can be INI or YAML. Hosts are names or IPs; ranges collapse repetition βserver[a:c]expands to servera, serverb, serverc. Two groups always exist implicitly:all(every host) andungrouped(hosts in no group).$ [webservers]\nserver[a:b].lab.example.comβ INI: a group + a host range$ ansible all --list-hosts -i inventoryβ what does Ansible think exists?$ ansible-inventory -i inventory --graphβ tree of groups β hosts
03Groups & group_vars β organise the fleet
Group hosts by role, location, or stage β a host can belong to many groups at once. Nest groups with
[parent:children]in INI (orchildren:in YAML) soallβproductionβwebserverscascades. Variables that apply to a whole group live in group_vars/GROUPNAME (and per-host overrides in host_vars/HOST). Ansible loads them automatically β no-eneeded.$ ansible webservers --list-hostsβ resolve a group to its hosts$ group_vars/webservers.ymlβ vars auto-applied to the group$ host_vars/servera.ymlβ per-host overrides win over group
04Test connectivity β the ping module
ansible all -m pingis the first thing you run. It is NOT an ICMP ping β thepingmodule logs in over SSH, runs Python on the far side, and repliespongonly if the whole path works. A greenSUCCESS β pongproves SSH auth, Python, and (if used) privilege escalation are all healthy. Connection details come from vars: ansible_host, ansible_user, and--become/-bfor sudo.$ ansible all -m pingβ SSH + Python reachability, not ICMP$ ansible all -m ping -u devops -bβ as user devops, escalate with sudo$ ansible all -m setupβ dump all gathered facts about a host
05Ad-hoc commands β one-off tasks, no playbook
For a quick, one-shot action you don't need a playbook β
ansible GROUP -m MODULE -a "ARGS". The default module iscommand(so-a "uptime"just runs uptime). Useshellwhen you need pipes or redirects. Add-bto escalate. Ad-hoc shines for checks and emergencies; anything you'll repeat belongs in a playbook so it's idempotent and reviewable.$ ansible all -a "uptime"β command module is the default$ ansible webservers -m dnf -a "name=httpd state=present" -bβ install a package, as root$ ansible all -m service -a "name=firewalld state=started enabled=true" -bβ start + enable
06ansible.cfg β stop repeating yourself
Typing
-i inventory -u devops -bevery time gets old. A project-local ansible.cfg sets those defaults. Precedence (first wins): ANSIBLE_CONFIG env var β ./ansible.cfg in the current dir β ~/.ansible.cfg β /etc/ansible/ansible.cfg. The project-local file is the convention β keep it beside your playbooks so the whole team gets the same inventory, remote_user, and become behaviour for free.$ [defaults]\ninventory = ./inventory\nremote_user = devopsβ no more -i / -u flags$ [privilege_escalation]\nbecome = trueβ escalate by default$ ansible-config dump --only-changedβ what did my cfg actually override?
Quiz β answers
- What does a managed node need installed for Ansible to manage it?
- βOnly an SSH server and Python β Ansible is agentless and pushes modules over SSH, runs them, then removes them.Ansible's defining feature is that it's agentless. The control node copies a small Python module over SSH, executes it on the target, and cleans up. Nothing persistent runs on managed nodes.
ansible all -m pingreturns SUCCESS β pong. What did that actually prove?- βSSH login worked, Python ran on the far side, and the result came back β the full management path is healthy. It is not an ICMP ping.The
pingmodule is a connectivity test for the whole Ansible path: SSH transport + Python execution (+ privilege escalation if you pass -b). A normal ICMP ping would tell you none of that. - You want to install httpd on the webservers group, just once, right now, as root. Best ad-hoc command?
- βansible webservers -m dnf -a "name=httpd state=present" -bUse the
dnfmodule (idempotent) with -b to escalate. The bare-a "dnf install httpd"runs the command module, which isn't idempotent and won't escalate without -b. There's no host called 'webservers' for plain ssh. - You run Ansible from a project directory that has its own ./ansible.cfg, and /etc/ansible/ansible.cfg also exists. Which wins?
- βThe project-local ./ansible.cfg wins β precedence is ANSIBLE_CONFIG env β ./ansible.cfg β ~/.ansible.cfg β /etc/ansible/ansible.cfg.The first match in that order is used wholesale (configs don't merge). A project-local ansible.cfg is the convention precisely because it overrides the system file for everyone working in that directory.
keep going β these pair well with what you just learned.
Ansible playbooks & YAML
Plays, tasks, and idempotency. Watch a playbook run task-by-task β ok, changed, skipped β then run it again and watch everything go green with 0 changed. RHCE-ready.
Ansible core modules
file, user, copy, dnf, service β the modules that do the real work. Declare the desired state and let Ansible converge to it, idempotently. RHCE-ready.
Ansible variables & facts
Which value wins when the same variable is set in five places? Variable precedence, gathered facts, and ansible_facts β visualized as a ladder. RHCE-ready.