Automating Infrastructure with Ansible: Best Practices

Learn how to automate infrastructure provisioning and configuration using Ansible, with tips for idempotency, modularity, and security.

HA
Hari Prasad
June 10, 2024
5 min read ...
Financial Planning Tool

PPF Calculator

Calculate your Public Provident Fund returns with detailed projections and tax benefits. Plan your financial future with precision.

Try Calculator
Free Forever Secure
10K+
Users
4.9★
Rating
Career Tool

Resume Builder

Create professional DevOps resumes with modern templates. Showcase your skills, experience, and certifications effectively.

Build Resume
No Login Export PDF
15+
Templates
5K+
Created
Kubernetes Tool

EKS Pod Cost Calculator

Calculate Kubernetes pod costs on AWS EKS. Optimize resource allocation and reduce your cloud infrastructure expenses.

Calculate Costs
Accurate Real-time
AWS
EKS Support
$$$
Save Money
AWS Cloud Tool

AWS VPC Designer Pro

Design and visualize AWS VPC architectures with ease. Create production-ready network diagrams with subnets, route tables, and security groups in minutes.

Design VPC
Visual Editor Export IaC
Multi-AZ
HA Design
Pro
Features
Subnets Security Routing
Explore More

Discover My DevOps Journey

Explore my portfolio, read insightful blogs, learn from comprehensive courses, and leverage powerful DevOps tools—all in one place.

50+
Projects
100+
Blog Posts
10+
Courses
20+
Tools

Infrastructure automation is a cornerstone of modern DevOps. Ansible is a powerful, agentless tool for automating server provisioning, configuration, and application deployment. In this comprehensive guide, we’ll explore best practices, real-world examples, and advanced techniques for mastering Ansible.

Why Ansible?

Ansible has become the go-to automation tool for DevOps engineers worldwide. Here’s why:

  • Agentless Architecture: Uses SSH for Linux/Unix and WinRM for Windows, no agent required on managed nodes
  • Idempotent Operations: Ensures repeatable, predictable results without unintended side effects
  • Modular Design: Roles and playbooks promote code reuse and maintainability
  • Simple YAML Syntax: Easy to learn and read, lowering the barrier to entry
  • Extensive Module Library: 5000+ modules covering cloud providers, databases, networking, and more
  • Active Community: Large ecosystem with Galaxy roles and continuous improvements

Getting Started: Installation & Setup

First, let’s set up Ansible on your control node:

# Install Ansible on Ubuntu/Debian
sudo apt update
sudo apt install -y software-properties-common
sudo add-apt-repository --yes --update ppa:ansible/ansible
sudo apt install -y ansible

# Verify installation
ansible --version

# Install Ansible on CentOS/RHEL
sudo yum install -y epel-release
sudo yum install -y ansible

# Install using Python pip
pip3 install ansible

Basic Inventory Configuration

Create an inventory file to define your managed hosts:

# inventory/hosts.ini
[webservers]
web1.example.com ansible_host=192.168.1.10
web2.example.com ansible_host=192.168.1.11

[databases]
db1.example.com ansible_host=192.168.1.20
db2.example.com ansible_host=192.168.1.21

[production:children]
webservers
databases

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

Real-World Example: Provisioning a Web Server

Let’s create a complete playbook to provision and configure an Nginx web server with SSL:

# playbooks/webserver.yml
---
- name: Configure Nginx Web Server with SSL
  hosts: webservers
  become: yes
  vars:
    nginx_port: 80
    nginx_ssl_port: 443
    domain_name: example.com
    ssl_certificate_path: /etc/ssl/certs
    
  tasks:
    - name: Update apt cache
      apt:
        update_cache: yes
        cache_valid_time: 3600
      when: ansible_os_family == "Debian"
    
    - name: Install Nginx
      apt:
        name: nginx
        state: present
      notify: restart nginx
    
    - name: Install SSL certificates
      copy:
        src: ""
        dest: ""
        mode: '0644'
      loop:
        - { src: 'files/.crt', dest: '/.crt' }
        - { src: 'files/.key', dest: '/.key' }
      notify: restart nginx
    
    - name: Deploy Nginx configuration
      template:
        src: templates/nginx.conf.j2
        dest: /etc/nginx/sites-available/
        mode: '0644'
      notify: reload nginx
    
    - name: Enable site configuration
      file:
        src: /etc/nginx/sites-available/
        dest: /etc/nginx/sites-enabled/
        state: link
      notify: reload nginx
    
    - name: Ensure Nginx is running
      service:
        name: nginx
        state: started
        enabled: yes
    
    - name: Configure firewall
      ufw:
        rule: allow
        port: ""
        proto: tcp
      loop:
        - ""
        - ""
  
  handlers:
    - name: restart nginx
      service:
        name: nginx
        state: restarted
    
    - name: reload nginx
      service:
        name: nginx
        state: reloaded

Nginx Configuration Template

Create a Jinja2 template for dynamic configuration:

# templates/nginx.conf.j2
server {
    listen ;
    listen [::]:;
    server_name  www.;
    
    # Redirect HTTP to HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen  ssl http2;
    listen [::]: ssl http2;
    server_name  www.;
    
    # SSL Configuration
    ssl_certificate /.crt;
    ssl_certificate_key /.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    
    # Security Headers
    add_header Strict-Transport-Security "max-age=31536000" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    
    root /var/www//html;
    index index.html index.htm;
    
    location / {
        try_files $uri $uri/ =404;
    }
    
    # Logging
    access_log /var/log/nginx/_access.log;
    error_log /var/log/nginx/_error.log;
}

Best Practices: Organizing with Roles

For complex projects, use Ansible roles for better organization:

# Create role structure
ansible-galaxy init roles/webserver

# Directory structure
roles/webserver/
├── defaults/
│   └── main.yml          # Default variables
├── files/                # Static files
├── handlers/
│   └── main.yml          # Handlers
├── meta/
│   └── main.yml          # Role dependencies
├── tasks/
│   └── main.yml          # Main tasks
├── templates/            # Jinja2 templates
├── tests/                # Test playbooks
└── vars/
    └── main.yml          # Role variables

Example role-based playbook:

# playbooks/site.yml
---
- name: Configure all servers
  hosts: all
  roles:
    - common
    - security
    
- name: Configure web servers
  hosts: webservers
  roles:
    - nginx
    - ssl
    - monitoring
    
- name: Configure database servers
  hosts: databases
  roles:
    - postgresql
    - backup
    - monitoring

Security: Using Ansible Vault

Never store sensitive data in plain text. Use Ansible Vault:

# Create encrypted variable file
ansible-vault create group_vars/production/vault.yml

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

# Encrypt existing file
ansible-vault encrypt secrets.yml

# Decrypt file
ansible-vault decrypt secrets.yml

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

# Use vault password file
ansible-playbook site.yml --vault-password-file ~/.vault_pass.txt

Example vault content:

# group_vars/production/vault.yml (encrypted)
vault_db_password: "SuperSecurePassword123!"
vault_api_key: "sk-1234567890abcdef"
vault_ssh_private_key: |
  -----BEGIN RSA PRIVATE KEY-----
  MIIEpAIBAAKCAQEA...
  -----END RSA PRIVATE KEY-----

Reference vault variables in your playbooks:

---
- name: Deploy application with secrets
  hosts: webservers
  vars:
    db_password: ""
    api_key: ""
  tasks:
    - name: Configure application
      template:
        src: app_config.j2
        dest: /etc/app/config.yml

Advanced Techniques

Dynamic Inventory for AWS

Use dynamic inventory to automatically discover EC2 instances:

# Install AWS plugin
pip3 install boto3 botocore

# Create inventory plugin configuration
cat > inventory/aws_ec2.yml <<EOF
plugin: aws_ec2
regions:
  - us-east-1
  - us-west-2
filters:
  tag:Environment: production
keyed_groups:
  - key: tags.Role
    prefix: role
  - key: tags.Environment
    prefix: env
hostnames:
  - private-ip-address
compose:
  ansible_host: private_ip_address
EOF

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

Testing with Molecule

Ensure your roles work correctly before deployment:

# Install Molecule
pip3 install molecule molecule-docker

# Initialize molecule in role directory
cd roles/webserver
molecule init scenario --driver-name docker

# Run tests
molecule test

# Test sequence breakdown
molecule create    # Create test instance
molecule converge  # Run playbook
molecule verify    # Run verification
molecule destroy   # Cleanup

Performance Optimization

Speed up playbook execution:

# ansible.cfg
[defaults]
forks = 20                    # Parallel execution
gathering = smart             # Smart fact gathering
fact_caching = jsonfile       # Cache facts
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 3600   # Cache for 1 hour
host_key_checking = False
pipelining = True             # SSH pipelining

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

Monitoring & Logging

Integrate with monitoring tools:

---
- name: Setup monitoring
  hosts: all
  roles:
    - prometheus_node_exporter
  
  tasks:
    - name: Install Filebeat for log shipping
      apt:
        name: filebeat
        state: present
    
    - name: Configure Filebeat
      template:
        src: filebeat.yml.j2
        dest: /etc/filebeat/filebeat.yml
      notify: restart filebeat
    
    - name: Enable and start Filebeat
      systemd:
        name: filebeat
        enabled: yes
        state: started

Troubleshooting Tips

Common debugging techniques:

# Check syntax
ansible-playbook playbook.yml --syntax-check

# Dry run (check mode)
ansible-playbook playbook.yml --check

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

# Verbose output
ansible-playbook playbook.yml -vvv

# Debug specific task
ansible-playbook playbook.yml --tags "debug" -vvv

# List all tasks
ansible-playbook playbook.yml --list-tasks

# List all hosts
ansible-playbook playbook.yml --list-hosts

Conclusion

Ansible is a powerful automation tool that scales from simple tasks to complex multi-tier applications. By following these best practices:

✅ Use roles for modular, reusable code
✅ Store secrets securely with Ansible Vault
✅ Test playbooks with Molecule before production
✅ Leverage dynamic inventory for cloud environments
✅ Implement proper error handling and idempotency
✅ Document your playbooks and roles thoroughly

You’ll build robust, maintainable automation that accelerates your infrastructure operations.

Additional Resources


Have questions or feedback? Feel free to reach out or leave a comment below!

HA
Author

Hari Prasad

Seasoned DevOps Lead with 11+ years of expertise in cloud infrastructure, CI/CD automation, and infrastructure as code. Proven track record in designing scalable, secure systems on AWS using Terraform, Kubernetes, Jenkins, and Ansible. Strong leadership in mentoring teams and implementing cost-effective cloud solutions.

Continue Reading

DevOps Tools & Calculators Free Tools

Power up your DevOps workflow with these handy tools

Enjoyed this article?

Explore more DevOps insights, tutorials, and best practices

View All Articles