Available for day contractsFrom 21st September I have availability for day and half day contracts. Please contact for more information.

Contact →
mikepreston.org

Ansible

Configuration management and automation tool for provisioning, configuration, and application deployment.

Ansible

Configuration management and automation tool for provisioning, configuration, and application deployment.

Overview

Ansible is an agentless automation platform that uses SSH to connect to managed nodes and push configurations. It uses YAML-based playbooks to define automation tasks, making infrastructure as code readable and maintainable. Ansible follows a declarative approach, ensuring systems reach a desired state through idempotent operations.

Control NodeSSHSSHSSHControl NodeManaged Node 1Managed Node 2Managed Node 3PlaybooksInventoryModulesPluginsControl NodeSSHSSHSSHControl NodeManaged Node 1Managed Node 2Managed Node 3PlaybooksInventoryModulesPlugins

Inventory Management

Inventory files define the hosts and groups that Ansible manages. They can be static (INI or YAML files) or dynamic (scripts that query external sources).

Key Concepts

  • Inventory file: Lists managed nodes organised into groups
  • Host groups: Logical groupings of servers (e.g., webservers, databases)
  • Host variables: Variables specific to individual hosts
  • Group variables: Variables applied to all hosts in a group
  • Dynamic inventory: Scripts that generate inventory from external sources (AWS, GCP, etc.)

Static Inventory

# /etc/ansible/hosts or inventory.ini

# Ungrouped hosts
server1.example.com

# Group definition
[webservers]
web1.example.com
web2.example.com ansible_port=2222

[databases]
db1.example.com
db2.example.com

# Group of groups
[production:children]
webservers
databases

# Group variables
[webservers:vars]
http_port=80
proxy_env=production

# Host with connection variables
[loadbalancers]
lb1.example.com ansible_host=192.168.1.50 ansible_user=admin ansible_ssh_private_key_file=/path/to/key

YAML Inventory Format

# inventory.yml
all:
  children:
    webservers:
      hosts:
        web1.example.com:
        web2.example.com:
          ansible_port: 2222
      vars:
        http_port: 80
    databases:
      hosts:
        db1.example.com:
        db2.example.com:
      vars:
        db_port: 5432
    production:
      children:
        webservers:
        databases:

Dynamic Inventory

# List hosts from AWS EC2
ansible-inventory -i aws_ec2.yml --list

# Example aws_ec2.yml plugin configuration
# aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
  - eu-west-1
keyed_groups:
  - key: tags.Environment
    prefix: env
filters:
  instance-state-name: running

Common Commands

# List all hosts in inventory
ansible-inventory --list -i inventory.yml

# List hosts in a specific group
ansible webservers --list-hosts -i inventory.ini

# Graph inventory structure
ansible-inventory --graph -i inventory.yml

# Test connectivity to all hosts
ansible all -m ping -i inventory.ini

# Test connectivity to specific group
ansible webservers -m ping -i inventory.ini

Playbook Structure

Playbooks are YAML files that define the desired state of managed systems through plays, tasks, and handlers.

Key Concepts

  • Play: Maps a group of hosts to tasks
  • Task: A single action to be performed (calls a module)
  • Handler: Task triggered by notifications from other tasks
  • Role: Reusable collection of tasks, handlers, variables, and files
PlaybookPlay 1Play 2TasksHandlersVariablesRolestasks/main.ymlhandlers/main.ymldefaults/main.ymltemplates/files/PlaybookPlay 1Play 2TasksHandlersVariablesRolestasks/main.ymlhandlers/main.ymldefaults/main.ymltemplates/files/

Basic Playbook Structure

---
# site.yml
- name: Configure webservers
  hosts: webservers
  become: yes
  vars:
    http_port: 80
    doc_root: /var/www/html

  tasks:
    - name: Install nginx
      apt:
        name: nginx
        state: present
        update_cache: yes

    - name: Copy nginx configuration
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify: Restart nginx

    - name: Ensure nginx is running
      service:
        name: nginx
        state: started
        enabled: yes

  handlers:
    - name: Restart nginx
      service:
        name: nginx
        state: restarted

- name: Configure databases
  hosts: databases
  become: yes
  roles:
    - postgresql
    - backup

Role Structure

# Create a new role
ansible-galaxy init myrole

# Standard role directory structure
myrole/
├── defaults/
│   └── main.yml      # Default variables (lowest priority)
├── files/
│   └── myfile.txt    # Static files to copy
├── handlers/
│   └── main.yml      # Handler definitions
├── meta/
│   └── main.yml      # Role metadata and dependencies
├── tasks/
│   └── main.yml      # Main task list
├── templates/
│   └── config.j2     # Jinja2 templates
├── tests/
│   ├── inventory
│   └── test.yml
└── vars/
    └── main.yml      # Role variables (higher priority)

Example Role Tasks

# roles/webserver/tasks/main.yml
---
- name: Install web server packages
  apt:
    name: "{{ item }}"
    state: present
  loop:
    - nginx
    - php-fpm
  tags:
    - packages

- name: Deploy configuration
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: '0644'
  notify: Reload nginx
  tags:
    - configuration

- name: Create document root
  file:
    path: "{{ doc_root }}"
    state: directory
    owner: www-data
    group: www-data
    mode: '0755'

Running Playbooks

# Run a playbook
ansible-playbook site.yml -i inventory.ini

# Run with specific inventory
ansible-playbook -i production.ini deploy.yml

# Limit to specific hosts or groups
ansible-playbook site.yml --limit webservers

# Run specific tags only
ansible-playbook site.yml --tags "configuration,packages"

# Skip specific tags
ansible-playbook site.yml --skip-tags "packages"

# Start at a specific task
ansible-playbook site.yml --start-at-task "Deploy configuration"

Modules

Modules are the units of work in Ansible. They perform specific tasks on managed nodes.

Key Concepts

  • Idempotent: Modules can be run multiple times without changing the result
  • Return values: Modules return JSON data about the operation
  • Check mode: Most modules support dry-run mode

Command and Shell Modules

---
- name: Execute commands
  hosts: all
  tasks:
    # command module - simple commands (no shell features)
    - name: Check disk space
      command: df -h
      register: disk_space
      changed_when: false

    # shell module - supports pipes, redirects, environment variables
    - name: Find large files
      shell: find /var/log -type f -size +100M | head -10
      register: large_files
      changed_when: false

    # raw module - bypasses the module subsystem
    - name: Install Python on minimal systems
      raw: apt-get install -y python3
      when: ansible_python_interpreter is not defined

File Management Modules

---
- name: File operations
  hosts: all
  tasks:
    # copy module - copy files from control node
    - name: Copy configuration file
      copy:
        src: files/app.conf
        dest: /etc/app/app.conf
        owner: root
        group: root
        mode: '0644'
        backup: yes

    # template module - process Jinja2 templates
    - name: Deploy templated configuration
      template:
        src: templates/nginx.conf.j2
        dest: /etc/nginx/nginx.conf
        owner: root
        group: root
        mode: '0644'
        validate: nginx -t -c %s
      notify: Reload nginx

    # file module - manage file properties
    - name: Create directory
      file:
        path: /opt/myapp
        state: directory
        owner: appuser
        group: appgroup
        mode: '0755'

    - name: Create symbolic link
      file:
        src: /opt/myapp/current
        dest: /var/www/app
        state: link

    # lineinfile module - manage lines in files
    - name: Ensure line in file
      lineinfile:
        path: /etc/hosts
        line: "192.168.1.100 app.local"
        state: present

    # blockinfile module - manage blocks of text
    - name: Add configuration block
      blockinfile:
        path: /etc/ssh/sshd_config
        block: |
          Match User deploy
            PasswordAuthentication no
            PubkeyAuthentication yes
        marker: "# {mark} ANSIBLE MANAGED BLOCK - deploy user"

Package Management Modules

---
- name: Package management
  hosts: all
  tasks:
    # apt module - Debian/Ubuntu
    - name: Install packages (apt)
      apt:
        name:
          - nginx
          - postgresql
          - redis-server
        state: present
        update_cache: yes
        cache_valid_time: 3600
      when: ansible_os_family == "Debian"

    - name: Remove package
      apt:
        name: apache2
        state: absent
        purge: yes
      when: ansible_os_family == "Debian"

    # yum module - RHEL/CentOS 7
    - name: Install packages (yum)
      yum:
        name:
          - nginx
          - postgresql-server
        state: present
      when: ansible_os_family == "RedHat" and ansible_distribution_major_version == "7"

    # dnf module - RHEL/CentOS 8+
    - name: Install packages (dnf)
      dnf:
        name:
          - nginx
          - postgresql-server
        state: present
      when: ansible_os_family == "RedHat" and ansible_distribution_major_version | int >= 8

    # package module - generic (auto-detects package manager)
    - name: Install package (generic)
      package:
        name: git
        state: present

Service Module

---
- name: Service management
  hosts: all
  tasks:
    - name: Ensure service is running and enabled
      service:
        name: nginx
        state: started
        enabled: yes

    - name: Restart service
      service:
        name: nginx
        state: restarted

    - name: Reload service configuration
      service:
        name: nginx
        state: reloaded

    # systemd module - additional systemd features
    - name: Reload systemd daemon
      systemd:
        daemon_reload: yes

    - name: Enable and start service
      systemd:
        name: myapp
        state: started
        enabled: yes
        masked: no

User and Group Modules

---
- name: User management
  hosts: all
  tasks:
    - name: Create group
      group:
        name: appgroup
        state: present
        gid: 1500

    - name: Create user
      user:
        name: appuser
        group: appgroup
        groups: sudo,docker
        shell: /bin/bash
        home: /home/appuser
        create_home: yes
        state: present

    - name: Add SSH key for user
      authorized_key:
        user: appuser
        key: "{{ lookup('file', 'files/id_rsa.pub') }}"
        state: present

Variables and Facts

Variables provide flexibility in playbooks, while facts are system information gathered from managed nodes.

Key Concepts

  • Variable precedence: Variables have a defined order of precedence (22 levels)
  • Facts: System information automatically gathered by Ansible
  • Magic variables: Special variables like hostvars, groups, inventory_hostname
  • Registered variables: Capture output from tasks

Variable Definition

---
# Playbook variables
- name: Deploy application
  hosts: webservers
  vars:
    app_name: myapp
    app_port: 8080
    features:
      - logging
      - monitoring

  vars_files:
    - vars/common.yml
    - vars/{{ env }}.yml

  tasks:
    - name: Use variables
      debug:
        msg: "Deploying {{ app_name }} on port {{ app_port }}"
# group_vars/webservers.yml
---
http_port: 80
https_port: 443
document_root: /var/www/html

# host_vars/web1.example.com.yml
---
http_port: 8080
custom_config: true

Variable Precedence (Simplified)

# From lowest to highest precedence:
# 1. Role defaults (roles/x/defaults/main.yml)
# 2. Inventory file or script group vars
# 3. Inventory group_vars/all
# 4. Playbook group_vars/all
# 5. Inventory group_vars/*
# 6. Playbook group_vars/*
# 7. Inventory file or script host vars
# 8. Inventory host_vars/*
# 9. Playbook host_vars/*
# 10. Host facts
# 11. Play vars
# 12. Play vars_prompt
# 13. Play vars_files
# 14. Role vars (roles/x/vars/main.yml)
# 15. Block vars
# 16. Task vars
# 17. Extra vars (-e) - HIGHEST PRIORITY

Gathering and Using Facts

---
- name: Work with facts
  hosts: all
  gather_facts: yes

  tasks:
    - name: Display OS information
      debug:
        msg: "OS: {{ ansible_distribution }} {{ ansible_distribution_version }}"

    - name: Display memory
      debug:
        msg: "Total memory: {{ ansible_memtotal_mb }} MB"

    - name: Display IP addresses
      debug:
        msg: "IPv4: {{ ansible_default_ipv4.address }}"

    - name: Conditional based on facts
      apt:
        name: nginx
        state: present
      when: ansible_os_family == "Debian"

    # Custom facts from /etc/ansible/facts.d/*.fact
    - name: Display custom facts
      debug:
        var: ansible_local.custom.section.key

Registered Variables

---
- name: Register and use output
  hosts: all
  tasks:
    - name: Get service status
      command: systemctl status nginx
      register: nginx_status
      ignore_errors: yes
      changed_when: false

    - name: Display return code
      debug:
        msg: "Return code: {{ nginx_status.rc }}"

    - name: Display stdout
      debug:
        msg: "{{ nginx_status.stdout_lines }}"

    - name: Act on result
      service:
        name: nginx
        state: started
      when: nginx_status.rc != 0

Special Variables

---
- name: Use special variables
  hosts: all
  tasks:
    - name: Current host info
      debug:
        msg: |
          Hostname: {{ inventory_hostname }}
          Short name: {{ inventory_hostname_short }}
          Groups: {{ group_names }}

    - name: Access other host variables
      debug:
        msg: "DB host IP: {{ hostvars['db1.example.com']['ansible_host'] }}"

    - name: List all hosts in group
      debug:
        msg: "Webservers: {{ groups['webservers'] }}"

    - name: Playbook directory
      debug:
        msg: "Playbook dir: {{ playbook_dir }}"

Conditionals and Loops

Conditionals control task execution, while loops iterate over lists of items.

Key Concepts

  • when: Execute task only if condition is true
  • loop: Iterate over a list of items
  • until: Retry task until condition is met
  • Jinja2 tests: Check variable types and values

Conditionals

---
- name: Conditional examples
  hosts: all
  vars:
    install_nginx: true
    deploy_env: production
    packages:
      - nginx
      - php

  tasks:
    - name: Simple boolean condition
      apt:
        name: nginx
        state: present
      when: install_nginx

    - name: String comparison
      debug:
        msg: "Production deployment"
      when: deploy_env == "production"

    - name: Multiple conditions (AND)
      service:
        name: nginx
        state: started
      when:
        - install_nginx
        - deploy_env == "production"

    - name: Multiple conditions (OR)
      debug:
        msg: "Non-standard environment"
      when: deploy_env == "development" or deploy_env == "staging"

    - name: Check if variable is defined
      debug:
        msg: "Variable exists"
      when: my_variable is defined

    - name: Check list membership
      debug:
        msg: "nginx will be installed"
      when: "'nginx' in packages"

    - name: Numeric comparison
      debug:
        msg: "Plenty of memory"
      when: ansible_memtotal_mb >= 4096

    - name: Check task result
      command: which nginx
      register: nginx_check
      ignore_errors: yes
      changed_when: false

    - name: Install if not present
      apt:
        name: nginx
        state: present
      when: nginx_check.rc != 0

Loops

---
- name: Loop examples
  hosts: all
  vars:
    packages:
      - nginx
      - postgresql
      - redis-server
    users:
      - name: alice
        groups: admin
      - name: bob
        groups: developers

  tasks:
    # Simple loop
    - name: Install packages
      apt:
        name: "{{ item }}"
        state: present
      loop: "{{ packages }}"

    # Loop with index
    - name: Create numbered files
      file:
        path: "/tmp/file{{ index }}.txt"
        state: touch
      loop: "{{ packages }}"
      loop_control:
        index_var: index

    # Loop over dictionaries
    - name: Create users
      user:
        name: "{{ item.name }}"
        groups: "{{ item.groups }}"
        state: present
      loop: "{{ users }}"

    # Loop with conditional
    - name: Start specific services
      service:
        name: "{{ item }}"
        state: started
      loop:
        - nginx
        - postgresql
        - redis-server
      when: item != "redis-server"

    # Nested loops with subelements
    - name: Add SSH keys for users
      authorized_key:
        user: "{{ item.0.name }}"
        key: "{{ item.1 }}"
      loop: "{{ users | subelements('ssh_keys', skip_missing=True) }}"

    # Loop until condition
    - name: Wait for service to be ready
      uri:
        url: "http://localhost:8080/health"
        status_code: 200
      register: result
      until: result.status == 200
      retries: 10
      delay: 5

    # Loop with dict2items
    - name: Set sysctl values
      sysctl:
        name: "{{ item.key }}"
        value: "{{ item.value }}"
        state: present
      loop: "{{ sysctl_settings | dict2items }}"
      vars:
        sysctl_settings:
          net.ipv4.ip_forward: 1
          vm.swappiness: 10

Block Structure

---
- name: Block examples
  hosts: all
  tasks:
    - name: Handle errors with blocks
      block:
        - name: Attempt risky operation
          command: /opt/scripts/deploy.sh

        - name: Verify deployment
          uri:
            url: http://localhost:8080/health
            status_code: 200

      rescue:
        - name: Rollback on failure
          command: /opt/scripts/rollback.sh

        - name: Send alert
          mail:
            to: ops@example.com
            subject: "Deployment failed"
            body: "Deployment failed on {{ inventory_hostname }}"

      always:
        - name: Clean up temporary files
          file:
            path: /tmp/deploy
            state: absent

Ansible Vault

Ansible Vault encrypts sensitive data such as passwords, keys, and certificates.

Key Concepts

  • Encryption: AES256 encryption for files and variables
  • Vault password: Can be provided via prompt, file, or script
  • Multiple vaults: Different passwords for different environments
  • Variable-level encryption: Encrypt individual variables within files

Common Commands

# Create encrypted file
ansible-vault create secrets.yml

# Edit encrypted file
ansible-vault edit secrets.yml

# Encrypt existing file
ansible-vault encrypt vars.yml

# Decrypt file
ansible-vault decrypt vars.yml

# View encrypted file
ansible-vault view secrets.yml

# Rekey (change password)
ansible-vault rekey secrets.yml

# Encrypt string (for inline use)
ansible-vault encrypt_string 'mysecret' --name 'db_password'

# Run playbook with vault password prompt
ansible-playbook site.yml --ask-vault-pass

# Run playbook with vault password file
ansible-playbook site.yml --vault-password-file ~/.vault_pass

# Multiple vault IDs
ansible-vault encrypt --vault-id prod@prompt secrets-prod.yml
ansible-playbook site.yml --vault-id prod@~/.vault_pass_prod

Using Encrypted Variables

# group_vars/production/vault.yml (encrypted)
---
vault_db_password: supersecret
vault_api_key: abc123xyz

# group_vars/production/vars.yml (unencrypted, references vault)
---
db_password: "{{ vault_db_password }}"
api_key: "{{ vault_api_key }}"

Inline Encrypted Variables

# vars.yml with encrypted string
---
db_password: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          61626364656667686970616263646566676869706162636465666768697061626364656667686970
          61626364656667686970616263646566676869706162636465666768697061626364656667686970
          61626364656667686970616263646566676869706162636465666768697061626364656667686970

Vault Configuration

# ansible.cfg
[defaults]
vault_password_file = ~/.vault_pass

# Or use environment variable
# export ANSIBLE_VAULT_PASSWORD_FILE=~/.vault_pass

Common Patterns

Best practices and patterns for writing effective Ansible automation.

Key Concepts

  • Idempotency: Tasks produce the same result regardless of how many times they run
  • Tags: Label tasks for selective execution
  • Handlers: Run tasks only when notified of changes
  • Check mode: Preview changes without applying them

Idempotency Patterns

---
- name: Idempotent operations
  hosts: all
  tasks:
    # Good - idempotent
    - name: Ensure package is installed
      apt:
        name: nginx
        state: present

    # Good - idempotent with creates
    - name: Download file only if not present
      get_url:
        url: https://example.com/file.tar.gz
        dest: /tmp/file.tar.gz
        checksum: sha256:abc123...

    # Good - idempotent command with creates
    - name: Extract archive only if directory missing
      command: tar xzf /tmp/file.tar.gz -C /opt
      args:
        creates: /opt/myapp

    # Good - check before acting
    - name: Check if config exists
      stat:
        path: /etc/myapp/config.yml
      register: config_stat

    - name: Create config if missing
      template:
        src: config.yml.j2
        dest: /etc/myapp/config.yml
      when: not config_stat.stat.exists

    # Avoid - not idempotent without controls
    - name: Run deployment script
      command: /opt/scripts/deploy.sh
      # Better with:
      args:
        creates: /opt/app/.deployed

Using Tags

---
- name: Tagged playbook
  hosts: all
  tasks:
    - name: Install packages
      apt:
        name: "{{ item }}"
        state: present
      loop:
        - nginx
        - php-fpm
      tags:
        - packages
        - install

    - name: Configure nginx
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      tags:
        - configuration
        - nginx
      notify: Reload nginx

    - name: Deploy application
      git:
        repo: https://github.com/example/app.git
        dest: /var/www/app
        version: main
      tags:
        - deploy
        - application

    # Special tags
    - name: Always run this task
      debug:
        msg: "Always executed"
      tags:
        - always

    - name: Never run unless explicitly called
      debug:
        msg: "Dangerous operation"
      tags:
        - never
        - dangerous

  handlers:
    - name: Reload nginx
      service:
        name: nginx
        state: reloaded
      tags:
        - configuration
# Run only specific tags
ansible-playbook site.yml --tags "configuration"

# Run multiple tags
ansible-playbook site.yml --tags "packages,configuration"

# Skip tags
ansible-playbook site.yml --skip-tags "deploy"

# List available tags
ansible-playbook site.yml --list-tags

Handler Patterns

---
- name: Handler examples
  hosts: all
  tasks:
    - name: Update nginx configuration
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify:
        - Validate nginx config
        - Reload nginx

    - name: Update nginx sites
      template:
        src: "{{ item }}.conf.j2"
        dest: "/etc/nginx/sites-available/{{ item }}.conf"
      loop:
        - site1
        - site2
      notify: Reload nginx

    # Force handler to run immediately
    - name: Flush handlers now
      meta: flush_handlers

    - name: Check service status
      uri:
        url: http://localhost/health
        status_code: 200

  handlers:
    - name: Validate nginx config
      command: nginx -t
      listen: "Validate nginx config"

    - name: Reload nginx
      service:
        name: nginx
        state: reloaded
      listen: "Reload nginx"

Role Dependencies and Includes

# roles/webapp/meta/main.yml
---
dependencies:
  - role: common
  - role: nginx
    vars:
      nginx_port: 8080
  - role: postgresql
    when: database_type == "postgresql"
# Dynamic includes
---
- name: Include tasks dynamically
  hosts: all
  tasks:
    - name: Include OS-specific tasks
      include_tasks: "{{ ansible_os_family | lower }}.yml"

    - name: Include role dynamically
      include_role:
        name: "{{ app_role }}"
      vars:
        app_port: 8080

Troubleshooting

Tools and techniques for debugging Ansible playbooks and resolving issues.

Key Concepts

  • Verbose mode: Increase output detail with -v flags
  • Check mode: Dry-run to preview changes
  • Diff mode: Show file changes
  • Debug module: Print variables and messages

Verbose and Check Modes

# Verbose levels
ansible-playbook site.yml -v      # Show task results
ansible-playbook site.yml -vv     # Show task input/output
ansible-playbook site.yml -vvv    # Show connection details
ansible-playbook site.yml -vvvv   # Show connection plugins, scripts

# Check mode (dry run)
ansible-playbook site.yml --check

# Check mode with diff
ansible-playbook site.yml --check --diff

# Diff mode (show changes)
ansible-playbook site.yml --diff

# Step through tasks
ansible-playbook site.yml --step

# Start at specific task
ansible-playbook site.yml --start-at-task "Deploy application"

# List tasks without executing
ansible-playbook site.yml --list-tasks

# List hosts that would be affected
ansible-playbook site.yml --list-hosts

Debug Module

---
- name: Debugging playbook
  hosts: all
  vars:
    my_var: "test value"

  tasks:
    - name: Print variable
      debug:
        var: my_var

    - name: Print message
      debug:
        msg: "The value is {{ my_var }}"

    - name: Print with verbosity control
      debug:
        msg: "Detailed debug info"
        verbosity: 2  # Only shows with -vv or higher

    - name: Print all variables
      debug:
        var: vars

    - name: Print hostvars
      debug:
        var: hostvars[inventory_hostname]

    - name: Register and debug
      command: cat /etc/os-release
      register: os_info
      changed_when: false

    - name: Show registered variable
      debug:
        var: os_info

Common Issues and Solutions

Issue Solution
SSH connection refused Check SSH service, firewall rules, and ansible_port
Permission denied Verify ansible_user, SSH keys, or use --ask-pass
Module not found Install required collection: ansible-galaxy collection install
Variable undefined Check variable name spelling, scope, and precedence
Vault password error Use --ask-vault-pass or set vault_password_file
Task timeout Increase ansible_timeout or use async with poll
Jinja2 template error Check syntax, quote strings: "{{ var }}" not {{ var }}
Idempotency issues Use creates, removes, or when conditions
Handler not running Ensure task reports changed, use meta: flush_handlers
Slow execution Use strategy: free, serial, or async tasks

Debugging Techniques

---
- name: Debugging techniques
  hosts: all
  tasks:
    # Assert expected conditions
    - name: Verify prerequisites
      assert:
        that:
          - ansible_memtotal_mb >= 2048
          - ansible_distribution == "Ubuntu"
        fail_msg: "System does not meet requirements"
        success_msg: "System requirements verified"

    # Fail with custom message
    - name: Check critical file
      stat:
        path: /etc/critical.conf
      register: critical_file

    - name: Fail if file missing
      fail:
        msg: "Critical configuration file is missing!"
      when: not critical_file.stat.exists

    # Pause for investigation
    - name: Pause for manual check
      pause:
        prompt: "Check the server and press Enter to continue"
      when: debug_mode | default(false)

Logging and Output

# ansible.cfg
[defaults]
# Log all output to file
log_path = /var/log/ansible.log

# Show timestamps
callbacks_enabled = timer, profile_tasks

# Display skipped hosts
display_skipped_hosts = True

# Show custom stats
show_custom_stats = True
# Save output to file
ansible-playbook site.yml | tee ansible_output.log

# JSON output for parsing
ansible-playbook site.yml --extra-vars "output_format=json"

# Use callback plugins
ANSIBLE_STDOUT_CALLBACK=yaml ansible-playbook site.yml

Quick Reference

Command Description
ansible-playbook site.yml Run a playbook
ansible-playbook site.yml -i inventory Specify inventory file
ansible-playbook site.yml --limit host1 Limit to specific hosts
ansible-playbook site.yml --tags deploy Run only tagged tasks
ansible-playbook site.yml --check Dry run (check mode)
ansible-playbook site.yml --diff Show file changes
ansible-playbook site.yml -e "var=value" Pass extra variables
ansible-playbook site.yml --ask-vault-pass Prompt for vault password
ansible-playbook site.yml -vvv Verbose output
ansible all -m ping Test connectivity
ansible all -m setup Gather facts
ansible-galaxy init role_name Create role skeleton
ansible-galaxy install -r requirements.yml Install roles/collections
ansible-vault create secrets.yml Create encrypted file
ansible-vault edit secrets.yml Edit encrypted file
ansible-vault encrypt_string 'text' Encrypt a string
ansible-inventory --list List inventory as JSON
ansible-doc module_name View module documentation

Common Issues and Solutions

Connection Problems

# Test SSH connectivity
ansible all -m ping -vvv

# Check SSH configuration
ssh -v user@host

# Use password authentication
ansible-playbook site.yml --ask-pass --ask-become-pass

# Specify private key
ansible-playbook site.yml --private-key=/path/to/key

Variable Issues

# Debug a variable (lookup returns the resolved value, with a safe default if undefined)
- debug:
    msg: |
      Variable 'my_var' value: {{ my_var }}
      Resolved value: {{ lookup('vars', 'my_var', default='undefined') }}

# Check if variable is defined
- fail:
    msg: "Required variable 'db_host' is not defined"
  when: db_host is not defined

Performance Optimisation

# ansible.cfg
[defaults]
forks = 20                    # Parallel processes
pipelining = True             # Reduce SSH operations
gathering = smart             # Cache facts
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 86400

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s

Related Topics

The following topics would complement this Ansible cheatsheet:

  1. Terraform - Infrastructure provisioning that pairs well with Ansible configuration management
  2. Vault (HashiCorp) - Advanced secrets management integrated with Ansible
  3. Jenkins - CI/CD pipelines that orchestrate Ansible playbook execution
  4. SOPS - Alternative secrets management for GitOps workflows
  5. systemd - Service management understanding for effective Ansible service tasks
  6. AWX/Ansible Tower - Web-based interface and API for enterprise Ansible automation