Ansible is an open-source, enterprise-grade IT automation engine created by Red Hat that simplifies configuration management, application deployment, cloud provisioning, and intra-service orchestration. Unlike traditional configuration management tools (such as Chef or Puppet), Ansible allows system administrators and DevOps engineers to define infrastructure state using human-readable YAML configurations called Playbooks rather than writing complex, system-specific shell scripts.
Agentless Architecture: Ansible requires no client software, agents, or background daemons installed on target managed nodes. It connects to remote servers over standard transport protocols—primarily SSH for Linux/Unix and WinRM or SSH for Windows. Once connected, Ansible pushes small Python execution scripts (modules) to the target host, executes them, and removes them upon completion. This eliminates agent installation overhead and minimizes potential attack surfaces.
Idempotence: A core principle of Ansible is idempotency. Executing an Ansible module or playbook multiple times against target servers guarantees the exact same system state. If a package is already installed or a configuration file matches the desired state, Ansible skips execution, preventing configuration drift and unintended side effects.
Declarative Playbooks: Ansible playbooks use YAML syntax to describe the desired end-state of infrastructure components rather than sequential execution procedures.
Ad-Hoc Commands & Modules:
Ansible provides hundreds of built-in modules for managing packages (yum, apt), services (systemd), files (copy, file), and users (user). Engineers can execute quick ad-hoc commands directly from the CLI (e.g., ansible all -m ping or ansible all -m shell -a "uptime") without creating a playbook file.
Inventory System:
Ansible relies on an Inventory file (in INI or YAML format) to define host machine IP addresses, domain names, host groupings, connection ports, and authentication parameters (e.g., ansible_user, ansible_ssh_pass).
iptables/firewalld), and auditing configurations for compliance standards.In this challenge, we configure a static INI-style Ansible inventory file on the Jump Host (jump_host) under /home/thor/playbook/inventory. This inventory targets all three Application Servers in the Stratos Datacenter (stapp01, stapp02, stapp03) to allow the DevOps team to perform automated application deployment testing without passing inline credentials or connection flags during playbook execution.
graph TD
subgraph ControlNode ["Jump Host (Ansible Control Node)"]
ThorUser["User: thor"]
InvFile["Inventory File<br/>/home/thor/playbook/inventory"]
AnsibleCLI["Ansible Engine<br/>ansible / ansible-playbook"]
end
subgraph StratosDC ["Stratos Datacenter (Managed Nodes)"]
subgraph AppGroup ["Inventory Group: [app_servers]"]
App1["stapp01<br/>IP: 172.16.238.10<br/>User: tony"]
App2["stapp02<br/>IP: 172.16.238.11<br/>User: steve"]
App3["stapp03<br/>IP: 172.16.238.12<br/>User: banner"]
end
end
InvFile -->|"Supply Hosts & Credentials"| AnsibleCLI
AnsibleCLI -->|"SSH (Port 22)"| App1
AnsibleCLI -->|"SSH (Port 22)"| App2
AnsibleCLI -->|"SSH (Port 22)"| App3
An INI inventory organizes hosts under group headers enclosed in square brackets [group_name]. Single hosts can belong to multiple groups, and variables can be defined inline per host or globally for an entire group.
When password authentication or custom SSH users are required, Ansible uses specific behavioral inventory parameters:
ansible_host: The IP address or domain name of the target host to connect to (if different from the inventory alias).ansible_user: The SSH username to use when connecting to the target host.ansible_ssh_pass: The SSH password to use for authentication (requires sshpass installed on the control node).ansible_port: The SSH port number on the target host (defaults to 22).ansible_become_pass: The sudo password used for privilege escalation on the target host.| Hostname | Domain / Alias | IP Address | SSH User | Password | Role |
|---|---|---|---|---|---|
| Jump Host | jump_host |
172.16.238.2 |
thor |
Native / Passwordless | Ansible Control Node |
| App Server 1 | stapp01 |
172.16.238.10 |
tony |
Ir0nM@n |
Application Server 1 |
| App Server 2 | stapp02 |
172.16.238.11 |
steve |
Am3ric@ |
Application Server 2 |
| App Server 3 | stapp03 |
172.16.238.12 |
banner |
BigGr33n |
Application Server 3 |
/home/thor/playbook/inventorystapp01, stapp02, stapp03 grouped under [app_servers] (or individual host entries)ansible_host, ansible_user, and ansible_ssh_pass configured per hostLog in to the Jump Host as user thor:
ssh thor@jump_host
Ensure the target directory /home/thor/playbook exists and navigate into it:
mkdir -p /home/thor/playbook
cd /home/thor/playbook
Create or edit the inventory file named inventory:
vi /home/thor/playbook/inventory
Add the host definitions, group names, and SSH connection parameters in standard INI format:
[app_servers]
stapp01 ansible_host=stapp01 ansible_user=tony ansible_ssh_pass=******
stapp02 ansible_host=stapp02 ansible_user=steve ansible_ssh_pass=******
stapp03 ansible_host=stapp03 ansible_user=banner ansible_ssh_pass=******
[!TIP] You can also define group-level variables using a
[group_name:vars]section if all target nodes share common properties (e.g., SSH port or privilege escalation settings).
Test that Ansible can read the inventory file and successfully authenticate with all target App Servers using the ping module:
ansible all -i /home/thor/playbook/inventory -m ping
Expected Output:
stapp01 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
stapp02 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
stapp03 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
To confirm that playbooks can execute seamlessly using the newly created inventory, run a test playbook (e.g., playbook.yml located in /home/thor/playbook/):
ansible-playbook -i /home/thor/playbook/inventory /home/thor/playbook/playbook.yml
Output:
PLAY [all] ****************************************************************************************
TASK [Gathering Facts] ****************************************************************************
ok: [stapp01]
TASK [Install httpd package] **********************************************************************
changed: [stapp01]
TASK [Start service httpd] ************************************************************************
changed: [stapp01]
PLAY RECAP ****************************************************************************************
stapp01 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Check that the inventory file exists in the exact specified path with correct read permissions:
ls -l /home/thor/playbook/inventory
Expected Output:
-rw-r--r-- 1 thor thor 265 Aug 5 18:00 /home/thor/playbook/inventory
List all hosts recognized by Ansible from the inventory file:
ansible-inventory -i /home/thor/playbook/inventory --list
[!WARNING] Storing plain-text SSH passwords inside inventory files (
ansible_ssh_pass) poses a security risk in production environments.
ansible-vault encrypt inventory.ansible_ssh_pass) with password-less SSH keys using ssh-copy-id for enterprise deployments.group_vars/): For larger deployments, store host and group variables in separate YAML files under group_vars/app_servers.yml rather than inline within the inventory file.