Ansible 101: Your First Ping

Just think for once, you have 10+ servers and a new user is to be created on every server, some xyz package to be installed. To do this you, you individually SSH into every server one by one to do the work. Half way you've already made a few typos.
I started learning ansible because I wanted a better answer to this problem. This series documents what I've learned practically and not just theory.
What is Ansible?
Ansible is an open-source configuration tool. In simple terms, it let's you define what your servers should look like and then makes them all look that way, from a single machine.
It's maintained by RedHat, and is written in python. What makes it different is it doesn't have agents. It just used SSH the same way you'd use to manually log into a server. If a machine accepts SSH connection and has python on it, ansible can manage it. That's it.
The Architecture
Ansible has two types of machines:
Control Node: The machine you run ansible from.
Managed Hosts: The machine you're automating. They need SSH access and Python. That's it.
Control Node Managed Hosts
┌──────────────────────┐
│ ansible-core │ ──SSH──▶│ server-1
│ │ ──SSH──▶│ server-2
│ hosts (inventory) │ ──SSH──▶│ server-3
│ ansible.cfg │ ──SSH──▶│ server-4
│ modules │ │ ...
│ playbooks │ │
└──────────────────────┘
Four things live on the control node that you'll constantly interact with:
inventory / hosts: A file listing all your managed machines. IPs, hostnames or both.
ansible.cfg: Your configuration file. Which SSH user to use, which key, whether to prompt for passwords, where your roles are, etc.
modules: These are small python scripts that do the actual work. There's a module for creating user, installing packages, etc. You just call them.
playbooks: YAML files where you define what you want done on which machine.
Basic Environment
Install ansible from here.
Verify using ansible --version.
Project-Based Setup (Do this instead of /etc/ansible)
don't work out of /etc/ansible
The default global config at /etc/ansible/ansible.cfg works, but it means every project on your machine shares the same configuration. Instead, create a dedicated project directory and put your hosts and ansible.cfg in there:
~/my-ansible-project/
├── ansible.cfg
├── hosts
├── ansible-ssh-key.pem
└── playbooks/
Ansible has a config file lookup order, it checks the current directory first. So when you cd into your project and run any Ansible command, it automatically picks up the local ansible.cfg.
The Inventory File
The inventory file is where Ansible learns about your managed hosts. By convention it's called hosts, but you can name it anything and point to it from ansible.cfg
Basic list
65.0.109.64
ec2-65-0-109-64.ap-south-1.compute.amazonaws.com
Plain and simple - one host per line. You can use IP addresses, hostnames, or mix both.
Groups
Groups let you target specific subsets of your infrastructure:
[redhat]
65.0.109.64
10.22.0.1
[ubuntu]
ec2-65-0-109-64.ap-south-1.compute.amazonaws.com
Now you can run a playbook against redhat only, or ubuntu only, or all of them. This is where Ansible starts to feel powerful.
- ansible all --list-hosts
- ansible redhat --list-hosts
Nested Groups
You can create groups of groups with the :children keyword:
[redhat]
65.0.109.64
[ubuntu]
ec2-65-0-109-64.ap-south-1.compute.amazonaws.com
[asia:children]
redhat
ubuntu
ansible asia now targets all hosts. Useful when you want regional or environment-based groupings.
ansible.cfg - The config file
This is where you define the defaults for how Ansible behaves. Here's mine:
[defaults]
inventory = ./hosts
remote_user = ansible
private_key_file = ./ansible-ssh-key.pem
host_key_checking = false
ask_pass = false
roles = ./roles
[privilege_escalation]
become = true
become_user = root
become_method = sudo
become_ask_pass = false
Now let's break this down:
[defaults] section:
| Setting | What it does |
|---|---|
inventory |
Where to find your hosts file |
remote_user |
The SSH user Ansible logs in as on managed hosts |
private_key_file |
Path to your .pem / SSH private key |
host_key_checking = false |
Skips the "are you sure you want to connect?" SSH prompt. Useful in cloud environments where hosts are frequently recreated |
ask_pass = false |
Don't prompt for SSH password (we're using key-based auth) |
[privilege_escalation] section:
This is your sudo configuration.
| Setting | What it does |
|---|---|
become = true |
Enable privilege escalation |
become_user = root |
Escalate to root |
become_method = sudo |
Use sudo to escalate |
become_ask_pass = false |
Don't prompt for sudo password |
The remote_user in my setup is a dedicated ansible user that exists on all managed hosts, it has SSH key access and passwordless sudo. This is the standard pattern you'll see in real environments.
Reading outputs:
The color of the output in ansible tell you something, like:
| Color | Status | Meaning |
|---|---|---|
| Red🔴 | FAILED |
Something went wrong |
| Green🟢 | OK |
Task ran successfully, nothing changed |
| Yellow🟡 | CHANGED |
Task ran and made a change on the host |
| Blue🔵 | SKIPPED |
Task was skipped (condition not met) |
The distinction between OK and CHANGED is more important than it seems. If you run a playbook that installs a package and the output is green (OK), it means the package was already installed. Yellow (CHANGED) means it actually installed something.
First ansible command
With the basic setup in place, let's verify connectivity to all managed hosts.
ansible all -m ping
A healthy response looks like this:
If it's green, it means you're in. Everything from here on is just telling Ansible what to do with that connection.
Next, we'll move from single commands to actual playbooks, writing YAML, understanding ad-hoc commands, etc. Soon all of this will look like a walk in the park. STAY TUNED!!





