Ansible is a free and open-source agentless automation platform for configuration management, application deployment, and task automation across multiple systems. Originally developed by Michael DeHaan and acquired by Red Hat, Ansible serves as the industry standard for infrastructure automation with enterprise-grade security and scalability. It provides a robust alternative to proprietary solutions like VMware vRealize Automation, Microsoft System Center, or Puppet Enterprise without vendor lock-in or licensing costs.
1. Prerequisites
2. Supported Operating Systems
This guide supports installation on:
3. Installation
Using Package Manager (Recommended)
#### Ubuntu/Debian
# Update package list
sudo apt update
# Install Ansible and dependencies
sudo apt install -y ansible python3-pip python3-venv sshpass
# Install additional collections
ansible-galaxy collection install community.general
ansible-galaxy collection install ansible.posix
ansible-galaxy collection install kubernetes.core
ansible-galaxy collection install amazon.aws
ansible-galaxy collection install azure.azcollection
ansible-galaxy collection install google.cloud
# Verify installation
ansible --version
ansible-galaxy collection list
#### RHEL/CentOS/Rocky Linux/AlmaLinux
# Install EPEL repository
sudo yum install -y epel-release
# Install Ansible
sudo yum install -y ansible python3-pip
# For newer distributions
sudo dnf install -y ansible python3-pip
# Install collections
ansible-galaxy collection install community.general
ansible-galaxy collection install ansible.posix
ansible-galaxy collection install kubernetes.core
# Verify installation
ansible --version
#### Fedora
# Install Ansible
sudo dnf install -y ansible python3-pip python3-virtualenv
# Install additional collections
ansible-galaxy collection install community.general
ansible-galaxy collection install ansible.posix
ansible-galaxy collection install kubernetes.core
# Verify installation
ansible --version
#### Arch Linux
# Install Ansible
sudo pacman -Syu ansible python-pip
# Install collections
ansible-galaxy collection install community.general
ansible-galaxy collection install ansible.posix
# Verify installation
ansible --version
#### Alpine Linux
# Install Ansible
sudo apk update
sudo apk add ansible python3 py3-pip openssh
# Install collections
ansible-galaxy collection install community.general
# Verify installation
ansible --version
#### openSUSE/SLES
# openSUSE Leap/Tumbleweed
sudo zypper refresh
# Install Ansible and dependencies
sudo zypper install -y ansible python3-pip python3-virtualenv
# SLES 15 (requires additional modules)
sudo SUSEConnect -p sle-module-python3/15.5/x86_64
sudo zypper install -y ansible python3-pip
# Install additional collections
ansible-galaxy collection install community.general
ansible-galaxy collection install ansible.posix
ansible-galaxy collection install kubernetes.core
# Verify installation
ansible --version
#### macOS
# Method 1: Using Homebrew (recommended)
# Install Homebrew if not already installed
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Install Ansible
brew install ansible
# Method 2: Using pip with virtual environment
# Install Python 3 if needed
brew install python3
# Create virtual environment
python3 -m venv ~/ansible-venv
source ~/ansible-venv/bin/activate
# Install Ansible
pip install --upgrade pip
pip install ansible ansible-core
# Install additional packages for cloud providers
pip install boto3 azure-cli google-cloud-storage
# Add to shell profile for persistence
echo 'source ~/ansible-venv/bin/activate' >> ~/.zshrc # or ~/.bash_profile
# Install collections
ansible-galaxy collection install community.general
ansible-galaxy collection install ansible.posix
ansible-galaxy collection install amazon.aws
ansible-galaxy collection install azure.azcollection
# Verify installation
ansible --version
#### FreeBSD
# Install from ports
cd /usr/ports/sysutils/ansible && make install clean
# Or install from packages
pkg install py39-ansible
# Install Python dependencies
pkg install python39 py39-pip py39-virtualenv
# Create virtual environment (recommended)
python3.9 -m venv ~/ansible-venv
source ~/ansible-venv/bin/activate
# Install additional packages
pip install paramiko jinja2 PyYAML cryptography
# Install collections
ansible-galaxy collection install community.general
ansible-galaxy collection install ansible.posix
# Verify installation
ansible --version
#### Windows (using WSL2)
# Enable WSL2 first
# Run in PowerShell as Administrator
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
# Restart computer, then set WSL2 as default
wsl --set-default-version 2
# Install Ubuntu from Microsoft Store or command line
wsl --install -d Ubuntu-22.04
# Inside WSL Ubuntu environment:
sudo apt update
sudo apt install -y ansible python3-pip python3-venv sshpass
# Create Windows-specific inventory for managing Windows hosts
mkdir -p ~/ansible-windows
cd ~/ansible-windows
# Install Windows collections
ansible-galaxy collection install ansible.windows
ansible-galaxy collection install community.windows
ansible-galaxy collection install chocolatey.chocolatey
# Create Windows inventory example
cat > inventory/windows-hosts.yml <<EOF
all:
children:
windows:
hosts:
win-server-01:
ansible_host: 192.168.1.100
ansible_user: Administrator
ansible_password: "{{ vault_windows_password }}"
ansible_connection: winrm
ansible_winrm_server_cert_validation: ignore
ansible_winrm_transport: basic
ansible_winrm_port: 5985
vars:
ansible_shell_type: powershell
ansible_become_method: runas
ansible_become_user: Administrator
EOF
# Verify installation
ansible --version
Using pip (Latest Version)
# Create virtual environment (recommended)
python3 -m venv ~/ansible-venv
source ~/ansible-venv/bin/activate
# Install Ansible via pip
pip install --upgrade pip
pip install ansible ansible-core
# Install additional packages
pip install paramiko jinja2 PyYAML cryptography
# Install cloud provider SDKs
pip install boto3 botocore azure-cli google-cloud-storage
# Install collections
ansible-galaxy collection install community.general
ansible-galaxy collection install ansible.posix
ansible-galaxy collection install kubernetes.core
ansible-galaxy collection install amazon.aws
ansible-galaxy collection install azure.azcollection
ansible-galaxy collection install google.cloud
# Add to PATH permanently
echo 'source ~/ansible-venv/bin/activate' >> ~/.bashrc
# Verify installation
ansible --version
Using Docker
# Create Ansible Docker container with mounted volumes
docker run --rm -it \
-v $(pwd):/ansible \
-v ~/.ssh:/root/.ssh:ro \
-v ~/.aws:/root/.aws:ro \
-v ~/.azure:/root/.azure:ro \
--workdir /ansible \
ansible/ansible:latest
# Create wrapper script for ease of use
sudo tee /usr/local/bin/ansible-docker > /dev/null <<'EOF'
#!/bin/bash
docker run --rm -it \
-v $(pwd):/ansible \
-v ~/.ssh:/root/.ssh:ro \
-v ~/.aws:/root/.aws:ro \
-v ~/.azure:/root/.azure:ro \
-v ~/.kube:/root/.kube:ro \
--workdir /ansible \
--network host \
ansible/ansible:latest "$@"
EOF
sudo chmod +x /usr/local/bin/ansible-docker
# Create aliases
echo 'alias ansible="ansible-docker ansible"' >> ~/.bashrc
echo 'alias ansible-playbook="ansible-docker ansible-playbook"' >> ~/.bashrc
Initial Configuration
First-Run Setup
1. SSH Key Setup:
# Generate SSH key pair for Ansible
ssh-keygen -t rsa -b 4096 -f ~/.ssh/ansible_key -C "ansible-automation"
# Set proper permissions
chmod 600 ~/.ssh/ansible_key
chmod 644 ~/.ssh/ansible_key.pub
# Copy public key to target hosts
ssh-copy-id -i ~/.ssh/ansible_key.pub user@target-host
# Or manually copy the key
cat ~/.ssh/ansible_key.pub | ssh user@target-host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
2. Basic Ansible Configuration:
# Create Ansible configuration directory
mkdir -p ~/.ansible/{facts_cache,inventory_cache,cp,logs}
# Create basic ansible.cfg
cat > ~/.ansible.cfg <<EOF
[defaults]
host_key_checking = True
remote_user = ansible
private_key_file = ~/.ssh/ansible_key
timeout = 30
retry_files_enabled = False
stdout_callback = yaml
gathering = smart
fact_caching = jsonfile
fact_caching_connection = ~/.ansible/facts_cache
fact_caching_timeout = 86400
interpreter_python = auto_silent
deprecation_warnings = True
command_warnings = True
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=3600s -o PreferredAuthentications=publickey
pipelining = True
control_path = ~/.ansible/cp/%%h-%%p-%%r
[privilege_escalation]
become = False
become_method = sudo
become_user = root
become_ask_pass = False
EOF
3. Create Basic Inventory:
# Create simple inventory file
mkdir -p ~/ansible-project/inventory
cat > ~/ansible-project/inventory/hosts.yml <<EOF
all:
children:
webservers:
hosts:
web-01:
ansible_host: 192.168.1.10
web-02:
ansible_host: 192.168.1.11
databases:
hosts:
db-01:
ansible_host: 192.168.1.20
vars:
ansible_user: ansible
ansible_ssh_private_key_file: ~/.ssh/ansible_key
EOF
4. Verify Target Host Python:
# Check Python availability on target hosts
ansible all -i inventory/hosts.yml -m raw -a "python3 --version || python --version"
# Install Python if needed (Ubuntu/Debian example)
ansible all -i inventory/hosts.yml -m raw -a "apt update && apt install -y python3" --become
Testing Initial Setup
# Test connectivity to all hosts
ansible all -i inventory/hosts.yml -m ping
# Gather facts from all hosts
ansible all -i inventory/hosts.yml -m setup
# Test privilege escalation
ansible all -i inventory/hosts.yml -m command -a "whoami" --become
# Check disk space on all hosts
ansible all -i inventory/hosts.yml -m command -a "df -h"
# Verify SSH keys are working
ansible all -i inventory/hosts.yml -m command -a "uptime"
# Test basic file operations
ansible all -i inventory/hosts.yml -m file -a "path=/tmp/ansible-test state=touch" --become
ansible all -i inventory/hosts.yml -m file -a "path=/tmp/ansible-test state=absent" --become
WARNING: Ensure SSH key authentication is working and hosts are accessible before proceeding with complex playbooks!
5. Service Management
Ansible Controller Service (systemd)
# Create Ansible controller service for scheduled playbooks
sudo tee /etc/systemd/system/ansible-controller.service > /dev/null <<EOF
[Unit]
Description=Ansible Controller Service
After=network.target
Wants=network-online.target
[Service]
Type=oneshot
User=ansible
Group=ansible
WorkingDirectory=/opt/ansible
ExecStart=/usr/local/bin/ansible-playbook -i inventories/production playbooks/maintenance.yml
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
# Create timer for regular execution
sudo tee /etc/systemd/system/ansible-controller.timer > /dev/null <<EOF
[Unit]
Description=Run Ansible Controller Service
Requires=ansible-controller.service
[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
EOF
# Enable and start the timer
sudo systemctl daemon-reload
sudo systemctl enable ansible-controller.timer
sudo systemctl start ansible-controller.timer
# Check status
sudo systemctl status ansible-controller.timer
Ansible Tower/AWX Service Management
# AWX service management (Docker-based)
# Start AWX services
sudo docker-compose -f /opt/awx/installer/docker-compose.yml up -d
# Stop AWX services
sudo docker-compose -f /opt/awx/installer/docker-compose.yml down
# Restart AWX services
sudo docker-compose -f /opt/awx/installer/docker-compose.yml restart
# View AWX logs
sudo docker-compose -f /opt/awx/installer/docker-compose.yml logs -f
# Check AWX service status
sudo docker-compose -f /opt/awx/installer/docker-compose.yml ps
# Update AWX
cd /opt/awx
git pull
sudo docker-compose -f installer/docker-compose.yml down
sudo docker-compose -f installer/docker-compose.yml build --no-cache
sudo docker-compose -f installer/docker-compose.yml up -d
Ansible Pull Service (for Pull-based Configuration)
# Create ansible-pull service for decentralized management
sudo tee /etc/systemd/system/ansible-pull.service > /dev/null <<EOF
[Unit]
Description=Ansible Pull Configuration Management
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=root
ExecStart=/usr/bin/ansible-pull -U https://github.com/company/ansible-config.git -i localhost, local.yml
StandardOutput=journal
StandardError=journal
TimeoutStartSec=600
[Install]
WantedBy=multi-user.target
EOF
# Create timer for ansible-pull
sudo tee /etc/systemd/system/ansible-pull.timer > /dev/null <<EOF
[Unit]
Description=Run Ansible Pull every 30 minutes
Requires=ansible-pull.service
[Timer]
OnCalendar=*:0/30
Persistent=true
RandomizedDelaySec=120
[Install]
WantedBy=timers.target
EOF
# Enable ansible-pull timer
sudo systemctl daemon-reload
sudo systemctl enable ansible-pull.timer
sudo systemctl start ansible-pull.timer
Cross-Platform Service Management
#### macOS (launchd)
# Create launchd plist for Ansible automation
sudo tee /Library/LaunchDaemons/com.company.ansible.plist > /dev/null <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.company.ansible</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/ansible-playbook</string>
<string>-i</string>
<string>/opt/ansible/inventory/hosts.yml</string>
<string>/opt/ansible/playbooks/maintenance.yml</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<key>StandardOutPath</key>
<string>/var/log/ansible.log</string>
<key>StandardErrorPath</key>
<string>/var/log/ansible.error.log</string>
<key>UserName</key>
<string>ansible</string>
<key>WorkingDirectory</key>
<string>/opt/ansible</string>
</dict>
</plist>
EOF
# Load and start the service
sudo launchctl load /Library/LaunchDaemons/com.company.ansible.plist
sudo launchctl start com.company.ansible
#### Windows (Task Scheduler via PowerShell)
# Create scheduled task for Ansible in Windows (running in WSL)
$TaskName = "Ansible-Configuration-Management"
$TaskDescription = "Run Ansible playbooks for system configuration"
$Action = New-ScheduledTaskAction -Execute "wsl" -Argument "ansible-playbook -i /home/ansible/inventory/hosts.yml /home/ansible/playbooks/windows-maintenance.yml"
$Trigger = New-ScheduledTaskTrigger -Daily -At "02:00AM"
$Principal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount
$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable
Register-ScheduledTask -TaskName $TaskName -Description $TaskDescription -Action $Action -Trigger $Trigger -Principal $Principal -Settings $Settings
# Start the task
Start-ScheduledTask -TaskName $TaskName
# Check task status
Get-ScheduledTask -TaskName $TaskName | Get-ScheduledTaskInfo
Project Structure and Configuration
Professional Project Structure
# Create comprehensive Ansible project structure
mkdir -p ~/ansible-infrastructure/{
inventories/{production,staging,development},
playbooks,
roles,
group_vars,
host_vars,
library,
filter_plugins,
callback_plugins,
vault,
collections,
logs
}
cd ~/ansible-infrastructure
# Create ansible.cfg with security best practices
cat > ansible.cfg <<EOF
[defaults]
inventory = inventories/production/hosts.yml
remote_user = ansible
private_key_file = ~/.ssh/ansible_key
host_key_checking = True
timeout = 30
retry_files_enabled = False
stdout_callback = yaml
bin_ansible_callbacks = True
gathering = smart
fact_caching = jsonfile
fact_caching_connection = ~/.ansible/facts_cache
fact_caching_timeout = 86400
interpreter_python = auto_silent
vault_password_file = ~/.ansible_vault_pass
# Logging
log_path = logs/ansible.log
display_skipped_hosts = False
display_ok_hosts = False
# Performance
forks = 20
poll_interval = 15
internal_poll_interval = 0.001
# Security
command_warnings = True
deprecation_warnings = True
action_warnings = True
localhost_warning = True
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=3600s -o PreferredAuthentications=publickey
pipelining = True
control_path = ~/.ansible/cp/%%h-%%p-%%r
retries = 3
[privilege_escalation]
become = False
become_method = sudo
become_user = root
become_ask_pass = False
[inventory]
enable_plugins = host_list, script, auto, yaml, ini, toml
cache = True
cache_plugin = jsonfile
cache_timeout = 3600
cache_connection = ~/.ansible/inventory_cache
[galaxy]
server_list = automation_hub, galaxy
EOF
Advanced Inventory Management
# Create production inventory with groups and variables
cat > inventories/production/hosts.yml <<EOF
all:
children:
webservers:
hosts:
web-prod-01:
ansible_host: 10.0.1.10
ansible_user: ansible
server_role: frontend
backup_enabled: true
web-prod-02:
ansible_host: 10.0.1.11
ansible_user: ansible
server_role: frontend
backup_enabled: true
web-prod-03:
ansible_host: 10.0.1.12
ansible_user: ansible
server_role: frontend
backup_enabled: true
vars:
http_port: 80
https_port: 443
max_clients: 200
environment: production
monitoring_enabled: true
dbservers:
hosts:
db-prod-01:
ansible_host: 10.0.2.10
ansible_user: ansible
mysql_server_id: 1
mysql_role: master
db-prod-02:
ansible_host: 10.0.2.11
ansible_user: ansible
mysql_server_id: 2
mysql_role: slave
vars:
mysql_port: 3306
mysql_root_password: "{{ vault_mysql_root_password }}"
mysql_replication_user: "{{ vault_mysql_replication_user }}"
mysql_replication_password: "{{ vault_mysql_replication_password }}"
loadbalancers:
hosts:
lb-prod-01:
ansible_host: 10.0.3.10
ansible_user: ansible
lb_algorithm: roundrobin
lb-prod-02:
ansible_host: 10.0.3.11
ansible_user: ansible
lb_algorithm: roundrobin
vars:
haproxy_stats_enabled: true
haproxy_stats_user: admin
haproxy_stats_password: "{{ vault_haproxy_stats_password }}"
monitoring:
hosts:
monitor-prod-01:
ansible_host: 10.0.4.10
ansible_user: ansible
prometheus_retention: 30d
grafana_admin_password: "{{ vault_grafana_admin_password }}"
bastion:
hosts:
bastion-prod-01:
ansible_host: bastion.example.com
ansible_user: ansible
ansible_port: 22
# Global variables for all hosts
webservers:
vars:
nginx_worker_processes: auto
nginx_worker_connections: 1024
ssl_certificate_path: /etc/ssl/certs
backup_schedule: "0 2 * * *"
dbservers:
vars:
mysql_innodb_buffer_pool_size: 2G
mysql_max_connections: 200
backup_schedule: "0 1 * * *"
monitoring_enabled: true
EOF
# Create dynamic inventory script for cloud environments
cat > inventories/production/aws_ec2.yml <<EOF
plugin: amazon.aws.aws_ec2
regions:
- us-west-2
- us-east-1
filters:
tag:Environment: production
tag:Ansible: managed
instance-state-name: running
hostnames:
- tag:Name
- dns-name
- private-ip-address
compose:
ansible_host: private_ip_address
ec2_state: ec2_state_name
ec2_arch: ec2_architecture
groups:
# Group by instance type
webservers: "'web' in tags.Role"
databases: "'db' in tags.Role"
loadbalancers: "'lb' in tags.Role"
# Group by environment
production: "tags.Environment == 'production'"
staging: "tags.Environment == 'staging'"
# Group by availability zone
us_west_2a: ec2_placement_availability_zone == "us-west-2a"
us_west_2b: ec2_placement_availability_zone == "us-west-2b"
keyed_groups:
# Create groups based on tags
- key: tags.Environment
prefix: env
- key: tags.Role
prefix: role
- key: ec2_instance_type
prefix: type
EOF
Ansible Vault Security
# Create strong vault password
openssl rand -base64 32 > ~/.ansible_vault_pass
chmod 600 ~/.ansible_vault_pass
# Create encrypted vault file for secrets
ansible-vault create group_vars/all/vault.yml
# Enter secure passwords and API keys:
# vault_mysql_root_password: your_secure_mysql_password
# vault_grafana_admin_password: your_secure_grafana_password
# vault_ssl_private_key: |
# -----BEGIN PRIVATE KEY-----
# your_private_key_content
# -----END PRIVATE KEY-----
# Create non-encrypted variables file
cat > group_vars/all/vars.yml <<EOF
# Non-sensitive variables
mysql_port: 3306
nginx_port: 80
grafana_port: 3000
prometheus_port: 9090
# Reference vault variables
mysql_root_password: "{{ vault_mysql_root_password }}"
grafana_admin_password: "{{ vault_grafana_admin_password }}"
# SSL configuration
ssl_certificate_path: /etc/ssl/certs/server.crt
ssl_private_key_path: /etc/ssl/private/server.key
ssl_private_key_content: "{{ vault_ssl_private_key }}"
# Security settings
ansible_ssh_common_args: '-o StrictHostKeyChecking=yes -o UserKnownHostsFile=~/.ssh/known_hosts'
ansible_become_method: sudo
ansible_become_user: root
EOF
# Edit vault file
ansible-vault edit group_vars/all/vault.yml
# View vault file (read-only)
ansible-vault view group_vars/all/vault.yml
# Change vault password
ansible-vault rekey group_vars/all/vault.yml
Comprehensive Playbooks
System Hardening Playbook
cat > playbooks/system-hardening.yml <<EOF
---
- name: System Security Hardening
hosts: all
become: yes
gather_facts: yes
vars:
security_packages:
- fail2ban
- ufw
- aide
- chkrootkit
- rkhunter
- clamav
- lynis
disabled_services:
- telnet
- rsh
- rlogin
- tftp
- talk
- finger
pre_tasks:
- name: Update package cache
package:
update_cache: yes
cache_valid_time: 3600
when: ansible_os_family in ["Debian", "RedHat"]
tasks:
# System updates
- name: Upgrade all packages
package:
name: "*"
state: latest
when: ansible_os_family == "RedHat"
- name: Upgrade all packages (Debian/Ubuntu)
apt:
upgrade: dist
autoremove: yes
autoclean: yes
when: ansible_os_family == "Debian"
# Install security packages
- name: Install security packages
package:
name: "{{ security_packages }}"
state: present
# User security
- name: Create ansible user with limited privileges
user:
name: ansible
groups: sudo
shell: /bin/bash
create_home: yes
generate_ssh_key: yes
ssh_key_bits: 4096
ssh_key_type: rsa
- name: Configure sudo for ansible user
lineinfile:
path: /etc/sudoers.d/ansible
line: 'ansible ALL=(ALL) NOPASSWD:ALL'
create: yes
mode: '0440'
validate: 'visudo -cf %s'
# SSH hardening
- name: Configure SSH security
lineinfile:
path: /etc/ssh/sshd_config
regexp: "{{ item.regexp }}"
line: "{{ item.line }}"
backup: yes
loop:
- { regexp: '^#?PermitRootLogin', line: 'PermitRootLogin no' }
- { regexp: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' }
- { regexp: '^#?X11Forwarding', line: 'X11Forwarding no' }
- { regexp: '^#?MaxAuthTries', line: 'MaxAuthTries 3' }
- { regexp: '^#?ClientAliveInterval', line: 'ClientAliveInterval 300' }
- { regexp: '^#?ClientAliveCountMax', line: 'ClientAliveCountMax 0' }
- { regexp: '^#?Protocol', line: 'Protocol 2' }
- { regexp: '^#?LogLevel', line: 'LogLevel VERBOSE' }
notify: restart ssh
# Firewall configuration
- name: Configure UFW firewall (Debian/Ubuntu)
ufw:
state: enabled
policy: deny
direction: incoming
when: ansible_os_family == "Debian"
- name: Allow SSH through UFW
ufw:
rule: allow
port: 22
proto: tcp
comment: 'SSH access'
when: ansible_os_family == "Debian"
- name: Configure firewalld (RHEL/CentOS/Fedora)
firewalld:
state: enabled
permanent: yes
immediate: yes
when: ansible_os_family == "RedHat"
- name: Allow SSH through firewalld
firewalld:
service: ssh
permanent: yes
state: enabled
immediate: yes
when: ansible_os_family == "RedHat"
# Disable unnecessary services
- name: Disable unnecessary services
systemd:
name: "{{ item }}"
enabled: no
state: stopped
loop: "{{ disabled_services }}"
ignore_errors: yes
# File system security
- name: Set proper permissions on sensitive files
file:
path: "{{ item.path }}"
mode: "{{ item.mode }}"
owner: root
group: root
loop:
- { path: '/etc/passwd', mode: '0644' }
- { path: '/etc/shadow', mode: '0640' }
- { path: '/etc/group', mode: '0644' }
- { path: '/etc/gshadow', mode: '0640' }
- { path: '/etc/ssh/sshd_config', mode: '0600' }
# Kernel security parameters
- name: Configure kernel security parameters
sysctl:
name: "{{ item.key }}"
value: "{{ item.value }}"
sysctl_set: yes
state: present
reload: yes
loop:
- { key: 'net.ipv4.ip_forward', value: '0' }
- { key: 'net.ipv4.conf.all.send_redirects', value: '0' }
- { key: 'net.ipv4.conf.default.send_redirects', value: '0' }
- { key: 'net.ipv4.conf.all.accept_source_route', value: '0' }
- { key: 'net.ipv4.conf.default.accept_source_route', value: '0' }
- { key: 'net.ipv4.conf.all.accept_redirects', value: '0' }
- { key: 'net.ipv4.conf.default.accept_redirects', value: '0' }
- { key: 'net.ipv4.conf.all.secure_redirects', value: '0' }
- { key: 'net.ipv4.conf.default.secure_redirects', value: '0' }
- { key: 'net.ipv4.conf.all.log_martians', value: '1' }
- { key: 'net.ipv4.conf.default.log_martians', value: '1' }
- { key: 'kernel.randomize_va_space', value: '2' }
# Configure fail2ban
- name: Configure fail2ban for SSH protection
template:
src: templates/fail2ban-jail.local.j2
dest: /etc/fail2ban/jail.local
backup: yes
notify: restart fail2ban
handlers:
- name: restart ssh
service:
name: "{{ 'ssh' if ansible_os_family == 'Debian' else 'sshd' }}"
state: restarted
- name: restart fail2ban
service:
name: fail2ban
state: restarted
EOF
Application Deployment Playbook
cat > playbooks/web-application-deployment.yml <<EOF
---
- name: Deploy Web Application Stack
hosts: webservers
become: yes
serial: "25%" # Rolling deployment
max_fail_percentage: 10
vars:
app_name: mywebapp
app_version: "{{ app_version | default('latest') }}"
app_port: 3000
nginx_workers: "{{ ansible_processor_cores }}"
pre_tasks:
- name: Check if maintenance mode file exists
stat:
path: /var/www/html/maintenance.html
register: maintenance_mode
- name: Fail if in maintenance mode
fail:
msg: "Server is in maintenance mode"
when: maintenance_mode.stat.exists and not force_deployment | default(false)
tasks:
# Application deployment
- name: Create application user
user:
name: "{{ app_name }}"
system: yes
shell: /bin/false
home: "/opt/{{ app_name }}"
create_home: yes
- name: Create application directories
file:
path: "{{ item }}"
state: directory
owner: "{{ app_name }}"
group: "{{ app_name }}"
mode: '0755'
loop:
- "/opt/{{ app_name }}"
- "/opt/{{ app_name }}/releases"
- "/opt/{{ app_name }}/shared"
- "/var/log/{{ app_name }}"
- name: Download application release
get_url:
url: "https://releases.example.com/{{ app_name }}/{{ app_version }}/{{ app_name }}-{{ app_version }}.tar.gz"
dest: "/tmp/{{ app_name }}-{{ app_version }}.tar.gz"
mode: '0644'
timeout: 300
register: download_result
- name: Extract application
unarchive:
src: "/tmp/{{ app_name }}-{{ app_version }}.tar.gz"
dest: "/opt/{{ app_name }}/releases/"
owner: "{{ app_name }}"
group: "{{ app_name }}"
remote_src: yes
creates: "/opt/{{ app_name }}/releases/{{ app_version }}"
- name: Create symlink to current release
file:
src: "/opt/{{ app_name }}/releases/{{ app_version }}"
dest: "/opt/{{ app_name }}/current"
state: link
owner: "{{ app_name }}"
group: "{{ app_name }}"
notify:
- reload application
- reload nginx
# Configuration management
- name: Deploy application configuration
template:
src: "templates/{{ app_name }}.conf.j2"
dest: "/opt/{{ app_name }}/shared/{{ app_name }}.conf"
owner: "{{ app_name }}"
group: "{{ app_name }}"
mode: '0640'
backup: yes
notify: reload application
# Service management
- name: Deploy systemd service file
template:
src: "templates/{{ app_name }}.service.j2"
dest: "/etc/systemd/system/{{ app_name }}.service"
mode: '0644'
notify:
- daemon reload
- restart application
- name: Enable and start application service
systemd:
name: "{{ app_name }}"
enabled: yes
state: started
daemon_reload: yes
# Health check
- name: Wait for application to be ready
uri:
url: "http://localhost:{{ app_port }}/health"
method: GET
status_code: 200
retries: 30
delay: 10
# Cleanup old releases
- name: Clean up old releases (keep last 3)
shell: |
cd /opt/{{ app_name }}/releases
ls -t | tail -n +4 | xargs rm -rf
args:
executable: /bin/bash
post_tasks:
- name: Verify application is running
uri:
url: "http://{{ ansible_default_ipv4.address }}:{{ app_port }}/health"
method: GET
status_code: 200
delegate_to: localhost
- name: Log deployment success
lineinfile:
path: "/var/log/{{ app_name }}/deployments.log"
line: "{{ ansible_date_time.iso8601 }} - Successfully deployed {{ app_version }} to {{ inventory_hostname }}"
create: yes
handlers:
- name: daemon reload
systemd:
daemon_reload: yes
- name: restart application
systemd:
name: "{{ app_name }}"
state: restarted
- name: reload application
systemd:
name: "{{ app_name }}"
state: reloaded
- name: reload nginx
service:
name: nginx
state: reloaded
EOF
Infrastructure as Code Playbook
cat > playbooks/infrastructure-provisioning.yml <<EOF
---
- name: Infrastructure Provisioning and Configuration
hosts: all
become: yes
strategy: free # Parallel execution
vars:
base_packages:
Debian:
- curl
- wget
- vim
- htop
- git
- python3
- python3-pip
- unzip
- tree
RedHat:
- curl
- wget
- vim
- htop
- git
- python3
- python3-pip
- unzip
- tree
- epel-release
tasks:
# System preparation
- name: Set hostname
hostname:
name: "{{ inventory_hostname }}"
- name: Update /etc/hosts
lineinfile:
path: /etc/hosts
line: "{{ ansible_default_ipv4.address }} {{ inventory_hostname }}"
backup: yes
# Package management
- name: Install base packages
package:
name: "{{ base_packages[ansible_os_family] | default(base_packages['Debian']) }}"
state: present
# Time synchronization
- name: Install and configure NTP
package:
name: "{{ 'ntp' if ansible_os_family == 'Debian' else 'chrony' }}"
state: present
- name: Start and enable time synchronization
service:
name: "{{ 'ntp' if ansible_os_family == 'Debian' else 'chronyd' }}"
state: started
enabled: yes
# Log management
- name: Configure logrotate for application logs
template:
src: templates/app-logrotate.j2
dest: /etc/logrotate.d/applications
mode: '0644'
# Monitoring agent installation
- name: Install Node Exporter for Prometheus monitoring
get_url:
url: "https://github.com/prometheus/node_exporter/releases/download/v1.6.1/node_exporter-1.6.1.linux-amd64.tar.gz"
dest: /tmp/node_exporter.tar.gz
mode: '0644'
- name: Extract Node Exporter
unarchive:
src: /tmp/node_exporter.tar.gz
dest: /tmp
remote_src: yes
- name: Copy Node Exporter binary
copy:
src: /tmp/node_exporter-1.6.1.linux-amd64/node_exporter
dest: /usr/local/bin/node_exporter
mode: '0755'
owner: root
group: root
remote_src: yes
- name: Create node_exporter systemd service
template:
src: templates/node_exporter.service.j2
dest: /etc/systemd/system/node_exporter.service
mode: '0644'
notify:
- daemon reload
- restart node_exporter
# Security configuration
- name: Configure kernel parameters for security
sysctl:
name: "{{ item.key }}"
value: "{{ item.value }}"
sysctl_set: yes
state: present
reload: yes
loop:
- { key: 'kernel.dmesg_restrict', value: '1' }
- { key: 'kernel.kptr_restrict', value: '2' }
- { key: 'kernel.yama.ptrace_scope', value: '1' }
- { key: 'net.ipv4.conf.all.log_martians', value: '1' }
- { key: 'net.ipv4.conf.default.log_martians', value: '1' }
- { key: 'net.ipv4.conf.all.send_redirects', value: '0' }
- { key: 'net.ipv4.conf.default.send_redirects', value: '0' }
# File integrity monitoring
- name: Initialize AIDE database
shell: |
aide --init
mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
args:
creates: /var/lib/aide/aide.db
- name: Schedule AIDE integrity checks
cron:
name: "AIDE integrity check"
minute: "0"
hour: "3"
job: "/usr/bin/aide --check"
user: root
handlers:
- name: daemon reload
systemd:
daemon_reload: yes
- name: restart node_exporter
service:
name: node_exporter
state: restarted
enabled: yes
EOF
Advanced Role Development
Comprehensive NGINX Role
# Create NGINX role structure
ansible-galaxy init roles/nginx
# Main tasks
cat > roles/nginx/tasks/main.yml <<EOF
---
# NGINX Installation and Configuration Role
- name: Include OS-specific variables
include_vars: "{{ ansible_os_family }}.yml"
- name: Install NGINX
include_tasks: "install-{{ ansible_os_family }}.yml"
- name: Create NGINX directories
file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: '0755'
loop:
- /etc/nginx/sites-available
- /etc/nginx/sites-enabled
- /etc/nginx/conf.d
- /var/log/nginx
- /var/cache/nginx
- name: Generate DH parameters
openssl_dhparam:
path: /etc/ssl/certs/dhparam.pem
size: 2048
when: nginx_ssl_enabled | default(false)
- name: Deploy NGINX main configuration
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
backup: yes
validate: 'nginx -t -c %s'
notify: reload nginx
- name: Deploy virtual host configurations
template:
src: vhost.conf.j2
dest: "/etc/nginx/sites-available/{{ item.name }}.conf"
backup: yes
loop: "{{ nginx_vhosts | default([]) }}"
notify: reload 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 | default([]) }}"
when: item.enabled | default(true)
notify: reload nginx
- name: Remove default site
file:
path: /etc/nginx/sites-enabled/default
state: absent
notify: reload nginx
- name: Start and enable NGINX
service:
name: nginx
state: started
enabled: yes
- name: Configure log rotation
template:
src: nginx-logrotate.j2
dest: /etc/logrotate.d/nginx
mode: '0644'
- name: Setup NGINX monitoring
include_tasks: monitoring.yml
when: nginx_monitoring_enabled | default(false)
- name: Configure SSL certificates
include_tasks: ssl.yml
when: nginx_ssl_enabled | default(false)
EOF
# OS-specific installation tasks
cat > roles/nginx/tasks/install-Debian.yml <<EOF
---
- name: Add NGINX signing key (Debian/Ubuntu)
apt_key:
url: https://nginx.org/keys/nginx_signing.key
state: present
- name: Add NGINX repository (Debian/Ubuntu)
apt_repository:
repo: "deb https://nginx.org/packages/{{ ansible_distribution | lower }}/ {{ ansible_distribution_release }} nginx"
state: present
- name: Install NGINX (Debian/Ubuntu)
apt:
name: nginx
state: present
update_cache: yes
EOF
cat > roles/nginx/tasks/install-RedHat.yml <<EOF
---
- name: Add NGINX repository (RHEL/CentOS)
yum_repository:
name: nginx
description: NGINX Repository
baseurl: "https://nginx.org/packages/centos/{{ ansible_distribution_major_version }}/$basearch/"
gpgcheck: yes
gpgkey: https://nginx.org/keys/nginx_signing.key
enabled: yes
- name: Install NGINX (RHEL/CentOS)
yum:
name: nginx
state: present
EOF
# Variables
cat > roles/nginx/vars/main.yml <<EOF
---
nginx_user: nginx
nginx_worker_processes: auto
nginx_worker_connections: 1024
nginx_keepalive_timeout: 65
nginx_ssl_enabled: false
nginx_monitoring_enabled: true
nginx_security_headers:
- "add_header X-Frame-Options SAMEORIGIN always;"
- "add_header X-Content-Type-Options nosniff always;"
- "add_header X-XSS-Protection '1; mode=block' always;"
- "add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains' always;"
nginx_default_vhost:
name: default
listen: 80
server_name: "_"
root: /var/www/html
index: index.html
enabled: false
EOF
# Templates
cat > roles/nginx/templates/nginx.conf.j2 <<EOF
user {{ nginx_user }};
worker_processes {{ nginx_worker_processes }};
pid /var/run/nginx.pid;
events {
worker_connections {{ nginx_worker_connections }};
use epoll;
multi_accept on;
}
http {
# Basic settings
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout {{ nginx_keepalive_timeout }};
types_hash_max_size 2048;
server_tokens off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# SSL configuration
{% if nginx_ssl_enabled %}
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
{% endif %}
# Gzip compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
application/javascript
application/json
application/xml
text/css
text/javascript
text/xml
text/plain;
# Security headers
{% for header in nginx_security_headers %}
{{ header }}
{% endfor %}
# Logging
log_format main '\$remote_addr - \$remote_user [\$time_local] "\$request" '
'\$status \$body_bytes_sent "\$http_referer" '
'"\$http_user_agent" "\$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log;
# Rate limiting
limit_req_zone \$binary_remote_addr zone=login:10m rate=10r/m;
limit_conn_zone \$binary_remote_addr zone=addr:10m;
# Include configurations
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
EOF
# Handlers
cat > roles/nginx/handlers/main.yml <<EOF
---
- name: restart nginx
service:
name: nginx
state: restarted
- name: reload nginx
service:
name: nginx
state: reloaded
- name: validate nginx config
command: nginx -t
changed_when: false
EOF
Security and Compliance
Ansible Security Scanner Integration
# Create security scanning playbook
cat > playbooks/security-scan.yml <<EOF
---
- name: Security Compliance Scanning
hosts: all
become: yes
gather_facts: yes
vars:
scan_results_dir: "/tmp/security-scans"
tasks:
- name: Create scan results directory
file:
path: "{{ scan_results_dir }}"
state: directory
mode: '0755'
# CIS benchmark scanning
- name: Download CIS benchmark script
get_url:
url: "https://github.com/dev-sec/cis-dil-benchmark/archive/master.zip"
dest: "/tmp/cis-benchmark.zip"
mode: '0644'
- name: Run CIS benchmark scan
shell: |
cd /tmp
unzip -o cis-benchmark.zip
cd cis-dil-benchmark-master
bash cis_ubuntu2204.sh > {{ scan_results_dir }}/cis-scan-{{ inventory_hostname }}.txt
args:
creates: "{{ scan_results_dir }}/cis-scan-{{ inventory_hostname }}.txt"
# Lynis security audit
- name: Install Lynis
package:
name: lynis
state: present
- name: Run Lynis security audit
command: lynis audit system --quiet --cronjob
register: lynis_result
changed_when: false
- name: Save Lynis results
copy:
content: "{{ lynis_result.stdout }}"
dest: "{{ scan_results_dir }}/lynis-scan-{{ inventory_hostname }}.txt"
mode: '0644'
# OpenSCAP compliance scanning
- name: Install OpenSCAP (RHEL/CentOS)
package:
name:
- openscap-scanner
- scap-security-guide
state: present
when: ansible_os_family == "RedHat"
- name: Install OpenSCAP (Debian/Ubuntu)
package:
name:
- libopenscap8
- ssg-debian
state: present
when: ansible_os_family == "Debian"
- name: Run OpenSCAP scan
shell: |
oscap xccdf eval --profile xccdf_org.ssgproject.content_profile_standard \
--results {{ scan_results_dir }}/oscap-results-{{ inventory_hostname }}.xml \
--report {{ scan_results_dir }}/oscap-report-{{ inventory_hostname }}.html \
/usr/share/xml/scap/ssg/content/ssg-{{ ansible_distribution | lower }}{{ ansible_distribution_major_version }}-xccdf.xml
ignore_errors: yes
when: ansible_os_family in ["RedHat", "Debian"]
# Vulnerability scanning with Trivy
- name: Install Trivy vulnerability scanner
shell: |
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
args:
creates: /usr/local/bin/trivy
- name: Run Trivy filesystem scan
shell: |
trivy fs --format json --output {{ scan_results_dir }}/trivy-scan-{{ inventory_hostname }}.json /
ignore_errors: yes
# Collect scan results
- name: Fetch scan results to control machine
fetch:
src: "{{ item }}"
dest: "./security-reports/{{ inventory_hostname }}/"
flat: yes
loop:
- "{{ scan_results_dir }}/cis-scan-{{ inventory_hostname }}.txt"
- "{{ scan_results_dir }}/lynis-scan-{{ inventory_hostname }}.txt"
- "{{ scan_results_dir }}/oscap-report-{{ inventory_hostname }}.html"
- "{{ scan_results_dir }}/trivy-scan-{{ inventory_hostname }}.json"
ignore_errors: yes
delegate_to: localhost
post_tasks:
- name: Generate compliance summary
template:
src: templates/compliance-summary.j2
dest: "{{ scan_results_dir }}/compliance-summary-{{ inventory_hostname }}.txt"
mode: '0644'
EOF
Ansible AWX/Tower Integration
# Install Ansible AWX (open source)
cat > playbooks/install-awx.yml <<EOF
---
- name: Install Ansible AWX
hosts: localhost
connection: local
become: yes
vars:
awx_namespace: awx
awx_admin_user: admin
awx_admin_password: "{{ vault_awx_admin_password }}"
tasks:
- name: Install prerequisite packages
package:
name:
- git
- curl
- docker.io
- docker-compose
state: present
- name: Clone AWX repository
git:
repo: https://github.com/ansible/awx.git
dest: /opt/awx
version: devel
- name: Create AWX Docker inventory
template:
src: templates/awx-docker-inventory.j2
dest: /opt/awx/installer/inventory
mode: '0644'
- name: Install AWX using Ansible
shell: ansible-playbook -i inventory install.yml
args:
chdir: /opt/awx/installer
environment:
ANSIBLE_HOST_KEY_CHECKING: False
- name: Wait for AWX to be ready
uri:
url: "http://localhost:80/api/v2/ping/"
method: GET
retries: 30
delay: 10
- name: Configure AWX organizations and projects
uri:
url: "http://localhost:80/api/v2/organizations/"
method: POST
user: "{{ awx_admin_user }}"
password: "{{ awx_admin_password }}"
force_basic_auth: yes
body_format: json
body:
name: "Production"
description: "Production environment organization"
status_code: [200, 201, 409]
EOF
Multi-Cloud Automation
AWS Infrastructure Automation
cat > playbooks/aws-infrastructure.yml <<EOF
---
- name: AWS Infrastructure Automation
hosts: localhost
connection: local
gather_facts: no
vars:
aws_region: "{{ aws_region | default('us-west-2') }}"
vpc_cidr: "{{ vpc_cidr | default('10.0.0.0/16') }}"
environment: "{{ environment | default('production') }}"
tasks:
# VPC Creation
- name: Create VPC
amazon.aws.ec2_vpc_info:
filters:
"tag:Name": "{{ environment }}-vpc"
region: "{{ aws_region }}"
register: existing_vpc
- name: Create new VPC if not exists
amazon.aws.ec2_vpc_net:
name: "{{ environment }}-vpc"
cidr_block: "{{ vpc_cidr }}"
region: "{{ aws_region }}"
state: present
dns_hostnames: yes
dns_support: yes
tags:
Environment: "{{ environment }}"
ManagedBy: ansible
register: vpc
when: existing_vpc.vpcs | length == 0
# Security Groups
- name: Create web security group
amazon.aws.ec2_group:
name: "{{ environment }}-web-sg"
description: "Security group for web servers"
vpc_id: "{{ vpc.vpc.id if vpc.vpc is defined else existing_vpc.vpcs[0].vpc_id }}"
region: "{{ aws_region }}"
rules:
- proto: tcp
ports:
- 80
- 443
cidr_ip: 0.0.0.0/0
rule_desc: "HTTP and HTTPS access"
- proto: tcp
ports:
- 22
group_id: "{{ environment }}-bastion-sg"
rule_desc: "SSH from bastion"
tags:
Environment: "{{ environment }}"
# Launch EC2 instances
- name: Launch web servers
amazon.aws.ec2_instance:
name: "{{ environment }}-web-{{ item }}"
instance_type: t3.medium
image_id: ami-0c02fb55956c7d316 # Amazon Linux 2
key_name: "{{ ec2_key_name }}"
vpc_subnet_id: "{{ web_subnet_id }}"
security_groups:
- "{{ environment }}-web-sg"
region: "{{ aws_region }}"
state: running
wait: yes
wait_timeout: 300
user_data: |
#!/bin/bash
yum update -y
yum install -y python3
tags:
Environment: "{{ environment }}"
Role: webserver
Ansible: managed
loop: "{{ range(1, web_server_count + 1) | list }}"
register: web_instances
# Add instances to inventory
- name: Add web servers to inventory
add_host:
name: "{{ item.instances[0].tags.Name }}"
hostname: "{{ item.instances[0].public_ip_address }}"
groups: webservers
ansible_ssh_private_key_file: "~/.ssh/{{ ec2_key_name }}.pem"
ansible_user: ec2-user
loop: "{{ web_instances.results }}"
changed_when: false
EOF
Kubernetes Cluster Management
cat > playbooks/k8s-cluster-management.yml <<EOF
---
- name: Kubernetes Cluster Management with Ansible
hosts: k8s_masters
become: yes
serial: 1
vars:
k8s_version: "1.28.2"
containerd_version: "1.6.24"
cni_version: "1.3.0"
tasks:
# Pre-flight checks
- name: Check system requirements
assert:
that:
- ansible_memtotal_mb >= 1700
- ansible_processor_cores >= 2
fail_msg: "System doesn't meet minimum requirements"
- name: Verify connectivity to all nodes
ping:
delegate_to: "{{ item }}"
loop: "{{ groups['k8s_all'] }}"
# Container runtime setup
- name: Install containerd
include_role:
name: containerd
vars:
containerd_version: "{{ containerd_version }}"
# Kubernetes installation
- name: Install Kubernetes components
include_role:
name: kubernetes
vars:
kubernetes_version: "{{ k8s_version }}"
# Cluster initialization
- name: Initialize Kubernetes cluster
shell: |
kubeadm init \
--pod-network-cidr=10.244.0.0/16 \
--service-cidr=10.96.0.0/12 \
--apiserver-advertise-address={{ ansible_default_ipv4.address }} \
--node-name={{ inventory_hostname }}
args:
creates: /etc/kubernetes/admin.conf
register: kubeadm_init
- name: Create .kube directory
file:
path: "{{ ansible_env.HOME }}/.kube"
state: directory
mode: '0755'
- name: Copy admin.conf to user's kube config
copy:
src: /etc/kubernetes/admin.conf
dest: "{{ ansible_env.HOME }}/.kube/config"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0644'
remote_src: yes
# Network plugin installation
- name: Install Flannel CNI
shell: kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
environment:
KUBECONFIG: "{{ ansible_env.HOME }}/.kube/config"
# Join worker nodes
- name: Get join command
shell: kubeadm token create --print-join-command
register: join_command
when: inventory_hostname in groups['k8s_masters'][0]
- name: Join worker nodes to cluster
shell: "{{ hostvars[groups['k8s_masters'][0]]['join_command'].stdout }}"
when: inventory_hostname in groups['k8s_workers']
post_tasks:
- name: Verify cluster status
shell: kubectl get nodes
environment:
KUBECONFIG: "{{ ansible_env.HOME }}/.kube/config"
register: cluster_status
when: inventory_hostname in groups['k8s_masters'][0]
- name: Display cluster status
debug:
var: cluster_status.stdout_lines
when: inventory_hostname in groups['k8s_masters'][0]
EOF
Performance Optimization
System-Level Tuning
# Optimize control machine for Ansible performance
# Kernel parameters for network performance
sudo tee -a /etc/sysctl.conf > /dev/null <<EOF
# Ansible performance optimization
net.core.somaxconn = 8192
net.core.netdev_max_backlog = 5000
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_max_tw_buckets = 400000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 10
net.ipv4.ip_local_port_range = 1024 65535
net.core.rmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_default = 262144
net.core.wmem_max = 16777216
fs.file-max = 100000
EOF
sudo sysctl -p
# Set resource limits for Ansible user
sudo tee -a /etc/security/limits.conf > /dev/null <<EOF
ansible soft nofile 65536
ansible hard nofile 65536
ansible soft nproc 32768
ansible hard nproc 32768
EOF
# SSH client optimization
mkdir -p ~/.ssh
cat >> ~/.ssh/config <<EOF
Host *
ControlMaster auto
ControlPersist 3600
ControlPath ~/.ssh/sockets/%r@%h-%p
Compression yes
ServerAliveInterval 60
ServerAliveCountMax 3
TCPKeepAlive yes
ConnectTimeout 10
StrictHostKeyChecking yes
UserKnownHostsFile ~/.ssh/known_hosts
EOF
mkdir -p ~/.ssh/sockets
Ansible Performance Configuration
# High-performance ansible.cfg
cat > ansible-performance.cfg <<EOF
[defaults]
# Core performance settings
forks = 50
poll_interval = 1
internal_poll_interval = 0.001
timeout = 30
host_key_checking = True
gather_timeout = 30
gathering = smart
fact_caching = redis
fact_caching_connection = localhost:6379:0
fact_caching_timeout = 86400
cache_plugins = memory
# Callback and display optimization
stdout_callback = yaml
callbacks_enabled = timer, profile_tasks, profile_roles
display_skipped_hosts = False
display_ok_hosts = False
display_failed_stderr = True
# SSH optimization
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=3600s -o PreferredAuthentications=publickey -o Compression=yes
pipelining = True
control_path = ~/.ansible/sockets/%%h-%%p-%%r
retries = 3
ssh_executable = /usr/bin/ssh
# Connection persistence
[persistent_connection]
connect_timeout = 30
connect_retry_timeout = 15
command_timeout = 30
# Privilege escalation optimization
[privilege_escalation]
become_plugins = sudo, su, pbrun, pfexec, doas, dzdo, ksu, runas, machinectl
become_allow_same_user = False
become_ask_pass = False
EOF
# Use Redis for fact caching (install Redis first)
sudo apt install -y redis-server # or relevant package manager
pip install redis
# Create performance monitoring script
cat > ansible-performance-monitor.sh <<'EOF'
#!/bin/bash
# Monitor Ansible performance metrics
PLAYBOOK="$1"
if [ -z "$PLAYBOOK" ]; then
echo "Usage: $0 <playbook>"
exit 1
fi
# Enable profiling
export ANSIBLE_CALLBACK_PLUGINS="~/.ansible/plugins/callback"
export ANSIBLE_CALLBACKS_ENABLED="timer,profile_tasks,profile_roles"
# Monitor system resources during playbook execution
(
while pgrep -f ansible-playbook > /dev/null; do
echo "$(date): CPU: $(top -bn1 | grep "Cpu(s)" | awk '{print $2}'), Memory: $(free -m | awk 'NR==2{printf "%.1f%%", $3*100/$2 }')"
sleep 5
done
) &
# Run playbook with timing
time ansible-playbook "$PLAYBOOK" --diff
# Kill monitoring
pkill -P $$
EOF
chmod +x ansible-performance-monitor.sh
Parallel Execution Strategies
# Create high-performance playbook configuration
cat > playbooks/high-performance-deployment.yml <<EOF
---
- name: High-Performance Deployment
hosts: all
become: yes
strategy: free # Parallel execution
serial: "30%" # Process 30% of hosts at a time
max_fail_percentage: 10
gather_facts: yes
fact_caching: smart
vars:
deployment_batch_size: 10
max_concurrent_tasks: 50
pre_tasks:
- name: Check system load
shell: uptime | awk '{print $(NF-2)}' | sed 's/,//'
register: system_load
changed_when: false
- name: Skip high-load systems
meta: end_host
when: system_load.stdout | float > 5.0
tasks:
- name: Update packages with retries
package:
name: "*"
state: latest
retries: 3
delay: 30
async: 300
poll: 10
- name: Deploy configuration files in parallel
template:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
mode: "{{ item.mode | default('0644') }}"
backup: yes
loop:
- { src: "nginx.conf.j2", dest: "/etc/nginx/nginx.conf" }
- { src: "mysql.cnf.j2", dest: "/etc/mysql/my.cnf" }
- { src: "redis.conf.j2", dest: "/etc/redis/redis.conf" }
async: 120
poll: 5
register: config_deployment
- name: Wait for all configuration deployments
async_status:
jid: "{{ item.ansible_job_id }}"
loop: "{{ config_deployment.results }}"
when: item.ansible_job_id is defined
handlers:
- name: restart services
service:
name: "{{ item }}"
state: restarted
loop:
- nginx
- mysql
- redis
listen: "restart all services"
EOF
# Performance monitoring playbook
cat > playbooks/performance-monitoring.yml <<EOF
---
- name: Performance Monitoring Setup
hosts: all
become: yes
vars:
monitoring_tools:
- htop
- iotop
- nethogs
- dstat
- sysstat
- perf
tasks:
- name: Install performance monitoring tools
package:
name: "{{ monitoring_tools }}"
state: present
- name: Configure system monitoring
template:
src: templates/sysstat.j2
dest: /etc/default/sysstat
backup: yes
when: ansible_os_family == "Debian"
- name: Enable system statistics collection
service:
name: "{{ 'sysstat' if ansible_os_family == 'Debian' else 'sysstat' }}"
enabled: yes
state: started
- name: Create performance monitoring script
template:
src: templates/performance-monitor.sh.j2
dest: /usr/local/bin/performance-monitor.sh
mode: '0755'
- name: Schedule performance monitoring
cron:
name: "Performance monitoring"
minute: "*/5"
job: "/usr/local/bin/performance-monitor.sh"
user: root
EOF
Reverse Proxy Setup
nginx Configuration for Ansible AWX/Tower
# /etc/nginx/sites-available/ansible-awx
upstream ansible_awx {
server 127.0.0.1:8080;
server 127.0.0.1:8081 backup;
}
server {
listen 80;
listen [::]:80;
server_name ansible.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name ansible.example.com;
# SSL Configuration
ssl_certificate /etc/ssl/certs/ansible.example.com.crt;
ssl_certificate_key /etc/ssl/private/ansible.example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# Security headers
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
location / {
proxy_pass http://ansible_awx;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_request_buffering off;
}
# WebSocket support
location /websocket/ {
proxy_pass http://ansible_awx;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
HAProxy Configuration
# /etc/haproxy/haproxy.cfg
global
daemon
chroot /var/lib/haproxy
user haproxy
group haproxy
log stdout local0 info
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
option httplog
frontend ansible_frontend
bind *:80
bind *:443 ssl crt /etc/ssl/certs/ansible.pem
redirect scheme https if !{ ssl_fc }
default_backend ansible_backend
backend ansible_backend
balance roundrobin
option httpchk GET /api/v2/ping/
server awx1 127.0.0.1:8080 check
server awx2 127.0.0.1:8081 check backup
Apache Configuration
# /etc/apache2/sites-available/ansible-awx.conf
<VirtualHost *:80>
ServerName ansible.example.com
Redirect permanent / https://ansible.example.com/
</VirtualHost>
<VirtualHost *:443>
ServerName ansible.example.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/ansible.example.com.crt
SSLCertificateKeyFile /etc/ssl/private/ansible.example.com.key
# Security headers
Header always set X-Frame-Options DENY
Header always set X-Content-Type-Options nosniff
Header always set X-XSS-Protection "1; mode=block"
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
ProxyRequests Off
ProxyPreserveHost On
ProxyPass /websocket/ ws://127.0.0.1:8080/websocket/
ProxyPassReverse /websocket/ ws://127.0.0.1:8080/websocket/
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>
Monitoring
Built-in Monitoring and Logging
# Create comprehensive monitoring playbook
cat > playbooks/ansible-monitoring.yml <<EOF
---
- name: Ansible Infrastructure Monitoring
hosts: all
become: yes
vars:
monitoring_tools:
- name: prometheus
port: 9090
- name: grafana
port: 3000
- name: alertmanager
port: 9093
log_paths:
- /var/log/ansible
- /var/log/awx
- /var/log/syslog
- /var/log/auth.log
tasks:
# Install monitoring agents
- name: Install Prometheus Node Exporter
get_url:
url: https://github.com/prometheus/node_exporter/releases/download/v1.6.1/node_exporter-1.6.1.linux-amd64.tar.gz
dest: /tmp/node_exporter.tar.gz
mode: '0644'
- name: Extract Node Exporter
unarchive:
src: /tmp/node_exporter.tar.gz
dest: /opt/
remote_src: yes
creates: /opt/node_exporter-1.6.1.linux-amd64
- name: Create node_exporter user
user:
name: node_exporter
system: yes
shell: /bin/false
home: /var/lib/node_exporter
- name: Install Node Exporter binary
copy:
src: /opt/node_exporter-1.6.1.linux-amd64/node_exporter
dest: /usr/local/bin/node_exporter
mode: '0755'
owner: node_exporter
group: node_exporter
remote_src: yes
- name: Create Node Exporter service
template:
src: templates/node_exporter.service.j2
dest: /etc/systemd/system/node_exporter.service
notify: restart node_exporter
- name: Enable and start Node Exporter
systemd:
name: node_exporter
state: started
enabled: yes
daemon_reload: yes
# Log monitoring setup
- name: Install rsyslog for centralized logging
package:
name: rsyslog
state: present
- name: Configure rsyslog for Ansible logs
template:
src: templates/ansible-rsyslog.conf.j2
dest: /etc/rsyslog.d/50-ansible.conf
backup: yes
notify: restart rsyslog
# Create monitoring scripts
- name: Create Ansible health check script
template:
src: templates/ansible-health-check.sh.j2
dest: /usr/local/bin/ansible-health-check.sh
mode: '0755'
- name: Schedule health checks
cron:
name: "Ansible health check"
minute: "*/5"
job: "/usr/local/bin/ansible-health-check.sh"
user: root
handlers:
- name: restart node_exporter
systemd:
name: node_exporter
state: restarted
- name: restart rsyslog
systemd:
name: rsyslog
state: restarted
EOF
Prometheus Configuration for Ansible
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "ansible_rules.yml"
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
scrape_configs:
- job_name: 'ansible-nodes'
static_configs:
- targets:
- 'ansible-control:9100'
- 'web-01:9100'
- 'web-02:9100'
- 'db-01:9100'
- job_name: 'ansible-awx'
static_configs:
- targets:
- 'awx-web:8080'
- 'awx-task:8080'
metrics_path: '/api/v2/metrics/'
scrape_interval: 30s
- job_name: 'node-exporter'
static_configs:
- targets:
- 'node1:9100'
- 'node2:9100'
- 'node3:9100'
Grafana Dashboard Configuration
{
"dashboard": {
"title": "Ansible Infrastructure Monitoring",
"panels": [
{
"title": "Ansible Job Success Rate",
"type": "stat",
"targets": [
{
"expr": "(ansible_job_successful_total / ansible_job_total) * 100",
"legendFormat": "Success Rate %"
}
]
},
{
"title": "Node Resource Usage",
"type": "graph",
"targets": [
{
"expr": "100 - (avg(rate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100)",
"legendFormat": "CPU Usage %"
},
{
"expr": "(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100",
"legendFormat": "Memory Usage %"
}
]
}
]
}
}
Integration Examples
Python Integration with Ansible
#!/usr/bin/env python3
# ansible_integration.py
import ansible_runner
import subprocess
import json
import os
from pathlib import Path
class AnsibleManager:
def __init__(self, project_dir, inventory_path):
self.project_dir = Path(project_dir)
self.inventory_path = inventory_path
self.private_data_dir = self.project_dir / 'runner_data'
def run_playbook(self, playbook_name, extra_vars=None, limit=None):
"""Run an Ansible playbook with error handling"""
try:
result = ansible_runner.run(
private_data_dir=str(self.private_data_dir),
playbook=playbook_name,
inventory=self.inventory_path,
extravars=extra_vars or {},
limit=limit,
verbosity=1
)
return {
'status': result.status,
'rc': result.rc,
'stdout': result.stdout.read() if result.stdout else '',
'stats': result.stats
}
except Exception as e:
return {'status': 'failed', 'error': str(e)}
def run_ad_hoc_command(self, module, module_args, hosts='all'):
"""Run ad-hoc Ansible commands"""
try:
result = ansible_runner.run(
private_data_dir=str(self.private_data_dir),
inventory=self.inventory_path,
module=module,
module_args=module_args,
host_pattern=hosts
)
return {
'status': result.status,
'rc': result.rc,
'events': [event for event in result.events if event['event'] == 'runner_on_ok']
}
except Exception as e:
return {'status': 'failed', 'error': str(e)}
def get_inventory_info(self):
"""Get inventory information"""
cmd = ['ansible-inventory', '-i', self.inventory_path, '--list']
try:
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
return json.loads(result.stdout)
except subprocess.CalledProcessError as e:
return {'error': e.stderr}
# Example usage
if __name__ == '__main__':
ansible_mgr = AnsibleManager(
project_dir='/home/ansible/infrastructure',
inventory_path='inventories/production/hosts.yml'
)
# Run a playbook
result = ansible_mgr.run_playbook(
'site.yml',
extra_vars={'environment': 'production'},
limit='webservers'
)
print(f"Playbook execution: {result['status']}")
# Run ad-hoc command
ping_result = ansible_mgr.run_ad_hoc_command('ping', '', 'all')
print(f"Ping test: {ping_result['status']}")
# Get inventory info
inventory = ansible_mgr.get_inventory_info()
print(f"Managed hosts: {len(inventory.get('_meta', {}).get('hostvars', {}))}")
Node.js Integration
// ansible_integration.js
const { spawn, exec } = require('child_process');
const path = require('path');
const fs = require('fs').promises;
class AnsibleManager {
constructor(projectDir, inventoryPath) {
this.projectDir = projectDir;
this.inventoryPath = inventoryPath;
}
async runPlaybook(playbookName, options = {}) {
return new Promise((resolve, reject) => {
const args = [
'ansible-playbook',
'-i', this.inventoryPath,
path.join(this.projectDir, 'playbooks', playbookName)
];
if (options.limit) {
args.push('--limit', options.limit);
}
if (options.extraVars) {
args.push('--extra-vars', JSON.stringify(options.extraVars));
}
if (options.check) {
args.push('--check');
}
const process = spawn(args[0], args.slice(1), {
cwd: this.projectDir,
stdio: ['pipe', 'pipe', 'pipe']
});
let stdout = '';
let stderr = '';
process.stdout.on('data', (data) => {
stdout += data.toString();
});
process.stderr.on('data', (data) => {
stderr += data.toString();
});
process.on('close', (code) => {
resolve({
exitCode: code,
stdout: stdout,
stderr: stderr,
success: code === 0
});
});
process.on('error', reject);
});
}
async runAdHocCommand(module, args, hosts = 'all') {
return new Promise((resolve, reject) => {
const command = `ansible ${hosts} -i ${this.inventoryPath} -m ${module} -a "${args}"`;
exec(command, { cwd: this.projectDir }, (error, stdout, stderr) => {
if (error) {
reject({ error, stderr });
} else {
resolve({ stdout, stderr });
}
});
});
}
async getInventoryInfo() {
return new Promise((resolve, reject) => {
const command = `ansible-inventory -i ${this.inventoryPath} --list`;
exec(command, { cwd: this.projectDir }, (error, stdout, stderr) => {
if (error) {
reject({ error, stderr });
} else {
try {
resolve(JSON.parse(stdout));
} catch (parseError) {
reject({ error: parseError, stdout });
}
}
});
});
}
}
// Example usage
async function main() {
const ansible = new AnsibleManager('/home/ansible/infrastructure', 'inventories/production/hosts.yml');
try {
// Run playbook
const result = await ansible.runPlaybook('site.yml', {
limit: 'webservers',
extraVars: { environment: 'production' },
check: true
});
console.log('Playbook check result:', result.success);
// Run ad-hoc command
const pingResult = await ansible.runAdHocCommand('ping', '', 'all');
console.log('Ping result:', pingResult.stdout);
// Get inventory
const inventory = await ansible.getInventoryInfo();
console.log('Inventory hosts:', Object.keys(inventory._meta.hostvars).length);
} catch (error) {
console.error('Error:', error);
}
}
if (require.main === module) {
main();
}
module.exports = AnsibleManager;
REST API Integration
#!/usr/bin/env python3
# ansible_rest_api.py
from flask import Flask, request, jsonify
import ansible_runner
import tempfile
import os
import yaml
from pathlib import Path
app = Flask(__name__)
class AnsibleAPI:
def __init__(self, base_dir):
self.base_dir = Path(base_dir)
self.inventory_file = self.base_dir / 'inventory' / 'hosts.yml'
def execute_playbook(self, playbook_content, inventory_data, extra_vars=None):
"""Execute a playbook with given inventory and variables"""
with tempfile.TemporaryDirectory() as tmpdir:
tmpdir = Path(tmpdir)
# Write playbook
playbook_file = tmpdir / 'playbook.yml'
with open(playbook_file, 'w') as f:
yaml.dump(playbook_content, f, default_flow_style=False)
# Write inventory
inventory_file = tmpdir / 'inventory.yml'
with open(inventory_file, 'w') as f:
yaml.dump(inventory_data, f, default_flow_style=False)
# Run playbook
result = ansible_runner.run(
private_data_dir=str(tmpdir),
playbook='playbook.yml',
inventory=str(inventory_file),
extravars=extra_vars or {}
)
return {
'status': result.status,
'rc': result.rc,
'stats': result.stats,
'events': [e for e in result.events if e['event'] == 'playbook_on_stats']
}
ansible_api = AnsibleAPI('/home/ansible')
@app.route('/api/playbook/run', methods=['POST'])
def run_playbook():
try:
data = request.get_json()
playbook = data.get('playbook')
inventory = data.get('inventory')
extra_vars = data.get('extra_vars', {})
if not playbook or not inventory:
return jsonify({'error': 'playbook and inventory are required'}), 400
result = ansible_api.execute_playbook(playbook, inventory, extra_vars)
return jsonify({
'success': result['status'] == 'successful',
'status': result['status'],
'return_code': result['rc'],
'stats': result['stats']
})
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/api/inventory', methods=['GET'])
def get_inventory():
try:
with open(ansible_api.inventory_file, 'r') as f:
inventory = yaml.safe_load(f)
return jsonify(inventory)
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/api/health', methods=['GET'])
def health_check():
return jsonify({
'status': 'healthy',
'ansible_version': '2.15.0', # This should be dynamic
'base_dir': str(ansible_api.base_dir)
})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=False)
Maintenance
Regular Maintenance Tasks
#!/bin/bash
# ansible-maintenance.sh - Comprehensive maintenance script
# Set variables
ANSIBLE_HOME="/home/ansible"
LOG_FILE="/var/log/ansible-maintenance.log"
BACKUP_DIR="/backup/ansible"
DATE=$(date +"%Y%m%d_%H%M%S")
# Logging function
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
log "Starting Ansible maintenance tasks"
# 1. Update Ansible and collections
log "Updating Ansible and collections"
pip install --upgrade ansible ansible-core
ansible-galaxy collection install --upgrade community.general
ansible-galaxy collection install --upgrade ansible.posix
ansible-galaxy collection install --upgrade kubernetes.core
# 2. Clean up old logs
log "Cleaning up old log files"
find "$ANSIBLE_HOME/logs" -type f -name "*.log" -mtime +30 -delete
find /var/log/ansible -type f -name "*.log" -mtime +30 -delete
# 3. Update facts cache
log "Refreshing facts cache"
ansible all -m setup --tree /tmp/facts_cache/
# 4. Validate all playbooks
log "Validating playbooks syntax"
find "$ANSIBLE_HOME/playbooks" -name "*.yml" -exec ansible-playbook {} --syntax-check \;
if [ $? -eq 0 ]; then
log "All playbooks passed syntax validation"
else
log "ERROR: Some playbooks failed syntax validation"
fi
# 5. Check inventory health
log "Checking inventory health"
ansible-inventory --list > /tmp/inventory_check.json
if [ $? -eq 0 ]; then
log "Inventory syntax is valid"
else
log "ERROR: Inventory has syntax errors"
fi
# 6. Test connectivity to all hosts
log "Testing connectivity to managed hosts"
ansible all -m ping --one-line > "/tmp/ping_results_$DATE.txt"
FAILED_HOSTS=$(grep -c "FAILED" "/tmp/ping_results_$DATE.txt")
if [ "$FAILED_HOSTS" -gt 0 ]; then
log "WARNING: $FAILED_HOSTS hosts failed connectivity test"
grep "FAILED" "/tmp/ping_results_$DATE.txt" >> "$LOG_FILE"
else
log "All hosts are reachable"
fi
# 7. Backup configurations
log "Creating configuration backup"
mkdir -p "$BACKUP_DIR/$DATE"
tar -czf "$BACKUP_DIR/$DATE/ansible-config-$DATE.tar.gz" \
"$ANSIBLE_HOME"/{ansible.cfg,inventories,group_vars,host_vars,playbooks,roles}
# 8. Clean up old backups (keep 30 days)
log "Cleaning up old backups"
find "$BACKUP_DIR" -type d -mtime +30 -exec rm -rf {} \; 2>/dev/null
# 9. Update SSH known_hosts
log "Updating SSH known_hosts"
for host in $(ansible all --list-hosts | grep -v hosts); do
ssh-keyscan -H "$host" >> ~/.ssh/known_hosts 2>/dev/null
done
sort -u ~/.ssh/known_hosts > ~/.ssh/known_hosts.tmp && mv ~/.ssh/known_hosts.tmp ~/.ssh/known_hosts
# 10. Generate maintenance report
log "Generating maintenance report"
REPORT_FILE="/tmp/ansible-maintenance-report-$DATE.txt"
cat > "$REPORT_FILE" << EOF
Ansible Maintenance Report - $DATE
========================================
Ansible Version: $(ansible --version | head -1)
Python Version: $(python3 --version)
System: $(uname -a)
Inventory Summary:
$(ansible-inventory --list | jq '."_meta"."hostvars" | keys | length') managed hosts
Connectivity Test Results:
$(wc -l < "/tmp/ping_results_$DATE.txt") total hosts tested
$FAILED_HOSTS hosts failed connectivity
Playbook Validation: $(find "$ANSIBLE_HOME/playbooks" -name "*.yml" | wc -l) playbooks checked
Disk Usage:
$(df -h "$ANSIBLE_HOME" | tail -1)
Backup Created: $BACKUP_DIR/$DATE/ansible-config-$DATE.tar.gz
Backup Size: $(du -sh "$BACKUP_DIR/$DATE/ansible-config-$DATE.tar.gz" | cut -f1)
Recent Log Entries (last 10):
$(tail -10 "$LOG_FILE")
EOF
log "Maintenance report created: $REPORT_FILE"
# 11. Send report via email (optional)
if command -v mail >/dev/null 2>&1; then
mail -s "Ansible Maintenance Report - $DATE" admin@example.com < "$REPORT_FILE"
log "Maintenance report emailed"
fi
log "Ansible maintenance tasks completed"
# Schedule this script to run weekly
# Add to crontab: 0 2 * * 0 /usr/local/bin/ansible-maintenance.sh
Ansible Version Management
#!/bin/bash
# ansible-version-manager.sh - Manage multiple Ansible versions
ANSIBLE_VERSIONS_DIR="/opt/ansible-versions"
CURRENT_LINK="/opt/ansible/current"
install_ansible_version() {
local version="$1"
local install_dir="$ANSIBLE_VERSIONS_DIR/$version"
if [ -d "$install_dir" ]; then
echo "Ansible $version is already installed"
return 0
fi
echo "Installing Ansible $version..."
python3 -m venv "$install_dir"
source "$install_dir/bin/activate"
pip install --upgrade pip
pip install "ansible-core==$version"
pip install ansible
# Install essential collections
ansible-galaxy collection install community.general
ansible-galaxy collection install ansible.posix
ansible-galaxy collection install kubernetes.core
deactivate
echo "Ansible $version installed successfully"
}
switch_ansible_version() {
local version="$1"
local install_dir="$ANSIBLE_VERSIONS_DIR/$version"
if [ ! -d "$install_dir" ]; then
echo "Ansible $version is not installed"
echo "Available versions:"
ls -1 "$ANSIBLE_VERSIONS_DIR" 2>/dev/null || echo "No versions installed"
return 1
fi
rm -f "$CURRENT_LINK"
ln -s "$install_dir" "$CURRENT_LINK"
echo "Switched to Ansible $version"
echo "Current version: $("$CURRENT_LINK/bin/ansible" --version | head -1)"
}
list_versions() {
echo "Installed Ansible versions:"
ls -1 "$ANSIBLE_VERSIONS_DIR" 2>/dev/null || echo "No versions installed"
if [ -L "$CURRENT_LINK" ]; then
local current=$(readlink "$CURRENT_LINK" | basename)
echo "Current version: $current"
else
echo "No current version set"
fi
}
remove_version() {
local version="$1"
local install_dir="$ANSIBLE_VERSIONS_DIR/$version"
if [ ! -d "$install_dir" ]; then
echo "Ansible $version is not installed"
return 1
fi
if [ "$(readlink "$CURRENT_LINK" 2>/dev/null)" = "$install_dir" ]; then
echo "Cannot remove currently active version"
return 1
fi
rm -rf "$install_dir"
echo "Removed Ansible $version"
}
case "$1" in
install)
install_ansible_version "$2"
;;
switch)
switch_ansible_version "$2"
;;
list)
list_versions
;;
remove)
remove_version "$2"
;;
*)
echo "Usage: $0 {install|switch|list|remove} [version]"
echo "Examples:"
echo " $0 install 2.15.0"
echo " $0 switch 2.15.0"
echo " $0 list"
echo " $0 remove 2.14.0"
exit 1
;;
esac
Performance Monitoring and Optimization
#!/bin/bash
# ansible-performance-monitor.sh - Monitor and optimize Ansible performance
log_performance() {
local playbook="$1"
local start_time=$(date +%s)
local log_file="/var/log/ansible-performance.log"
# Run playbook with timing
/usr/bin/time -v ansible-playbook "$playbook" --extra-vars="gather_facts=True" 2>&1 | \
tee "/tmp/ansible-run-$start_time.log"
local end_time=$(date +%s)
local duration=$((end_time - start_time))
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Playbook: $playbook, Duration: ${duration}s" >> "$log_file"
# Extract performance metrics
local max_memory=$(grep "Maximum resident set size" "/tmp/ansible-run-$start_time.log" | awk '{print $6}')
local user_time=$(grep "User time" "/tmp/ansible-run-$start_time.log" | awk '{print $4}')
local system_time=$(grep "System time" "/tmp/ansible-run-$start_time.log" | awk '{print $4}')
echo "Performance Metrics - Memory: ${max_memory}KB, User: ${user_time}s, System: ${system_time}s" >> "$log_file"
# Clean up
rm "/tmp/ansible-run-$start_time.log"
}
# Example usage in a wrapper script
if [ "$1" = "monitor" ] && [ -n "$2" ]; then
log_performance "$2"
else
echo "Usage: $0 monitor <playbook_path>"
exit 1
fi
Testing and Validation
Molecule Testing Framework
# Install Molecule for role testing
pip install molecule[docker] molecule[vagrant] molecule[libvirt]
# Initialize Molecule in role directory
cd roles/nginx
molecule init scenario --driver-name docker
# Create molecule configuration
cat > molecule/default/molecule.yml <<EOF
---
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: nginx-ubuntu
image: ubuntu:22.04
pre_build_image: true
privileged: true
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:ro
command: /lib/systemd/systemd
networks:
- name: molecule
- name: nginx-centos
image: centos:8
pre_build_image: true
privileged: true
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:ro
command: /usr/sbin/init
networks:
- name: molecule
provisioner:
name: ansible
config_options:
defaults:
callbacks_enabled: profile_tasks,timer,yaml
inventory:
host_vars:
nginx-ubuntu:
ansible_python_interpreter: /usr/bin/python3
nginx-centos:
ansible_python_interpreter: /usr/bin/python3
verifier:
name: ansible
lint: |
set -e
yamllint .
ansible-lint
flake8
EOF
# Create test playbook
cat > molecule/default/converge.yml <<EOF
---
- name: Converge
hosts: all
become: true
tasks:
- name: "Include nginx role"
include_role:
name: nginx
vars:
nginx_vhosts:
- name: default
server_name: localhost
listen: 80
root: /var/www/html
enabled: true
EOF
# Create test verification
cat > molecule/default/verify.yml <<EOF
---
- name: Verify
hosts: all
gather_facts: false
tasks:
- name: Check if NGINX is running
uri:
url: http://localhost
method: GET
register: nginx_check
- name: Verify NGINX is responding
assert:
that:
- nginx_check.status == 200
fail_msg: "NGINX is not responding correctly"
- name: Check NGINX configuration
shell: nginx -t
changed_when: false
- name: Verify NGINX process
shell: pgrep nginx
changed_when: false
EOF
# Run tests
molecule test
Ansible Lint and Security Scanning
# Install Ansible Lint
pip install ansible-lint
# Create .ansible-lint configuration
cat > .ansible-lint <<EOF
---
profile: production
exclude_paths:
- .cache/
- .github/
- molecule/
- .venv/
skip_list:
- yaml[line-length] # Allow longer lines for readability
warn_list:
- experimental # Warn about experimental features
# Custom rules
rules:
braces:
min-spaces-inside: 0
max-spaces-inside: 1
brackets:
min-spaces-inside: 0
max-spaces-inside: 0
colons:
max-spaces-before: 0
min-spaces-after: 1
max-spaces-after: 1
commas:
max-spaces-before: 0
min-spaces-after: 1
max-spaces-after: 1
comments:
min-spaces-from-content: 1
document-start: disable
empty-lines:
max: 2
max-start: 0
max-end: 1
hyphens:
max-spaces-after: 1
indentation:
spaces: 2
indent-sequences: true
line-length:
max: 120
new-line-at-end-of-file: enable
trailing-spaces: enable
truthy:
allowed-values: ['true', 'false', 'yes', 'no']
EOF
# Create security-focused playbook linting
cat > .yamllint <<EOF
---
extends: default
rules:
braces:
min-spaces-inside: 0
max-spaces-inside: 1
brackets:
min-spaces-inside: 0
max-spaces-inside: 0
colons:
max-spaces-before: 0
min-spaces-after: 1
max-spaces-after: 1
commas:
max-spaces-before: 0
min-spaces-after: 1
max-spaces-after: 1
comments: disable
comments-indentation: disable
document-start: disable
empty-lines:
max: 2
max-start: 0
max-end: 1
hyphens:
max-spaces-after: 1
indentation:
spaces: 2
indent-sequences: true
check-multi-line-strings: false
key-duplicates: enable
line-length:
max: 120
new-line-at-end-of-file: enable
octal-values:
forbid-implicit-octal: true
trailing-spaces: enable
truthy: disable
EOF
# Run linting
ansible-lint playbooks/
yamllint .
Backup and Disaster Recovery
Comprehensive Backup Automation
cat > playbooks/backup-automation.yml <<EOF
---
- name: Automated Backup System
hosts: all
become: yes
vars:
backup_root: /backup
backup_retention_days: 30
backup_schedule:
databases: "0 2 * * *"
files: "0 3 * * *"
configs: "0 4 * * *"
tasks:
- name: Create backup directories
file:
path: "{{ backup_root }}/{{ item }}"
state: directory
mode: '0755'
owner: root
group: root
loop:
- databases
- files
- configs
- logs
# Database backups
- name: Create database backup script
template:
src: templates/mysql-backup.sh.j2
dest: /usr/local/bin/mysql-backup.sh
mode: '0755'
when: "'dbservers' in group_names"
- name: Schedule database backups
cron:
name: "MySQL backup"
minute: "0"
hour: "2"
job: "/usr/local/bin/mysql-backup.sh"
user: root
when: "'dbservers' in group_names"
# File system backups
- name: Create file backup script
template:
src: templates/file-backup.sh.j2
dest: /usr/local/bin/file-backup.sh
mode: '0755'
- name: Schedule file backups
cron:
name: "File system backup"
minute: "0"
hour: "3"
job: "/usr/local/bin/file-backup.sh"
user: root
# Configuration backups
- name: Backup system configurations
archive:
path:
- /etc/nginx
- /etc/mysql
- /etc/systemd/system
- /etc/crontab
- /etc/hosts
- /etc/fstab
dest: "{{ backup_root }}/configs/system-config-{{ ansible_date_time.epoch }}.tar.gz"
mode: '0600'
# Remote backup synchronization
- name: Synchronize backups to remote storage
synchronize:
src: "{{ backup_root }}/"
dest: "backup-server.example.com:{{ backup_root }}/{{ inventory_hostname }}/"
delete: yes
rsync_opts:
- "--exclude=*.tmp"
- "--compress"
- "--archive"
when: backup_remote_sync | default(true)
# Cleanup old backups
- name: Clean up old backup files
find:
paths: "{{ backup_root }}"
age: "{{ backup_retention_days }}d"
file_type: file
register: old_backups
- name: Remove old backup files
file:
path: "{{ item.path }}"
state: absent
loop: "{{ old_backups.files }}"
EOF
Verification and Monitoring
Cross-Platform System Verification
# Create comprehensive verification playbook
cat > playbooks/system-verification.yml <<EOF
---
- name: System Verification and Health Check
hosts: all
become: yes
gather_facts: yes
vars:
health_checks:
- service: nginx
port: 80
process: nginx
- service: mysql
port: 3306
process: mysqld
- service: redis
port: 6379
process: redis-server
tasks:
# System information gathering
- name: Gather system information
setup:
gather_subset:
- all
- !facter
- !ohai
# Service verification
- name: Check critical services status
service_facts:
- name: Verify services are running
assert:
that:
- "ansible_facts.services[item.service + '.service'].state == 'running'"
fail_msg: "Service {{ item.service }} is not running"
success_msg: "Service {{ item.service }} is healthy"
loop: "{{ health_checks }}"
when: "item.service + '.service' in ansible_facts.services"
# Network connectivity tests
- name: Test network connectivity
wait_for:
host: "{{ item.host }}"
port: "{{ item.port }}"
timeout: 5
loop:
- { host: "8.8.8.8", port: 53 }
- { host: "1.1.1.1", port: 53 }
ignore_errors: yes
# Disk space monitoring
- name: Check disk space usage
shell: df -h | awk '$5 > "85%" {print $0}'
register: disk_usage
changed_when: false
failed_when: disk_usage.stdout != ""
# Memory usage check
- name: Check memory usage
shell: free | grep Mem | awk '{printf "%.0f", $3/$2 * 100.0}'
register: memory_usage
changed_when: false
- name: Alert on high memory usage
debug:
msg: "WARNING: Memory usage is {{ memory_usage.stdout }}%"
when: memory_usage.stdout | int > 85
# Security verification
- name: Check for failed login attempts
shell: grep "Failed password" /var/log/auth.log | tail -10
register: failed_logins
changed_when: false
ignore_errors: yes
when: ansible_os_family == "Debian"
- name: Check firewall status
shell: |
if command -v ufw >/dev/null 2>&1; then
ufw status
elif command -v firewall-cmd >/dev/null 2>&1; then
firewall-cmd --state
else
echo "No supported firewall found"
fi
register: firewall_status
changed_when: false
post_tasks:
- name: Generate system health report
template:
src: templates/health-report.j2
dest: "/tmp/health-report-{{ inventory_hostname }}-{{ ansible_date_time.epoch }}.txt"
mode: '0644'
- name: Fetch health reports
fetch:
src: "/tmp/health-report-{{ inventory_hostname }}-{{ ansible_date_time.epoch }}.txt"
dest: "./reports/"
flat: yes
EOF
Continuous Compliance Monitoring
cat > playbooks/compliance-monitoring.yml <<EOF
---
- name: Continuous Compliance Monitoring
hosts: all
become: yes
vars:
compliance_standards:
- cis
- pci_dss
- soc2
- hipaa
audit_log_paths:
- /var/log/auth.log
- /var/log/syslog
- /var/log/audit/audit.log
tasks:
# Install audit tools
- name: Install audit daemon
package:
name: "{{ 'auditd' if ansible_os_family == 'RedHat' else 'auditd' }}"
state: present
- name: Configure audit rules
template:
src: templates/audit.rules.j2
dest: /etc/audit/rules.d/audit.rules
backup: yes
notify: restart auditd
# CIS compliance checks
- name: Run CIS benchmark checks
shell: |
if [ -f /opt/cis-cat/CIS-CAT.sh ]; then
/opt/cis-cat/CIS-CAT.sh -b /opt/cis-cat/benchmarks/
else
echo "CIS-CAT not installed, skipping"
fi
register: cis_results
ignore_errors: yes
# File integrity monitoring
- name: Install AIDE
package:
name: aide
state: present
- name: Initialize AIDE database
shell: |
aide --init
mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db
args:
creates: /var/lib/aide/aide.db
- name: Schedule AIDE integrity checks
cron:
name: "AIDE integrity check"
minute: "0"
hour: "3"
job: "/usr/bin/aide --check | mail -s 'AIDE Report' security@example.com"
user: root
# Log monitoring
- name: Install log monitoring tools
package:
name:
- logwatch
- rsyslog
state: present
- name: Configure centralized logging
template:
src: templates/rsyslog.conf.j2
dest: /etc/rsyslog.conf
backup: yes
notify: restart rsyslog
handlers:
- name: restart auditd
service:
name: auditd
state: restarted
- name: restart rsyslog
service:
name: rsyslog
state: restarted
EOF
CI/CD Integration
Jenkins Pipeline Integration
# Create Jenkinsfile for Ansible automation
cat > Jenkinsfile <<EOF
pipeline {
agent any
environment {
ANSIBLE_CONFIG = 'ansible.cfg'
ANSIBLE_HOST_KEY_CHECKING = 'False'
}
stages {
stage('Lint') {
steps {
sh 'ansible-lint playbooks/'
sh 'yamllint .'
}
}
stage('Syntax Check') {
steps {
sh 'ansible-playbook playbooks/site.yml --syntax-check'
}
}
stage('Dry Run') {
steps {
sh 'ansible-playbook playbooks/site.yml --check --diff'
}
}
stage('Deploy to Staging') {
when {
branch 'develop'
}
steps {
sh 'ansible-playbook -i inventories/staging playbooks/site.yml'
}
}
stage('Deploy to Production') {
when {
branch 'main'
}
steps {
input 'Deploy to Production?'
sh 'ansible-playbook -i inventories/production playbooks/site.yml'
}
}
stage('Verify Deployment') {
steps {
sh 'ansible-playbook playbooks/system-verification.yml'
}
}
}
post {
always {
archiveArtifacts artifacts: 'logs/*.log', allowEmptyArchive: true
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'reports',
reportFiles: '*.html',
reportName: 'Ansible Report'
])
}
failure {
emailext (
subject: "Failed Pipeline: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "Something is wrong with ${env.BUILD_URL}",
to: "${env.DEFAULT_RECIPIENTS}"
)
}
}
}
EOF
GitLab CI Integration
cat > .gitlab-ci.yml <<EOF
stages:
- lint
- test
- deploy-staging
- deploy-production
variables:
ANSIBLE_CONFIG: ansible.cfg
ANSIBLE_HOST_KEY_CHECKING: "False"
before_script:
- pip install ansible ansible-lint yamllint
- ansible-galaxy install -r requirements.yml
lint:
stage: lint
script:
- ansible-lint playbooks/
- yamllint .
rules:
- if: '$CI_MERGE_REQUEST_IID'
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
syntax-check:
stage: test
script:
- ansible-playbook playbooks/site.yml --syntax-check
- ansible-playbook playbooks/site.yml --check --diff -i inventories/staging
rules:
- if: '$CI_MERGE_REQUEST_IID'
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
deploy-staging:
stage: deploy-staging
script:
- ansible-playbook -i inventories/staging playbooks/site.yml
rules:
- if: '$CI_COMMIT_BRANCH == "develop"'
deploy-production:
stage: deploy-production
script:
- ansible-playbook -i inventories/production playbooks/site.yml
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
when: manual
environment:
name: production
url: https://production.example.com
EOF
6. Troubleshooting (Cross-Platform)
Common Issues and Solutions
# Debug connection issues
ansible all -m ping -vvv
# Check SSH connectivity
ansible all -m setup --ask-pass --ask-become-pass
# Verify inventory
ansible-inventory --list
ansible-inventory --graph
# Test specific host
ansible target-host -m command -a "uptime"
# Check syntax without execution
ansible-playbook playbooks/site.yml --syntax-check
# Dry run with verbose output
ansible-playbook playbooks/site.yml --check --diff -vvv
# Debug variable resolution
ansible-playbook playbooks/site.yml --extra-vars="debug=true" --tags debug
# Check facts
ansible all -m setup | grep ansible_os_family
# Connection troubleshooting
ssh -vvv user@target-host
# Permission issues
ansible all -m file -a "path=/tmp/test state=touch" --become
# Vault issues
ansible-vault view group_vars/all/vault.yml
# Performance debugging
ansible-playbook playbooks/site.yml --start-at-task="specific task name"
ansible-playbook playbooks/site.yml --step
# Module testing
ansible localhost -m debug -a "var=hostvars"
ansible localhost -m setup
# Check for syntax errors in roles
find roles/ -name "*.yml" -exec ansible-playbook {} --syntax-check \;
Advanced Debugging
# Enable comprehensive logging
export ANSIBLE_DEBUG=1
export ANSIBLE_VERBOSITY=4
ansible-playbook playbooks/site.yml
# Profile playbook execution
ansible-playbook playbooks/site.yml --extra-vars="profile_tasks_sort_order=none"
# Memory usage analysis
ansible-playbook playbooks/site.yml --extra-vars="ansible_python_interpreter=/usr/bin/python3"
# Network debugging
ansible all -m command -a "ss -tulpn"
ansible all -m command -a "netstat -rn"
# Process debugging
ansible all -m command -a "ps aux --sort=-%mem | head -10"
# Disk usage analysis
ansible all -m command -a "df -h"
ansible all -m command -a "du -sh /var/log/*"
# Service debugging
ansible all -m systemd -a "name=nginx" --become
ansible all -m command -a "systemctl status nginx" --become
# Variable debugging
ansible-playbook playbooks/site.yml --extra-vars="debug_vars=true" --tags debug_vars
Additional Resources
---
Note: This guide is part of the HowToMgr collection.