Automation18 min read1,806 lines

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

  1. Ansible Fundamentals
  2. Installation & Setup
  3. Inventory Management
  4. Ad-Hoc Commands
  5. Playbooks
  6. Variables & Facts
  7. Conditionals & Loops
  8. Handlers & Notifications
  9. Templates (Jinja2)
  10. Roles
  11. Ansible Vault
  12. Common Modules
  13. Error Handling
  14. Ansible Galaxy
  15. 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 command for simple commands, shell when you need pipes/redirects
  • Always use --become for privileged operations
  • -m specifies module, -a specifies arguments
  • --check for dry-run mode
  • --diff shows 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)

  1. Role defaults
  2. Inventory file/script group vars
  3. Inventory group_vars/all
  4. Playbook group_vars/all
  5. Inventory group_vars/*
  6. Playbook group_vars/*
  7. Inventory file/script host vars
  8. Inventory host_vars/*
  9. Playbook host_vars/*
  10. Host facts
  11. Play vars
  12. Play vars_files
  13. Role vars
  14. Block vars
  15. Task vars
  16. Extra vars (-e on 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

  1. Use Ansible Vault for secrets
  2. Don't commit vault passwords
  3. Use SSH keys, not passwords
  4. Limit privilege escalation
  5. Use least privilege principle
  6. Regularly update Ansible
  7. Use no_log for 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.