Learning state
Track this guide
Saved in this browser only. No account required.
Ansible Master Class
Engineering-Grade Reference Manual for Ansible
A comprehensive guide to configuration management and automation with Ansible
Table of Contents
- Ansible Fundamentals
- Installation & Setup
- Inventory Management
- Ad-Hoc Commands
- Playbooks
- Variables & Facts
- Conditionals & Loops
- Handlers & Notifications
- Templates (Jinja2)
- Roles
- Ansible Vault
- Common Modules
- Error Handling
- Ansible Galaxy
- Best Practices
Ansible Fundamentals
๐น What is Ansible?
Agentless automation tool for configuration management, application deployment, and task automation.
Key Concepts:
- Control Node - Machine where Ansible is installed
- Managed Nodes - Servers managed by Ansible (no agent required)
- Inventory - List of managed nodes
- Playbooks - YAML files defining automation tasks
- Modules - Units of code Ansible executes
- Tasks - Units of action in Ansible
- Roles - Reusable automation content
- Facts - System information gathered from managed nodes
Why Ansible?
- โ Agentless (uses SSH)
- โ Simple YAML syntax
- โ Idempotent operations
- โ Large module library
- โ Strong community support
Installation & Setup
๐น Installing Ansible
โ๏ธ macOS
# Using Homebrew
brew install ansible
# Verify installation
ansible --version
โ๏ธ Linux (Ubuntu/Debian)
# Add repository
sudo apt update
sudo apt install software-properties-common
sudo add-apt-repository --yes --update ppa:ansible/ansible
# Install Ansible
sudo apt install ansible
# Verify
ansible --version
โ๏ธ Linux (RHEL/CentOS)
# Enable EPEL repository
sudo yum install epel-release
# Install Ansible
sudo yum install ansible
# Verify
ansible --version
โ๏ธ Using pip
# Install via pip
pip install ansible
# Verify
ansible --version
๐น Initial Configuration
โ๏ธ Ansible Configuration File
# /etc/ansible/ansible.cfg or ~/.ansible.cfg
[defaults]
inventory = ./inventory
remote_user = ansible
host_key_checking = False
retry_files_enabled = False
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 86400
[privilege_escalation]
become = True
become_method = sudo
become_user = root
become_ask_pass = False
[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
โ๏ธ SSH Key Setup
# Generate SSH key
ssh-keygen -t rsa -b 4096 -C "ansible@example.com"
# Copy to managed nodes
ssh-copy-id user@server1
ssh-copy-id user@server2
# Test connection
ssh user@server1
Inventory Management
๐น Static Inventory
โ๏ธ Basic INI Format
# inventory/hosts
[webservers]
web1.example.com
web2.example.com
web3.example.com
[databases]
db1.example.com
db2.example.com
[loadbalancers]
lb1.example.com
[production:children]
webservers
databases
loadbalancers
โ๏ธ Inventory with Variables
# inventory/hosts
[webservers]
web1.example.com ansible_host=192.168.1.10 ansible_port=22
web2.example.com ansible_host=192.168.1.11 ansible_port=22
[webservers:vars]
ansible_user=ubuntu
ansible_python_interpreter=/usr/bin/python3
http_port=80
max_clients=200
[databases]
db1.example.com ansible_host=192.168.1.20
[databases:vars]
ansible_user=ubuntu
db_port=5432
โ๏ธ YAML Inventory
# inventory/hosts.yml
all:
children:
webservers:
hosts:
web1.example.com:
ansible_host: 192.168.1.10
web2.example.com:
ansible_host: 192.168.1.11
vars:
ansible_user: ubuntu
http_port: 80
databases:
hosts:
db1.example.com:
ansible_host: 192.168.1.20
vars:
ansible_user: ubuntu
db_port: 5432
production:
children:
webservers:
databases:
โ๏ธ Dynamic Inventory
#!/usr/bin/env python3
# inventory/dynamic_inventory.py
import json
inventory = {
"webservers": {
"hosts": ["web1.example.com", "web2.example.com"],
"vars": {
"ansible_user": "ubuntu",
"http_port": 80
}
},
"databases": {
"hosts": ["db1.example.com"],
"vars": {
"ansible_user": "ubuntu"
}
},
"_meta": {
"hostvars": {
"web1.example.com": {"ansible_host": "192.168.1.10"},
"web2.example.com": {"ansible_host": "192.168.1.11"},
"db1.example.com": {"ansible_host": "192.168.1.20"}
}
}
}
print(json.dumps(inventory, indent=2))
Ad-Hoc Commands
๐น Command: ansible
โ๏ธ What It Does
Executes single tasks on managed nodes without writing playbooks.
โ๏ธ Syntax Examples
# Basic: Ping all hosts
ansible all -m ping
# Real-world: Check disk space
ansible webservers -m shell -a "df -h"
# Production: Install package
ansible webservers -m apt -a "name=nginx state=present" --become
# Copy file
ansible all -m copy -a "src=/local/file dest=/remote/file"
# Restart service
ansible webservers -m service -a "name=nginx state=restarted" --become
# Gather facts
ansible all -m setup
# Run command
ansible all -m command -a "uptime"
# Check specific host
ansible web1.example.com -m ping
# Use specific user
ansible all -m ping -u ubuntu
# Limit to specific hosts
ansible all -m ping --limit "web*"
โ๏ธ Common Modules for Ad-Hoc
# ping - Test connectivity
ansible all -m ping
# command - Execute commands (no shell processing)
ansible all -m command -a "ls -la /tmp"
# shell - Execute commands (with shell processing)
ansible all -m shell -a "echo $HOME"
# apt/yum - Package management
ansible all -m apt -a "name=vim state=present" --become
# service - Service management
ansible all -m service -a "name=nginx state=started" --become
# copy - Copy files
ansible all -m copy -a "src=file.txt dest=/tmp/file.txt"
# file - Manage files/directories
ansible all -m file -a "path=/tmp/test state=directory"
# user - User management
ansible all -m user -a "name=john state=present" --become
# git - Git operations
ansible all -m git -a "repo=https://github.com/user/repo dest=/opt/app"
โ๏ธ Notes, Tips & Common Mistakes
- Use
commandfor simple commands,shellwhen you need pipes/redirects - Always use
--becomefor privileged operations -mspecifies module,-aspecifies arguments--checkfor dry-run mode--diffshows changes
Playbooks
๐น Basic Playbook Structure
โ๏ธ Simple Playbook
# playbook.yml
---
- name: Configure web servers
hosts: webservers
become: yes
tasks:
- name: Install nginx
apt:
name: nginx
state: present
update_cache: yes
- name: Start nginx service
service:
name: nginx
state: started
enabled: yes
- name: Copy index.html
copy:
src: files/index.html
dest: /var/www/html/index.html
owner: www-data
group: www-data
mode: '0644'
โ๏ธ Running Playbooks
# Basic: Run playbook
ansible-playbook playbook.yml
# Real-world: Check mode (dry run)
ansible-playbook playbook.yml --check
# Production: Show differences
ansible-playbook playbook.yml --check --diff
# Limit to specific hosts
ansible-playbook playbook.yml --limit webservers
# Use specific inventory
ansible-playbook -i inventory/production playbook.yml
# Verbose output
ansible-playbook playbook.yml -v
ansible-playbook playbook.yml -vv
ansible-playbook playbook.yml -vvv
# Start at specific task
ansible-playbook playbook.yml --start-at-task="Install nginx"
# Use tags
ansible-playbook playbook.yml --tags "configuration"
ansible-playbook playbook.yml --skip-tags "packages"
โ๏ธ Multi-Play Playbook
---
- name: Configure web servers
hosts: webservers
become: yes
tasks:
- name: Install nginx
apt:
name: nginx
state: present
- name: Configure databases
hosts: databases
become: yes
tasks:
- name: Install PostgreSQL
apt:
name: postgresql
state: present
- name: Configure load balancers
hosts: loadbalancers
become: yes
tasks:
- name: Install HAProxy
apt:
name: haproxy
state: present
Variables & Facts
๐น Variable Definition
โ๏ธ Playbook Variables
---
- name: Deploy application
hosts: webservers
become: yes
vars:
app_name: myapp
app_version: 1.2.3
app_port: 8080
tasks:
- name: Create app directory
file:
path: "/opt/{{ app_name }}"
state: directory
- name: Display app info
debug:
msg: "Deploying {{ app_name }} version {{ app_version }} on port {{ app_port }}"
โ๏ธ Variable Files
# vars/main.yml
app_name: myapp
app_version: 1.2.3
app_port: 8080
database_host: db1.example.com
database_port: 5432
# playbook.yml
---
- name: Deploy application
hosts: webservers
become: yes
vars_files:
- vars/main.yml
tasks:
- name: Show database connection
debug:
msg: "Connecting to {{ database_host }}:{{ database_port }}"
โ๏ธ Group Variables
# inventory/group_vars/webservers.yml
ansible_user: ubuntu
http_port: 80
max_connections: 200
# inventory/group_vars/databases.yml
ansible_user: postgres
db_port: 5432
max_connections: 100
# inventory/host_vars/web1.example.com.yml
server_id: 1
datacenter: us-east
โ๏ธ Variable Precedence (lowest to highest)
- Role defaults
- Inventory file/script group vars
- Inventory group_vars/all
- Playbook group_vars/all
- Inventory group_vars/*
- Playbook group_vars/*
- Inventory file/script host vars
- Inventory host_vars/*
- Playbook host_vars/*
- Host facts
- Play vars
- Play vars_files
- Role vars
- Block vars
- Task vars
- Extra vars (
-eon command line)
๐น Facts
โ๏ธ Using Facts
---
- name: Display system facts
hosts: all
tasks:
- name: Show OS family
debug:
msg: "OS: {{ ansible_os_family }}"
- name: Show IP address
debug:
msg: "IP: {{ ansible_default_ipv4.address }}"
- name: Show hostname
debug:
msg: "Hostname: {{ ansible_hostname }}"
- name: Show memory
debug:
msg: "Total memory: {{ ansible_memtotal_mb }} MB"
โ๏ธ Custom Facts
# Create custom fact on managed node
# /etc/ansible/facts.d/custom.fact
#!/bin/bash
echo "{\"app_version\": \"1.2.3\", \"environment\": \"production\"}"
# Use custom facts
---
- name: Use custom facts
hosts: all
tasks:
- name: Show custom fact
debug:
msg: "App version: {{ ansible_local.custom.app_version }}"
โ๏ธ Disable Fact Gathering
---
- name: Quick playbook
hosts: all
gather_facts: no
tasks:
- name: Simple task
debug:
msg: "No facts gathered"
Conditionals & Loops
๐น Conditionals
โ๏ธ When Conditions
---
- name: Conditional tasks
hosts: all
become: yes
tasks:
- name: Install Apache on Debian
apt:
name: apache2
state: present
when: ansible_os_family == "Debian"
- name: Install Apache on RedHat
yum:
name: httpd
state: present
when: ansible_os_family == "RedHat"
- name: Restart service if config changed
service:
name: nginx
state: restarted
when: config_file.changed
- name: Multiple conditions (AND)
debug:
msg: "Production web server"
when:
- ansible_hostname == "web1"
- environment == "production"
- name: Multiple conditions (OR)
debug:
msg: "Development or staging"
when: environment == "dev" or environment == "staging"
โ๏ธ Failed When
---
- name: Custom failure conditions
hosts: all
tasks:
- name: Check disk space
shell: df -h / | tail -1 | awk '{print $5}' | sed 's/%//'
register: disk_usage
failed_when: disk_usage.stdout|int > 90
- name: Check service status
command: systemctl status nginx
register: service_status
failed_when: "'inactive' in service_status.stdout"
๐น Loops
โ๏ธ Simple Loop
---
- name: Loop examples
hosts: all
become: yes
tasks:
- name: Install multiple packages
apt:
name: "{{ item }}"
state: present
loop:
- nginx
- git
- vim
- curl
- name: Create multiple users
user:
name: "{{ item }}"
state: present
loop:
- alice
- bob
- charlie
โ๏ธ Loop with Dictionary
---
- name: Loop with dictionaries
hosts: all
become: yes
tasks:
- name: Create users with specific settings
user:
name: "{{ item.name }}"
uid: "{{ item.uid }}"
groups: "{{ item.groups }}"
state: present
loop:
- { name: 'alice', uid: 1001, groups: 'developers' }
- { name: 'bob', uid: 1002, groups: 'admins' }
- { name: 'charlie', uid: 1003, groups: 'developers' }
โ๏ธ Loop with Register
---
- name: Loop with register
hosts: all
tasks:
- name: Check multiple services
service:
name: "{{ item }}"
state: started
loop:
- nginx
- postgresql
- redis
register: service_results
- name: Display results
debug:
msg: "{{ item.item }} is {{ item.state }}"
loop: "{{ service_results.results }}"
โ๏ธ Loop Control
---
- name: Loop control
hosts: all
tasks:
- name: Loop with pause
debug:
msg: "Processing {{ item }}"
loop:
- item1
- item2
- item3
loop_control:
pause: 2
label: "{{ item }}"
Handlers & Notifications
๐น Handlers
โ๏ธ Basic Handlers
---
- name: Configure web server
hosts: webservers
become: yes
tasks:
- name: Copy nginx config
copy:
src: files/nginx.conf
dest: /etc/nginx/nginx.conf
notify: Restart nginx
- name: Copy site config
template:
src: templates/site.conf.j2
dest: /etc/nginx/sites-available/default
notify:
- Restart nginx
- Clear cache
handlers:
- name: Restart nginx
service:
name: nginx
state: restarted
- name: Clear cache
command: rm -rf /var/cache/nginx/*
โ๏ธ Handler with Listen
---
- name: Configure services
hosts: all
become: yes
tasks:
- name: Update nginx config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Restart web services
- name: Update php-fpm config
template:
src: php-fpm.conf.j2
dest: /etc/php/7.4/fpm/php-fpm.conf
notify: Restart web services
handlers:
- name: Restart nginx
service:
name: nginx
state: restarted
listen: Restart web services
- name: Restart php-fpm
service:
name: php7.4-fpm
state: restarted
listen: Restart web services
โ๏ธ Force Handler Execution
---
- name: Force handlers
hosts: all
tasks:
- name: Update config
copy:
src: config.conf
dest: /etc/app/config.conf
notify: Restart app
- name: Force all handlers to run now
meta: flush_handlers
- name: Verify app is running
wait_for:
port: 8080
delay: 5
handlers:
- name: Restart app
service:
name: myapp
state: restarted
Templates (Jinja2)
๐น Basic Templates
โ๏ธ Simple Template
{# templates/nginx.conf.j2 #}
user {{ nginx_user }};
worker_processes {{ ansible_processor_vcpus }};
events {
worker_connections {{ worker_connections }};
}
http {
server {
listen {{ http_port }};
server_name {{ server_name }};
location / {
root {{ document_root }};
index index.html;
}
}
}
# playbook.yml
---
- name: Configure nginx
hosts: webservers
become: yes
vars:
nginx_user: www-data
worker_connections: 1024
http_port: 80
server_name: example.com
document_root: /var/www/html
tasks:
- name: Deploy nginx config
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Restart nginx
handlers:
- name: Restart nginx
service:
name: nginx
state: restarted
โ๏ธ Template with Conditionals
{# templates/app.conf.j2 #}
[app]
name = {{ app_name }}
version = {{ app_version }}
{% if environment == "production" %}
debug = false
log_level = warning
{% else %}
debug = true
log_level = debug
{% endif %}
[database]
host = {{ db_host }}
port = {{ db_port }}
{% if db_ssl_enabled %}
ssl_mode = require
{% endif %}
โ๏ธ Template with Loops
{# templates/hosts.j2 #}
127.0.0.1 localhost
# Web servers
{% for host in groups['webservers'] %}
{{ hostvars[host]['ansible_default_ipv4']['address'] }} {{ host }}
{% endfor %}
# Database servers
{% for host in groups['databases'] %}
{{ hostvars[host]['ansible_default_ipv4']['address'] }} {{ host }}
{% endfor %}
โ๏ธ Template Filters
{# Common Jinja2 filters #}
{# String manipulation #}
{{ app_name | upper }}
{{ app_name | lower }}
{{ app_name | capitalize }}
{{ message | replace('old', 'new') }}
{# Default values #}
{{ variable | default('default_value') }}
{# List operations #}
{{ packages | join(', ') }}
{{ numbers | max }}
{{ numbers | min }}
{{ items | length }}
{# Type conversion #}
{{ port | int }}
{{ value | string }}
{{ flag | bool }}
{# File paths #}
{{ '/path/to/file' | basename }}
{{ '/path/to/file' | dirname }}
Roles
๐น Role Structure
โ๏ธ Standard Role Directory Structure
roles/
โโโ webserver/
โโโ defaults/
โ โโโ main.yml # Default variables
โโโ files/
โ โโโ index.html # Static files
โโโ handlers/
โ โโโ main.yml # Handlers
โโโ meta/
โ โโโ main.yml # Role metadata
โโโ tasks/
โ โโโ main.yml # Main tasks
โโโ templates/
โ โโโ nginx.conf.j2 # Jinja2 templates
โโโ tests/
โ โโโ inventory
โ โโโ test.yml # Test playbook
โโโ vars/
โโโ main.yml # Role variables
โ๏ธ Creating a Role
# Create role structure
ansible-galaxy init webserver
# Or manually create
mkdir -p roles/webserver/{tasks,handlers,templates,files,vars,defaults,meta}
โ๏ธ Example Role: Webserver
# roles/webserver/defaults/main.yml
---
nginx_port: 80
nginx_user: www-data
document_root: /var/www/html
# roles/webserver/vars/main.yml
---
nginx_package: nginx
nginx_service: nginx
# roles/webserver/tasks/main.yml
---
- name: Install nginx
apt:
name: "{{ nginx_package }}"
state: present
update_cache: yes
- name: Deploy nginx configuration
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Restart nginx
- name: Create document root
file:
path: "{{ document_root }}"
state: directory
owner: "{{ nginx_user }}"
group: "{{ nginx_user }}"
- name: Deploy index.html
copy:
src: index.html
dest: "{{ document_root }}/index.html"
- name: Start nginx service
service:
name: "{{ nginx_service }}"
state: started
enabled: yes
# roles/webserver/handlers/main.yml
---
- name: Restart nginx
service:
name: "{{ nginx_service }}"
state: restarted
# roles/webserver/meta/main.yml
---
galaxy_info:
author: Your Name
description: Nginx web server role
company: Your Company
license: MIT
min_ansible_version: 2.9
platforms:
- name: Ubuntu
versions:
- focal
- jammy
galaxy_tags:
- web
- nginx
dependencies: []
โ๏ธ Using Roles in Playbooks
# playbook.yml
---
- name: Configure web servers
hosts: webservers
become: yes
roles:
- webserver
# With variables
---
- name: Configure web servers
hosts: webservers
become: yes
roles:
- role: webserver
vars:
nginx_port: 8080
document_root: /var/www/myapp
# Multiple roles
---
- name: Full stack deployment
hosts: all
become: yes
roles:
- common
- security
- webserver
- database
- monitoring
โ๏ธ Role Dependencies
# roles/webserver/meta/main.yml
---
dependencies:
- role: common
- role: firewall
vars:
allowed_ports:
- 80
- 443
Ansible Vault
๐น Encrypting Sensitive Data
โ๏ธ Create Encrypted File
# Create new encrypted file
ansible-vault create secrets.yml
# Edit encrypted file
ansible-vault edit secrets.yml
# View encrypted file
ansible-vault view secrets.yml
# Encrypt existing file
ansible-vault encrypt vars/passwords.yml
# Decrypt file
ansible-vault decrypt vars/passwords.yml
# Rekey (change password)
ansible-vault rekey secrets.yml
โ๏ธ Encrypted Variables
# secrets.yml (encrypted)
---
db_password: supersecret123
api_key: abc123xyz789
ssl_private_key: |
[REDACTED PRIVATE KEY]
โ๏ธ Using Vault in Playbooks
# Run playbook with vault password
ansible-playbook playbook.yml --ask-vault-pass
# Use password file
ansible-playbook playbook.yml --vault-password-file ~/.vault_pass
# Multiple vault passwords
ansible-playbook playbook.yml --vault-id dev@~/.vault_pass_dev --vault-id prod@~/.vault_pass_prod
# playbook.yml
---
- name: Deploy application
hosts: all
become: yes
vars_files:
- secrets.yml
tasks:
- name: Configure database connection
template:
src: config.j2
dest: /etc/app/config.ini
vars:
database_password: "{{ db_password }}"
โ๏ธ Encrypt Specific Variables
# Encrypt a string
ansible-vault encrypt_string 'supersecret' --name 'db_password'
# vars/main.yml
---
db_host: localhost
db_user: admin
db_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
66386439653765386661393063336336...
Common Modules
๐น Package Management
โ๏ธ apt (Debian/Ubuntu)
- name: Install package
apt:
name: nginx
state: present
update_cache: yes
- name: Install multiple packages
apt:
name:
- nginx
- git
- vim
state: present
- name: Remove package
apt:
name: apache2
state: absent
purge: yes
- name: Upgrade all packages
apt:
upgrade: dist
update_cache: yes
โ๏ธ yum/dnf (RHEL/CentOS)
- name: Install package
yum:
name: nginx
state: present
- name: Install from URL
yum:
name: https://example.com/package.rpm
state: present
๐น File Operations
โ๏ธ copy
- name: Copy file
copy:
src: files/config.conf
dest: /etc/app/config.conf
owner: root
group: root
mode: '0644'
backup: yes
โ๏ธ template
- name: Deploy template
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
validate: 'nginx -t -c %s'
โ๏ธ file
- name: Create directory
file:
path: /opt/app
state: directory
owner: appuser
group: appuser
mode: '0755'
- name: Create symlink
file:
src: /opt/app/current
dest: /opt/app/releases/v1.2.3
state: link
- name: Remove file
file:
path: /tmp/oldfile
state: absent
โ๏ธ lineinfile
- name: Ensure line in file
lineinfile:
path: /etc/hosts
line: '192.168.1.100 myserver.local'
state: present
- name: Replace line
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^PermitRootLogin'
line: 'PermitRootLogin no'
๐น Service Management
โ๏ธ service/systemd
- name: Start service
service:
name: nginx
state: started
enabled: yes
- name: Restart service
service:
name: nginx
state: restarted
- name: Reload service
systemd:
name: nginx
state: reloaded
daemon_reload: yes
๐น User Management
โ๏ธ user
- name: Create user
user:
name: john
uid: 1001
groups: developers,docker
shell: /bin/bash
create_home: yes
state: present
- name: Add SSH key
authorized_key:
user: john
key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
state: present
๐น Command Execution
โ๏ธ command vs shell
# command - No shell processing
- name: Run command
command: ls -la /tmp
args:
chdir: /opt
# shell - With shell processing
- name: Run shell command
shell: echo $HOME > /tmp/home.txt
# script - Run local script on remote
- name: Run script
script: scripts/setup.sh
๐น Git Operations
โ๏ธ git
- name: Clone repository
git:
repo: https://github.com/user/repo.git
dest: /opt/app
version: main
force: yes
- name: Update repository
git:
repo: https://github.com/user/repo.git
dest: /opt/app
update: yes
Error Handling
๐น Error Control
โ๏ธ Ignore Errors
- name: Task that might fail
command: /bin/false
ignore_errors: yes
- name: Continue even if this fails
shell: some_command_that_might_fail
ignore_errors: yes
โ๏ธ Failed When
- name: Check application status
command: /opt/app/healthcheck.sh
register: health_check
failed_when: "'ERROR' in health_check.stdout"
โ๏ธ Changed When
- name: Run idempotent script
command: /opt/scripts/configure.sh
register: result
changed_when: "'Configuration updated' in result.stdout"
โ๏ธ Block/Rescue/Always
- name: Error handling with blocks
block:
- name: Attempt deployment
command: /opt/deploy.sh
- name: Verify deployment
command: /opt/verify.sh
rescue:
- name: Rollback on failure
command: /opt/rollback.sh
- name: Send alert
debug:
msg: "Deployment failed, rolled back"
always:
- name: Cleanup
file:
path: /tmp/deploy
state: absent
Ansible Galaxy
๐น Using Galaxy Roles
โ๏ธ Install Roles
# Install role from Galaxy
ansible-galaxy install geerlingguy.nginx
# Install specific version
ansible-galaxy install geerlingguy.nginx,2.8.0
# Install from requirements file
ansible-galaxy install -r requirements.yml
# Install to specific path
ansible-galaxy install geerlingguy.nginx -p ./roles
โ๏ธ Requirements File
# requirements.yml
---
roles:
- name: geerlingguy.nginx
version: 2.8.0
- name: geerlingguy.postgresql
version: 3.3.0
- src: https://github.com/user/custom-role.git
name: custom-role
version: main
collections:
- name: community.general
version: 5.0.0
- name: ansible.posix
โ๏ธ List and Remove Roles
# List installed roles
ansible-galaxy list
# Remove role
ansible-galaxy remove geerlingguy.nginx
Best Practices
โ Project Structure
ansible-project/
โโโ ansible.cfg
โโโ inventory/
โ โโโ production/
โ โ โโโ hosts
โ โ โโโ group_vars/
โ โ โโโ all.yml
โ โ โโโ webservers.yml
โ โโโ staging/
โ โโโ hosts
โ โโโ group_vars/
โโโ playbooks/
โ โโโ site.yml
โ โโโ webservers.yml
โ โโโ databases.yml
โโโ roles/
โ โโโ common/
โ โโโ webserver/
โ โโโ database/
โโโ files/
โโโ templates/
โโโ vars/
โ โโโ secrets.yml (encrypted)
โโโ requirements.yml
โ Playbook Best Practices
1. Use Descriptive Names
# Bad
- name: Install stuff
apt:
name: nginx
# Good
- name: Install nginx web server
apt:
name: nginx
state: present
2. Use Tags
- name: Configure application
hosts: all
tasks:
- name: Install packages
apt:
name: "{{ item }}"
loop: "{{ packages }}"
tags: packages
- name: Deploy configuration
template:
src: config.j2
dest: /etc/app/config
tags: configuration
3. Use Check Mode
- name: Potentially destructive task
file:
path: /important/data
state: absent
check_mode: yes
4. Use Blocks for Organization
- name: Web server setup
block:
- name: Install nginx
apt:
name: nginx
- name: Configure nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
when: ansible_os_family == "Debian"
become: yes
tags: webserver
5. Use Ansible Lint
# Install ansible-lint
pip install ansible-lint
# Lint playbook
ansible-lint playbook.yml
# Lint all playbooks
ansible-lint playbooks/
โ Security Best Practices
- Use Ansible Vault for secrets
- Don't commit vault passwords
- Use SSH keys, not passwords
- Limit privilege escalation
- Use least privilege principle
- Regularly update Ansible
- Use
no_logfor sensitive tasks
- name: Set password
user:
name: john
password: "{{ user_password }}"
no_log: true
โ Performance Optimization
# ansible.cfg
[defaults]
forks = 20
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 86400
[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
Quick Reference Card
Essential Commands
# Ad-hoc commands
ansible all -m ping
ansible all -m shell -a "uptime"
ansible webservers -m apt -a "name=nginx state=present" --become
# Playbooks
ansible-playbook playbook.yml
ansible-playbook playbook.yml --check
ansible-playbook playbook.yml --check --diff
ansible-playbook playbook.yml --limit webservers
ansible-playbook playbook.yml --tags "configuration"
# Inventory
ansible-inventory --list
ansible-inventory --graph
# Vault
ansible-vault create secrets.yml
ansible-vault edit secrets.yml
ansible-vault encrypt file.yml
ansible-vault decrypt file.yml
# Galaxy
ansible-galaxy install geerlingguy.nginx
ansible-galaxy install -r requirements.yml
ansible-galaxy list
# Documentation
ansible-doc apt
ansible-doc -l
Common Playbook Patterns
# Basic playbook
---
- name: Configure servers
hosts: all
become: yes
tasks:
- name: Install package
apt:
name: nginx
state: present
# With variables
---
- name: Deploy app
hosts: webservers
become: yes
vars:
app_version: 1.2.3
tasks:
- name: Deploy version {{ app_version }}
copy:
src: "app-{{ app_version }}.tar.gz"
dest: /opt/app/
# With roles
---
- name: Full stack
hosts: all
become: yes
roles:
- common
- webserver
- database
๐ Ansible Master Class Complete
This comprehensive guide covers everything from basic Ansible concepts to advanced automation patterns. Use this as your reference for building scalable, maintainable infrastructure automation.