Ansible Tutorial: Learn Automation from Scratch (2026)
When I managed my first fleet of 50 servers, configuration drift was a constant headache. Some servers had the right NTP settings, others did not. Some had the correct SSH hardening, others were open. I tried shell scripts, but they were brittle and not idempotent. Ansible changed everything because it is agentless — you do not install anything on the managed nodes, just Python and SSH. Ansible describes the desired state of your infrastructure declaratively and converges each machine toward that state. This tutorial walks through Ansible from inventory to playbooks to roles, covering the patterns I have used to automate thousands of servers in production.
Ansible Architecture and Installation
Ansible operates on a push model: the control node runs ansible-playbook, which connects to managed nodes via SSH and executes modules. There is no agent, no database, no daemon — just SSH and Python on the target. This simplicity is why Ansible won the configuration management wars for many teams. Install Ansible on the control node using pip, your OS package manager, or the official container image. Version 2.18 in 2026 supports Python 3.12 and introduces improved collections management and faster execution. The control node can manage Windows targets via WinRM and network devices via vendor-specific modules. I keep my control node as a dedicated VM or a container in our CI system rather than my laptop, so automation runs from a consistent environment.
sudo apt update && sudo apt install ansible -y
ansible --version
ansible all -i inventory.ini -m ping
Inventory Management: Static and Dynamic
The inventory defines which hosts Ansible manages. A static inventory is an INI or YAML file listing hostnames or IP addresses grouped logically. I organize groups by function: webservers, databases, loadbalancers. Variables can be assigned at the host or group level. For cloud environments, static inventories become unmanageable. Dynamic inventories query cloud providers like AWS EC2, Azure VMs, or vSphere to discover hosts on the fly. The aws_ec2 plugin tags instances and automatically populates groups. I use constructed inventories to create dynamic groups based on instance tags, regions, or VPC IDs. This lets me run playbooks against auto-scaling groups without updating inventory files.
[webservers]
web-01 ansible_host=10.0.1.10 ansible_user=admin
web-02 ansible_host=10.0.1.11 ansible_user=admin
[databases]
db-primary ansible_host=10.0.2.20
[all:vars]
ansible_python_interpreter=/usr/bin/python3
Playbooks: The Heart of Ansible Automation
A playbook is a YAML file containing one or more plays, each mapping a group of hosts to a set of tasks. Tasks use modules to make specific changes: copy files, install packages, start services, create users. I write playbooks to be idempotent — running them multiple times produces the same result. Modules like apt, yum, and service check the current state before making changes. If a package is already installed, the apt module reports OK and does nothing. The play output shows changed, ok, failed, or unreachable for each task, giving clear auditability. I use --check mode to dry-run playbooks during code review and --diff to see what files would change.
---
- name: Configure web servers
hosts: webservers
become: yes
tasks:
- name: Install Nginx
apt:
name: nginx
state: present
update_cache: yes
- name: Start and enable Nginx
service:
name: nginx
state: started
enabled: yes
- name: Deploy site config
copy:
src: files/default.conf
dest: /etc/nginx/sites-available/default
notify: reload nginx
handlers:
- name: reload nginx
service:
name: nginx
state: reloaded
Variables, Facts, and Templates
Hard-coding values in playbooks defeats the purpose of automation. Variables let you parameterize playbooks for different environments. Define variables in group_vars, host_vars, or the playbook vars section. Ansible gathers facts about each host before running tasks — IP addresses, OS family, CPU architecture, memory. I use facts in conditionals and templates. The template module renders Jinja2 templates with variable substitution. For example, an Nginx configuration template can dynamically set the server name and proxy pass URL based on inventory variables. Ansible Vault encrypts sensitive variables like passwords and API keys so they can be stored in version control safely.
- name: Configure application
hosts: app_servers
vars:
app_port: 8080
app_env: production
tasks:
- name: Render config from template
template:
src: app_config.properties.j2
dest: /opt/app/config.properties
- name: Print OS family
debug:
msg: "{{ ansible_facts['os_family'] }} {{ ansible_facts['distribution_version'] }}"
Roles for Reusable Automation
As playbooks grow, roles provide structure and reusability. A role is a directory with a standard tree: tasks, handlers, templates, files, vars, defaults, and meta. The nginx role, for example, contains everything needed to install and configure Nginx across any project. I publish common roles to Ansible Galaxy for team-wide use. The role directory structure enforces separation of concerns. The main.yml file in each subdirectory is automatically loaded. Dependencies are declared in meta/main.yml. When composing a playbook, I list roles and override role variables as needed. This pattern mirrors infrastructure as code best practices and makes automation auditable and shareable.
# roles/nginx/tasks/main.yml
---
- name: Install Nginx
apt:
name: nginx
state: "{{ nginx_version | default('present') }}"
- name: Remove default site
file:
path: /etc/nginx/sites-enabled/default
state: absent
- name: Deploy vhost config
template:
src: vhost.conf.j2
dest: /etc/nginx/sites-available/{{ site_name }}
notify: reload nginx
Ansible Tower, AWX, and Execution Environments
Running playbooks from the command line works for small teams but lacks auditing, scheduling, and RBAC. AWX is the upstream open-source project that became Red Hat Ansible Automation Platform. It provides a web UI, REST API, job scheduling, credential management, and workflow visualization. Execution Environments are container images that bundle Ansible, collections, and dependencies into a single, immutable unit. This solves the dependency hell problem where a playbook works on one control node but fails on another because of Python library versions. I build execution environments with ansible-builder and push them to a registry. AWX pulls the right execution environment for each job template, ensuring consistent runtime across the team.
ansible-builder build --tag my-ee:latest --container-runtime=docker
# Execution environment definition (execution-environment.yml):
# version: 1
# build_arg_defaults:
# EE_BASE_IMAGE: quay.io/ansible/ansible-runner:latest
Frequently Asked Questions
What is the difference between Ansible and Terraform?
Ansible is a configuration management and automation tool focused on configuring existing systems — installing software, managing files, restarting services. Terraform is an infrastructure provisioning tool focused on creating and destroying cloud resources — VMs, networks, load balancers. Many teams use both: Terraform to provision the infrastructure and Ansible to configure it.
How do I handle secrets in Ansible?
Use Ansible Vault to encrypt sensitive variables. Create an encrypted file with ansible-vault create secrets.yml, reference it in your playbook, and supply the vault password at runtime via --ask-vault-pass or a vault password file. For dynamic secrets, integrate with HashiCorp Vault using the community.hashi_vault lookup plugin.
Is Ansible idempotent by default?
Ansible modules are designed to be idempotent, but idempotency depends on how you write your playbooks. Using state=present for packages and copy with force=no creates idempotent tasks. Running shell or command modules without checks is not idempotent — always use creates, removes, or when conditions to guard them. Use --check mode to test.
Can Ansible manage Windows servers?
Yes. Ansible connects to Windows hosts via WinRM instead of SSH. Install the pywinrm library on the control node and set ansible_connection=winrm in the inventory. Most core modules have Windows counterparts. Windows modules use PowerShell under the hood. The ansible.windows collection contains modules for Windows-specific tasks like registry management and IIS configuration.
Originally published on Ayodhyyya. Last updated June 1, 2026.