Ansible Configuration Management
Complete guide to Ansible configuration management including playbooks, roles, inventory, vault, and best practices for infrastructure automation.
# Ansible Configuration Management
Introduction
Ansible is an open-source automation tool for configuration management, application deployment, and task automation. Ansible uses SSH for communication (no agent required), YAML for playbook definitions, and follows an idempotent model where running the same playbook multiple times produces the same result.
This tutorial covers Ansible installation, inventory management, playbooks, roles, modules, vault encryption, and production best practices.
Learning Objectives
By the end of this tutorial, you will be able to:
- Install and configure Ansible
- Manage inventory files and groups
- Write Ansible playbooks with tasks and modules
- Create reusable Ansible roles
- Use Ansible Vault for secrets encryption
- Implement idempotent configuration management
- Integrate Ansible with CI/CD pipelines
- Apply Ansible best practices for production
Technical Background
Ansible Architecture
flowchart LR
Control[Control Node] -->|SSH/WinRM| Target1[Target 1]
Control -->|SSH/WinRM| Target2[Target 2]
Control -->|SSH/WinRM| TargetN[Target N]
Control --> Inventory[Inventory]
Control --> Playbook[Playbook]
Control --> Modules[Modules]
subgraph Control Node
Control
Inventory
Playbook
Modules
end
subgraph Managed Nodes
Target1
Target2
TargetN
endKey Ansible Concepts
| Concept | Description | |---------|-------------| | Control node | Machine where Ansible is installed | | Managed node | Remote machine being configured | | Inventory | List of managed nodes and groups | | Module | Unit of code executed on managed nodes | | Task | Single unit of work (calls a module) | | Playbook | YAML file containing plays and tasks | | Play | Maps hosts to tasks | | Role | Reusable group of tasks, variables, files | | Fact | Information discovered about managed node | | Vault | Encryption for sensitive data |
Idempotency
Ansible tasks are idempotent—running them multiple times produces the same result without side effects. This is achieved by checking the current state before making changes.
Prerequisites
- Linux control node (Ubuntu, RHEL, or similar)
- Python 3.8 or later
- SSH access to managed nodes
- Sudo privileges on managed nodes
- Understanding of YAML syntax
Hardware and Software Requirements
| Component | Requirement | |-----------|-------------| | Control Node | Linux with Python 3.8+ | | Managed Nodes | Linux/Windows with SSH/WinRM | | Ansible | Latest stable version | | SSH | Key-based authentication | | Sudo | Passwordless sudo for ansible user |
Step-by-Step Implementation
Phase 1: Install Ansible
# Ubuntu/Debian
sudo apt update
sudo apt install ansible -y
# RHEL/CentOS
sudo yum install ansible -y
# Verify
ansible --versionPhase 2: Configure Inventory
#### Static Inventory
# inventory.ini
[web_servers]
web1.example.com ansible_host=192.168.1.10
web2.example.com ansible_host=192.168.1.11
[db_servers]
db1.example.com ansible_host=192.168.1.20
[load_balancers]
lb1.example.com ansible_host=192.168.1.30
[all:vars]
ansible_user=deploy
ansible_ssh_private_key_file=~/.ssh/id_ed25519
ansible_become=yes
ansible_become_method=sudo#### Group Variables
# group_vars/web_servers.yml
nginx_port: 80
nginx_worker_processes: auto
web_root: /var/www/html
# group_vars/db_servers.yml
db_port: 5432
db_max_connections: 100
db_version: "15"#### Host Variables
# host_vars/web1.example.com.yml
ansible_host: 192.168.1.10
custom_setting: web1-specific-valuePhase 3: Write Playbooks
#### Basic Playbook
# playbooks/configure-web.yml
---
- name: Configure Web Servers
hosts: web_servers
become: yes
vars:
nginx_version: "1.24"
tasks:
- name: Install Nginx
ansible.builtin.apt:
name: nginx
state: present
update_cache: yes
- name: Start and enable Nginx
ansible.builtin.service:
name: nginx
state: started
enabled: yes
- name: Configure Nginx
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/default
owner: root
group: root
mode: '0644'
notify: Restart Nginx
- name: Ensure web root exists
ansible.builtin.file:
path: /var/www/html
state: directory
owner: www-data
group: www-data
mode: '0755'
- name: Deploy index.html
ansible.builtin.copy:
src: files/index.html
dest: /var/www/html/index.html
owner: www-data
group: www-data
mode: '0644'
handlers:
- name: Restart Nginx
ansible.builtin.service:
name: nginx
state: restarted#### Run Playbook
# Run playbook
ansible-playbook -i inventory.ini playbooks/configure-web.yml
# Run with syntax check
ansible-playbook -i inventory.ini playbooks/configure-web.yml --syntax-check
# Run in check mode (dry run)
ansible-playbook -i inventory.ini playbooks/configure-web.yml --check
# Run with verbose output
ansible-playbook -i inventory.ini playbooks/configure-web.yml -v
# Run on specific host
ansible-playbook -i inventory.ini playbooks/configure-web.yml --limit web1.example.comPhase 4: Use Ansible Modules
#### Common Modules
# Package management
- name: Install Apache
ansible.builtin.apt:
name: apache2
state: present
# File management
- name: Create configuration file
ansible.builtin.copy:
content: |
server {
listen 80;
server_name example.com;
}
dest: /etc/nginx/sites-available/example.com
owner: root
group: root
mode: '0644'
# Service management
- name: Restart Apache
ansible.builtin.service:
name: apache2
state: restarted
# User management
- name: Create deploy user
ansible.builtin.user:
name: deploy
shell: /bin/bash
groups: sudo
append: yes
# Command execution
- name: Run custom command
ansible.builtin.command: /usr/local/bin/health-check.sh
register: health_result
changed_when: health_result.rc == 0
# Template with Jinja2
- name: Deploy config with template
ansible.builtin.template:
src: templates/myapp.conf.j2
dest: /etc/myapp/myapp.conf
vars:
app_port: 8080
app_host: "0.0.0.0"Phase 5: Create Roles
#### Role Structure
roles/
nginx/
defaults/
main.yml
tasks/
main.yml
handlers/
main.yml
templates/
nginx.conf.j2
files/
index.html
vars/
main.yml
meta/
main.yml
README.md#### Create Role with ansible-galaxy
# Initialize role
ansible-galaxy role init roles/nginx
# Install role from Galaxy
ansible-galaxy install geerlingguy.nginx#### Role Tasks
# roles/nginx/tasks/main.yml
---
- name: Install Nginx
ansible.builtin.apt:
name: nginx
state: present
- name: Configure Nginx
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Restart Nginx
- name: Start Nginx
ansible.builtin.service:
name: nginx
state: started
enabled: yes#### Role Handlers
# roles/nginx/handlers/main.yml
---
- name: Restart Nginx
ansible.builtin.service:
name: nginx
state: restarted#### Use Role in Playbook
# playbooks/site.yml
---
- name: Configure Web Servers
hosts: web_servers
become: yes
roles:
- role: nginx
vars:
nginx_port: 8080
- role: common
- role: security
when: ansible_os_family == "Debian"Phase 6: Ansible Vault for Secrets
#### Encrypt Variables
# Create encrypted file
ansible-vault create group_vars/web_servers/vault.yml
# Edit encrypted file
ansible-vault edit group_vars/web_servers/vault.yml
# Encrypt existing file
ansible-vault encrypt group_vars/web_servers/vault.yml
# Decrypt file
ansible-vault decrypt group_vars/web_servers/vault.yml
# View encrypted file
ansible-vault view group_vars/web_servers/vault.yml#### Use Vault in Playbook
# group_vars/web_servers/vault.yml
db_password: "super-secret-password"
api_key: "sk-live-1234567890abcdef"# playbooks/deploy.yml
---
- name: Deploy Application
hosts: web_servers
become: yes
vars_files:
- group_vars/web_servers/vault.yml
tasks:
- name: Configure database password
ansible.builtin.template:
src: templates/db.conf.j2
dest: /etc/app/db.conf
mode: '0600'#### Run with Vault
# Provide vault password
ansible-playbook -i inventory.ini playbooks/deploy.yml --ask-vault-pass
# Use vault password file
ansible-playbook -i inventory.ini playbooks/deploy.yml --vault-password-file ~/.vault_passPhase 7: Ansible Best Practices
#### Project Structure
ansible/
├── ansible.cfg
├── inventory.ini
├── group_vars/
│ ├── all.yml
│ ├── web_servers.yml
│ └── db_servers.yml
├── host_vars/
├── roles/
│ ├── common/
│ ├── nginx/
│ └── postgresql/
├── playbooks/
│ ├── site.yml
│ └── deploy.yml
└── vault/
└── vault_pass#### ansible.cfg
[defaults]
inventory = inventory.ini
remote_user = deploy
host_key_checking = False
roles_path = roles
retry_files_enabled = False
stdout_callback = yaml
timeout = 30
[privilege_escalation]
become = yes
become_method = sudoExpected Output
- Ansible control node configured and ready
- Inventory with host and group variables
- Playbooks for application deployment and configuration
- Reusable roles for common components
- Encrypted secrets with Ansible Vault
- Idempotent configuration management
Verification Procedures
| Check | Method | Expected Result | |-------|--------|-----------------| | Ansible connectivity | `ansible all -m ping` | All hosts reachable | | Playbook syntax | `ansible-playbook --syntax-check` | No syntax errors | | Dry run | `ansible-playbook --check` | Shows changes without applying | | Idempotency | Run playbook twice | Second run reports no changes | | Role execution | Run playbook with roles | All roles applied successfully |
Troubleshooting Scenarios
| Issue | Diagnosis | Resolution | |-------|-----------|------------| | SSH connection failed | SSH key not installed or wrong user | Copy SSH key; verify ansible_user | | Permission denied | Missing sudo privileges | Configure passwordless sudo | | Module not found | Wrong module name or collection | Use FQCN: ansible.builtin.apt | | Template error | Jinja2 syntax error | Fix template syntax | | Vault decryption fails | Wrong vault password | Check vault password file | | Task fails on specific host | Host-specific issue | Use --limit to isolate; check host facts |
Best Practices
- **Use FQCN:** Always use fully qualified collection names (ansible.builtin.apt)
- **Keep playbooks idempotent:** Tasks should produce same result on repeated runs
- **Use roles:** Organize code into reusable roles
- **Separate variables:** Use group_vars and host_vars instead of hardcoding
- **Encrypt secrets:** Use Ansible Vault for all sensitive data
- **Test in check mode:** Always run --check before applying to production
- **Use handlers:** Restart services only when configuration changes
- **Document roles:** Include README.md in each role
Security Considerations
- **Vault encryption:** Encrypt all passwords, API keys, and certificates
- **Restrict vault access:** Limit who can decrypt vault files
- **Use SSH keys:** Never use password authentication
- **Limit sudo:** Configure specific sudo commands for ansible user
- **Audit playbook changes:** Review changes to playbooks and roles
- **Secure control node:** Protect the Ansible control node with strong access controls
Conclusion
Ansible provides agentless configuration management with a simple YAML syntax and powerful module library. By following best practices for inventory organization, role design, vault encryption, and idempotency, teams can automate infrastructure configuration reliably and consistently. Ansible's agentless architecture makes it easy to adopt without installing software on managed nodes.
References
- Ansible Documentation: https://docs.ansible.com/
- Ansible Galaxy: https://galaxy.ansible.com/
- Ansible Best Practices: https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_best_practices.html
- Ansible Vault: https://docs.ansible.com/ansible/latest/vault_guide/index.html
- Ansible Modules: https://docs.ansible.com/ansible/latest/collections/index.html