From beginner basics to practical playbooks, variables, templates, handlers, roles, vault, and project structure
1. What is Ansible?
Ansible is an open-source automation and configuration management tool. It helps you manage many servers from one place without manually logging into each server one by one.
Think of this problem:
Install Apache on 1 server -> easy manually
Install Apache on 10 servers -> boring
Install Apache on 100 servers -> painful
Install Apache on 1000 servers -> impossible manually
Ansible solves this by allowing you to describe the desired state of your systems in YAML files called playbooks.
Ansible is agentless. You install Ansible on a control node, and it manages remote machines over SSH, PowerShell remoting, WinRM, or other supported transports. Managed Linux/Unix nodes do not need Ansible installed, though they commonly need Python for many modules. (docs.ansible.com)
2. Why do we use Ansible?
Ansible helps with:
| Benefit | Meaning |
|---|---|
| Save time | Automate repeated server tasks |
| Save cost | Fewer manual operations |
| Reduce mistakes | Same steps run consistently |
| Manage scale | One control node can manage many servers |
| Documentation as code | Playbooks show exactly what should happen |
| Repeatability | Same playbook can be run again and again |
| Idempotency | Most modules change things only when needed |
Example:
Requirement:
Deploy package X to 100 servers.
Manual method:
SSH into every server and install manually.
Ansible method:
Write one playbook and run it against all 100 servers.
Tiny bit magical, but not scary magic. More like “YAML with a wrench.”
3. What can Ansible manage?
Ansible can manage almost every common system configuration area.
3.1 Hardware-related configuration
Examples:
Disk
Mount points
Network interfaces
Storage setup
Cloud instances
3.2 Software configuration
Examples:
Files
Directories
Packages
Services
Repositories
Commands
Applications
Web servers
Databases
3.3 People and access
Examples:
Users
Groups
SSH keys
Permissions
Sudo access
3.4 Process and policy
Examples:
Security hardening
Patch management
Application deployment
Compliance checks
Service restart policies
Backup jobs
Cron jobs
4. Ansible products and ecosystem
There are a few related names students should understand.
| Product / Project | Type | Purpose |
|---|---|---|
ansible-core | Open source CLI runtime | Minimal Ansible engine with built-in modules/plugins |
ansible package | Open source CLI package | Larger package including ansible-core plus community collections |
| AWX | Open source web UI | Upstream project for Red Hat Ansible Automation Platform |
| Automation Controller | Enterprise web UI/API | Commercial controller inside Red Hat Ansible Automation Platform |
| Red Hat Ansible Automation Platform | Enterprise platform | Full enterprise automation platform with controller, web console, API, analytics, execution environments, RBAC, and more |
The official docs describe ansible-core as the minimal package and ansible as the larger “batteries included” community package. (docs.ansible.com) AWX provides a web UI, REST API, and task engine and is an upstream project for Red Hat Ansible Automation Platform. (GitHub) Red Hat Ansible Automation Platform includes automation controller, web console, REST API, analytics, execution environments, and more. (docs.ansible.com)
5. How Ansible works: architecture
Basic architecture:
Human
|
v
Ansible Control Server / Control Node
|
| SSH for Linux/Unix
| WinRM / PSRP / SSH for Windows
v
Ansible Remote Servers / Managed Nodes
In your rough notes:
HUMAN -----> ACS -----> ARS
|
| ansible command / ansible-playbook
|
+---- SSH ---- Linux servers
+---- WinRM -- Windows servers
5.1 Control node
The control node is the machine where Ansible is installed and from where commands are executed.
Examples:
Ubuntu server
RHEL server
Debian server
macOS
WSL on Windows
Windows without WSL is not natively supported as an Ansible control node, according to the official installation guide. (docs.ansible.com)
5.2 Managed nodes
The managed nodes are the servers/devices that Ansible manages.
Examples:
Web servers
Database servers
Application servers
Load balancers
Cloud VMs
Network devices
Windows servers
For Linux/Unix managed nodes, Ansible usually connects using SSH. For Windows, Ansible commonly uses WinRM or PSRP, and Windows must be configured appropriately before Ansible can manage it. (docs.ansible.com)
6. Main Ansible components
To deploy something using Ansible, you usually need:
1. Ansible installed on the control node
2. Inventory file
3. Playbook file
4. SSH key or password access to managed nodes
5. Optional ansible.cfg configuration file
Example workflow:
Step 1: Install Ansible
Step 2: Create inventory file
Step 3: Create playbook file
Step 4: Ensure SSH access to remote servers
Step 5: Optional: configure ansible.cfg
Step 6: Run ansible-playbook
Command:
ansible-playbook -i inventory web.yaml -u ubuntu --private-key node.pem
Older examples often use:
--key-file=node.pem
But the clearer modern option is:
--private-key node.pem
7. Installing Ansible
7.1 Recommended method using pipx
The official installation docs recommend pipx as a good option on systems where installing Python packages globally is restricted. (docs.ansible.com)
sudo apt update
sudo apt install -y pipx
pipx ensurepath
pipx install --include-deps ansible
Check installation:
ansible --version
7.2 Install using pip
python3 -m pip install --user ansible
Check:
ansible --version
ansible-community --version
The official docs show ansible --version to confirm the installed ansible-core version and ansible-community --version to check the community package version. (docs.ansible.com)
7.3 Ubuntu/Debian package manager method
For labs, this is often easiest:
sudo apt update
sudo apt install -y ansible
ansible --version
7.4 RHEL/CentOS/Fedora style
sudo dnf install -y ansible
ansible --version
8. Creating your first project directory
Create a clean lab directory:
mkdir ansible-demo
cd ansible-demo
Recommended beginner structure:
ansible-demo/
├── ansible.cfg
├── inventory
├── web.yaml
├── index.html
└── templates/
└── index.html.j2
Later, for roles:
ansible-demo/
├── ansible.cfg
├── inventory
├── site.yml
├── group_vars/
│ └── all.yml
├── host_vars/
│ └── server1.yml
└── roles/
└── apache/
├── tasks/
│ └── main.yml
├── handlers/
│ └── main.yml
├── templates/
│ └── index.html.j2
├── files/
│ └── index.html
├── vars/
│ └── main.yml
├── defaults/
│ └── main.yml
└── meta/
└── main.yml
The official playbook guide covers reusable files, roles, includes/imports, tags, conditionals, loops, handlers, facts, and related playbook features. (docs.ansible.com)
9. Inventory file
An inventory is the list of managed nodes that Ansible should connect to. Official Ansible docs define inventory as the list of managed nodes or hosts that Ansible deploys and configures. (docs.ansible.com)
9.1 Simple inventory
Create a file named inventory:
3.110.83.207
3.110.83.208
3.110.83.209
Test connectivity:
ansible -i inventory all -m ping -u ubuntu --private-key node.pem
9.2 Inventory with groups
[web_servers]
3.110.83.207
3.110.83.208
[db_servers]
3.110.83.209 3.110.83.210
Now you can target only web servers:
ansible -i inventory web_servers -m ping -u ubuntu --private-key node.pem
Or only database servers:
ansible -i inventory db_servers -m ping -u ubuntu --private-key node.pem
9.3 Inventory with host variables
[web_servers]
web1 ansible_host=3.110.83.207 ansible_user=ubuntu ansible_private_key_file=./node.pem
web2 ansible_host=3.110.83.208 ansible_user=ubuntu ansible_private_key_file=./node.pem
[db_servers]
db1 ansible_host=3.110.83.209 ansible_user=ubuntu ansible_private_key_file=./node.pem
Now you can run:
ansible -i inventory all -m ping
No need to pass -u ubuntu --private-key node.pem every time.
9.4 Inventory with group variables
[web_servers]
web1 ansible_host=3.110.83.207
web2 ansible_host=3.110.83.208
[db_servers]
db1 ansible_host=3.110.83.209
[all:vars]
ansible_user=ubuntu ansible_private_key_file=./node.pem
9.5 Inventory group naming tip
Your notes used:
[web-servers]
That can work, but for beginner labs I recommend:
[web_servers]
or:
[webservers]
Then keep the same name in your playbook:
hosts: web_servers
Consistency saves lives. Well, at least YAML errors.
10. Ansible configuration file: ansible.cfg
ansible.cfg controls Ansible behavior. The official docs say the stock configuration is enough for most users, but settings can be adjusted using a configuration file, environment variables, or command-line options. (docs.ansible.com)
Create ansible.cfg in your project directory:
[defaults]
inventory = ./inventory
remote_user = ubuntu
private_key_file = ./node.pem
host_key_checking = False
forks = 10
stdout_callback = yaml
[privilege_escalation]
become = True become_method = sudo become_user = root become_ask_pass = False
Now commands become shorter:
ansible all -m ping
ansible-playbook web.yaml
Important lab note:
host_key_checking = False
This is convenient for training labs, but in production, disabling host key checking is not recommended unless your security team approves it.
11. Ad-hoc Ansible commands
Ad-hoc commands are one-line Ansible commands used for quick tasks.
Syntax:
ansible <host-pattern> -i <inventory> -m <module> -a "<module arguments>"
11.1 Ping all servers
ansible -i inventory all -m ping -u ubuntu --private-key node.pem
11.2 Run uptime
ansible -i inventory all -m command -a "uptime" -u ubuntu --private-key node.pem
11.3 Check disk space
ansible -i inventory all -m shell -a "df -h" -u ubuntu --private-key node.pem
11.4 Install package
Ubuntu/Debian:
ansible -i inventory web_servers -m apt -a "name=apache2 state=present update_cache=yes" -b
RHEL/CentOS/Amazon Linux:
ansible -i inventory web_servers -m yum -a "name=httpd state=present" -b
11.5 Restart service
Ubuntu/Debian:
ansible -i inventory web_servers -m service -a "name=apache2 state=restarted" -b
RHEL/CentOS:
ansible -i inventory web_servers -m service -a "name=httpd state=restarted" -b
12. What is a playbook?
A playbook is a YAML file that describes automation steps. Official docs call playbooks “automation blueprints” in YAML format that Ansible uses to deploy and configure nodes in an inventory. (docs.ansible.com)
Hierarchy:
Playbook
|
+-- Play
|
+-- hosts
+-- vars
+-- tasks
+-- handlers
12.1 Playbook
A playbook can contain one or more plays.
12.2 Play
A play maps hosts to tasks.
Example:
- name: Configure web servers
hosts: web_servers
become: true
tasks:
- name: Install Apache
ansible.builtin.apt:
name: apache2
state: present
12.3 Task
A task calls one module.
Example:
- name: Copy index file
ansible.builtin.copy:
src: index.html
dest: /var/www/html/index.html
12.4 Module
A module is the unit of work Ansible runs.
Examples:
ansible.builtin.copy
ansible.builtin.template
ansible.builtin.apt
ansible.builtin.yum
ansible.builtin.service
ansible.builtin.user
ansible.builtin.file
ansible.builtin.command
ansible.builtin.shell
The official module index lists modules such as setup, shell, template, uri, user, wait_for, and many more. (docs.ansible.com)
13. What is a module?
A module is a reusable script/plugin that performs a specific task.
Example:
ansible.builtin.copy:
src: index.html
dest: /var/www/html/index.html
Meaning:
Take index.html from the Ansible control node
Copy it to /var/www/html/index.html on the remote server
The official ansible.builtin.copy module copies files or directories to remote locations and can also set permissions, ownership, and other file metadata. (docs.ansible.com)
13.1 Common modules
| Module | Purpose |
|---|---|
ansible.builtin.ping | Test Ansible connectivity |
ansible.builtin.copy | Copy files |
ansible.builtin.template | Deploy Jinja2 templates |
ansible.builtin.file | Manage files, directories, permissions |
ansible.builtin.apt | Manage Debian/Ubuntu packages |
ansible.builtin.yum | Manage older RHEL-style packages |
ansible.builtin.dnf | Manage newer RHEL/Fedora packages |
ansible.builtin.service | Manage services |
ansible.builtin.systemd_service | Manage systemd units |
ansible.builtin.user | Manage users |
ansible.builtin.group | Manage groups |
ansible.builtin.command | Run commands without shell features |
ansible.builtin.shell | Run shell commands |
ansible.builtin.debug | Print messages and variable values |
ansible.builtin.setup | Gather facts |
14. First complete playbook: install Apache
Create web.yaml:
---
- name: Install and start Apache on web servers
hosts: web_servers
become: true
tasks:
- name: Update apt cache and install Apache2
ansible.builtin.apt:
name: apache2
state: present
update_cache: true
- name: Ensure Apache service is started and enabled
ansible.builtin.service:
name: apache2
state: started
enabled: true
Run:
ansible-playbook -i inventory web.yaml -u ubuntu --private-key node.pem
Or, if ansible.cfg is configured:
ansible-playbook web.yaml
Check in browser:
http://<server-public-ip>
15. Corrected handler example from your notes
Your notes had notify: Restart Apache, but the handlers: section was missing. Here is the complete version.
---
- name: Install Apache using handlers
hosts: web_servers
become: true
tasks:
- name: Install Apache2
ansible.builtin.apt:
name: apache2
state: latest
update_cache: true
notify:
- Restart Apache
- name: Ensure Apache service is enabled and running
ansible.builtin.service:
name: apache2
state: started
enabled: true
handlers:
- name: Restart Apache
ansible.builtin.service:
name: apache2
state: restarted
What is a handler?
A handler is a special task that runs only when notified by another task.
Example:
If template changed -> restart Apache
If template did not change -> do not restart Apache
This prevents unnecessary service restarts.
16. Copy files using copy
Create index.html:
<h1>Welcome to DevOpsSchool Ansible Demo</h1>
<p>This page was copied using Ansible.</p>
Playbook:
---
- name: Copy website file
hosts: web_servers
become: true
tasks:
- name: Install Apache2
ansible.builtin.apt:
name: apache2
state: present
update_cache: true
- name: Copy index.html to Apache document root
ansible.builtin.copy:
src: index.html
dest: /var/www/html/index.html
owner: root
group: root
mode: '0644'
- name: Start Apache
ansible.builtin.service:
name: apache2
state: started
enabled: true
Run:
ansible-playbook web.yaml
17. Variables in Ansible
Variables help avoid hardcoding values.
Example:
vars:
package_name: apache2
service_name: apache2
Ansible variables can be defined in playbooks, inventory, reusable files, roles, command-line arguments, or created during playbook execution using registered task results. (docs.ansible.com)
17.1 Variable example
---
- name: Install Apache using variables
hosts: web_servers
become: true
vars:
package_name: apache2
service_name: apache2
app_name: "DevOpsSchool Demo App"
tasks:
- name: Show app name
ansible.builtin.debug:
msg: "Installing {{ package_name }} for {{ app_name }}"
- name: Install package
ansible.builtin.apt:
name: "{{ package_name }}"
state: present
update_cache: true
- name: Start service
ansible.builtin.service:
name: "{{ service_name }}"
state: started
enabled: true
17.2 Variable from command line
ansible-playbook web.yaml -e "package_name=apache2 service_name=apache2"
17.3 Variables in inventory
[web_servers]
web1 ansible_host=3.110.83.207 app_name="Web App 1"
web2 ansible_host=3.110.83.208 app_name="Web App 2"
Playbook:
- name: Print app name
ansible.builtin.debug:
msg: "This host runs {{ app_name }}"
17.4 Variables in group_vars
Create:
group_vars/
└── web_servers.yml
Content:
package_name: apache2
service_name: apache2
document_root: /var/www/html
Use in playbook:
- name: Install package
ansible.builtin.apt:
name: "{{ package_name }}"
state: present
18. Facts in Ansible
Facts are automatically discovered information about remote systems.
Examples:
Operating system
IP address
Hostname
Memory
CPU
Disk
Architecture
Distribution
Kernel
Playbook:
---
- name: Display facts
hosts: all
tasks:
- name: Show OS distribution
ansible.builtin.debug:
msg: "This server is running {{ ansible_distribution }} {{ ansible_distribution_version }}"
- name: Show hostname
ansible.builtin.debug:
msg: "Hostname is {{ ansible_hostname }}"
Run:
ansible-playbook facts.yaml
Ad-hoc fact gathering:
ansible all -m setup
19. Conditionals: when
Conditionals let tasks run only when a condition is true.
Example:
---
- name: Install web server based on OS
hosts: web_servers
become: true
tasks:
- name: Install Apache on Ubuntu/Debian
ansible.builtin.apt:
name: apache2
state: present
update_cache: true
when: ansible_os_family == "Debian"
- name: Install Apache on RHEL/CentOS/Amazon Linux
ansible.builtin.dnf:
name: httpd
state: present
when: ansible_os_family == "RedHat"
19.1 Conditional service names
---
- name: Start correct Apache service
hosts: web_servers
become: true
tasks:
- name: Start apache2 on Debian family
ansible.builtin.service:
name: apache2
state: started
enabled: true
when: ansible_os_family == "Debian"
- name: Start httpd on RedHat family
ansible.builtin.service:
name: httpd
state: started
enabled: true
when: ansible_os_family == "RedHat"
20. Loops and iterations
Loops help repeat one task multiple times.
20.1 Install multiple packages
---
- name: Install multiple packages
hosts: web_servers
become: true
tasks:
- name: Install packages
ansible.builtin.apt:
name: "{{ item }}"
state: present
update_cache: true
loop:
- apache2
- git
- curl
- vim
20.2 Create multiple users
---
- name: Create multiple users
hosts: all
become: true
tasks:
- name: Create users
ansible.builtin.user:
name: "{{ item }}"
state: present
loop:
- raj
- amit
- priya
20.3 Loop with dictionaries
---
- name: Create users with groups
hosts: all
become: true
tasks:
- name: Ensure groups exist
ansible.builtin.group:
name: "{{ item.group }}"
state: present
loop:
- { name: "raj", group: "devops" }
- { name: "amit", group: "qa" }
- name: Create users
ansible.builtin.user:
name: "{{ item.name }}"
group: "{{ item.group }}"
state: present
loop:
- { name: "raj", group: "devops" }
- { name: "amit", group: "qa" }
21. Templates using Jinja2
Use copy when the file is static.
Use template when the file contains variables.
The official template module processes templates using the Jinja2 templating language. (docs.ansible.com)
21.1 Create template file
Create:
templates/index.html.j2
Content:
<html>
<head>
<title>{{ app_name }}</title>
</head>
<body>
<h1>Welcome to {{ app_name }}</h1>
<p>Deployed by Ansible.</p>
<p>Hostname: {{ ansible_hostname }}</p>
<p>Operating System: {{ ansible_distribution }} {{ ansible_distribution_version }}</p>
</body>
</html>
21.2 Playbook using template
---
- name: Apache setup with template
hosts: web_servers
become: true
vars:
app_name: "DevOpsSchool Demo App"
tasks:
- name: Install Apache2
ansible.builtin.apt:
name: apache2
state: present
update_cache: true
- name: Deploy index.html from Jinja2 template
ansible.builtin.template:
src: templates/index.html.j2
dest: /var/www/html/index.html
owner: root
group: root
mode: '0644'
notify:
- Restart Apache
- name: Ensure Apache is running
ansible.builtin.service:
name: apache2
state: started
enabled: true
handlers:
- name: Restart Apache
ansible.builtin.service:
name: apache2
state: restarted
22. Register task output
Sometimes you want to store command output and use it later.
---
- name: Register command output
hosts: all
tasks:
- name: Run uptime command
ansible.builtin.command: uptime
register: uptime_output
- name: Print uptime
ansible.builtin.debug:
var: uptime_output.stdout
23. Error handling
23.1 Ignore errors
- name: Try to stop non-existing service
ansible.builtin.service:
name: fake_service
state: stopped
ignore_errors: true
Use carefully. Ignoring errors blindly is how infrastructure goblins are born.
23.2 Mark task as failed manually
- name: Check if Apache is reachable
ansible.builtin.uri:
url: "http://localhost"
status_code: 200
register: result
failed_when: result.status != 200
23.3 Mark task as changed manually
- name: Run custom command
ansible.builtin.command: /usr/local/bin/custom-check
register: result
changed_when: "'changed' in result.stdout"
24. Include and import
For large automation, do not keep hundreds of tasks in one file.
Bad:
site.yaml with 500 tasks
Better:
site.yaml
web.yaml
app.yaml
db.yaml
24.1 Import another playbook
site.yml:
---
- import_playbook: web.yml
- import_playbook: app.yml
- import_playbook: db.yml
Run:
ansible-playbook site.yml
24.2 Include tasks
Directory:
tasks/
├── install.yml
├── configure.yml
└── service.yml
Playbook:
---
- name: Apache setup using included tasks
hosts: web_servers
become: true
tasks:
- name: Include install tasks
ansible.builtin.include_tasks: tasks/install.yml
- name: Include configure tasks
ansible.builtin.include_tasks: tasks/configure.yml
- name: Include service tasks
ansible.builtin.include_tasks: tasks/service.yml
25. Tags
Tags let you run only selected parts of a playbook.
---
- name: Apache with tags
hosts: web_servers
become: true
tasks:
- name: Install Apache
ansible.builtin.apt:
name: apache2
state: present
update_cache: true
tags:
- install
- name: Copy website
ansible.builtin.template:
src: templates/index.html.j2
dest: /var/www/html/index.html
tags:
- deploy
- name: Restart Apache
ansible.builtin.service:
name: apache2
state: restarted
tags:
- restart
Run only install tasks:
ansible-playbook web.yaml --tags install
Skip restart:
ansible-playbook web.yaml --skip-tags restart
The official playbook guide includes tags as a way to run selected parts of a playbook. (docs.ansible.com)
26. Check mode and diff mode
26.1 Check mode
Check mode shows what would change, without applying changes.
ansible-playbook web.yaml --check
26.2 Diff mode
Diff mode shows file differences.
ansible-playbook web.yaml --diff
26.3 Use both
ansible-playbook web.yaml --check --diff
The official playbook execution guide includes check mode and diff mode for validating tasks. (docs.ansible.com)
27. Privilege escalation: become
Many tasks need root privilege.
Example:
become: true
Full play:
---
- name: Install package with sudo
hosts: web_servers
become: true
tasks:
- name: Install Apache
ansible.builtin.apt:
name: apache2
state: present
Command with sudo password prompt:
ansible-playbook web.yaml --ask-become-pass
Short form:
ansible-playbook web.yaml -K
28. Ansible Vault
Ansible Vault encrypts sensitive data such as passwords, tokens, and secret variables. The official docs describe Vault as a way to encrypt and manage sensitive data such as passwords. (docs.ansible.com)
28.1 Create encrypted file
ansible-vault create secrets.yml
Example content:
db_password: "SuperSecretPassword"
api_token: "abcdef123456"
28.2 Edit encrypted file
ansible-vault edit secrets.yml
28.3 View encrypted file
ansible-vault view secrets.yml
28.4 Use vault file in playbook
---
- name: Use vault variables
hosts: all
vars_files:
- secrets.yml
tasks:
- name: Print secret variable safely
ansible.builtin.debug:
msg: "Password is loaded but not printed here"
Run:
ansible-playbook secure.yaml --ask-vault-pass
28.5 Encrypt existing file
ansible-vault encrypt secrets.yml
28.6 Decrypt file
ansible-vault decrypt secrets.yml
29. Roles
A role is a reusable Ansible structure. Roles organize tasks, handlers, variables, templates, and files into standard folders.
Instead of this:
one huge playbook with 300 lines
Use this:
roles/apache/
roles/mysql/
roles/app/
roles/common/
29.1 Create role manually
mkdir -p roles/apache/{tasks,handlers,templates,files,vars,defaults,meta}
touch roles/apache/tasks/main.yml
touch roles/apache/handlers/main.yml
29.2 Role structure
roles/apache/
├── tasks/
│ └── main.yml
├── handlers/
│ └── main.yml
├── templates/
│ └── index.html.j2
├── files/
│ └── index.html
├── vars/
│ └── main.yml
├── defaults/
│ └── main.yml
└── meta/
└── main.yml
29.3 Role task file
roles/apache/tasks/main.yml:
---
- name: Install Apache2
ansible.builtin.apt:
name: "{{ apache_package }}"
state: present
update_cache: true
- name: Deploy index.html
ansible.builtin.template:
src: index.html.j2
dest: /var/www/html/index.html
owner: root
group: root
mode: '0644'
notify:
- Restart Apache
- name: Ensure Apache is running
ansible.builtin.service:
name: "{{ apache_service }}"
state: started
enabled: true
29.4 Role handler file
roles/apache/handlers/main.yml:
---
- name: Restart Apache
ansible.builtin.service:
name: "{{ apache_service }}"
state: restarted
29.5 Role defaults
roles/apache/defaults/main.yml:
---
apache_package: apache2
apache_service: apache2
app_name: "DevOpsSchool Apache App"
29.6 Role template
roles/apache/templates/index.html.j2:
<h1>{{ app_name }}</h1>
<p>Managed by Ansible role.</p>
<p>Host: {{ ansible_hostname }}</p>
29.7 Use role in playbook
site.yml:
---
- name: Configure web servers using role
hosts: web_servers
become: true
roles:
- apache
Run:
ansible-playbook site.yml
30. Collections
Collections are a distribution format for Ansible content. They can include playbooks, roles, modules, and plugins. (docs.ansible.com)
30.1 Install a collection
ansible-galaxy collection install community.general
30.2 List installed collections
ansible-galaxy collection list
30.3 Use fully qualified collection names
Recommended style:
ansible.builtin.copy:
Instead of only:
copy:
The official module docs often recommend using fully qualified collection names, such as ansible.builtin.apt, to avoid conflicts and make documentation linking easier. (docs.ansible.com)
31. Complete beginner lab: Apache deployment
31.1 Final directory
ansible-demo/
├── ansible.cfg
├── inventory
├── site.yml
└── roles/
└── apache/
├── tasks/
│ └── main.yml
├── handlers/
│ └── main.yml
├── defaults/
│ └── main.yml
└── templates/
└── index.html.j2
31.2 inventory
[web_servers]
web1 ansible_host=3.110.83.207
web2 ansible_host=3.110.83.208
[db_servers]
db1 ansible_host=3.110.83.209
[all:vars]
ansible_user=ubuntu ansible_private_key_file=./node.pem
31.3 ansible.cfg
[defaults]
inventory = ./inventory
remote_user = ubuntu
private_key_file = ./node.pem
host_key_checking = False
stdout_callback = yaml
[privilege_escalation]
become = True become_method = sudo become_user = root become_ask_pass = False
31.4 site.yml
---
- name: Configure Apache web servers
hosts: web_servers
become: true
roles:
- apache
31.5 roles/apache/defaults/main.yml
---
apache_package: apache2
apache_service: apache2
app_name: "DevOpsSchool Ansible Demo"
document_root: /var/www/html
31.6 roles/apache/tasks/main.yml
---
- name: Install Apache package
ansible.builtin.apt:
name: "{{ apache_package }}"
state: present
update_cache: true
- name: Deploy home page
ansible.builtin.template:
src: index.html.j2
dest: "{{ document_root }}/index.html"
owner: root
group: root
mode: '0644'
notify:
- Restart Apache
- name: Ensure Apache service is running and enabled
ansible.builtin.service:
name: "{{ apache_service }}"
state: started
enabled: true
31.7 roles/apache/handlers/main.yml
---
- name: Restart Apache
ansible.builtin.service:
name: "{{ apache_service }}"
state: restarted
31.8 roles/apache/templates/index.html.j2
<!DOCTYPE html>
<html>
<head>
<title>{{ app_name }}</title>
</head>
<body>
<h1>{{ app_name }}</h1>
<p>This server is managed by Ansible.</p>
<p>Hostname: {{ ansible_hostname }}</p>
<p>OS: {{ ansible_distribution }} {{ ansible_distribution_version }}</p>
</body>
</html>
31.9 Run the lab
Check syntax:
ansible-playbook site.yml --syntax-check
Dry run:
ansible-playbook site.yml --check --diff
Apply changes:
ansible-playbook site.yml
Run only on one host:
ansible-playbook site.yml --limit web1
Run with verbose output:
ansible-playbook site.yml -v
More verbose:
ansible-playbook site.yml -vvv
32. Troubleshooting common Ansible errors
32.1 SSH permission denied
Error:
Permission denied (publickey)
Fix:
chmod 400 node.pem
ssh -i node.pem ubuntu@3.110.83.207
Then test:
ansible -i inventory all -m ping
32.2 Wrong remote user
Try different users:
-u ubuntu
-u ec2-user
-u admin
-u centos
AWS examples:
Ubuntu AMI -> ubuntu
Amazon Linux -> ec2-user
CentOS -> centos
RHEL -> ec2-user or cloud-user
Debian -> admin or debian
32.3 Sudo password required
Use:
ansible-playbook site.yml -K
Or configure passwordless sudo for lab users.
32.4 Python missing on managed node
Error may look like:
/usr/bin/python: not found
Fix for Ubuntu/Debian:
sudo apt update
sudo apt install -y python3
You can set interpreter:
[all:vars]
ansible_python_interpreter=/usr/bin/python3
32.5 Inventory group mismatch
Inventory:
[web_servers]
3.110.83.207
Playbook must use:
hosts: web_servers
Not:
hosts: webservers
Tiny typo. Big headache. Classic YAML gremlin.
33. Best practices
Use these habits from the beginning:
1. Use Git for Ansible code.
2. Use meaningful task names.
3. Use FQCN module names, such as ansible.builtin.copy.
4. Do not hardcode secrets in playbooks.
5. Use Ansible Vault for passwords and tokens.
6. Use roles for reusable automation.
7. Use variables instead of repeating values.
8. Test with --syntax-check.
9. Preview with --check --diff.
10. Use handlers for service restarts.
11. Avoid shell/command when a proper module exists.
12. Keep inventory, variables, playbooks, and roles organized.
13. Use group_vars and host_vars.
14. Keep production and staging inventories separate.
15. Keep host_key_checking enabled in production unless you have a valid reason.
34. Mini cheat sheet
Inventory test
ansible-inventory -i inventory --list
Ping all nodes
ansible all -m ping
Run command
ansible all -m command -a "uptime"
Run playbook
ansible-playbook site.yml
Syntax check
ansible-playbook site.yml --syntax-check
Dry run
ansible-playbook site.yml --check
Show diff
ansible-playbook site.yml --diff
Limit to one host
ansible-playbook site.yml --limit web1
Run by tag
ansible-playbook site.yml --tags install
Skip tag
ansible-playbook site.yml --skip-tags restart
Pass extra variable
ansible-playbook site.yml -e "app_name=DemoApp"
Ask sudo password
ansible-playbook site.yml -K
Ask vault password
ansible-playbook site.yml --ask-vault-pass
Reference URLs
URLs from your rough notes
https://www.devopsschool.com/blog/ansible-installation-and-configuration-guide/
https://docs.ansible.com/projects/ansible-core/devel/collections/ansible/builtin/copy_module.html#ansible-collections-ansible-builtin-copy-module
https://docs.ansible.com/projects/ansible/latest/collections/index_module.html
https://docs.ansible.com/projects/ansible/latest/collections/all_plugins.html
Ansible Tutorials: Ansible Variables in Playbook
How to set condition module executations in Ansible Playbook with Example?
https://www.devopsschool.com/blog/how-to-loop-and-iteration-executions-in-ansible-playbook-with-example/
Ansible Tutorials: Calling one Play & Tasks from another play in Playbook
Ansible Playbook Example
Ansible Template and Handlers explained with Example
Additional official reference URLs added
https://docs.ansible.com/projects/ansible/latest/index.html
https://docs.ansible.com/projects/ansible/latest/installation_guide/index.html
https://docs.ansible.com/projects/ansible/latest/installation_guide/intro_installation.html
https://docs.ansible.com/projects/ansible/latest/installation_guide/installation_distros.html
https://docs.ansible.com/projects/ansible/latest/installation_guide/intro_configuration.html
https://docs.ansible.com/projects/ansible/latest/inventory_guide/index.html
https://docs.ansible.com/projects/ansible/latest/playbook_guide/index.html
https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_variables.html
https://docs.ansible.com/projects/ansible/latest/vault_guide/index.html
https://docs.ansible.com/projects/ansible/latest/module_plugin_guide/index.html
https://docs.ansible.com/projects/ansible/latest/collections_guide/index.html
https://docs.ansible.com/projects/ansible/latest/collections/ansible/builtin/index.html
https://docs.ansible.com/projects/ansible/latest/collections/ansible/builtin/copy_module.html
https://docs.ansible.com/projects/ansible/latest/collections/ansible/builtin/template_module.html
https://docs.ansible.com/projects/ansible/latest/collections/ansible/builtin/apt_module.html
https://docs.ansible.com/projects/ansible/latest/os_guide/intro_windows.html
https://docs.ansible.com/projects/ansible/latest/os_guide/windows_winrm.html
https://docs.ansible.com/collections.html
https://github.com/ansible/awx
https://docs.ansible.com/projects/ansible/latest/reference_appendices/tower.html
https://www.redhat.com/en/technologies/management/ansible/automation-controller
https://www.redhat.com/en/technologies/management/ansible/compare-awx-vs-ansible-automation-platform