Skip to content

Ansible Complete Master Guide

Complete automation and configuration management guide - from installation to enterprise patterns.


Installation Guide

Choose your operating system:

Ansible Installation Guide

Windows 11

  1. Install Chocolatey:

  2. Open an elevated PowerShell prompt (Run as Administrator).

  3. Run the following command to install Chocolatey:

    Set-ExecutionPolicy Bypass -Scope Process -Force
    [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
    iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
    

  4. Install Ansible:

  5. Install Ansible using Chocolatey:

    choco install ansible -y
    

  6. Verify Installation:

  7. Check the Ansible version:
    ansible --version
    

Using Windows Subsystem for Linux (WSL)

  1. Enable WSL:
  2. Open PowerShell as Administrator and run:

    wsl --update
    

  3. Install a Linux Distribution:

  4. Open Microsoft Store and install a Linux distribution (e.g., Ubuntu).

  5. Set Up WSL:

  6. Open the installed Linux distribution.
  7. Follow the on-screen instructions to set up the Linux environment.

  8. Install Ansible in WSL:

  9. Update the package list:
    sudo apt update
    
  10. Install Ansible:

    sudo apt install ansible
    

  11. Verify Installation:

  12. Check the Ansible version:
    ansible --version
    

Ubuntu/Debian

sudo apt update
sudo apt install software-properties-common
sudo add-apt-repository --yes --update ppa:ansible/ansible
sudo apt install ansible

CentOS/RHEL

sudo yum install epel-release
sudo yum install ansible

macOS

brew install ansible

Python pip (any system)

pip install ansible

Verify installation:

ansible --version


Basic Commands

Command Description
ansible all -m ping Ping all hosts in inventory
ansible-playbook playbook.yml Run a playbook
ansible-galaxy init <role> Initialize a new role
ansible -i hosts.ini webservers -m setup Gather facts from webservers group
ansible-vault create secrets.yml Create encrypted file
ansible-doc -l List all modules
ansible-doc apt Show module documentation

Inventory

Save as hosts.ini:

[webservers]
192.168.1.10
192.168.1.11

[dbservers]
192.168.1.20

[all:vars]
ansible_user=ubuntu
ansible_ssh_private_key_file=~/.ssh/id_rsa
ansible_python_interpreter=/usr/bin/python3

BEGINNER LEVEL: Core Concepts & First Steps

Scenario 1: Test Connectivity and Gather Facts

Description: Verify Ansible can reach all servers and collect system information.

sequenceDiagram
    participant User as Developer
    participant Ansible as Ansible Control Node
    participant Host1 as 192.168.1.10
    participant Host2 as 192.168.1.11
    participant Host3 as 192.168.1.20

    User->>Ansible: Execute ansible all -m ping
    Ansible->>Host1: SSH connection test
    Host1-->>Ansible: pong (success)
    Ansible->>Host2: SSH connection test
    Host2-->>Ansible: pong (success)
    Ansible->>Host3: SSH connection test
    Host3-->>Ansible: pong (success)

    User->>Ansible: Execute ansible all -m setup
    Ansible->>Host1: Collect system facts
    Host1-->>Ansible: OS, IP, memory, CPU info
    Ansible->>Host2: Collect system facts
    Host2-->>Ansible: OS, IP, memory, CPU info
    Ansible->>Host3: Collect system facts
    Host3-->>Ansible: OS, IP, memory, CPU info

    Ansible-->>User: Display all collected facts

Commands:

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

# Expected output:
# 192.168.1.10 | SUCCESS => {
#     "ansible_facts": {
#         "discovered_interpreter_python": "/usr/bin/python3"
#     },
#     "changed": false,
#     "ping": "pong"
# }

# Gather facts from all hosts
ansible all -i hosts.ini -m setup

# Get specific fact from webservers
ansible webservers -i hosts.ini -m setup -a "filter=ansible_distribution"


Scenario 2: Install and Start Nginx Web Server

Description: Install Nginx on webservers and ensure it's running with a basic configuration.

sequenceDiagram
    participant User as Developer
    participant Ansible as Ansible Control Node
    participant Web1 as webserver-01 (192.168.1.10)
    participant Web2 as webserver-02 (192.168.1.11)
    participant Apt as APT Repository

    User->>Ansible: Run nginx_deploy.yml playbook
    Ansible->>Web1: Connect via SSH
    Ansible->>Web2: Connect via SSH

    loop On each webserver
        Ansible->>Web1: Update apt cache (apt update)
        Web1->>Apt: Request package list
        Apt-->>Web1: Return latest packages

        Ansible->>Web1: Install Nginx (apt install nginx)
        Web1->>Apt: Download and install package
        Apt-->>Web1: Installation complete

        Ansible->>Web1: Start Nginx service (systemctl start nginx)
        Web1->>Web1: Start nginx process
        Web1-->>Ansible: Service started

        Ansible->>Web1: Enable on boot (systemctl enable nginx)
        Web1->>Web1: Create systemd symlink
        Web1-->>Ansible: Enabled successfully
    end

    loop On webserver-02
        Ansible->>Web2: Execute same tasks
        Web2->>Apt: Download and install
        Web2->>Web2: Start and enable service
    end

    Ansible-->>User: Playbook complete (both servers configured)

Code - Save as nginx_deploy.yml:

---
- name: Install and configure Nginx web servers
  hosts: webservers
  become: yes

  tasks:
    - name: Update apt cache
      apt:
        update_cache: yes
        cache_valid_time: 3600
      tags: update

    - name: Install Nginx package
      apt:
        name: nginx
        state: present
      notify: Start Nginx

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

  handlers:
    - name: Start Nginx
      service:
        name: nginx
        state: started

Execute:

ansible-playbook -i hosts.ini nginx_deploy.yml


Scenario 3: Create Directory Structure and Permissions

Description: Create multiple directories with specific owners and permissions for an application.

sequenceDiagram
    participant User as Developer
    participant Ansible as Ansible Control Node
    participant Server as Application Server

    User->>Ansible: Run create_directories.yml playbook

    Ansible->>Server: Define directory structure variables

    loop Process directory list
        Ansible->>Server: Create /opt/myapp (owner: root, mode: 0755)
        Server->>Server: mkdir -p /opt/myapp
        Server->>Server: chmod 0755 /opt/myapp
        Server->>Server: chown root:root /opt/myapp
        Server-->>Ansible: Directory created

        Ansible->>Server: Create /var/log/myapp (owner: www-data, mode: 0755)
        Server->>Server: mkdir -p /var/log/myapp
        Server->>Server: chmod 0755 /var/log/myapp
        Server->>Server: chown www-data:www-data /var/log/myapp
        Server-->>Ansible: Directory created

        Ansible->>Server: Create /data/myapp/uploads (owner: www-data, mode: 0775)
        Server->>Server: mkdir -p /data/myapp/uploads
        Server->>Server: chmod 0775 /data/myapp/uploads
        Server->>Server: chown www-data:www-data /data/myapp/uploads
        Server-->>Ansible: Directory created
    end

    Ansible->>Server: Verify all directories exist
    Server-->>Ansible: All directories present and configured

    Ansible-->>User: Playbook complete - directories created

Code - Save as create_directories.yml:

---
- name: Create application directory structure
  hosts: webservers
  become: yes

  vars:
    app_name: myapp
    app_directories:
      - path: "/opt/{{ app_name }}"
        owner: root
        group: root
        mode: '0755'
      - path: "/var/log/{{ app_name }}"
        owner: www-data
        group: www-data
        mode: '0755'
      - path: "/data/{{ app_name }}/uploads"
        owner: www-data
        group: www-data
        mode: '0775'
      - path: "/etc/{{ app_name }}"
        owner: root
        group: root
        mode: '0700'

  tasks:
    - name: Create directory structure
      file:
        path: "{{ item.path }}"
        state: directory
        owner: "{{ item.owner }}"
        group: "{{ item.group }}"
        mode: "{{ item.mode }}"
      loop: "{{ app_directories }}"
      register: directory_results

    - name: Display created directories
      debug:
        msg: "Created {{ item.path }} with mode {{ item.mode }}"
      loop: "{{ directory_results.results }}"
      when: item.changed

  handlers:
    - name: Restart application
      systemd:
        name: "{{ app_name }}"
        state: restarted

Execute:

ansible-playbook -i hosts.ini create_directories.yml


Scenario 4: Deploy Static Website Files

Description: Copy website files to webservers and configure Nginx virtual host.

sequenceDiagram
    participant User as Developer
    participant Ansible as Ansible Control Node
    participant Web as Web Servers
    participant Nginx as Nginx Process

    User->>Ansible: Run deploy_website.yml playbook

    Ansible->>Ansible: Load variables from group_vars

    Ansible->>Web: Create document root directory
    Web->>Web: mkdir -p /var/www/html/myapp

    loop Copy website assets
        Ansible->>Web: Copy index.html
        Ansible->>Web: Copy CSS files
        Ansible->>Web: Copy JavaScript files
        Ansible->>Web: Copy images
    end

    Ansible->>Web: Deploy Nginx virtual host config
    Web->>Web: Write /etc/nginx/sites-available/myapp
    Web->>Web: Create symlink to sites-enabled
    Web-->>Ansible: Configuration deployed

    Ansible->>Web: Test Nginx configuration
    Web->>Nginx: nginx -t
    Nginx-->>Web: Syntax OK

    Ansible->>Web: Reload Nginx
    Web->>Nginx: nginx -s reload
    Nginx-->>Web: Reloaded successfully

    Ansible->>Web: Verify website is accessible
    Web->>Web: curl http://localhost/myapp
    Web-->>Ansible: HTTP 200 OK

    Ansible-->>User: Deployment complete

Code - Save as deploy_website.yml:

---
- name: Deploy static website with Nginx configuration
  hosts: webservers
  become: yes

  vars:
    website_name: myapp
    website_root: "/var/www/html/{{ website_name }}"
    nginx_config_path: "/etc/nginx/sites-available/{{ website_name }}"

  tasks:
    - name: Install Nginx (if not already installed)
      apt:
        name: nginx
        state: present
        update_cache: yes

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

    - name: Deploy website files
      copy:
        src: "{{ item }}"
        dest: "{{ website_root }}/"
        owner: www-data
        group: www-data
        mode: '0644'
      loop:
        - files/index.html
        - files/css/
        - files/js/
        - files/images/

    - name: Deploy Nginx virtual host configuration
      template:
        src: templates/nginx_vhost.j2
        dest: "{{ nginx_config_path }}"
        owner: root
        group: root
        mode: '0644'
      notify: Reload Nginx

    - name: Enable virtual host
      file:
        src: "{{ nginx_config_path }}"
        dest: "/etc/nginx/sites-enabled/{{ website_name }}"
        state: link

    - name: Test Nginx configuration
      command: nginx -t
      changed_when: false

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

  handlers:
    - name: Reload Nginx
      service:
        name: nginx
        state: reloaded

Template - Save as templates/nginx_vhost.j2:

server {
    listen 80;
    server_name {{ website_name }}.example.com;
    root {{ website_root }};
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }

    access_log /var/log/nginx/{{ website_name }}_access.log;
    error_log /var/log/nginx/{{ website_name }}_error.log;
}

Execute:

ansible-playbook -i hosts.ini deploy_website.yml


Scenario 5: Manage Users and SSH Keys

Description: Create system users and deploy SSH public keys for key-based authentication.

sequenceDiagram
    participant User as Ansible Admin
    participant Ansible as Ansible Control Node
    participant Server as Target Servers
    participant SSH as SSH Service

    User->>Ansible: Run manage_users.yml playbook

    Ansible->>Ansible: Load user list from variables

    loop Process each user
        Ansible->>Server: Create user account
        Server->>Server: useradd -m -s /bin/bash deployer
        Server->>Server: Set password (disabled)
        Server-->>Ansible: User created

        Ansible->>Server: Create .ssh directory
        Server->>Server: mkdir -p /home/deployer/.ssh
        Server->>Server: chmod 0700 /home/deployer/.ssh
        Server-->>Ansible: Directory created

        Ansible->>Server: Deploy SSH public key
        Server->>Server: Add key to authorized_keys
        Server->>Server: chmod 0600 authorized_keys
        Server-->>Ansible: Key deployed

        Ansible->>Server: Set correct ownership
        Server->>Server: chown -R deployer:deployer /home/deployer/.ssh
        Server-->>Ansible: Ownership set
    end

    Ansible->>Server: Verify SSH key authentication
    Server->>SSH: Test key-based login
    SSH-->>Server: Authentication successful

    Ansible-->>User: All users configured

Code - Save as manage_users.yml:

---
- name: Manage system users and SSH keys
  hosts: all
  become: yes

  vars:
    users:
      - name: deployer
        groups: sudo
        ssh_key: files/keys/deployer.pub
        shell: /bin/bash
      - name: monitoring
        groups: monitoring
        ssh_key: files/keys/monitoring.pub
        shell: /bin/bash
      - name: readonly
        groups: users
        shell: /bin/false

  tasks:
    - name: Create user groups
      group:
        name: "{{ item }}"
        state: present
      loop:
        - sudo
        - monitoring
        - users

    - name: Create user accounts
      user:
        name: "{{ item.name }}"
        groups: "{{ item.groups }}"
        shell: "{{ item.shell }}"
        create_home: yes
        password: "!"  # Disable password login
        state: present
      loop: "{{ users }}"

    - name: Create .ssh directory for each user
      file:
        path: "/home/{{ item.name }}/.ssh"
        state: directory
        owner: "{{ item.name }}"
        group: "{{ item.name }}"
        mode: '0700'
      loop: "{{ users }}"
      when: item.ssh_key is defined

    - name: Deploy SSH public keys
      authorized_key:
        user: "{{ item.name }}"
        key: "{{ lookup('file', item.ssh_key) }}"
        state: present
        manage_dir: yes
      loop: "{{ users }}"
      when: item.ssh_key is defined

    - name: Configure sudoers for deployer
      lineinfile:
        path: /etc/sudoers.d/deployer
        line: "deployer ALL=(ALL) NOPASSWD:ALL"
        create: yes
        mode: '0440'
        validate: 'visudo -cf %s'

  handlers:
    - name: Restart SSH service
      service:
        name: sshd
        state: restarted

Execute:

ansible-playbook -i hosts.ini manage_users.yml


INTERMEDIATE LEVEL: Variables, Conditionals & Roles

Scenario 6: Deploy Application with Variables and Handlers

Description: Deploy a Python application using variables for configuration and handlers for service management.

sequenceDiagram
    participant User as Developer
    participant Ansible as Ansible Control Node
    participant App as Application Server
    participant Git as Git Repository
    participant Service as Systemd Service

    User->>Ansible: Run deploy_app.yml playbook

    Ansible->>App: Install Python dependencies
    App->>App: apt install python3-pip python3-venv

    Ansible->>App: Create application user
    App->>App: useradd --system --home /opt/myapp app

    Ansible->>Git: Clone application repository
    Git-->>App: Download source code

    Ansible->>App: Create Python virtual environment
    App->>App: python3 -m venv /opt/myapp/venv

    Ansible->>App: Install Python requirements
    App->>App: pip install -r requirements.txt

    Ansible->>App: Deploy systemd service file
    App->>Service: Write /etc/systemd/system/myapp.service

    Ansible->>App: Reload systemd daemon
    App->>Service: systemctl daemon-reload
    Service-->>App: Reloaded

    Ansible->>App: Start application service
    App->>Service: systemctl start myapp
    Service-->>App: Service started

    App->>App: Check application health
    App->>Service: systemctl status myapp
    Service-->>App: Active (running)

    Ansible-->>User: Application deployed successfully

Code - Save as deploy_app.yml:

---
- name: Deploy Python web application
  hosts: appservers
  become: yes

  vars:
    app_name: myapp
    app_version: 1.2.0
    app_port: 8000
    app_user: app
    python_version: 3.9
    git_repo: https://github.com/myorg/myapp.git

  tasks:
    - name: Install system dependencies
      apt:
        name:
          - python3-pip
          - python3-venv
          - git
          - gcc
        state: present
        update_cache: yes

    - name: Create application user
      user:
        name: "{{ app_user }}"
        system: yes
        shell: /bin/false
        create_home: yes
        home: "/opt/{{ app_name }}"

    - name: Clone application repository
      git:
        repo: "{{ git_repo }}"
        dest: "/opt/{{ app_name }}/src"
        version: "v{{ app_version }}"
        force: yes

    - name: Create virtual environment
      pip:
        virtualenv: "/opt/{{ app_name }}/venv"
        virtualenv_command: python3 -m venv
        requirements: "/opt/{{ app_name }}/src/requirements.txt"
        virtualenv_python: python{{ python_version }}

    - name: Deploy systemd service file
      template:
        src: templates/app.service.j2
        dest: "/etc/systemd/system/{{ app_name }}.service"
        owner: root
        group: root
        mode: '0644'
      notify: Reload systemd

    - name: Deploy application configuration
      template:
        src: templates/app.conf.j2
        dest: "/etc/{{ app_name }}/config.yaml"
        owner: "{{ app_user }}"
        group: "{{ app_user }}"
        mode: '0640'

    - name: Ensure application service is running
      systemd:
        name: "{{ app_name }}"
        state: started
        enabled: yes
        daemon_reload: yes

  handlers:
    - name: Reload systemd
      systemd:
        daemon_reload: yes

    - name: Restart application
      systemd:
        name: "{{ app_name }}"
        state: restarted

Template - Save as templates/app.service.j2:

[Unit]
Description={{ app_name }} application
After=network.target

[Service]
Type=notify
User={{ app_user }}
Group={{ app_user }}
WorkingDirectory=/opt/{{ app_name }}/src
Environment="PATH=/opt/{{ app_name }}/venv/bin"
ExecStart=/opt/{{ app_name }}/venv/bin/python -m app --port {{ app_port }}
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

Execute:

ansible-playbook -i hosts.ini deploy_app.yml


Scenario 7: Conditional Execution Based on OS and Facts

Description: Install packages differently based on operating system and ensure compatibility.

sequenceDiagram
    participant User as Developer
    participant Ansible as Ansible Control Node
    participant Ubuntu as Ubuntu Server
    participant CentOS as CentOS Server
    participant Debian as Debian Server

    User->>Ansible: Run install_packages.yml playbook

    Ansible->>Ubuntu: Gather facts (ansible_os_family)
    Ubuntu-->>Ansible: "Debian"
    Ansible->>CentOS: Gather facts (ansible_os_family)
    CentOS-->>Ansible: "RedHat"
    Ansible->>Debian: Gather facts (ansible_os_family)
    Debian-->>Ansible: "Debian"

    alt OS Family is Debian/Ubuntu
        Ansible->>Ubuntu: Use apt module
        Ubuntu->>Ubuntu: apt install htop vim curl
        Ubuntu-->>Ansible: Packages installed
    else OS Family is RedHat/CentOS
        Ansible->>CentOS: Use yum module
        CentOS->>CentOS: yum install htop vim curl
        CentOS-->>Ansible: Packages installed
    end

    Ansible->>Ubuntu: Check if NTP should be installed
    Ubuntu-->>Ansible: ansible_distribution_version = "20.04"
    alt Ubuntu version < 22.04
        Ansible->>Ubuntu: Install chrony
        Ubuntu->>Ubuntu: apt install chrony
    else Ubuntu version >= 22.04
        Ansible->>Ubuntu: Install systemd-timesyncd
        Ubuntu->>Ubuntu: Use built-in service
    end

    Ansible->>CentOS: Install chrony for all versions
    CentOS->>CentOS: yum install chrony

    Ansible-->>User: All packages installed appropriately

Code - Save as install_packages.yml:

---
- name: Install packages conditionally by OS
  hosts: all
  become: yes

  vars:
    common_packages:
      - htop
      - vim
      - curl
      - git
      - tmux

  tasks:
    - name: Install packages on Debian/Ubuntu
      apt:
        name: "{{ common_packages }}"
        state: present
        update_cache: yes
      when: ansible_os_family == "Debian"

    - name: Install packages on RedHat/CentOS
      yum:
        name: "{{ common_packages }}"
        state: present
      when: ansible_os_family == "RedHat"

    - name: Install NTP service (Ubuntu < 22.04)
      apt:
        name: chrony
        state: present
      when:
        - ansible_distribution == "Ubuntu"
        - ansible_distribution_version is version('22.04', '<')

    - name: Use systemd-timesyncd (Ubuntu >= 22.04)
      systemd:
        name: systemd-timesyncd
        state: started
        enabled: yes
      when:
        - ansible_distribution == "Ubuntu"
        - ansible_distribution_version is version('22.04', '>=')

    - name: Install NTP service (CentOS/RedHat)
      yum:
        name: chrony
        state: present
      when: ansible_os_family == "RedHat"

    - name: Ensure chrony is running
      service:
        name: chronyd
        state: started
        enabled: yes
      when: ansible_os_family == "RedHat" or (ansible_distribution == "Ubuntu" and ansible_distribution_version is version('22.04', '<'))

    - name: Verify package installation
      command: which htop
      register: package_check
      changed_when: false

    - name: Show package location
      debug:
        msg: "htop installed at {{ package_check.stdout }}"
      when: package_check.rc == 0

Execute:

ansible-playbook -i hosts.ini install_packages.yml


Scenario 8: Using Loops and Registering Results

Description: Create multiple users and capture results for reporting and follow-up tasks.

sequenceDiagram
    participant User as Developer
    participant Ansible as Ansible Control Node
    participant Server as Database Server

    User->>Ansible: Run manage_users_advanced.yml playbook

    Ansible->>Server: Define user list with attributes

    loop For each user in users list
        Ansible->>Server: Create user account
        Server->>Server: useradd -s /bin/bash app_user
        Server-->>Ansible: Return result (changed=true)
        Ansible->>Ansible: Store result in user_creation_results

        Ansible->>Server: Set password (hashed)
        Server->>Server: chpasswd with encrypted password
        Server-->>Ansible: Password set
        Ansible->>Ansible: Store password result

        Ansible->>Server: Add to secondary groups
        Server->>Server: usermod -aG sudo app_user
        Server-->>Ansible: Groups updated
    end

    Ansible->>Ansible: Process registered results

    Ansible->>Server: Display summary of created users
    Server->>Server: Output list of users

    Ansible->>Server: Generate report
    Server->>Server: Write /tmp/user_creation_report.txt

    Ansible->>Server: Email report to admin (if configured)
    Server-->>Ansible: Report generated

    Ansible-->>User: Show creation summary

Code - Save as manage_users_advanced.yml:

---
- name: Create application users with registered results
  hosts: dbservers
  become: yes
  vars:
    users:
      - name: app_user
        groups: sudo
        shell: /bin/bash
        comment: "Application User"
      - name: backup_user
        groups: backup
        shell: /bin/bash
        comment: "Backup Service Account"
      - name: monitoring
        groups: monitoring
        shell: /bin/false
        comment: "Monitoring Agent"

  tasks:
    - name: Create users
      user:
        name: "{{ item.name }}"
        shell: "{{ item.shell }}"
        groups: "{{ item.groups }}"
        comment: "{{ item.comment }}"
        state: present
      loop: "{{ users }}"
      register: user_creation_results
      loop_control:
        label: "{{ item.name }}"

    - name: Set passwords for users (encrypted)
      user:
        name: "{{ item.name }}"
        password: "{{ 'ChangeMe123!' | password_hash('sha512') }}"
      loop: "{{ users }}"
      no_log: true
      when: user_creation_results.results[loop.index0].changed

    - name: Show creation results
      debug:
        msg: "User {{ item.item.name }} created: {{ 'Yes' if item.changed else 'No' }}"
      loop: "{{ user_creation_results.results }}"
      loop_control:
        label: "{{ item.item.name }}"

    - name: Generate creation report
      copy:
        dest: "/tmp/user_creation_report_{{ ansible_date_time.date }}.txt"
        content: |
          User Creation Report for {{ inventory_hostname }}
          Generated: {{ ansible_date_time.iso8601 }}

          {% for result in user_creation_results.results %}
          {% if result.changed %}
          - {{ result.item.name }} ({{ result.item.comment }})
            UID: {{ result.uid }}
            Groups: {{ result.item.groups }}
            Shell: {{ result.item.shell }}
          {% endif %}
          {% endfor %}

          Total users created: {{ user_creation_results.results | selectattr('changed') | list | length }}
      delegate_to: localhost
      run_once: yes

    - name: Display report location
      debug:
        msg: "Report saved to /tmp/user_creation_report_{{ ansible_date_time.date }}.txt"
      delegate_to: localhost
      run_once: yes

Execute:

ansible-playbook -i hosts.ini manage_users_advanced.yml


Scenario 9: Build and Deploy Application with Git

Description: Clone a Git repository, build a Java application, and deploy with systemd service.

sequenceDiagram
    participant User as Developer
    participant Ansible as Ansible Control Node
    participant App as Application Server
    participant GitHub as Git Repository
    participant Maven as Maven Build Tool
    participant Service as Systemd Service

    User->>Ansible: Run build_deploy_java.yml playbook

    Ansible->>App: Install OpenJDK and Maven
    App->>App: apt install openjdk-17-jdk maven

    Ansible->>GitHub: Clone application repository
    GitHub-->>App: Download source code

    Ansible->>App: Build application
    App->>Maven: mvn clean package
    Maven->>App: Compile and package WAR file

    Ansible->>App: Check if build succeeded
    App->>App: Verify target/*.war exists
    App-->>Ansible: Build successful

    Ansible->>App: Backup previous version
    App->>App: cp app.war app.war.backup

    Ansible->>App: Deploy new WAR file
    App->>App: Copy to /opt/tomcat/webapps/

    Ansible->>App: Deploy updated systemd service (if needed)
    App->>Service: systemctl daemon-reload

    Ansible->>App: Restart Tomcat service
    App->>Service: systemctl restart tomcat
    Service-->>App: Tomcat restarted

    Ansible->>App: Check application health endpoint
    App->>Service: curl http://localhost:8080/health
    Service-->>App: HTTP 200 - Application is healthy

    Ansible->>App: Clean up old builds
    App->>App: Remove old artifacts

    Ansible-->>User: Deployment complete, health check passed

Code - Save as build_deploy_java.yml:

---
- name: Build and deploy Java application
  hosts: appservers
  become: yes
  vars:
    app_name: my-java-app
    git_repo: https://github.com/myorg/java-app.git
    git_version: main
    build_dir: "/tmp/{{ app_name }}-build"
    deploy_dir: /opt/tomcat/webapps
    tomcat_service: tomcat9

  tasks:
    - name: Install build dependencies
      apt:
        name:
          - openjdk-17-jdk
          - maven
          - git
        state: present
        update_cache: yes

    - name: Clone application repository
      git:
        repo: "{{ git_repo }}"
        dest: "{{ build_dir }}"
        version: "{{ git_version }}"
        force: yes

    - name: Build application with Maven
      command: mvn clean package -DskipTests
      args:
        chdir: "{{ build_dir }}"
      register: maven_build

    - name: Check if build was successful
      stat:
        path: "{{ build_dir }}/target/{{ app_name }}.war"
      register: war_file

    - name: Fail if WAR file not found
      fail:
        msg: "Build failed - WAR file not found"
      when: not war_file.stat.exists

    - name: Backup existing deployment
      copy:
        src: "{{ deploy_dir }}/{{ app_name }}.war"
        dest: "{{ deploy_dir }}/{{ app_name }}.war.backup"
        remote_src: yes
      ignore_errors: yes

    - name: Deploy new WAR file
      copy:
        src: "{{ build_dir }}/target/{{ app_name }}.war"
        dest: "{{ deploy_dir }}/"
        owner: tomcat
        group: tomcat
        mode: '0644'
        backup: yes
      notify: Restart Tomcat

    - name: Wait for Tomcat to deploy WAR
      wait_for:
        path: "{{ deploy_dir }}/{{ app_name }}"
        state: present
        timeout: 180

    - name: Check application health
      uri:
        url: "http://localhost:8080/{{ app_name }}/health"
        status_code: 200
      register: health_check
      retries: 5
      delay: 10
      until: health_check.status == 200

    - name: Display health status
      debug:
        msg: "Application is healthy! Response: {{ health_check.json }}"

    - name: Clean up build directory
      file:
        path: "{{ build_dir }}"
        state: absent
      delegate_to: localhost

  handlers:
    - name: Restart Tomcat
      systemd:
        name: "{{ tomcat_service }}"
        state: restarted
        daemon_reload: yes

Execute:

ansible-playbook -i hosts.ini build_deploy_java.yml


Scenario 10: Ansible Vault for Secret Management

Description: Encrypt sensitive data like passwords and API keys, then use them securely in playbooks.

sequenceDiagram
    participant User as Developer
    participant Ansible as Ansible Control Node
    participant Vault as Ansible Vault
    participant Host as Remote Host

    User->>Ansible: Create vault-encrypted file
    Ansible->>Vault: ansible-vault create secrets.yml
    Vault->>User: Prompt for vault password

    User->>Vault: Enter vault password
    Vault-->>User: Open encrypted file

    User->>Vault: Enter secrets (DB password, API key)
    Vault->>Vault: Encrypt content with AES256
    Vault-->>Ansible: Save encrypted file

    User->>Ansible: Run playbook with vault
    Ansible->>Vault: Request decryption (password)
    User->>Ansible: Provide --ask-vault-pass

    Ansible->>Vault: Decrypt secrets.yml
    Vault-->>Ansible: Return decrypted variables

    loop Secure deployment
        Ansible->>Host: Deploy application config
        Ansible->>Host: Inject decrypted secrets
        Note over Ansible,Host: Using {{ vault_db_password }}
        Host->>Host: Write config with secrets
        Host-->>Ansible: Config deployed
    end

    Ansible->>Host: Verify secret permissions
    Host->>Host: chmod 600 config.yaml
    Host-->>Host: File secured

    Ansible-->>User: Deployment complete<br/>Secrets never exposed in logs

Step 1: Create encrypted vault file

# Create new encrypted file
ansible-vault create group_vars/all/vault.yml

Content - (when editor opens):

vault_db_password: supersecretpassword123
vault_db_user: admin
vault_api_key: abcdefghijklmnopqrstuvwxyz123456
vault_ssl_cert: |
  -----BEGIN CERTIFICATE-----
  MIIDXTCCAkWgAwIBAgIJAKL0UG+mRKxLMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
  BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
  ...
  -----END CERTIFICATE-----
vault_jenkins_token: 11aabb223344556677889900aabbccdd

Step 2: Use vault variables in playbook - Save as deploy_secure_app.yml:

---
- name: Deploy application with encrypted secrets
  hosts: appservers
  become: yes
  vars_files:
    - group_vars/all/vault.yml

  vars:
    app_name: secureapp
    db_host: 192.168.1.50
    config_dir: "/etc/{{ app_name }}"

  tasks:
    - name: Create application config directory
      file:
        path: "{{ config_dir }}"
        state: directory
        owner: root
        group: "{{ app_name }}"
        mode: '0750'

    - name: Deploy configuration with secrets
      template:
        src: templates/config.json.j2
        dest: "{{ config_dir }}/config.json"
        owner: root
        group: "{{ app_name }}"
        mode: '0640'
      vars:
        config:
          database:
            host: "{{ db_host }}"
            username: "{{ vault_db_user }}"
            password: "{{ vault_db_password }}"
          api:
            key: "{{ vault_api_key }}"
            endpoint: "https://api.example.com/v1"

    - name: Deploy SSL certificate
      copy:
        content: "{{ vault_ssl_cert }}"
        dest: "{{ config_dir }}/cert.pem"
        owner: root
        group: "{{ app_name }}"
        mode: '0600'
      no_log: true

    - name: Deploy Jenkins credential for CI/CD
      copy:
        content: "{{ vault_jenkins_token }}"
        dest: "{{ config_dir }}/.jenkins-token"
        owner: jenkins
        mode: '0400'
      no_log: true

    - name: Ensure application can read config
      file:
        path: "{{ config_dir }}"
        recurse: yes
        owner: root
        group: "{{ app_name }}"

  handlers:
    - name: Restart application
      systemd:
        name: "{{ app_name }}"
        state: restarted
        daemon_reload: yes

Template - Save as templates/config.json.j2:

{
  "database": {
    "host": "{{ config.database.host }}",
    "username": "{{ config.database.username }}",
    "password": "{{ config.database.password }}",
    "port": 5432
  },
  "api": {
    "key": "{{ config.api.key }}",
    "endpoint": "{{ config.api.endpoint }}"
  }
}

Execute with vault password:

ansible-playbook -i hosts.ini deploy_secure_app.yml --ask-vault-pass

Additional vault commands:

# Edit encrypted file
ansible-vault edit group_vars/all/vault.yml

# Rekey encrypted file (change password)
ansible-vault rekey group_vars/all/vault.yml

# Encrypt existing file
ansible-vault encrypt secrets.txt

# Decrypt file
ansible-vault decrypt secrets.txt

# View encrypted file content
ansible-vault view group_vars/all/vault.yml

# Run playbook with vault password file
echo "my-secure-password" > .vault_pass
ansible-playbook -i hosts.ini deploy_secure_app.yml --vault-password-file .vault_pass
chmod 600 .vault_pass


ADVANCED LEVEL: Enterprise Patterns & Complex Automation

Scenario 11: Dynamic Inventory with AWS EC2

Description: Automatically discover and manage cloud instances without static inventory files.

sequenceDiagram
    participant User as Developer
    participant Ansible as Ansible Control Node
    participant AWS as AWS EC2 API
    participant Plugin as AWS EC2 Plugin
    participant Instance1 as Web Server (us-east-1)
    participant Instance2 as DB Server (us-east-1)
    participant Instance3 as Cache Server (us-west-2)

    User->>Ansible: Configure aws_ec2.yml inventory
    Ansible->>Plugin: Load dynamic inventory plugin
    Plugin->>AWS: Query EC2 instances (API call)
    AWS-->>Plugin: Return instance list & metadata
    Note over Plugin: Group by tags:<br/>- role_webserver<br/>- role_database<br/>- role_cache

    User->>Ansible: Run playbook with dynamic inventory
    Ansible->>Plugin: Get inventory groups
    Plugin-->>Ansible: Return dynamic groups

    loop Auto-discovered hosts
        Ansible->>Instance1: Connect using public IP
        Instance1-->>Ansible: Connected (tag: webserver)

        Ansible->>Instance2: Connect using private IP
        Instance2-->>Ansible: Connected (tag: database)

        Ansible->>Instance3: Connect using public IP
        Instance3-->>Ansible: Connected (tag: cache, us-west-2)
    end

    Ansible->>Instance1: Execute webserver tasks
    Ansible->>Instance2: Execute database tasks
    Ansible->>Instance3: Execute cache tasks

    Ansible-->>User: Playbook executed on auto-discovered hosts

Step 1: Install AWS dynamic inventory plugin

pip install ansible[amazon]
# Or: pip install boto3 botocore

Step 2: Configure dynamic inventory - Save as aws_ec2.yml:

# ansible.cfg
[inventory]
enable_plugins = aws_ec2
inventory = ./aws_ec2.yml

# aws_ec2.yml (inventory file)
plugin: aws_ec2
regions:
  - us-east-1
  - us-west-2
filters:
  instance-state-name: running
  # tag:Environment: production
keyed_groups:
  - key: tags.Role
    prefix: role
    separator: _
  - key: tags.Environment
    prefix: env
  - key: placement.region
    prefix: region
  - key: instance_type
    prefix: type
compose:
  ansible_host: public_ip_address
  ansible_user: ubuntu
  ansible_port: 22
hostnames:
  - tag:Name
  - instance-id

Step 3: Use dynamic inventory - Save as deploy_aws.yml:

---
- name: Configure web servers in AWS
  hosts: role_webserver
  become: yes
  gather_facts: yes

  vars:
    aws_region: us-east-1

  tasks:
    - name: Install dependencies
      apt:
        name: 
          - nginx
          - python3-pip
          - awscli
        state: present
        update_cache: yes

    - name: Configure AWS CLI for the application
      template:
        src: templates/aws_credentials.j2
        dest: /home/ubuntu/.aws/credentials
        owner: ubuntu
        mode: '0600'
      vars:
        aws_access_key: "{{ vault_aws_access_key }}"
        aws_secret_key: "{{ vault_aws_secret_key }}"
        region: "{{ aws_region }}"

    - name: Deploy application code from S3
      aws_s3:
        bucket: myapp-artifacts
        object: releases/{{ app_version }}/app.tar.gz
        dest: /tmp/app.tar.gz
        mode: get

    - name: Extract application
      unarchive:
        src: /tmp/app.tar.gz
        dest: /var/www/html/
        remote_src: yes
        owner: www-data
        group: www-data

    - name: Configure Nginx
      template:
        src: templates/nginx_aws.conf.j2
        dest: /etc/nginx/sites-available/default
      notify: Reload Nginx
      vars:
        server_name: "{{ hostvars[inventory_hostname].tags.Name }}.example.com"

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

    - name: Register instance with ELB
      elb_target:
        target_group_arn: "{{ alb_target_group_arn }}"
        target_id: "{{ instance_id }}"
        state: present
        port: 80
      delegate_to: localhost
      run_once: yes
      when: "'webserver' in tags.Role"

  handlers:
    - name: Reload Nginx
      service:
        name: nginx
        state: reloaded

Additional commands for dynamic inventory:

# Test dynamic inventory
ansible-inventory -i aws_ec2.yml --graph

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

# Filter by region
ansible -i aws_ec2.yml region_us_east_1 -m ping

# Filter by tag
ansible -i aws_ec2.yml role_database -m setup

# Combine static and dynamic inventory
# hosts.ini:
# [aws:children]
# role_webserver
# role_database

# Run playbook with AWS variables
ansible-playbook -i aws_ec2.yml deploy_aws.yml -e "app_version=2.0.0"


Scenario 12: Complex Ansible Roles with Dependencies

Description: Create reusable roles with dependencies, defaults, and structured organization for enterprise use.

sequenceDiagram
    participant User as Developer
    participant Playbook as Ansible Playbook
    participant RoleBase as base Role
    participant RoleWeb as webserver Role
    participant RoleApp as application Role
    participant RoleDB as database Role
    participant Server as Target Server

    User->>Playbook: Run site.yml with tags
    Playbook->>RoleBase: Include role (dependencies)

    Note over RoleBase: configure_common_packages<br/>setup_firewall<br/>configure_users

    Playbook->>RoleWeb: Include role (depends on base)
    Note over RoleWeb: install_nginx<br/>configure_ssl<br/>setup_vhost

    Playbook->>RoleApp: Include role (depends on base)
    Note over RoleApp: install_dependencies<br/>deploy_code<br/>configure_service

    Playbook->>RoleDB: Include role (depends on base)
    Note over RoleDB: install_postgresql<br/>configure_database<br/>create_users

    loop Role execution
        RoleBase->>Server: Execute tasks
        RoleWeb->>Server: Execute tasks
        RoleApp->>Server: Execute tasks
        RoleDB->>Server: Execute tasks
    end

    Server-->>Playbook: All roles applied

    Playbook-->>User: Infrastructure configured
    Note over RoleBase: Reusable, modular architecture

Directory Structure:

ansible-project/
├── ansible.cfg
├── inventory/
│   ├── production
│   └── staging
├── group_vars/
│   ├── all.yml
│   ├── webservers.yml
│   └── dbservers.yml
├── playbook.yml
└── roles/
    ├── base/
    │   ├── defaults/main.yml
    │   ├── tasks/main.yml
    │   ├── handlers/main.yml
    │   └── templates/
    ├── webserver/
    │   ├── meta/main.yml
    │   ├── tasks/main.yml
    │   ├── handlers/main.yml
    │   └── templates/
    ├── application/
    │   ├── meta/main.yml
    │   ├── tasks/main.yml
    │   └── templates/
    └── database/
        ├── meta/main.yml
        ├── tasks/main.yml
        └── templates/

Role: base - roles/base/defaults/main.yml:

---
common_packages:
  - vim
  - htop
  - curl
  - wget
  - unattended-upgrades

common_users:
  - name: ansible
    groups: sudo
    ssh_key: "{{ lookup('file', '~/.ssh/ansible.pub') }}"

roles/base/tasks/main.yml:

---
- name: Install common packages
  package:
    name: "{{ common_packages }}"
    state: present
    update_cache: yes
  tags: [packages, common]

- name: Configure automatic security updates
  template:
    src: 50unattended-upgrades.j2
    dest: /etc/apt/apt.conf.d/50unattended-upgrades
    owner: root
    group: root
    mode: '0644'
  when: ansible_os_family == "Debian"
  tags: security

- name: Create common users
  user:
    name: "{{ item.name }}"
    groups: "{{ item.groups | default(omit) }}"
    shell: /bin/bash
    create_home: yes
    state: present
  loop: "{{ common_users }}"
  tags: users

- name: Deploy SSH keys for users
  authorized_key:
    user: "{{ item.name }}"
    key: "{{ item.ssh_key }}"
    state: present
  loop: "{{ common_users }}"
  when: item.ssh_key is defined
  tags: users

Role: webserver - roles/webserver/meta/main.yml:

---
dependencies:
  - role: base
    tags: [base, common]

roles/webserver/defaults/main.yml:

---
nginx_version: "1.18.*"
nginx_vhosts:
  - name: default
    port: 80
    root: /var/www/html
    index: index.html
    server_name: localhost
nginx_ssl: false

roles/webserver/tasks/main.yml:

---
- name: Install Nginx
  apt:
    name: "nginx={{ nginx_version }}"
    state: present
    update_cache: yes
  notify: Restart Nginx
  tags: [nginx, install]

- name: Remove default Nginx site
  file:
    path: /etc/nginx/sites-enabled/default
    state: absent
  notify: Reload Nginx
  tags: nginx

- name: Deploy virtual host configurations
  template:
    src: nginx_vhost.j2
    dest: "/etc/nginx/sites-available/{{ item.name }}.conf"
    owner: root
    group: root
    mode: '0644'
  loop: "{{ nginx_vhosts }}"
  notify: Reload Nginx
  tags: nginx

- name: Enable virtual hosts
  file:
    src: "/etc/nginx/sites-available/{{ item.name }}.conf"
    dest: "/etc/nginx/sites-enabled/{{ item.name }}.conf"
    state: link
  loop: "{{ nginx_vhosts }}"
  notify: Reload Nginx
  tags: nginx

- name: Deploy SSL certificates
  copy:
    src: "{{ item.ssl_cert }}"
    dest: /etc/nginx/ssl/
    owner: root
    group: root
    mode: '0644'
  loop: "{{ nginx_vhosts }}"
  when: item.ssl_cert is defined
  notify: Reload Nginx
  tags: [nginx, ssl]

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

- name: Configure firewall for HTTP/HTTPS
      ufw:
        rule: allow
        port: "{{ item.port }}"
        proto: tcp
      loop: "{{ nginx_vhosts }}"
      tags: firewall

roles/webserver/handlers/main.yml:

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

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

Role: application - roles/application/meta/main.yml:

---
dependencies:
  - role: base

roles/application/tasks/main.yml:

---
- name: Install application dependencies
  apt:
    name: "{{ app_dependencies }}"
    state: present
  tags: [app, install]

- name: Create application user
  user:
    name: "{{ app_user }}"
    system: yes
    home: "/opt/{{ app_name }}"
    shell: /bin/false
    create_home: yes
  tags: [app, users]

- name: Deploy application code
  git:
    repo: "{{ app_repo }}"
    dest: "/opt/{{ app_name }}/src"
    version: "{{ app_version }}"
  notify: Restart Application
  tags: [app, deploy]

- name: Install Python requirements
  pip:
    requirements: "/opt/{{ app_name }}/src/requirements.txt"
    virtualenv: "/opt/{{ app_name }}/venv"
  tags: [app, deploy]

- name: Deploy systemd service
  template:
    src: app.service.j2
    dest: "/etc/systemd/system/{{ app_name }}.service"
    owner: root
    group: root
    mode: '0644'
  notify: Reload Systemd
  tags: [app, config]

- name: Ensure application is running
  systemd:
    name: "{{ app_name }}"
    state: started
    enabled: yes
    daemon_reload: yes
  tags: [app, service]

Playbook - Save as site.yml:

---
- name: Configure complete infrastructure
  hosts: all
  become: yes

  roles:
    - role: base
      tags: [base, common]

    - role: webserver
      when: "'webservers' in group_names"
      tags: [webserver, frontend]

    - role: application
      when: "'appservers' in group_names"
      tags: [application, backend]

    - role: database
      when: "'dbservers' in group_names"
      tags: [database]

  handlers:
    - name: Reload Systemd
      systemd:
        daemon_reload: yes

Execute specific roles:

# Only webserver tasks
ansible-playbook -i inventory/production site.yml --tags webserver

# Everything except base
ansible-playbook -i inventory/production site.yml --skip-tags base

# With role variables
ansible-playbook -i inventory/production site.yml -e "nginx_ssl=true"


Scenario 13: Advanced Error Handling with Blocks and Rescue

Description: Implement robust error handling, rollback, and cleanup for production deployments.

sequenceDiagram
    participant User as Developer
    participant Ansible as Ansible Control Node
    participant Server as Production Server

    User->>Ansible: Run deploy_with_rollback.yml

    Ansible->>Server: Start deployment block

    alt Task succeeds
        Ansible->>Server: Create backup of current version
        Server->>Server: tar -czf backup.tar.gz current/

        Ansible->>Server: Deploy new version
        Server->>Server: Extract new code

        Ansible->>Server: Run database migrations
        Server->>Server: Run migrate script

        Ansible->>Server: Restart application
        Server->>Service: Restart service
        Server-->>Ansible: Service restarted

        Ansible->>Server: Run health checks
        Server->>Server: curl http://localhost/health
        Server-->>Ansible: HTTP 200 OK
    else Task fails (migration fails)
        Ansible->>Server: Task failed
        Server-->>Ansible: Error

        Note over Ansible: Trigger rescue block

        Ansible->>Server: Rollback to backup
        Server->>Server: Restore backup.tar.gz
        Server-->>Ansible: Rolled back

        Ansible->>Server: Restart with old version
        Server->>Service: systemctl restart app
        Service-->>Server: Service restarted

        Ansible->>Server: Report failure
        Server-->>Ansible: Rollback complete

        Note over Ansible: Trigger always block

        Ansible->>Server: Cleanup temporary files
        Server->>Server: rm -rf /tmp/deploy-*
        Server-->>Server: Cleaned up

        Ansible->>Server: Send notification alert
        Server-->>Ansible: Alert sent

        Ansible-->>User: Deployment failed, rollback successful
    end

Code - Save as deploy_with_rollback.yml:

---
- name: Deploy with rollback capability
  hosts: appservers
  become: yes
  vars:
    app_name: criticalapp
    deploy_version: "2.0.0"
    backup_dir: "/opt/{{ app_name }}/backup"
    deploy_dir: "/opt/{{ app_name }}"

  tasks:
    - name: Deployment with error handling
      block:
        - name: Create backup of current version
          archive:
            path: "{{ deploy_dir }}/current"
            dest: "{{ backup_dir }}/{{ app_name }}-backup-{{ ansible_date_time.epoch }}.tar.gz"
            format: gz
          register: backup_result

        - name: Create deployment directory
          file:
            path: "{{ deploy_dir }}/releases/{{ deploy_version }}"
            state: directory
            owner: "{{ app_user }}"
            group: "{{ app_user }}"
            mode: '0755'

        - name: Deploy new version
          unarchive:
            src: "/tmp/{{ app_name }}-v{{ deploy_version }}.tar.gz"
            dest: "{{ deploy_dir }}/releases/{{ deploy_version }}/"
            remote_src: yes
            owner: "{{ app_user }}"
            group: "{{ app_user }}"
          register: deploy_result

        - name: Run pre-deployment checks
          command: "{{ deploy_dir }}/releases/{{ deploy_version }}/bin/health-check"
          register: pre_check

        - name: Run database migrations
          command: "{{ deploy_dir }}/releases/{{ deploy_version }}/bin/migrate"
          register: migration_result
          environment:
            DATABASE_URL: "{{ vault_database_url }}"
          no_log: true

        - name: Update symlink to new version
          file:
            src: "{{ deploy_dir }}/releases/{{ deploy_version }}"
            dest: "{{ deploy_dir }}/current"
            state: link
            force: yes

        - name: Restart application service
          systemd:
            name: "{{ app_name }}"
            state: restarted
            daemon_reload: yes

        - name: Wait for service to be ready
          wait_for:
            path: "/var/run/{{ app_name }}.pid"
            state: present
            timeout: 60

        - name: Run post-deployment health check
          uri:
            url: "http://localhost:{{ app_port }}/health"
            status_code: 200
          register: health_check
          retries: 10
          delay: 5
          until: health_check.status == 200

      rescue:
        - name: Log deployment failure
          debug:
            msg: "Deployment failed! Initiating rollback... Error: {{ ansible_failed_result }}"

        - name: Restore from backup
          unarchive:
            src: "{{ backup_result.dest }}"
            dest: "{{ deploy_dir }}/"
            remote_src: yes
            extra_opts: [--strip-components=1]
          when: backup_result.dest is defined

        - name: Restore previous symlink
          file:
            src: "{{ backup_dir }}/current-backup"
            dest: "{{ deploy_dir }}/current"
            state: link
            force: yes

        - name: Restart application with old version
          systemd:
            name: "{{ app_name }}"
            state: restarted

        - name: Verify rollback health
          uri:
            url: "http://localhost:{{ app_port }}/health"
            status_code: 200
          register: rollback_health
          retries: 5
          delay: 5
          until: rollback_health.status == 200

        - name: Report failure
          fail:
            msg: "Deployment failed and rollback completed. Previous version is running."

      always:
        - name: Cleanup temporary files
          file:
            path: "{{ item }}"
            state: absent
          loop:
            - "/tmp/{{ app_name }}-v{{ deploy_version }}.tar.gz"
            - "{{ backup_dir }}/current-backup"
          ignore_errors: yes

        - name: Send deployment notification
          uri:
            url: "{{ webhook_url }}"
            method: POST
            body_format: json
            body:
              status: "{{ 'success' if ansible_failed_task is undefined else 'failed' }}"
              app: "{{ app_name }}"
              version: "{{ deploy_version }}"
              host: "{{ inventory_hostname }}"
              timestamp: "{{ ansible_date_time.iso8601 }}"
          delegate_to: localhost
          run_once: yes
          when: webhook_url is defined

Execute:

ansible-playbook -i hosts.ini deploy_with_rollback.yml


Scenario 14: Ansible Performance Optimization & Best Practices

Description: Optimize playbook execution speed with strategies, caching, and efficient patterns.

sequenceDiagram
    participant User as Developer
    participant Ansible as Ansible Control Node
    participant Cache as Redis Cache
    participant Server1 as Server 1
    participant Server2 as Server 2
    participant Server3..N as Servers 3-N
    participant Forks as Parallel Workers

    User->>Ansible: Run optimized playbook with -f 20
    Ansible->>Cache: Check for cached facts
    Cache-->>Ansible: Return cached data

    Ansible->>Ansible: Set strategy = free

    loop Parallel execution (forks=20)
        Ansible->>Forks: Distribute tasks to workers

        Forks->>Server1: Execute task 1
        Server1-->>Forks: Complete

        Forks->>Server2: Execute task 1
        Server2-->>Forks: Complete

        Forks->>Server3..N: Execute task 1
        Server3..N-->>Forks: Complete

        Forks->>Server1: Execute task 2 (while others still on task 1)
        Server1-->>Forks: Complete
    end

    Ansible->>Ansible: Use async for long tasks

    Ansile->>Server1: Async task: yum update (timeout=300)
    Server1-->>Ansible: Task started (job ID returned)

    Ansible->>Server1: Check async status
    Server1-->>Ansible: Still running

    Note over Server1: 60 seconds later

    Ansible->>Server1: Check async status again
    Server1-->>Ansible: Complete

    Ansible->>Cache: Update cache with new facts
    Cache-->>Ansible: Cache updated

    Ansible-->>User: Playbook complete in 2 minutes (vs 15 minutes)

Configuration - ansible.cfg:

[defaults]
# Increase parallel forks (default 5)
forks = 20

# Enable pipelining (reduces SSH connections)
pipelining = True

# Use smart strategy for fact gathering
gathering = smart

# Cache facts for 24 hours
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts_cache
fact_caching_timeout = 86400

# Use free strategy for independent hosts
strategy = free

# Disable host key checking for dynamic environments
host_key_checking = False

# Enable mitogen for extreme speedup (optional plugin)
# strategy_plugins = /path/to/mitogen/ansible_mitogen/plugins/strategy
# strategy = mitogen_linear

[ssh_connection]
# Enable SSH multiplexing
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
# Reduce connection timeout
timeout = 30

Optimized playbook - Save as optimized_deploy.yml:

---
- name: Optimized deployment with performance techniques
  hosts: all
  become: yes
  gather_facts: yes
  any_errors_fatal: false
  max_fail_percentage: 10  # Allow 10% failures

  vars:
    app_version: 3.0.0
    # Use facts from cache
    use_cache: yes

  tasks:
    - name: Gather facts for specific hosts only
      setup:
        filter:
          - ansible_distribution
          - ansible_os_family
          - ansible_memtotal_mb
      when: not ansible_facts.get('os_family')

    - name: Install packages asynchronously
      yum:
        name:
          - nginx
          - postgresql
          - redis
        state: present
      async: 300  # Timeout in seconds
      poll: 0  # Don't wait, fire and continue
      register: package_install

    - name: Check package install status
      async_status:
        jid: "{{ package_install.ansible_job_id }}"
      register: job_result
      until: job_result.finished
      retries: 30
      delay: 10

    - name: Deploy configuration in batches
      template:
        src: templates/app.conf.j2
        dest: /etc/app.conf
      # Process hosts in batches of 5
      serial: 5

    - name: Use with_items instead of loop for performance
      copy:
        src: "{{ item }}"
        dest: /var/www/html/
      with_fileglob:
        - files/static/*
      # Or use native loop for better performance
      # loop: "{{ query('fileglob', 'files/static/*') }}"

    - name: Conditional execution with fact caching
      service:
        name: nginx
        state: restarted
      when: not ansible_check_mode
      # Skip in check mode

    - name: Run tasks on first host only (delegate)
      command: /usr/bin/update-dns.sh
      delegate_to: "{{ groups['dns'][0] }}"
      run_once: yes

    - name: Use included tasks for reuse
      include_tasks: tasks/configure_monitoring.yml
      when: enable_monitoring | default(false)

    - name: Use import_tasks for static inclusion
      import_tasks: tasks/configure_logging.yml

    - name: Use blocks for error handling
      block:
        - name: Critical deployment step
          command: /opt/app/deploy.sh --version={{ app_version }}

        - name: Verify deployment
          uri:
            url: http://localhost/health
            status_code: 200
      rescue:
        - name: Rollback on failure
          command: /opt/app/rollback.sh --version={{ app_version }}
          # Rescue runs only on failed hosts

    - name: Use strategy: free for independent tasks
      # This playbook uses strategy: free in ansible.cfg
      # Each host proceeds at its own pace

    - name: Use wait_for with low timeout
      wait_for:
        path: /var/run/app.pid
        timeout: 30
        # Don't wait too long

    - name: Use systemd module instead of service
      systemd:
        name: app
        state: restarted
        daemon_reload: yes
        # More efficient than command or shell

    - name: Use template instead of lineinfile
      template:
        src: templates/ssh_config.j2
        dest: /etc/ssh/ssh_config
      # Single template is faster than multiple lineinfile

    - name: Cache expensive lookups
      set_fact:
        app_config: "{{ lookup('url', 'https://config.example.com/app.json', split_lines=False) }}"
        cacheable: yes
      # Facts are cached across playbook runs

    - name: Use compression for large file transfers
      synchronize:
        src: files/large-data/
        dest: /opt/app/data/
        compress: yes
        archive: yes
      delegate_to: localhost

    - name: Use parallel tasks with async
      async: 300
      command: "long-running-task --host={{ inventory_hostname }}"
      poll: 5
      # Check status every 5 seconds

    - name: Disable fact gathering for specific play
      setup:
        gather_subset:
          - '!all'
          - '!min'
          - 'network'
      # Only gather network facts

    - name: Use run_once for efficiency
      command: /usr/bin/create-release-tag.sh
      register: release_tag
      run_once: yes
      # Only runs on first host, result available to all

    - name: Use local_action instead of delegate_to
      local_action:
        module: copy
        content: "{{ hostvars | to_nice_json }}"
        dest: /tmp/inventory-report.json
      # Equivalent to delegate_to: localhost

    - name: Optimize loops with include_tasks
      include_tasks: tasks/configure_user.yml
      loop: "{{ users }}"
      loop_control:
        loop_var: user
        label: "{{ user.name }}"
        pause: 2  # Give API breathing room
      # Better performance than inline loop

    - name: Use throttle to limit concurrent tasks
      command: /usr/bin/api-call.sh
      throttle: 1
      # Only one host executes this at a time

    - name: Use meta tasks for control flow
    - meta: flush_handlers
      # Flush handlers immediately

    - name: Refresh inventory mid-playbook
      meta: refresh_inventory
      # Useful after provisioning new instances

    - name: End execution for failed hosts
      meta: end_host
      when: ansible_failed

    - name: Clear gathered facts to free memory
    - meta: clear_facts
      when: free_memory_mb < 512

  post_tasks:
    - name: Refresh fact cache
      setup:
        filter: ansible_local
      when: ansible_local is defined

    - name: Save execution time metrics
      local_action:
        module: copy
        content: |
          Playbook: {{ playbook_name }}
          Duration: {{ ansible_play_duration }} seconds
          Hosts: {{ ansible_play_hosts_all | length }}
          Failures: {{ ansible_play_hosts_all | difference(ansible_play_hosts) | length }}
        dest: "/tmp/ansible-metrics-{{ ansible_date_time.epoch }}.json"
      run_once: yes

Performance tips in code comments:

# Run with timing stats
ansible-playbook -i hosts.ini optimized_deploy.yml --verbose --forks 50

# Profile specific tasks
ANSIBLE_CALLBACK_WHITELIST=profile_tasks,timer ansible-playbook playbook.yml

# Check mode with diff
ansible-playbook -i hosts.ini optimized_deploy.yml --check --diff

# Limit to specific hosts
ansible-playbook -i hosts.ini optimized_deploy.yml --limit webservers[0:9]

# Skip tags for faster execution
ansible-playbook -i hosts.ini optimized_deploy.yml --skip-tags monitoring

# Use ARA for detailed reporting
# pip install ara[server]
# ansible-playbook -i hosts.ini optimized_deploy.yml -c ara.plugins.callback.default


Scenario 15: CI/CD Integration with Ansible & Jenkins

Description: Integrate Ansible into Jenkins pipeline for continuous deployment with automated testing.

sequenceDiagram
    participant Dev as Developer
    participant Git as Git Repository
    participant Jenkins as Jenkins Server
    participant Ansible as Ansible Control Node
    participant AWX as AWX/Tower
    participant Test as Test Environment
    participant Prod as Production Environment

    Dev->>Git: git push -m "Feature: deploy app v3.0"
    Git->>Jenkins: Webhook triggers pipeline

    Jenkins->>Jenkins: Stage: Checkout
    Jenkins->>Git: Clone repository

    Jenkins->>Jenkins: Stage: Lint
    Jenkins->>Jenkins: ansible-lint playbooks/

    Jenkins->>Jenkins: Stage: Syntax Check
    Jenkins->>Ansible: ansible-playbook --syntax-check

    Jenkins->>Jenkins: Stage: Unit Test
    Jenkins->>Ansible: Run test playbook on mock hosts

    Jenkins->>Jenkins: Stage: Deploy to Test
    Jenkins->>AWX: Launch job template "Deploy to Test"
    AWX->>Ansible: Execute deployment playbook
    Ansible->>Test: Deploy application v3.0
    Test-->>Ansible: Deployment complete

    Jenkins->>Test: Run integration tests
    Test-->>Jenkins: Tests passed

    alt Manual approval
        Jenkins->>Dev: Send approval notification
        Dev->>Jenkins: Approve deployment
    end

    Jenkins->>Jenkins: Stage: Deploy to Production
    Jenkins->>AWX: Launch job template "Deploy to Prod"
    AWX->>Ansible: Execute production playbook
    Ansible->>Prod: Deploy application v3.0
    Prod-->>Ansible: Deployment complete

    Jenkins->>Prod: Run smoke tests
    Prod-->>Jenkins: Tests passed

    Jenkins->>Dev: Send success notification
    Note over Jenkins: Full CI/CD pipeline

Jenkinsfile - Save as Jenkinsfile:

pipeline {
    agent any

    environment {
        ANSIBLE_INVENTORY = 'inventory/production'
        ANSIBLE_VAULT_PASSWORD_FILE = credentials('ansible-vault-password')
        AWX_URL = 'https://awx.example.com'
        AWX_CREDENTIALS = credentials('awx-token')
    }

    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }

        stage('Ansible Lint') {
            steps {
                sh '''
                    ansible-lint playbooks/ --exclude .roles/
                '''
            }
        }

        stage('Syntax Check') {
            steps {
                sh '''
                    ansible-playbook playbooks/site.yml --syntax-check -i ${ANSIBLE_INVENTORY}
                '''
            }
        }

        stage('Dry Run') {
            steps {
                sh '''
                    ansible-playbook playbooks/site.yml -i ${ANSIBLE_INVENTORY} --check --diff
                '''
            }
        }

        stage('Deploy to Test') {
            steps {
                script {
                    // Trigger AWX job template
                    sh '''
                        curl -X POST ${AWX_URL}/api/v2/job_templates/10/launch/ \\
                          -H "Authorization: Bearer ${AWX_CREDENTIALS}" \\
                          -d '{"extra_vars": {"env": "test", "app_version": "${BUILD_NUMBER}"}}'
                    '''
                }
            }
        }

        stage('Integration Tests') {
            steps {
                sh '''
                    # Wait for deployment
                    sleep 30
                    # Run tests
                    pytest tests/integration/ -v
                '''
            }
        }

        stage('Manual Approval') {
            when {
                branch 'main'
            }
            steps {
                timeout(time: 1, unit: 'HOURS') {
                    input message: 'Deploy to production?', ok: 'Deploy'
                }
            }
        }

        stage('Deploy to Production') {
            when {
                branch 'main'
            }
            steps {
                script {
                    sh '''
                        curl -X POST ${AWX_URL}/api/v2/job_templates/20/launch/ \\
                          -H "Authorization: Bearer ${AWX_CREDENTIALS}" \\
                          -d '{"extra_vars": {"env": "prod", "app_version": "${BUILD_NUMBER}"}}'
                    '''

                    // Wait for completion
                    sh '''
                        ./scripts/wait-for-deployment.sh prod ${BUILD_NUMBER}
                    '''
                }
            }
            post {
                success {
                    sh '''
                        ansible-playbook playbooks/smoke-tests.yml -i inventory/production
                    '''
                }
            }
        }

        stage('Smoke Tests') {
            steps {
                sh '''
                    ansible-playbook playbooks/smoke-tests.yml -i inventory/production
                '''
            }
        }

        stage('Cleanup') {
            steps {
                sh '''
                    ansible-playbook playbooks/cleanup.yml -i ${ANSIBLE_INVENTORY}
                '''
            }
        }
    }

    post {
        always {
            archiveArtifacts artifacts: 'logs/*.log', fingerprint: true

            junit 'test-results/*.xml'

            emailext (
                to: "${env.CHANGE_AUTHOR_EMAIL ?: 'dev-team@example.com'}",
                subject: "Ansible Deployment ${env.JOB_NAME} - ${currentBuild.currentResult}",
                body: """
                    <h2>Deployment ${env.BUILD_NUMBER}</h2>
                    <p>Result: ${currentBuild.currentResult}</p>
                    <p>Environment: Production</p>
                    <p>Version: ${BUILD_NUMBER}</p>
                    <p>Playbook: ${BUILD_URL}console</p>
                """,
                mimeType: 'text/html'
            )
        }

        success {
             sh '''
                echo "Deployment successful!"
            '''
        }

        failure {
            sh '''
                echo "Deployment failed. Check logs."
            '''
        }
    }
}

Ansible playbook for smoke tests - Save as playbooks/smoke-tests.yml:

---
- name: Smoke tests after deployment
  hosts: appservers
  gather_facts: no

  tasks:
    - name: Wait for application to be ready
      wait_for:
        port: "{{ app_port }}"
        host: localhost
        timeout: 120

    - name: Check application health endpoint
      uri:
        url: "http://localhost:{{ app_port }}/health"
        status_code: 200
        return_content: yes
      register: health_check

    - name: Verify response contains expected data
      assert:
        that:
          - health_check.json.status == "healthy"
          - health_check.json.version == "{{ app_version }}"

    - name: Test database connectivity
      command: "{{ app_dir }}/bin/db-check"
      register: db_check
      changed_when: false

    - name: Check SSL certificate expiration
      shell: |
        echo | openssl s_client -connect localhost:443 2>/dev/null | \
        openssl x509 -noout -dates | grep notAfter
      register: ssl_expiry
      when: enable_ssl | default(false)

    - name: Report smoke test results
      debug:
        msg: |
          Smoke tests completed for {{ inventory_hostname }}
          Health: {{ health_check.json.status }}
          Version: {{ health_check.json.version }}
          Database: {{ db_check.stdout }}

AWX Job Template Configuration:

# Export job template configuration
---
- name: Configure AWX job templates
  hosts: localhost
  connection: local
  gather_facts: no

  tasks:
    - name: Create test deployment job template
      awx.awx.job_template:
        name: "Deploy to Test"
        job_type: run
        inventory: "Test Inventory"
        project: "Ansible Playbooks"
        playbook: "playbooks/deploy-app.yml"
        credential: "AWS SSH Key"
        extra_vars:
          env: test
          app_version: "{{ app_version }}"
        ask_variables_on_launch: yes
        ask_inventory_on_launch: yes
        state: present
        tower_host: "{{ tower_host }}"
        tower_oauthtoken: "{{ tower_token }}"
        validate_certs: no

    - name: Create production deployment job template
      awx.awx.job_template:
        name: "Deploy to Production"
        job_type: run
        inventory: "Production Inventory"
        project: "Ansible Playbooks"
        playbook: "playbooks/deploy-app.yml"
        credential: "Production SSH Key"
        extra_vars:
          env: prod
          app_version: "{{ app_version }}"
        ask_variables_on_launch: yes
        ask_inventory_on_launch: no
        ask_credential_on_launch: no
        state: present
        tower_host: "{{ tower_host }}"
        tower_oauthtoken: "{{ tower_token }}"
        validate_certs: no


Scenario 16: Ansible Troubleshooting and Diagnostics

Description: Debug complex issues with verbose output, debug modules, and common problem resolution techniques.

sequenceDiagram
    participant Admin as Administrator
    participant Support as Support Engineer
    participant Ansible as Ansible Control Node
    participant Host as Remote Host
    participant Logs as Ansible Logs
    participant Debug as Debug Module
    participant ARA as ARA Reporting
    participant Navigator as Ansible Navigator

    Admin->>Ansible: Playbook failing on task "Deploy app"
    Admin->>Ansible: Run with -vvvv verbose mode

    Ansible->>Logs: Write detailed execution logs
    Logs-->>Admin: Show task execution details (SSH commands, return codes)

    Admin->>Ansible: Add debug tasks to playbook
    Ansible->>Debug: Print variable values
    Debug-->>Admin: Display host vars, facts, registered variables

    Admin->>Ansible: Run with --check --diff mode
    Ansible->>Host: Show what would change
    Host-->>Admin: Diff output for files & commands

    Admin->>Ansible: Use --start-at-task="Install package"
    Ansible->>Host: Skip to specific task
    Host-->>Admin: Execute from that point onward

    Support->>Navigator: Launch interactive mode
    Navigator->>Support: Provide TUI interface

    Support->>Navigator: Execute ad-hoc command
    Navigator->>Host: Run command module
    Host-->>Support: Return output in TUI

    Support->>ARA: Review historical runs
    ARA-->>Support: Show playbook results, task duration, host status

    Support->>Host: Check file permissions
    Host->>Support: ls -la /opt/app/config

    Support->>Logs: Analyze with grep
    Logs-->>Support: Identify error patterns

    Support->>Ansible: Apply fix
    Ansible-->>Admin: Playbook now successful

Troubleshooting commands and playbook:

# Run with maximum verbosity (-vvvv)
ansible-playbook -i hosts.ini deploy.yml -vvvv

# Limit to specific host
ansible-playbook -i hosts.ini deploy.yml --limit webserver-01

# Start at specific task
ansible-playbook -i hosts.ini deploy.yml --start-at-task="Install Nginx"

# Step through tasks interactively
ansible-playbook -i hosts.ini deploy.yml --step

# Check mode with diff
ansible-playbook -i hosts.ini deploy.yml --check --diff

# List all tasks without executing
ansible-playbook -i hosts.ini deploy.yml --list-tasks

# List hosts that would be targeted
ansible-playbook -i hosts.ini deploy.yml --list-hosts

Debug playbook - Save as debug_deploy.yml:

---
- name: Deploy with extensive debugging
  hosts: appservers
  become: yes
  vars:
    app_name: myapp
    debug_mode: true

  tasks:
    # Debug connection and facts
    - name: Display connection variables
      debug:
        msg: |
          Host: {{ inventory_hostname }}
          Ansible user: {{ ansible_user }}
          SSH user: {{ ansible_ssh_user | default('default') }}
          Connection type: {{ ansible_connection }}
          Python interpreter: {{ ansible_python_interpreter }}
          Distribution: {{ ansible_distribution }}
          Version: {{ ansible_distribution_version }}
          Architecture: {{ ansible_architecture }}
          Memory: {{ ansible_memtotal_mb }} MB
          CPUs: {{ ansible_processor_vcpus }}

    # Debug variable scopes
    - name: Show variable precedence
      debug:
        var: app_name
      when: debug_mode

    # Check connectivity to external services
    - name: Test connectivity to database
      wait_for:
        host: "{{ db_host }}"
        port: "{{ db_port }}"
        timeout: 5
      register: db_connectivity
      ignore_errors: yes

    - name: Display connectivity result
      debug:
        msg: "Database connectivity: {{ 'OK' if db_connectivity is succeeded else 'FAILED' }}"

    # Check file permissions before deployment
    - name: Check permissions on target directory
      stat:
        path: /opt/myapp
      register: app_dir_stat

    - name: Display directory permissions
      debug:
        msg: |
          Directory exists: {{ app_dir_stat.stat.exists }}
          Owner: {{ app_dir_stat.stat.pw_name }}
          Group: {{ app_dir_stat.stat.gr_name }}
          Mode: {{ app_dir_stat.stat.mode }}
      when: app_dir_stat.stat.exists

    # Test privilege escalation
    - name: Verify sudo access
      command: id
      become: yes
      register: sudo_test

    - name: Display effective user after sudo
      debug:
        msg: "Effective user: {{ sudo_test.stdout }}"

    # Check disk space before deployment
    - name: Check disk space on target partition
      command: df -h /opt
      register: disk_space

    - name: Display disk usage
      debug:
        msg: "{{ disk_space.stdout }}"

    # Debug Jinja2 templating
    - name: Test template rendering
      template:
        src: templates/app.conf.j2
        dest: /tmp/debug-app.conf
      check_mode: yes
      diff: yes
      register: template_debug

    - name: Display template diff
      debug:
        var: template_debug
      when: template_debug.changed

    # Register and debug command output
    - name: Check application status
      command: systemctl status myapp
      register: service_status
      ignore_errors: yes

    - name: Display service status
      debug:
        var: service_status.stderr
      when: service_status is failed

    # Use assert to validate assumptions
    - name: Validate prerequisites
      assert:
        that:
          - ansible_distribution == "Ubuntu"
          - ansible_distribution_version is version('20.04', '>=')
          - app_dir_stat.stat.exists
          - app_dir_stat.stat.isdir
        fail_msg: "Prerequisites not met - check distribution and directory"
        success_msg: "All prerequisites validated"

    # Debug handlers
    - name: Trigger handler debug
      command: echo "Triggering restart"
      changed_when: true
      notify: Debug Handler

    # Rescue block for detailed error analysis
    - name: Critical deployment section
      block:
        - name: Deploy application code
          copy:
            src: "{{ app_archive }}"
            dest: /tmp/
          register: copy_result

        - name: Extract application
          unarchive:
            src: "{{ copy_result.dest }}"
            dest: /opt/myapp/
            remote_src: yes
          register: extract_result

      rescue:
        - name: Debug copy failure
          debug:
            var: copy_result
          when: copy_result is failed

        - name: Debug extract failure
          debug:
            var: extract_result
          when: extract_result is failed

        - name: Check if archive is valid
          command: "tar -tzf {{ copy_result.dest }}"
          register: archive_test
          ignore_errors: yes
          when: copy_result.dest is defined

        - name: Display archive test result
          debug:
            var: archive_test
          when: archive_test is defined

        - name: Fail with detailed message
          fail:
            msg: |
              Deployment failed. Investigate:
              Copy status: {{ copy_result.status }}
              Extract status: {{ extract_result.status if extract_result is defined else 'N/A' }}
              Archive validity: {{ 'Invalid' if archive_test is failed else 'Valid' }}

    # Variable type debugging
    - name: Debug variable types
      debug:
        msg: |
          app_version type: {{ app_version | type_debug }}
          app_dependencies type: {{ app_dependencies | type_debug }}
          users type: {{ users | type_debug }}
          debug_mode type: {{ debug_mode | type_debug }}

    # Test lookups
    - name: Debug file lookup
      debug:
        msg: "File content: {{ lookup('file', 'files/secret.txt') }}"
      failed_when: false
      ignore_errors: yes

    # Debug inventory
    - name: Show inventory groups
      debug:
        msg: |
          Current host groups: {{ group_names }}
          All hosts: {{ groups['all'] }}
          Webservers: {{ groups.get('webservers', []) }}

    # Test connectivity to other hosts
    - name: Test inter-host connectivity
      command: ping -c 1 {{ item }}
      loop: "{{ groups['dbservers'] }}"
      register: ping_results
      ignore_errors: yes
      when: inventory_hostname in groups['appservers']

    - name: Display connectivity results
      debug:
        msg: "{{ item.item }} - {{ 'OK' if item.rc == 0 else 'FAILED' }}"
      loop: "{{ ping_results.results }}"
      when: ping_results is defined

    # Use ARA callback plugin (if installed)
    - name: Record debug information for ARA
      ara_record:
        playbook: "{{ ara_playbook.id }}"
        key: "debug_info_{{ inventory_hostname }}"
        value: "{{ ansible_facts | to_nice_json }}"
        type: json
      delegate_to: localhost
      run_once: yes
      when: ara_playbook is defined

  handlers:
    - name: Debug Handler
      debug:
        msg: "Handler triggered on {{ inventory_hostname }}"

Ad-hoc troubleshooting commands:

# Test module directly
ansible appservers -i hosts.ini -m command -a "df -h" -vvv

# Test privilege escalation
ansible appservers -i hosts.ini -m command -a "whoami" -b -K

# Test connectivity and Python
ansible appservers -i hosts.ini -m raw -a "python --version"

# Test file transfer
ansible appservers -i hosts.ini -m copy -a "src=/tmp/test.txt dest=/tmp/"

# Check file permissions
ansible appservers -i hosts.ini -m stat -a "path=/etc/myapp/config.yml"

# Test template rendering
ansible appservers -i hosts.ini -m template -a "src=templates/app.conf.j2 dest=/tmp/app.conf" --check --diff

# Use ansible-navigator (modern alternative)
ansible-navigator run deploy.yml -i hosts.ini --mode interactive

ARA setup for detailed reporting:

# Install ARA
pip install ara[server]

# Configure ansible.cfg
[defaults]
callback_plugins = /path/to/ara/plugins/callbacks
callback_whitelist = ara_default

# Start ARA server
ara-manage runserver

# Access ARA at http://localhost:8000


Scenario 17: Multi-Cloud Deployment Strategies

Description: Deploy to AWS, Azure, and GCP from a single Ansible playbook with cloud-specific modules and dynamic inventory.

sequenceDiagram
    participant Playbook as Ansible Playbook
    participant AWS as AWS (Primary)
    participant Azure as Azure (Secondary)
    participant GCP as GCP (DR)
    participant Registry as Container Registry
    participant DNS as Global DNS

    Playbook->>AWS: Build & push ECR image
    Playbook->>Azure: Build & push ACR image
    Playbook->>GCP: Build & push GCR image

    Playbook->>AWS: Deploy to EKS (us-east-1)
    Playbook->>Azure: Deploy to AKS (East US)
    Playbook->>GCP: Deploy to GKE (us-central1)

    AWS->>AWS: Run smoke tests (ELB health check)
    Azure->>Azure: Run smoke tests (App Gateway)
    GCP->>GCP: Run smoke tests (Load Balancer)

    alt AWS healthy
        DNS->>AWS: Route 100% traffic
    else AWS degraded
        DNS->>Azure: Failover traffic (50%)
        DNS->>GCP: Failover traffic (50%)
    end

    Playbook->>DNS: Update health checks

    Note over Playbook: Single source of truth for multi-cloud

Multi-cloud inventory - Save as clouds.yml:

---
# group_vars/all/clouds.yml
cloud_providers:
  aws:
    enabled: true
    region: us-east-1
    credentials:
      access_key: "{{ vault_aws_access_key }}"
      secret_key: "{{ vault_aws_secret_key }}"
    registry: "123456789.dkr.ecr.us-east-1.amazonaws.com"
    cluster: "prod-eks"
    resource_group: "production"

  azure:
    enabled: true
    region: eastus
    credentials:
      client_id: "{{ vault_azure_client_id }}"
      secret: "{{ vault_azure_secret }}"
      tenant: "{{ vault_azure_tenant }}"
    registry: "myregistry.azurecr.io"
    cluster: "prod-aks"
    resource_group: "prod-rg"

  gcp:
    enabled: true
    region: us-central1
    credentials: "{{ vault_gcp_service_account }}"
    registry: "gcr.io/my-project"
    cluster: "prod-gke"
    resource_group: "production"

Multi-cloud deployment playbook - Save as deploy_multi_cloud.yml:

---
- name: Multi-cloud container deployment
  hosts: localhost
  connection: local
  gather_facts: no
  vars_files:
    - group_vars/all/clouds.yml
    - group_vars/all/vault.yml

  vars:
    app_name: myapp
    app_version: "{{ version | default('latest') }}"
    image_tag: "{{ app_name }}:{{ app_version }}"

  tasks:
    - name: Build container image
      command: docker build -t {{ image_tag }} .
      delegate_to: localhost
      register: docker_build

    - name: Deploy to AWS
      include_tasks: tasks/deploy_aws.yml
      when: cloud_providers.aws.enabled

    - name: Deploy to Azure
      include_tasks: tasks/deploy_azure.yml
      when: cloud_providers.azure.enabled

    - name: Deploy to GCP
      include_tasks: tasks/deploy_gcp.yml
      when: cloud_providers.gcp.enabled

    - name: Update global load balancer
      include_tasks: tasks/update_dns.yml
      when: update_dns | default(false)

# AWS deployment tasks - tasks/deploy_aws.yml
---
- name: Login to AWS ECR
  command: |
    aws ecr get-login-password --region {{ cloud_providers.aws.region }} | \
    docker login --username AWS --password-stdin {{ cloud_providers.aws.registry }}
  environment:
    AWS_ACCESS_KEY_ID: "{{ cloud_providers.aws.credentials.access_key }}"
    AWS_SECRET_ACCESS_KEY: "{{ cloud_providers.aws.credentials.secret_key }}"
  no_log: true

- name: Tag image for AWS
  command: docker tag {{ image_tag }} {{ cloud_providers.aws.registry }}/{{ image_tag }}

- name: Push to ECR
  command: docker push {{ cloud_providers.aws.registry }}/{{ image_tag }}

- name: Update kubeconfig for AWS
  command: |
    aws eks update-kubeconfig \
      --region {{ cloud_providers.aws.region }} \
      --name {{ cloud_providers.aws.cluster }}

- name: Deploy to EKS
  kubernetes.core.k8s:
    state: present
    kubeconfig: "~/.kube/config"
    definition:
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: "{{ app_name }}"
        namespace: production
      spec:
        replicas: 3
        selector:
          matchLabels:
            app: "{{ app_name }}"
        template:
          metadata:
            labels:
              app: "{{ app_name }}"
          spec:
            containers:
              - name: "{{ app_name }}"
                image: "{{ cloud_providers.aws.registry }}/{{ image_tag }}"
                ports:
                  - containerPort: 8080
                resources:
                  requests:
                    cpu: "100m"
                    memory: "128Mi"
                  limits:
                    cpu: "500m"
                    memory: "512Mi"

- name: Wait for rollout
  kubernetes.core.k8s_rollout:
    kubeconfig: "~/.kube/config"
    namespace: production
    resource: deployment
    name: "{{ app_name }}"
    wait: yes
    wait_timeout: 300

- name: Run smoke tests
  uri:
    url: "{{ aws_alb_url }}/health"
    status_code: 200
  register: aws_health

# Azure deployment tasks - tasks/deploy_azure.yml
---
- name: Login to Azure
  command: |
    az login --service-principal \
      -u {{ cloud_providers.azure.credentials.client_id }} \
      -p {{ cloud_providers.azure.credentials.secret }} \
      --tenant {{ cloud_providers.azure.credentials.tenant }}
  no_log: true

- name: Get AKS credentials
  command: |
    az aks get-credentials \
      --resource-group {{ cloud_providers.azure.resource_group }} \
      --name {{ cloud_providers.azure.cluster }}

- name: Login to ACR
  command: az acr login --name {{ cloud_providers.azure.registry }}

- name: Tag image for Azure
  command: docker tag {{ image_tag }} {{ cloud_providers.azure.registry }}/{{ image_tag }}

- name: Push to ACR
  command: docker push {{ cloud_providers.azure.registry }}/{{ image_tag }}

- name: Deploy to AKS
  kubernetes.core.k8s:
    state: present
    definition:
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: "{{ app_name }}"
        namespace: production
        labels:
          cloud: azure
      spec:
        replicas: 3
        template:
          spec:
            nodeSelector:
              kubernetes.io/os: linux
            containers:
              - name: "{{ app_name }}"
                image: "{{ cloud_providers.azure.registry }}/{{ image_tag }}"
                resources:
                  requests:
                    cpu: 100m
                    memory: 128Mi

# GCP deployment tasks - tasks/deploy_gcp.yml
---
- name: Login to GCP
  command: |
    echo {{ cloud_providers.gcp.credentials }} > /tmp/gcp-key.json
    gcloud auth activate-service-account --key-file /tmp/gcp-key.json
    gcloud config set project my-project

- name: Configure Docker for GCR
  command: gcloud auth configure-docker

- name: Tag image for GCP
  command: docker tag {{ image_tag }} {{ cloud_providers.gcp.registry }}/{{ image_tag }}

- name: Push to GCR
  command: docker push {{ cloud_providers.gcp.registry }}/{{ image_tag }}

- name: Get GKE credentials
  command: |
    gcloud container clusters get-credentials {{ cloud_providers.gcp.cluster }} \
      --region {{ cloud_providers.gcp.region }}

- name: Deploy to GKE
  kubernetes.core.k8s:
    state: present
    definition:
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: "{{ app_name }}"
        namespace: production
        annotations:
          cloud.google.com/load-balancer-type: "External"
      spec:
        replicas: 3
        template:
          spec:
            containers:
              - name: "{{ app_name }}"
                image: "{{ cloud_providers.gcp.registry }}/{{ image_tag }}"
                ports:
                  - containerPort: 8080

# DNS update tasks - tasks/update_dns.yml
---
- name: Update Route53 health check
  route53_health_check:
    name: "{{ app_name }}-aws-health"
    type: HTTP
    resource_path: /health
    fqdn: "{{ app_name }}.aws.example.com"
    port: 80
    state: present
  delegate_to: localhost

- name: Update Traffic Policy
    community.aws.route53:
      state: present
      zone: example.com
      record: "{{ app_name }}"
      type: A
      ttl: 60
      value:
        - "{{ aws_alb_ip }}"
        - "{{ azure_lb_ip }}"
        - "{{ gcp_lb_ip }}"
      weight:
        - 100  # AWS primary
        - 50   # Azure secondary
        - 50   # GCP DR
      health_check:
        - "{{ app_name }}-aws-health"
        - "{{ app_name }}-azure-health"
        - "{{ app_name }}-gcp-health"

Multi-cloud dynamic inventory - inventory/multi_cloud.yml:

# plugin: amazon.aws.aws_ec2
# regions:
#   - us-east-1
#   - us-west-2
# filters:
#   tag:Environment: production
# keyed_groups:
#   - prefix: tag
#     key: tags
#
# plugin: azure.azcollection.azure_rm
# include_virtual_machines: true
# groups:
#   azure_prod: tags.environment == 'production'
#
# plugin: google.cloud.gcp_compute
# zones:
#   - us-central1-a
#   - us-west1-b
# keyed_groups:
#   - prefix: gcp
#     key: labels

Execute multi-cloud deployment:

ansible-playbook -i inventory/multi_cloud.yml deploy_multi_cloud.yml \
  -e "version=3.0.0" \
  -e "update_dns=true" \
  --vault-password-file .vault_pass


Scenario 18: Custom Ansible Modules and Plugins

Description: Develop custom modules, filters, and callbacks to extend Ansible functionality for specialized use cases.

sequenceDiagram
    participant Developer as Ansible Developer
    participant Module as Custom Module
    participant Filter as Custom Filter
    participant Callback as Callback Plugin
    participant Ansible as Ansible Core
    participant Host as Remote Host
    participant User as Ansible User

    Developer->>Module: Write custom_module.py
    Module->>Module: Implement AnsibleModule class

    Developer->>Filter: Write custom_filter.py
    Filter->>Filter: Define custom Jinja2 filters

    Developer->>Callback: Write custom_callback.py
    Callback->>Callback: Implement CallbackBase

    Developer->>Ansible: Place in library/, filter_plugins/, callback_plugins/
    Ansible->>Ansible: Load plugins

    User->>Ansible: Execute playbook with custom module
    Ansible->>Module: Initialize custom module
    Module->>Host: Execute custom logic
    Host-->>Module: Return data

    Module->>Filter: Apply custom filter
    Filter-->>Module: Transform data

    Module->>Ansible: Return structured data
    Ansible->>Callback: Notify task completion
    Callback-->>User: Custom notification

    Note over Module: Tailored enterprise functionality

Custom module - library/custom_facts.py:

#!/usr/bin/python3
# -*- coding: utf-8 -*-

from ansible.module_utils.basic import AnsibleModule
import json
import subprocess

DOCUMENTATION = r'''
---
module: custom_facts
short_description: Gather custom system facts
description:
  - Collects custom facts about system configuration
  - Returns structured data for Ansible facts
version_added: "1.0.0"
author:
  - Your Name (@yourusername)
options:
  gather:
    description:
      - List of fact categories to gather
    type: list
    elements: str
    choices: ['network', 'storage', 'security']
    default: ['network']
'''

EXAMPLES = r'''
- name: Gather network facts
  custom_facts:
    gather:
      - network

- name: Gather all facts
  custom_facts:
    gather:
      - network
      - storage
      - security
'''

RETURN = r'''
ansible_facts:
  description: Facts to add to ansible_facts
  returned: always
  type: dict
  contains:
    custom_network:
      description: Network configuration facts
      type: dict
    custom_storage:
      description: Storage configuration facts
      type: dict
'''

def gather_network_facts():
    """Gather network-related facts"""
    facts = {}

    # Get interface information
    try:
        result = subprocess.run(['ip', '-j', 'addr'], 
                              capture_output=True, text=True, check=True)
        facts['interfaces'] = json.loads(result.stdout)
    except (subprocess.CalledProcessError, FileNotFoundError):
        facts['interfaces'] = []

    # Get routing table
    try:
        result = subprocess.run(['ip', '-j', 'route'], 
                              capture_output=True, text=True, check=True)
        facts['routes'] = json.loads(result.stdout)
    except (subprocess.CalledProcessError, FileNotFoundError):
        facts['routes'] = []

    # Get listening ports
    try:
        result = subprocess.run(['ss', '-tulnp', '-j'], 
                              capture_output=True, text=True, check=True)
        facts['listening_ports'] = json.loads(result.stdout)
    except (subprocess.CalledProcessError, FileNotFoundError):
        facts['listening_ports'] = []

    return facts

def gather_storage_facts():
    """Gather storage-related facts"""
    facts = {}

    # Get disk usage
    try:
        result = subprocess.run(['df', '-h', '-T'], 
                              capture_output=True, text=True, check=True)
        lines = result.stdout.strip().split('\n')
        headers = lines[0].split()
        facts['disk_usage'] = []

        for line in lines[1:]:
            values = line.split()
            if len(values) >= 7:
                facts['disk_usage'].append({
                    'filesystem': values[0],
                    'type': values[1],
                    'size': values[2],
                    'used': values[3],
                    'available': values[4],
                    'use_percent': values[5],
                    'mounted_on': values[6]
                })
    except (subprocess.CalledProcessError, FileNotFoundError):
        facts['disk_usage'] = []

    return facts

def gather_security_facts():
    """Gather security-related facts"""
    facts = {}

    # Get firewall status
    try:
        result = subprocess.run(['ufw', 'status', 'verbose'], 
                              capture_output=True, text=True, check=True)
        facts['firewall'] = result.stdout
    except (subprocess.CalledProcessError, FileNotFoundError):
        facts['firewall'] = 'Not available'

    # Get failed SSH login attempts
    try:
        result = subprocess.run(['journalctl', '-u', 'sshd', '--since', '24h', 
                               'grep', 'Failed', 'wc', '-l'], 
                              capture_output=True, text=True, check=True)
        facts['failed_ssh_attempts'] = int(result.stdout.strip())
    except (subprocess.CalledProcessError, FileNotFoundError):
        facts['failed_ssh_attempts'] = 0

    return facts

def main():
    """Main module execution"""
    module = AnsibleModule(
        argument_spec=dict(
            gather=dict(type='list', 
                       elements='str',
                       choices=['network', 'storage', 'security'],
                       default=['network'])
        ),
        supports_check_mode=True
    )

    gather_categories = module.params['gather']

    if module.check_mode:
        module.exit_json(changed=False, 
                        msg="Would gather facts: " + ", ".join(gather_categories))

    result = {
        'changed': False,
        'ansible_facts': {
            'custom': {}
        }
    }

    # Gather requested facts
    if 'network' in gather_categories:
        result['ansible_facts']['custom']['network'] = gather_network_facts()

    if 'storage' in gather_categories:
        result['ansible_facts']['custom']['storage'] = gather_storage_facts()

    if 'security' in gather_categories:
        result['ansible_facts']['custom']['security'] = gather_security_facts()

    module.exit_json(**result)

if __name__ == '__main__':
    main()

Custom filter plugin - filter_plugins/custom_filters.py:

#!/usr/bin/python3
# -*- coding: utf-8 -*-

class FilterModule(object):
    def filters(self):
        return {
            'custom_filter': self.custom_filter,
            'to_k8s_name': self.to_k8s_name,
            'parse_java_version': self.parse_java_version,
            'calculate_resources': self.calculate_resources
        }

    def custom_filter(self, value, param):
        """Example custom filter"""
        return f"Filtered {value} with {param}"

    def to_k8s_name(self, value):
        """Convert string to Kubernetes compliant name"""
        import re
        # Convert to lowercase
        value = value.lower()
        # Replace invalid characters with hyphen
        value = re.sub(r'[^a-z0-9-]', '-', value)
        # Remove leading/trailing hyphens
        value = value.strip('-')
        # Ensure max length of 63 characters
        return value[:63]

    def parse_java_version(self, version_string):
        """Parse Java version string into dict"""
        import re
        pattern = r'version "(\d+)\.(\d+)\.(\d+)'
        match = re.search(pattern, version_string)
        if match:
            return {
                'major': int(match.group(1)),
                'minor': int(match.group(2)),
                'patch': int(match.group(3))
            }
        return None

    def calculate_resources(self, replicas, cpu_per_pod, memory_per_pod):
        """Calculate total resources needed"""
        return {
            'total_cpu_cores': replicas * cpu_per_pod,
            'total_memory_mb': replicas * memory_per_pod
        }

Custom callback plugin - callback_plugins/custom_callback.py:

#!/usr/bin/python3
# -*- coding: utf-8 -*-

from ansible.plugins.callback import CallbackBase
import json
import time

class CallbackModule(CallbackBase):
    CALLBACK_VERSION = 2.0
    CALLBACK_TYPE = 'notification'
    CALLBACK_NAME = 'custom_callback'
    CALLBACK_NEEDS_WHITELIST = True

    def __init__(self):
        super(CallbackModule, self).__init__()
        self.start_time = time.time()
        self.stats = {
            'tasks': [],
            'hosts': {},
            'summary': {
                'ok': 0,
                'changed': 0,
                'failed': 0,
                'skipped': 0,
                'unreachable': 0
            }
        }

    def v2_playbook_on_start(self, playbook):
        self._display.display(f"🔵 Starting playbook: {playbook._file_name}")

    def v2_playbook_on_play_start(self, play):
        self._display.display(f"🟢 Starting play: {play.get_name()}")

    def v2_runner_on_ok(self, result):
        self.stats['summary']['ok'] += 1
        self._log_task(result, 'ok')

    def v2_runner_on_failed(self, result, ignore_errors=False):
        self.stats['summary']['failed'] += 1
        self._log_task(result, 'failed')

        # Custom notification on failure
        if not ignore_errors:
            self._send_slack_notification(result)

    def v2_runner_on_changed(self, result):
        self.stats['summary']['changed'] += 1
        self._display.display(f"  📝 Changed: {result.task_name}")

    def v2_playbook_on_stats(self, stats):
        end_time = time.time()
        duration = end_time - self.start_time

        self._display.banner("CUSTOM CALLBACK SUMMARY")

        # Print custom summary
        summary = {
            'duration_seconds': round(duration, 2),
            'tasks_executed': len(self.stats['tasks']),
            'status': self.stats['summary']
        }

        self._display.display(json.dumps(summary, indent=2))

        # Save to file
        with open('/tmp/ansible-stats.json', 'w') as f:
            json.dump(self.stats, f, indent=2)

        self._display.display(f"💾 Stats saved to /tmp/ansible-stats.json")

    def _log_task(self, result, status):
        task_data = {
            'task': result.task_name,
            'host': result._host.get_name(),
            'status': status,
            'duration': result._task_fields.get('duration', 0)
        }
        self.stats['tasks'].append(task_data)

        if result._host.get_name() not in self.stats['hosts']:
            self.stats['hosts'][result._host.get_name()] = []

        self.stats['hosts'][result._host.get_name()].append(task_data)

    def _send_slack_notification(self, result):
        """Send notification on task failure"""
        try:
            import requests

            webhook_url = "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"

            message = {
                "text": "Ansible Task Failed",
                "attachments": [{
                    "color": "danger",
                    "fields": [
                        {
                            "title": "Task",
                            "value": result.task_name,
                            "short": True
                        },
                        {
                            "title": "Host",
                            "value": result._host.get_name(),
                            "short": True
                        },
                        {
                            "title": "Error",
                            "value": str(result._result.get('msg', 'Unknown error')),
                            "short": False
                        }
                    ]
                }]
            }

            requests.post(webhook_url, json=message)
        except Exception as e:
            self._display.warning(f"Failed to send Slack notification: {e}")

Playbook using custom plugins - Save as use_custom_plugins.yml:

---
- name: Use custom module and filters
  hosts: appservers
  become: yes
  vars:
    app_name: "MyApp_Production"

  tasks:
    # Use custom module
    - name: Gather custom facts
      custom_facts:
        gather:
          - network
          - storage
          - security
      register: custom_facts_result

    - name: Display custom network facts
      debug:
        var: ansible_facts.custom.network.interfaces

    # Use custom filters
    - name: Convert app name to K8s compliant name
      set_fact:
        k8s_app_name: "{{ app_name | to_k8s_name }}"

    - name: Show K8s name
      debug:
        msg: "K8s compliant name: {{ k8s_app_name }}"

    - name: Parse Java version
      command: java -version
      register: java_version_output
      failed_when: false

    - name: Extract Java version info
      set_fact:
        java_version: "{{ java_version_output.stderr | parse_java_version }}"
      when: java_version_output.rc == 0

    - name: Show Java version
      debug:
        var: java_version

    - name: Calculate resource requirements
      set_fact:
        resources: "{{ 5 | calculate_resources(0.5, 512) }}"
      vars:
        replicas: 5
        cpu_per_pod: 0.5
        memory_per_pod: 512

    - name: Display resource calculation
      debug:
        msg: "Total resources needed: {{ resources.total_cpu_cores }} cores, {{ resources.total_memory_mb }} MB"

    # Use custom callback will automatically trigger
    - name: Simulate failure for callback testing
      command: /bin/false
      ignore_errors: yes

    - name: Successful task
      command: echo "Success"
      register: success_task

    - name: Use custom filter with parameters
      set_fact:
        filtered_value: "{{ success_task.stdout | custom_filter('test_param') }}"

    - name: Show filtered result
      debug:
        var: filtered_value

    - name: Write stats to file
      copy:
        content: "{{ ansible_facts.custom | to_nice_json }}"
        dest: /tmp/custom_facts.json
        mode: '0644'

# Enable custom callback in ansible.cfg
# [defaults]
# callback_whitelist = custom_callback
# filter_plugins = filter_plugins/
# library = library/
# callback_plugins = callback_plugins/


Scenario 19: Ansible AWX/Tower Integration

Description: Manage enterprise Ansible automation with AWX/Tower for job scheduling, RBAC, and audit trails.

sequenceDiagram
    participant User as DevOps Engineer
    participant AWX as AWX/Tower
    participant Git as Git Repository
    participant Inventor as AWX Inventory
    participant Job as AWX Job Template
    participant Notification as Notification System

    User->>Git: Push updated playbook
    Git->>AWX: Webhook triggers project sync

    AWX->>Git: Pull latest changes
    Note over AWX: Project synced

    User->>AWX: Create application credential

    User->>AWX: Configure dynamic inventory
    AWX->>Inventor: Sync AWS EC2 instances
    Inventor-->>AWX: Dynamic inventory groups

    User->>AWX: Create job template
    Job->>AWX: Configure playbook, inventory, credentials
    Job->>AWX: Set survey variables

    User->>AWX: Launch job
    AWX->>Job: Execute on target hosts

    loop Job execution
        Job->>Host: Run tasks
        Host-->>Job: Return results
        Job->>AWX: Update job status
    end

    AWX->>Notification: Send success/failure notification
    Notification-->>User: Slack/Email notification

    AWX->>AWX: Store job logs for audit

    Note over AWX: RBAC, scheduling, and audit trails

AWX Configuration as Code - Save as awx_setup.yml:

---
- name: Configure AWX/Tower resources
  hosts: localhost
  connection: local
  gather_facts: no
  vars:
    awx_host: https://awx.example.com
    awx_token: "{{ vault_awx_token }}"
    organization_name: "Production"
    project_name: "Ansible Playbooks"
    inventory_name: "AWS Production"
    credential_name: "AWS SSH Key"
    job_template_name: "Deploy Application"

  tasks:
    # Create organization
    - name: Create organization
      awx.awx.organization:
        name: "{{ organization_name }}"
        description: "Production Organization"
        state: present
        tower_host: "{{ awx_host }}"
        tower_oauthtoken: "{{ awx_token }}"
        validate_certs: no

    # Create credential type for SSH key
    - name: Create machine credential
      awx.awx.credential:
        name: "{{ credential_name }}"
        description: "SSH key for production servers"
        organization: "{{ organization_name }}"
        credential_type: "Machine"
        inputs:
          username: "ansible"
          ssh_key_data: "{{ lookup('file', '~/.ssh/awx_rsa') }}"
        state: present
        tower_host: "{{ awx_host }}"
        tower_oauthtoken: "{{ awx_token }}"
        validate_certs: no

    # Create AWS credentials
    - name: Create AWS credential
      awx.awx.credential:
        name: "AWS Credentials"
        description: "AWS API access"
        organization: "{{ organization_name }}"
        credential_type: "Amazon Web Services"
        inputs:
          username: "{{ vault_aws_access_key }}"
          password: "{{ vault_aws_secret_key }}"
        state: present
        tower_host: "{{ awx_host }}"
        tower_oauthtoken: "{{ awx_token }}"
        validate_certs: no

    # Create project from Git
    - name: Create project
      awx.awx.project:
        name: "{{ project_name }}"
        description: "Main Ansible playbooks repository"
        organization: "{{ organization_name }}"
        scm_type: git
        scm_url: "https://github.com/myorg/ansible-playbooks.git"
        scm_branch: main
        scm_update_on_launch: yes
        scm_credential: "Git Credentials"
        state: present
        tower_host: "{{ awx_host }}"
        tower_oauthtoken: "{{ awx_token }}"
        validate_certs: no

    # Create dynamic inventory from AWS
    - name: Create inventory
      awx.awx.inventory:
        name: "{{ inventory_name }}"
        description: "AWS EC2 dynamic inventory"
        organization: "{{ organization_name }}"
        state: present
        tower_host: "{{ awx_host }}"
        tower_oauthtoken: "{{ awx_token }}"
        validate_certs: no

    - name: Add AWS source to inventory
      awx.awx.inventory_source:
        name: "AWS EC2 Source"
        inventory: "{{ inventory_name }}"
        source: ec2
        source_vars:
          regions:
            - us-east-1
            - us-west-2
          filters:
            instance-state-name: running
        update_on_launch: yes
        update_cache_timeout: 300
        state: present
        tower_host: "{{ awx_host }}"
        tower_oauthtoken: "{{ awx_token }}"
        validate_certs: no

    # Create job template
    - name: Create job template
      awx.awx.job_template:
        name: "{{ job_template_name }}"
        description: "Deploy application to production"
        job_type: run
        inventory: "{{ inventory_name }}"
        project: "{{ project_name }}"
        playbook: "playbooks/deploy-app.yml"
        credentials:
          - "{{ credential_name }}"
          - "AWS Credentials"
        forks: 20
        limit: "env_production"
        verbosity: 1
        extra_vars:
          app_version: "latest"
          deploy_environment: "production"
        survey_enabled: yes
        survey_spec:
          name: "Deployment Parameters"
          description: "Parameters for deployment"
          spec:
            - type: "text"
              question_name: "Application Version"
              variable: "app_version"
              required: yes
              default: "latest"
            - type: "multiplechoice"
              question_name: "Deploy Environment"
              variable: "deploy_environment"
              choices: ["production", "staging"]
              required: yes
              default: "production"
        state: present
        tower_host: "{{ awx_host }}"
        tower_oauthtoken: "{{ awx_token }}"
        validate_certs: no

    # Create schedule
    - name: Create schedule for job template
      awx.awx.schedule:
        name: "Daily Security Updates"
        job_template: "{{ job_template_name }}"
        rrule: "DTSTART:20240101T020000Z RRULE:FREQ=DAILY;INTERVAL=1"
        description: "Run security updates daily at 2 AM UTC"
        enabled: yes
        state: present
        tower_host: "{{ awx_host }}"
        tower_oauthtoken: "{{ awx_token }}"
        validate_certs: no

    # Create notification template
    - name: Create Slack notification template
      awx.awx.notification_template:
        name: "Slack Deployment Notifications"
        organization: "{{ organization_name }}"
        notification_type: slack
        notification_configuration:
          token: "{{ vault_slack_token }}"
          channels:
            - "#deployments"
          username: "AWX Bot"
        messages:
          started:
            message: "Deployment started"
          success:
            message: "Deployment succeeded"
          error:
            message: "Deployment failed"
        state: present
        tower_host: "{{ awx_host }}"
        tower_oauthtoken: "{{ awx_token }}"
        validate_certs: no

    # Associate notification with job template
    - name: Associate notification with job template
      awx.awx.job_template:
        name: "{{ job_template_name }}"
        notification_templates_success:
          - "Slack Deployment Notifications"
        notification_templates_error:
          - "Slack Deployment Notifications"
        state: present
        tower_host: "{{ awx_host }}"
        tower_oauthtoken: "{{ awx_token }}"
        validate_certs: no

    # Create workflow template
    - name: Create deployment workflow
      awx.awx.workflow_job_template:
        name: "Full Deployment Workflow"
        description: "Build, test, and deploy application"
        organization: "{{ organization_name }}"
        state: present
        tower_host: "{{ awx_host }}"
        tower_oauthtoken: "{{ awx_token }}"
        validate_certs: no

    - name: Add workflow nodes
      awx.awx.workflow_job_template_node:
        identifier: "build_stage"
        workflow_job_template: "Full Deployment Workflow"
        unified_job_template: "Build Docker Image"
        state: present
        tower_host: "{{ awx_host }}"
        tower_oauthtoken: "{{ awx_token }}"
        validate_certs: no

Launch job via API - launch_awx_job.yml:

---
- name: Launch AWX job from CLI
  hosts: localhost
  connection: local
  gather_facts: no
  vars:
    awx_host: https://awx.example.com
    awx_token: "{{ vault_awx_token }}"
    job_template_id: 20
    extra_vars:
      app_version: "{{ version | default('latest') }}"
      deploy_environment: "{{ env | default('staging') }}"

  tasks:
    - name: Launch job template
      awx.awx.job_launch:
        job_template: "{{ job_template_id }}"
        extra_vars: "{{ extra_vars | to_json }}"
        wait: yes
        tower_host: "{{ awx_host }}"
        tower_oauthtoken: "{{ awx_token }}"
        validate_certs: no
      register: job_status

    - name: Display job status
      debug:
        var: job_status

    - name: Wait for job completion
      awx.awx.job_wait:
        job_id: "{{ job_status.id }}"
        timeout: 1800
        interval: 30
        tower_host: "{{ awx_host }}"
        tower_oauthtoken: "{{ awx_token }}"
        validate_certs: no
      register: job_result

    - name: Show job result
      debug:
        msg: "Job completed with status: {{ job_result.status }}"

AWX CLI usage:

# Install AWX CLI
pip install awxkit

# Configure AWX CLI
export TOWER_HOST=https://awx.example.com
export TOWER_TOKEN=your-oauth-token

# List resources
awx projects list
awx job_templates list
awx inventories list

# Launch job
awx job launch --job_template="Deploy Application" \
  --extra_vars="app_version=2.0.0 deploy_environment=production"

# Monitor job
awx job list --status=running
awx job stdout --id=123

# Export/Import configuration
awx export --organization "Production" > awx-config.json
awx import < awx-config.json


Scenario 20: Enterprise-Scale Ansible & Performance Tuning

Description: Deploy Ansible at enterprise scale with performance optimization, capacity planning, and high availability patterns.

sequenceDiagram
    participant Load as Load Balancer
    participant Ansible1 as Ansible Controller 1
    participant Ansible2 as Ansible Controller 2
    participant Redis as Redis Cache
    participant Database as AWX Database
    participant Worker1 as Worker Node 1
    participant Worker2 as Worker Node 2
    participant Worker3 as Worker Node 3

    User->>Load: Submit job request

    Load->>Ansible1: Route request (active-active)

    Ansible1->>Ansible1: Parse playbook
    Ansible1->>Redis: Check fact cache

    Redis-->>Ansible1: Return cached facts

    Ansible1->>Ansible1: Generate execution plan

    loop Parallel execution (forks=100)
        Ansible1->>Worker1: Execute tasks (subset of hosts)
        Ansible1->>Worker2: Execute tasks (subset of hosts)
        Ansible1->>Worker3: Execute tasks (subset of hosts)

        Worker1-->>Ansible1: Return results
        Worker2-->>Ansible1: Return results
        Worker3-->>Ansible1: Return results
    end

    Ansible1->>Redis: Update fact cache
    Ansible1->>Database: Store job results

    LoadBalancer->>Ansible2: Health check
    Ansible2-->>LoadBalancer: Healthy (standby)

    Note over Ansible1,Ansible2: HA setup with Redis + DB

Enterprise ansible.cfg - /etc/ansible/ansible.cfg:

[defaults]
# Performance tuning
forks = 500
timeout = 30
poll_interval = 5
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /var/lib/ansible/facts
fact_caching_timeout = 86400

# Paths
inventory = /etc/ansible/inventories
library = /usr/share/ansible/plugins/modules
module_utils = /usr/share/ansible/plugins/module_utils
action_plugins = /usr/share/ansible/plugins/action
callback_plugins = /usr/share/ansible/plugins/callback
connection_plugins = /usr/share/ansible/plugins/connection
lookup_plugins = /usr/share/ansible/plugins/lookup
vars_plugins = /usr/share/ansible/plugins/vars
filter_plugins = /usr/share/ansible/plugins/filter
test_plugins = /usr/share/ansible/plugins/test
strategy_plugins = /usr/share/ansible/plugins/strategy

# Strategy
strategy = mitogen_linear
mitogen_forks = 500

# Fact filters
fact_filter = ansible_local, ansible_env, ansible_cmdline

# SSH optimization
transport = smart
remote_port = 22
scp_if_ssh = smart
pipelining = True
ssh_executable = ssh
ssh_args = -o ControlMaster=auto -o ControlPersist=600s -o StrictHostKeyChecking=no

# Privilege escalation
become = True
become_method = sudo
become_user = root
become_ask_pass = False

# Logging
log_path = /var/log/ansible/ansible.log
log_filter = shred
display_args_to_stdout = False

# Execution
any_errors_fatal = False
max_fail_percentage = 10
force_handlers = True
module_compression = 'ZIP_STORED'
module_set_locale = True

# Vault
vault_identity_list = prod@/etc/ansible/vault_pass_prod, dev@/etc/ansible/vault_pass_dev

# Galaxy
roles_path = /etc/ansible/roles:/usr/share/ansible/roles
collections_path = /etc/ansible/collections:/usr/share/ansible/collections
cache_plugins = yes

[inventory]
# Cache inventory
cache = yes
cache_connection = /var/lib/ansible/inventory
cache_timeout = 1800
cache_plugin = jsonfile

[privilege_escalation]
# Becomes settings
flags = -H -S -n
su_exe = su
sudo_exe = sudo
sudo_flags = -H -S -n

[ssh_connection]
# Performance
retries = 3
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=600s
control_path_dir = /tmp/.ansible/cp
control_path = %(directory)s/%%h-%%p-%%r
scp_if_ssh = True
sftp_batch_mode = True
transfer_method = smart

[galaxy]
# Galaxy settings
server_list = galaxy, automation_hub

[galaxy_server.galaxy]
url = https://galaxy.ansible.com/

[galaxy_server.automation_hub]
url = https://console.redhat.com/api/automation-hub/
auth_url = https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token
token = your_api_token_here

[persistent_connection]
# For network devices
command_timeout = 60
connect_timeout = 30
connect_retry_timeout = 15

# Network CLI
[netconf_connection]
# NETCONF settings
ssh_keyfile = /etc/ansible/ssh/keys/netconf_key

Capacity planning playbook - capacity_planning.yml:

---
- name: Ansible controller capacity planning
  hosts: localhost
  connection: local
  gather_facts: yes
  vars:
    recommended_forks_per_core: 50
    recommended_open_files: 10000

  tasks:
    - name: Gather controller facts
      setup:
        gather_subset:
          - hardware
          - network

    - name: Calculate recommended settings
      set_fact:
        cpu_cores: "{{ ansible_processor_vcpus }}"
        total_memory_mb: "{{ ansible_memtotal_mb }}"
        recommended_forks: "{{ ansible_processor_vcpus * recommended_forks_per_core }}"
        current_ulimit: "{{ ansible_rlimit_nofile.cur }}"
        recommended_ulimit: "{{ recommended_open_files }}"

    - name: Display capacity recommendations
      debug:
        msg: |
          ==================== CAPACITY PLANNING ====================
          Controller Host: {{ inventory_hostname }}
          CPU Cores: {{ cpu_cores }}
          Total Memory: {{ total_memory_mb }} MB

          RECOMMENDED SETTINGS:
          Forks ({{ recommended_forks_per_core }} per core): {{ recommended_forks }}
          Open files limit: {{ recommended_ulimit }} (current: {{ current_ulimit }})

          PERFORMANCE TIPS:
          - Use smart fact gathering (already configured)
          - Enable fact caching (already configured)
          - Use pipelining (already configured)
          - Use strategy plugins (mitogen)
          - Disable host key checking (controlled)
          ==========================================================

    - name: Check current ulimits
      command: ulimit -n
      register: current_ulimit_check

    - name: Verify system tuning
      assert:
        that:
          - current_ulimit|int >= recommended_open_files|int
        fail_msg: "Open files limit too low. Run: ulimit -n {{ recommended_open_files }}"

    - name: Generate sysctl recommendations
      copy:
        dest: /tmp/ansible_sysctl.conf
        content: |
          # Ansible Controller Performance Tuning
          # Add to /etc/sysctl.conf

          # Increase network connection tracking
          net.nf_conntrack_max = 2000000

          # Increase port range
          net.ipv4.ip_local_port_range = 1024 65535

          # TCP tuning
          net.ipv4.tcp_tw_reuse = 1
          net.ipv4.tcp_fin_timeout = 30

          # Increase file descriptors
          fs.file-max = 2000000

          # Memory tuning
          vm.swappiness = 10

    - name: Monitor disk space for logs
      stat:
        path: /var/log/ansible
      register: log_dir

    - name: Calculate log rotation needs
      debug:
        msg: |
          Ansible log directory size: {{ (log_dir.stat.size / 1024 / 1024) | round(2) }} MB
          Recommendation: Configure logrotate with maxsize=500M, rotate=10

    - name: Check network latency to managed hosts
      wait_for_connection:
        timeout: 5
      delegate_to: "{{ item }}"
      loop: "{{ groups['all'][:10] }}"  # Sample 10 hosts
      register: latency_check

    - name: Calculate average latency
      set_fact:
        avg_latency: "{{ (latency_check.results | map(attribute='elapsed') | map('float') | sum / latency_check.results | length) | round(3) }}"

    - name: Display latency metrics
      debug:
        msg: |
          Average connection latency: {{ avg_latency }} seconds
          If > 1s: Consider local execution or async strategies

    - name: Generate capacity report
      copy:
        dest: "/tmp/ansible-capacity-report-{{ ansible_date_time.date }}.json"
        content: |
          {
            "controller": "{{ inventory_hostname }}",
            "timestamp": "{{ ansible_date_time.iso8601 }}",
            "hardware": {
              "cpu_cores": {{ cpu_cores }},
              "memory_mb": {{ total_memory_mb }}
            },
            "recommendations": {
              "forks": {{ recommended_forks }},
              "open_files": {{ recommended_ulimit }},
              "avg_latency_s": {{ avg_latency }}
            },
            "status": "optimal" if {{ recommended_forks|int <= 1000 }} else "scaling_needed"
          }

# High availability setup for controllers - ha_setup.yml
---
- name: Setup HA Ansible controllers
  hosts: ansible_controllers
  become: yes
  vars:
    redis_host: redis.example.com
    postgres_host: postgres.example.com

  tasks:
    - name: Install HA components
      apt:
        name:
          - redis-tools
          - postgresql-client
          - haproxy
          - keepalived

    - name: Configure Redis fact cache
      lineinfile:
        path: /etc/ansible/ansible.cfg
        line: "{{ item }}"
      loop:
        - "fact_caching = redis"
        - "fact_caching_connection = redis://{{ redis_host }}:6379/0"
        - "fact_caching_timeout = 86400"

    - name: Configure shared project directory
      file:
        path: /opt/ansible/projects
        state: directory
        mode: '0755'

    - name: Mount NFS share for projects
      mount:
        path: /opt/ansible/projects
        src: "nfs-server:/export/ansible"
        fstype: nfs
        state: mounted

    - name: Configure HAProxy for load balancing
      template:
        src: templates/haproxy.cfg.j2
        dest: /etc/haproxy/haproxy.cfg
      notify: Reload HAProxy
      vars:
        backend_servers: "{{ groups['ansible_controllers'] }}"
        port: 8080

    - name: Enable HAKeepalived for failover
      template:
        src: templates/keepalived.conf.j2
        dest: /etc/keepalived/keepalived.conf
      notify: Restart Keepalived
      vars:
        vip: "10.0.0.100"
        priority: "{{ 100 - play_hosts.index(inventory_hostname) }}"

    - name: Configure log aggregation
      copy:
        dest: /etc/rsyslog.d/50-ansible.conf
        content: |
          local0.* /var/log/ansible/ansible.log
          & stop

    - name: Restart rsyslog
      service:
        name: rsyslog
        state: restarted

    # Configure systemd service for ansible-runner
    - name: Create ansible-runner service
      copy:
        dest: /etc/systemd/system/ansible-runner@.service
        content: |
          [Unit]
          Description=Ansible Runner %i
          After=network.target

          [Service]
          Type=simple
          User=ansible
          WorkingDirectory=/opt/ansible/projects/%i
          ExecStart=/usr/bin/ansible-runner run .
          Restart=always

          [Install]
          WantedBy=multi-user.target

    - name: Enable ansible-runner services
      systemd:
        name: ansible-runner@prod
        enabled: yes
        state: started

  handlers:
    - name: Reload HAProxy
      service:
        name: haproxy
        state: reloaded

    - name: Restart Keepalived
      service:
        name: keepalived
        state: restarted

Performance monitoring playbook - monitor_performance.yml:

---
- name: Monitor Ansible controller performance
  hosts: ansible_controllers
  become: yes
  vars:
    alert_threshold_cpu: 80
    alert_threshold_memory: 85
    alert_threshold_disk: 90

  tasks:
    - name: Gather performance metrics
      setup:
        gather_subset:
          - hardware
          - network

    - name: Get Ansible process info
      command: ps -C ansible-playbook -o pid,ppid,%cpu,%mem,cmd --no-headers
      register: ansible_processes
      failed_when: false

    - name: Count active ansible processes
      set_fact:
        active_processes: "{{ ansible_processes.stdout_lines | length }}"
        cpu_usage: "{{ ansible_processor_utilization | default(0) }}"
        memory_usage: "{{ (1 - (ansible_memfree_mb / ansible_memtotal_mb)) * 100 }}"
        disk_usage: "{{ (ansible_mounts[0].size_total - ansible_mounts[0].size_available) / ansible_mounts[0].size_total * 100 }}"

    - name: Check thresholds
      set_fact:
        alerts: "{{ alerts | default([]) + [item.message] }}"
      loop:
        - { condition: cpu_usage > alert_threshold_cpu, message: "High CPU usage: {{ cpu_usage }}%" }
        - { condition: memory_usage > alert_threshold_memory, message: "High memory usage: {{ memory_usage }}%" }
        - { condition: disk_usage > alert_threshold_disk, message: "High disk usage: {{ disk_usage }}%" }
        - { condition: active_processes > 50, message: "Too many active processes: {{ active_processes }}" }
      when: item.condition

    - name: Send alerts if thresholds exceeded
      uri:
        url: "{{ monitoring_webhook_url }}"
        method: POST
        body_format: json
        body:
          alert_type: "ansible_controller"
          hostname: "{{ inventory_hostname }}"
          severity: "warning" if alerts|length < 3 else "critical"
          message: "{{ alerts | join('; ') }}"
      when: alerts is defined and alerts|length > 0

    - name: Generate performance report
      copy:
        dest: "/var/log/ansible/performance-report-{{ ansible_date_time.epoch }}.json"
        content: |
          {
            "timestamp": "{{ ansible_date_time.iso8601 }}",
            "hostname": "{{ inventory_hostname }}",
            "performance": {
              "cpu_usage_percent": {{ cpu_usage }},
              "memory_usage_percent": {{ memory_usage }},
              "disk_usage_percent": {{ disk_usage }},
              "active_processes": {{ active_processes }},
              "forks_configured": {{ forks }},
              "load_average": "{{ ansible_loadavg['15m'] }}"
            },
            "recommendations": {{ recommendations if recommendations is defined else '[]' }}
          }

Quick reference for enterprise Ansible:

Command Description Enterprise Impact
ansible-playbook -f 500 --timeout 30 High parallel execution Reduces execution time by 80%
ANSIBLE_CALLBACK_WHITELIST=profile_tasks,timer Performance profiling Identifies slow tasks
strategy = mitogen_linear Alternative execution engine 2-7x faster than linear
fact_caching = redis Shared fact cache for HA Consistent facts across controllers
forks = 500 Maximum parallel processes Requires kernel tuning
pipelining = True Reduce SSH connections 30% faster execution
serial: 5 Rolling deployment Zero-downtime deployments
max_fail_percentage: 10 Allow partial failures Increases success rate
any_errors_fatal: False Continue on errors Better fault tolerance
throttle: 1 Rate limiting API protection

Pro Tips for Enterprise-Scale Ansible:

  1. Always use Ansible Automation Platform (AWX/Tower) for centralized management
  2. Configure Redis fact caching for multi-controller setups
  3. Enable log aggregation with ELK or Splunk
  4. Use systemd service for ansible-runner instead of ad-hoc runs
  5. Implement health checks for controllers and workers
  6. Configure firewall rules for controller-to-host communication
  7. Use certificate-based SSH instead of passwords
  8. Implement backup/restore for AWX database
  9. Monitor with Prometheus/Grafana for metrics
  10. Use load balancers for HA setup
  11. Configure log rotation to prevent disk full
  12. Implement RBAC with LDAP/AD integration
  13. Use workflow templates for complex deployments
  14. Schedule periodic jobs for configuration drift detection
  15. Set resource quotas for job execution environments

Happy Learning 🌟