Operations 39 min read

Master Ansible Automation: From Beginner to Expert with a Complete Project Walkthrough

This comprehensive guide walks you through every aspect of Ansible configuration automation—from basic concepts and installation on various Linux distributions to advanced topics like inventory management, playbook design, modules, roles, Vault encryption, CI/CD integration, troubleshooting, security hardening, and real‑world enterprise deployment examples—equipping you to become an Ansible expert.

Ops Community
Ops Community
Ops Community
Master Ansible Automation: From Beginner to Expert with a Complete Project Walkthrough

From Zero to Expert: Ansible Configuration Automation Full Guide (with Complete Project Example)

1. Introduction

In modern IT operations, configuration management automation is essential for improving efficiency, ensuring consistency, and reducing human error. Ansible, an open‑source configuration management tool, is popular among ops engineers because it is simple to use and agent‑less. This article provides a complete overview of Ansible automation, covering basic concepts to advanced usage, helping engineers master this powerful tool.

2. Ansible Basics

2.1 What is Ansible?

Ansible is an open‑source automation platform for configuration management, application deployment, task automation, and IT orchestration. It uses a agent‑less architecture, connects to target hosts via SSH, and defines playbooks in YAML, offering low learning cost and simple deployment.

2.2 Core Components

Control Node

Machine where Ansible is installed

Executes playbooks and ad‑hoc commands

Manages the inventory file

Managed Node

Target machines managed by Ansible

SSH service must be enabled

Usually requires a Python environment

Inventory

Defines the list of managed hosts

Supports static and dynamic inventories

Allows host and group variables

Playbook

YAML configuration files

Describe the execution flow of automation tasks

Contain one or more plays

Module

Execution units of Ansible

Perform specific task operations

Include thousands of built‑in modules

Plugin

Components that extend Ansible functionality

Include connection plugins, filter plugins, etc.

3. Installation and Configuration

3.1 Install Ansible

CentOS/RHEL

# Use EPEL repository
yum install epel-release -y
yum install ansible -y

# Or install via pip
pip install ansible

Ubuntu/Debian

# Add PPA repository
sudo apt-add-repository ppa:ansible/ansible
sudo apt update
sudo apt install ansible -y

# Or install via pip
pip install ansible

Verify Installation

ansible --version

3.2 Configuration File Structure

Ansible searches for configuration files in the following order: $ANSIBLE_CONFIG environment variable ansible.cfg in the current directory ~/.ansible.cfg in the user’s home directory /etc/ansible/ansible.cfg system configuration file

Key configuration items (example):

[defaults]
# Default inventory file path
inventory = /etc/ansible/hosts
# Remote user
remote_user = root
# SSH private key
private_key_file = ~/.ssh/id_rsa
# Disable host key checking
host_key_checking = False
# Number of parallel forks
forks = 20
# Timeout (seconds)
timeout = 30
# Log file
log_path = /var/log/ansible.log

[ssh_connection]
# SSH arguments
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
# Enable pipelining
pipelining = True

4. Inventory Management

4.1 Static Inventory

Basic Format

# Single host
web1.example.com

# Host group
[webservers]
web1.example.com
web2.example.com
web3.example.com

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

# Host variables
[webservers]
web1.example.com http_port=80 maxRequestsPerChild=808
web2.example.com http_port=8080 maxRequestsPerChild=909

# Group variables
[webservers:vars]
ntp_server=ntp.example.com
proxy=proxy.example.com

4.2 Host Ranges

# Numeric range
[webservers]
web[01:50].example.com

# Alphabetic range
[databases]
db[a:f].example.com

# Multiple ranges
[servers]
server[01:20][1:3].example

4.3 Dynamic Inventory

Example: AWS EC2 dynamic inventory script (Python)

#!/usr/bin/env python3
import boto3, json

def get_ec2_instances():
    ec2 = boto3.client('ec2')
    response = ec2.describe_instances()
    inventory = {
        '_meta': {'hostvars': {}},
        'all': {'hosts': []},
        'webservers': {'hosts': []},
        'databases': {'hosts': []}
    }
    for reservation in response['Reservations']:
        for instance in reservation['Instances']:
            if instance['State']['Name'] == 'running':
                public_ip = instance.get('PublicIpAddress')
                if public_ip:
                    inventory['all']['hosts'].append(public_ip)
                for tag in instance.get('Tags', []):
                    if tag['Key'] == 'Role':
                        role = tag['Value']
                        if role not in inventory:
                            inventory[role] = {'hosts': []}
                        inventory[role]['hosts'].append(public_ip)
    return inventory

if __name__ == '__main__':
    print(json.dumps(get_ec2_instances(), indent=2))

5. Ad‑hoc Commands

5.1 Basic Syntax

ansible <pattern> -m <module> -a <arguments>

5.2 Common Ad‑hoc Commands

Execute Shell Command

# Run on all hosts
ansible all -m shell -a "uptime"

# Run on a specific group
ansible webservers -m shell -a "systemctl status nginx"

# Use sudo privileges
ansible all -m shell -a "systemctl restart nginx" --become

File Operations

# Copy file
ansible all -m copy -a "src=/tmp/test.txt dest=/tmp/test.txt"

# Create directory
ansible all -m file -a "path=/tmp/testdir state=directory"

# Change file permissions
ansible all -m file -a "path=/tmp/test.txt mode=0644"

Package Management

# Install package
ansible all -m yum -a "name=nginx state=present"

# Remove package
ansible all -m yum -a "name=nginx state=absent"

# Update all packages
ansible all -m yum -a "name=* state=latest"

Service Management

# Start service
ansible all -m service -a "name=nginx state=started"

# Stop service
ansible all -m service -a "name=nginx state=stopped"

# Restart service
ansible all -m service -a "name=nginx state=restarted"

6. Writing Playbooks

6.1 Basic Structure

---
- name: Configure Web Server
  hosts: webservers
  become: yes
  vars:
    http_port: 80
    max_clients: 200
  tasks:
    - name: Install Nginx
      yum:
        name: nginx
        state: present
    - name: Start Nginx service
      service:
        name: nginx
        state: started
        enabled: yes

6.2 Using Variables

---
- name: Variable Example
  hosts: all
  vars:
    packages:
      - nginx
      - mysql
      - php
    user_info:
      name: webapp
      group: webapp
      shell: /bin/bash
  tasks:
    - name: Install packages
      yum:
        name: "{{ packages }}"
        state: present
    - name: Create user
      user:
        name: "{{ user_info.name }}"
        group: "{{ user_info.group }}"
        shell: "{{ user_info.shell }}"

6.3 Conditional Execution

---
- name: Conditional Example
  hosts: all
  tasks:
    - name: Install Apache on RedHat
      yum:
        name: httpd
        state: present
      when: ansible_os_family == "RedHat"

    - name: Install Apache on Debian
      apt:
        name: apache2
        state: present
      when: ansible_os_family == "Debian"

    - name: Check disk space
      shell: df -h /
      register: disk_usage

    - name: Warn if disk usage > 90%
      debug:
        msg: "Disk space low, please clean up"
      when: "'90%' in disk_usage.stdout"

6.4 Loops

---
- name: Loop Example
  hosts: all
  tasks:
    - name: Install multiple packages
      yum:
        name: "{{ item }}"
        state: present
      loop:
        - nginx
        - mysql
        - php
        - redis

    - name: Create multiple users
      user:
        name: "{{ item.name }}"
        group: "{{ item.group }}"
        state: present
      loop:
        - { name: 'user1', group: 'web' }
        - { name: 'user2', group: 'db' }
        - { name: 'user3', group: 'app' }

    - name: Process dictionary loop
      debug:
        msg: "{{ item.key }}: {{ item.value }}"
      loop: "{{ lookup('dict', {'a': 1, 'b': 2, 'c': 3}) }}"

6.5 Error Handling

---
- name: Error Handling Example
  hosts: all
  tasks:
    - name: Try to start service
      service:
        name: nginx
        state: started
      ignore_errors: yes

    - name: Capture command error
      shell: /bin/false
      register: result
      failed_when: result.rc != 0
      ignore_errors: yes

    - name: Act on error result
      debug:
        msg: "Command failed"
      when: result.failed

    - name: Rescue block
      block:
        - name: Potentially failing task
          shell: /bin/false
      rescue:
        - name: Rescue task
          debug:
            msg: "Rescue operation executed"
      always:
        - name: Always run task
          debug:
            msg: "This runs regardless of success or failure"

7. Module Details

7.1 System Modules

User Module

- name: Create user
  user:
    name: webapp
    group: webapp
    shell: /bin/bash
    home: /home/webapp
    createhome: yes
    password: "{{ 'password' | password_hash('sha512') }}"

Group Module

- name: Create group
  group:
    name: webapp
    gid: 1001
    state: present

Cron Module

- name: Add scheduled task
  cron:
    name: "backup database"
    minute: "0"
    hour: "2"
    job: "/usr/local/bin/backup.sh"
    user: root

7.2 File Modules

File Module

- name: Create directory
  file:
    path: /var/www/html
    state: directory
    owner: nginx
    group: nginx
    mode: '0755'

- name: Create symbolic link
  file:
    src: /var/www/html
    dest: /var/www/site
    state: link

Copy Module

- name: Copy file
  copy:
    src: /tmp/source.txt
    dest: /tmp/dest.txt
    owner: root
    group: root
    mode: '0644'
    backup: yes

Template Module

- name: Generate configuration file
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: '0644'
    notify: restart nginx

7.3 Package Modules

Yum Module

- name: Install packages with yum
  yum:
    name:
      - nginx
      - mysql-server
      - php
    state: present
    update_cache: yes

- name: Install specific version
  yum:
    name: nginx-1.18.0
    state: present

Apt Module

- name: Update apt cache
  apt:
    update_cache: yes
    cache_valid_time: 3600

- name: Install packages with apt
  apt:
    name: "{{ packages }}"
    state: present
  vars:
    packages:
      - nginx
      - mysql-server
      - php

7.4 Service Modules

Service Module

- name: Start and enable service
  service:
    name: nginx
    state: started
    enabled: yes

Systemd Module

- name: Reload systemd
  systemd:
    daemon_reload: yes

- name: Start service via systemd
  systemd:
    name: nginx
    state: started
    enabled: yes

8. Templates and Filters

8.1 Jinja2 Templates

Example nginx.conf.j2:

# nginx.conf.j2
user {{ nginx_user }};
worker_processes {{ ansible_processor_cores }};
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;

events {
    worker_connections {{ nginx_worker_connections }};
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    {% for upstream in nginx_upstreams %}
    upstream {{ upstream.name }} {
        {% for server in upstream.servers %}
        server {{ server }};
        {% endfor %}
    }
    {% endfor %}

    server {
        listen {{ http_port }};
        server_name {{ server_name }};

        location / {
            root {{ document_root }};
            index index.html index.htm;
        }
    }
}

8.2 Common Filters

---
- name: Filter examples
  hosts: all
  vars:
    items: [1, 2, 3, 4, 5]
    text: "Hello World"
  tasks:
    - name: Max value filter
      debug:
        msg: "{{ items | max }}"   # 5

    - name: Lowercase filter
      debug:
        msg: "{{ text | lower }}"   # hello world

    - name: Default value filter
      debug:
        msg: "{{ undefined_var | default('default_value') }}"

    - name: Password hash filter
      debug:
        msg: "{{ 'password' | password_hash('sha512') }}"

    - name: DateTime filter
      debug:
        msg: "{{ ansible_date_time.iso8601 | to_datetime }}"

9. Handlers and Notifications

9.1 Basic Handler Usage

---
- name: Web server configuration
  hosts: webservers
  tasks:
    - name: Install Nginx
      yum:
        name: nginx
        state: present

    - name: Configure Nginx
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify:
        - restart nginx
        - reload nginx

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

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

  - name: reload nginx
    service:
      name: nginx
      state: reloaded

9.2 Conditional Handlers

---
- name: Conditional handler example
  hosts: all
  tasks:
    - name: Check configuration file existence
      stat:
        path: /etc/nginx/nginx.conf
      register: nginx_config

    - name: Update configuration if it exists
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      when: nginx_config.stat.exists
      notify: restart nginx

  handlers:
    - name: restart nginx
      service:
        name: nginx
        state: restarted
      when: nginx_config.stat.exists

10. Roles

10.1 Role Structure

roles/
└── nginx/
    ├── tasks/
    │   └── main.yml
    ├── handlers/
    │   └── main.yml
    ├── templates/
    │   └── nginx.conf.j2
    ├── files/
    │   └── index.html
    ├── vars/
    │   └── main.yml
    ├── defaults/
    │   └── main.yml
    ├── meta/
    │   └── main.yml
    └── README.md

10.2 Creating a Role

tasks/main.yml

---
- name: Install Nginx
  yum:
    name: nginx
    state: present

- name: Create configuration file
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
  notify: restart nginx

- name: Start Nginx service
  service:
    name: nginx
    state: started
    enabled: yes

handlers/main.yml

---
- name: restart nginx
  service:
    name: nginx
    state: restarted

defaults/main.yml

---
nginx_user: nginx
nginx_worker_connections: 1024
http_port: 80
document_root: /usr/share/nginx/html

meta/main.yml

---
galaxy_info:
  author: your-name
  description: Nginx web server role
  company: your-company
  license: MIT
  min_ansible_version: 2.9
  platforms:
    - name: EL
      versions:
        - 7
        - 8
    - name: Ubuntu
      versions:
        - 18.04
        - 20.04
  dependencies: []

10.3 Using a Role

---
- name: Deploy web servers
  hosts: webservers
  roles:
    - nginx
    - mysql
    - php

# Passing variables to a role
- name: Deploy web servers with custom vars
  hosts: webservers
  roles:
    - role: nginx
      nginx_user: www-data
      http_port: 8080

11. Vault Encryption

11.1 Creating Encrypted Files

# Create encrypted file
ansible-vault create secret.yml

# Edit encrypted file
ansible-vault edit secret.yml

# View encrypted file
ansible-vault view secret.yml

# Encrypt existing file
ansible-vault encrypt existing_file.yml

# Decrypt file
ansible-vault decrypt secret.yml

11.2 Using Encrypted Variables

Example vault.yml (encrypted):

---
vault_database_password: secretpassword123
vault_api_key: abc123def456

Reference in a playbook:

---
- name: Use encrypted variables
  hosts: databases
  vars_files:
    - vault.yml
  tasks:
    - name: Create database user
      mysql_user:
        name: myapp
        password: "{{ vault_database_password }}"
        priv: "myapp.*:ALL"
        state: present

Run with vault password:

ansible-playbook -i inventory site.yml --ask-vault-pass
# Or use a password file
ansible-playbook -i inventory site.yml --vault-password-file ~/.vault_pass

12. Advanced Features

12.1 Strategy Control

---
- name: Strategy control example
  hosts: all
  strategy: free   # Free strategy, each host runs independently
  tasks:
    - name: Long‑running task
      shell: sleep 30

    - name: Quick task
      debug:
        msg: "This task finishes quickly"

12.2 Delegation and Proxy

---
- name: Delegated execution example
  hosts: webservers
  tasks:
    - name: Check service status on monitoring server
      uri:
        url: "http://{{ inventory_hostname }}/health"
        method: GET
      delegate_to: monitor.example.com

    - name: Run command locally
      shell: echo "Running on control node"
      delegate_to: localhost
      run_once: true

12.3 Rolling Updates

---
- name: Rolling update example
  hosts: webservers
  serial: 2   # Update two servers at a time
  tasks:
    - name: Remove from load balancer
      uri:
        url: "http://lb.example.com/remove/{{ inventory_hostname }}"
        method: POST
      delegate_to: localhost

    - name: Update application
      yum:
        name: myapp
        state: latest
      notify: restart myapp

    - name: Wait for service to start
      wait_for:
        port: 8080
        delay: 10

    - name: Add back to load balancer
      uri:
        url: "http://lb.example.com/add/{{ inventory_hostname }}"
        method: POST
      delegate_to: localhost

  handlers:
    - name: restart myapp
      service:
        name: myapp
        state: restarted

12.4 Asynchronous Tasks

---
- name: Asynchronous task example
  hosts: all
  tasks:
    - name: Long‑running task
      shell: /opt/long_running_script.sh
      async: 3600   # Max runtime (seconds)
      poll: 0       # Do not wait for result
      register: long_task

    - name: Continue with other tasks
      debug:
        msg: "Continuing with other tasks"

    - name: Check async task status
      async_status:
        jid: "{{ long_task.ansible_job_id }}"
      register: job_result
      until: job_result.finished
      retries: 30
      delay: 10

13. Best Practices

13.1 Directory Structure

ansible-project/
├── inventories/
│   ├── production/
│   │   ├── hosts
│   │   └── group_vars/
│   └── staging/
│       ├── hosts
│       └── group_vars/
├── roles/
│   ├── common/
│   ├── nginx/
│   └── mysql/
├── playbooks/
│   ├── site.yml
│   ├── webservers.yml
│   └── databases.yml
├── group_vars/
│   └── all.yml
├── host_vars/
├── vault/
│   └── secrets.yml
├── files/
├── templates/
├── library/
├── filter_plugins/
└── ansible.cfg

13.2 Naming Conventions

---
# Use descriptive names
- name: Install and configure Nginx web server
  hosts: webservers
  tasks:
    - name: Install Nginx package
      yum:
        name: nginx
        state: present

    - name: Create Nginx configuration file
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify: restart Nginx service

    - name: Start and enable Nginx service
      service:
        name: nginx
        state: started
        enabled: yes

  handlers:
    - name: restart Nginx service
      service:
        name: nginx
        state: restarted

13.3 Variable Management

# Use clear variable hierarchy
nginx_config:
  user: nginx
  worker_processes: auto
  worker_connections: 1024
  keepalive_timeout: 65

server:
  listen: 80
  server_name: example.com
  root: /var/www/html
  index: index.html

upstream:
  name: backend
  servers:
    - 192.168.1.10:8080
    - 192.168.1.11:8080

13.4 Error Handling

---
- name: Robust error handling
  hosts: all
  tasks:
    - name: Verify system requirements
      assert:
        that:
          - ansible_distribution_major_version|int >= 7
          - ansible_memtotal_mb >= 1024
        fail_msg: "System does not meet minimum requirements"

    - name: Backup configuration file
      copy:
        src: /etc/nginx/nginx.conf
        dest: /etc/nginx/nginx.conf.bak
        remote_src: yes
      ignore_errors: yes

    - name: Update configuration
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
        validate: nginx -t -c %s
      notify: restart nginx

14. Monitoring and Logging

14.1 Log Configuration

# ansible.cfg
[defaults]
log_path = /var/log/ansible.log
display_skipped_hosts = False
display_ok_hosts = False

14.2 Callback Plugins

# callback_plugins/timer.py
from ansible.plugins.callback import CallbackBase
from datetime import datetime

class CallbackModule(CallbackBase):
    def __init__(self):
        super(CallbackModule, self).__init__()
        self.start_time = datetime.now()

    def v2_playbook_on_task_start(self, task, is_conditional):
        self.task_start_time = datetime.now()

    def v2_runner_on_ok(self, result):
        duration = datetime.now() - self.task_start_time
        self._display.display(f"Task completed in {duration.total_seconds():.2f}s")

14.3 Performance Monitoring

---
- name: Performance monitoring example
  hosts: all
  tasks:
    - name: Gather system information
      setup:
        gather_subset:
          - hardware
          - network
          - virtual

    - name: Record performance metrics
      debug:
        msg: |
          CPU cores: {{ ansible_processor_cores }}
          Memory: {{ ansible_memtotal_mb }}MB
          Root disk free: {{ ansible_mounts | selectattr('mount', 'equalto', '/') | map(attribute='size_available') | first }}

15. CI/CD Integration

15.1 GitLab CI Integration

# .gitlab-ci.yml
stages:
  - test
  - deploy

ansible-lint:
  stage: test
  script:
    - ansible-lint playbooks/site.yml
  only:
    - merge_requests

deploy-staging:
  stage: deploy
  script:
    - ansible-playbook -i inventories/staging/hosts playbooks/site.yml --vault-password-file $VAULT_PASSWORD_FILE
  environment:
    name: staging
    url: https://staging.example.com
  only:
    - develop

deploy-production:
  stage: deploy
  script:
    - ansible-playbook -i inventories/production/hosts playbooks/site.yml --vault-password-file $VAULT_PASSWORD_FILE
  environment:
    name: production
    url: https://production.example.com
  when: manual
  only:
    - master

15.2 Jenkins Integration

// Jenkinsfile
pipeline {
    agent any
    environment {
        ANSIBLE_HOST_KEY_CHECKING = 'False'
        VAULT_PASSWORD_FILE = credentials('ansible-vault-password')
    }
    stages {
        stage('Checkout') {
            steps { checkout scm }
        }
        stage('Syntax Check') {
            steps { sh 'ansible-playbook --syntax-check playbooks/site.yml' }
        }
        stage('Lint') {
            steps { sh 'ansible-lint playbooks/site.yml' }
        }
        stage('Deploy Staging') {
            steps {
                sh '''ansible-playbook -i inventories/staging/hosts playbooks/site.yml --vault-password-file $VAULT_PASSWORD_FILE'''
            }
        }
        stage('Deploy Production') {
            when { branch 'master' }
            steps {
                input message: 'Deploy to Production?'
                sh '''ansible-playbook -i inventories/production/hosts playbooks/site.yml --vault-password-file $VAULT_PASSWORD_FILE'''
            }
        }
    }
    post {
        always { archiveArtifacts artifacts: 'logs/*.log', allowEmptyArchive: true }
        failure { emailext subject: "Ansible Deployment Failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}", body: "Build failed. Check console output at ${env.BUILD_URL}", to: "${env.CHANGE_AUTHOR_EMAIL}" }
    }
}

15.3 GitHub Actions Integration

# .github/workflows/deploy.yml
name: Deploy with Ansible

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: '3.8'
      - name: Install dependencies
        run: |
          pip install ansible ansible-lint
      - name: Run ansible-lint
        run: ansible-lint playbooks/site.yml
      - name: Syntax check
        run: ansible-playbook --syntax-check playbooks/site.yml

  deploy-staging:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/develop'
    steps:
      - uses: actions/checkout@v2
      - name: Deploy to Staging
        env:
          ANSIBLE_HOST_KEY_CHECKING: False
          VAULT_PASSWORD: ${{ secrets.VAULT_PASSWORD }}
        run: |
          echo "$VAULT_PASSWORD" > .vault_pass
          ansible-playbook -i inventories/staging/hosts playbooks/site.yml --vault-password-file .vault_pass
          rm .vault_pass

  deploy-production:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    environment: production
    steps:
      - uses: actions/checkout@v2
      - name: Deploy to Production
        env:
          ANSIBLE_HOST_KEY_CHECKING: False
          VAULT_PASSWORD: ${{ secrets.VAULT_PASSWORD }}
        run: |
          echo "$VAULT_PASSWORD" > .vault_pass
          ansible-playbook -i inventories/production/hosts playbooks/site.yml --vault-password-file .vault_pass
          rm .vault_pass

16. Troubleshooting

16.1 Common Issues and Solutions

SSH Connection Problems

# Test SSH connectivity
ansible all -m ping

# Verbose output
ansible all -m ping -vvv

# Disable host key checking
export ANSIBLE_HOST_KEY_CHECKING=False

Permission Issues

# Use sudo
ansible all -m shell -a "systemctl status nginx" --become

# Specify sudo user
ansible all -m shell -a "systemctl status nginx" --become-user=root

Missing Modules

# Check module availability
ansible-doc -l | grep module_name

# Install required Python module
pip install required_module

16.2 Debugging Tips

---
- name: Debug example
  hosts: all
  tasks:
    - name: Gather facts
      setup:
      register: facts

    - name: Show variable content
      debug:
        var: facts
      verbosity: 2

    - name: Conditional debug
      debug:
        msg: "Disk space low"
      when: ansible_mounts | selectattr('mount', 'equalto', '/') | map(attribute='size_available') | first < 1000000000

    - name: Pause execution
      pause:
        prompt: "Check system status and press Enter to continue"
      when: debug_mode | default(false)

16.3 Performance Optimization

# ansible.cfg optimization
[defaults]
forks = 50          # Increase parallelism
#gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts_cache
fact_caching_timeout = 3600

[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=300s
control_path = /tmp/ansible-ssh-%%h-%%p-%%r

17. Security Considerations

17.1 Access Control

---
- name: Security configuration example
  hosts: all
  become: yes
  become_method: sudo
  tasks:
    - name: Create dedicated user
      user:
        name: ansible
        groups: wheel
        shell: /bin/bash
        create_home: yes

    - name: Configure sudo privileges
      lineinfile:
        path: /etc/sudoers.d/ansible
        line: 'ansible ALL=(ALL) NOPASSWD: ALL'
        create: yes
        validate: 'visudo -cf %s'

    - name: Configure SSH key for ansible user
      authorized_key:
        user: ansible
        key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
        state: present

17.2 Protecting Sensitive Information

---
- name: Sensitive data handling
  hosts: all
  vars:
    sensitive_data: "{{ vault_sensitive_data }}"
  tasks:
    - name: Use sensitive data
      shell: echo "{{ sensitive_data }}"
      no_log: true   # Prevent logging of sensitive data

    - name: Handle password securely
      user:
        name: dbuser
        password: "{{ vault_db_password | password_hash('sha512') }}"
        no_log: true

17.3 Network Security

---
- name: Network security configuration
  hosts: all
  tasks:
    - name: Configure firewall (firewalld)
      firewalld:
        service: "{{ item }}"
        permanent: yes
        state: enabled
        immediate: yes
      loop:
        - ssh
        - http
        - https

    - name: Restrict SSH password authentication
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^#?PasswordAuthentication'
        line: 'PasswordAuthentication no'
      notify: restart sshd

    - name: Configure SELinux
      selinux:
        policy: targeted
        state: enforcing

  handlers:
    - name: restart sshd
      service:
        name: sshd
        state: restarted

18. Enterprise Deployment Cases

18.1 LAMP Stack Automation

---
- name: Deploy LAMP stack
  hosts: webservers
  become: yes
  vars:
    mysql_root_password: "{{ vault_mysql_root_password }}"
    app_user: webapp
    app_group: webapp
  roles:
    - common
    - apache
    - mysql
    - php
    - application
  post_tasks:
    - name: Verify deployment
      uri:
        url: "http://{{ inventory_hostname }}/health"
        method: GET
        status_code: 200
      delegate_to: localhost

18.2 Docker Container Deployment

---
- name: Docker container deployment
  hosts: docker_hosts
  become: yes
  tasks:
    - name: Install Docker
      yum:
        name: docker
        state: present

    - name: Start Docker service
      service:
        name: docker
        state: started
        enabled: yes

    - name: Pull application image
      docker_image:
        name: "{{ app_image }}"
        tag: "{{ app_version }}"
        source: pull

    - name: Deploy application container
      docker_container:
        name: "{{ app_name }}"
        image: "{{ app_image }}:{{ app_version }}"
        state: started
        restart_policy: always
        ports:
          - "80:8080"
        env:
          DB_HOST: "{{ db_host }}"
          DB_PASSWORD: "{{ vault_db_password }}"

    - name: Wait for application to start
      wait_for:
        port: 80
        delay: 10
        timeout: 60

18.3 Kubernetes Cluster Deployment

---
- name: Deploy Kubernetes cluster
  hosts: k8s_masters
  become: yes
  tasks:
    - name: Initialize cluster
      shell: kubeadm init --pod-network-cidr=10.244.0.0/16
      register: kubeadm_init
      run_once: true

    - name: Save join command
      set_fact:
        join_command: "{{ kubeadm_init.stdout_lines | select('match', 'kubeadm join.*') | first }}"
      run_once: true

    - name: Configure kubectl for root
      shell: |
        mkdir -p $HOME/.kube
        cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
        chown $(id -u):$(id -g) $HOME/.kube/config
      run_once: true

    - name: Deploy flannel network plugin
      shell: kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
      run_once: true

- name: Join worker nodes
  hosts: k8s_workers
  become: yes
  tasks:
    - name: Join cluster
      shell: "{{ hostvars[groups['k8s_masters'][0]]['join_command'] }}"

19. Summary

Ansible is a vital tool for modern IT infrastructure automation, offering simplicity, agent‑less operation, a rich module ecosystem, and strong extensibility. It scales from small scripts to enterprise‑level deployments, integrates smoothly with CI/CD pipelines, and supports advanced features like roles, Vault, and strategy control.

To master Ansible, engineers should follow these steps:

Fundamental Learning : Understand core concepts, modules, and playbook syntax.

Practical Application : Start with simple system configuration and progress to multi‑tier architecture automation.

Best Practices : Adopt naming conventions, directory structures, and security hardening.

Advanced Features : Explore roles, Vault, strategy control, and asynchronous tasks.

Integration and Optimization : Embed Ansible into existing DevOps workflows for end‑to‑end automation.

By adhering to these guidelines and leveraging the comprehensive examples provided, ops engineers can significantly improve efficiency, reduce manual errors, and advance their careers toward high‑value SRE/DevOps positions.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Ops Community
Written by

Ops Community

A leading IT operations community where professionals share and grow together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.