Ansible Best Practices
Project Structure
Directory Organization
ansible-project/
├── ansible.cfg # Ansible configuration
├── site.yml # Main playbook
├── requirements.yml # Dependencies
├── inventories/ # Environment-specific inventories
│ ├── production/
│ │ ├── hosts # Production hosts
│ │ ├── group_vars/
│ │ └── host_vars/
│ └── staging/
│ ├── hosts # Staging hosts
│ ├── group_vars/
│ └── host_vars/
├── group_vars/ # Global group variables
│ └── all/
│ ├── vars.yml # Non-sensitive variables
│ └── vault.yml # Encrypted variables
├── roles/ # Role definitions
│ ├── common/
│ ├── monitoring/
│ └── webserver/
└── playbooks/ # Task-specific playbooks
├── backup.yml
├── maintenance.yml
└── security.yml
Playbook Organization
Main Playbook Structure
# site.yml
---
- name: Base configuration for all hosts
hosts: all
roles:
- common
- monitoring
- name: Configure web servers
hosts: webservers
roles:
- nginx
- php-fpm
- deployment
- name: Configure databases
hosts: dbservers
roles:
- postgresql
- backup
Role Dependencies
# roles/webserver/meta/main.yml
---
dependencies:
- role: common
vars:
priority: high
- role: security
vars:
level: production
Variable Management
Variable Precedence
# Group variables (group_vars/all/vars.yml)
---
app_name: myapp
environment: production
# Role defaults (roles/webserver/defaults/main.yml)
---
http_port: 80
worker_processes: auto
# Host variables (host_vars/web1.example.com/vars.yml)
---
http_port: 8080
ssl_enabled: true
Environment-Specific Variables
# inventories/production/group_vars/all/vars.yml
---
environment: production
debug_enabled: false
log_level: ERROR
backup_retention: 30
# inventories/staging/group_vars/all/vars.yml
---
environment: staging
debug_enabled: true
log_level: DEBUG
backup_retention: 7
Task Design
Task Naming Conventions
# Good Examples
- name: Ensure nginx package is installed
apt:
name: nginx
state: present
- name: Configure nginx virtual host
template:
src: vhost.conf.j2
dest: /etc/nginx/sites-available/{{ app_name }}
# Bad Examples
- name: nginx install
- name: configure
Idempotency
# Good - Idempotent
- name: Ensure configuration directory exists
file:
path: /etc/myapp
state: directory
mode: '0755'
# Bad - Not Idempotent
- name: Create configuration directory
command: mkdir /etc/myapp
Security Practices
Vault Usage
# Store sensitive data in vault
- name: Configure database connection
template:
src: database.conf.j2
dest: /etc/myapp/database.conf
vars_files:
- vars/vault.yml
File Permissions
- name: Ensure secure file permissions
file:
path: "{{ item.path }}"
state: file
owner: "{{ item.owner }}"
group: "{{ item.group }}"
mode: "{{ item.mode }}"
loop:
- path: /etc/ssl/private/cert.key
owner: root
group: root
mode: '0600'
- path: /etc/myapp/config.yml
owner: myapp
group: myapp
mode: '0640'
Async Tasks
- name: Update packages
apt:
update_cache: yes
cache_valid_time: 3600
async: 3600
poll: 0
register: pkg_update
- name: Long running task
command: /usr/local/bin/long_operation
async: 3600
poll: 0
- name: Check async status
async_status:
jid: "{{ pkg_update.ansible_job_id }}"
register: job_result
until: job_result.finished
retries: 30
delay: 60
Fact Caching
# ansible.cfg
[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /path/to/facts
fact_caching_timeout = 86400
Testing
Molecule Testing
# molecule/default/molecule.yml
---
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: instance
image: ubuntu:20.04
pre_build_image: true
provisioner:
name: ansible
verifier:
name: ansible
Integration Tests
# tests/integration/test_webserver.yml
---
- hosts: webservers
tasks:
- name: Verify nginx is running
command: systemctl status nginx
register: nginx_status
failed_when: nginx_status.rc != 0
- name: Verify web page is accessible
uri:
url: http://localhost
return_content: yes
register: webpage
failed_when: webpage.status != 200
CI/CD Integration
Pipeline Configuration
# .gitlab-ci.yml
stages:
- lint
- test
- deploy
lint:
stage: lint
script:
- ansible-lint site.yml
molecule:
stage: test
script:
- molecule test
deploy_staging:
stage: deploy
script:
- ansible-playbook -i inventories/staging site.yml
environment:
name: staging
Documentation
Role Documentation
# roles/webserver/README.md
# Webserver Role
## Requirements
- Ubuntu 20.04 or later
- Python 3.8+
## Variables
| Variable | Default | Description |
|----------|---------|-------------|
| http_port | 80 | HTTP port |
| ssl_enabled | false | Enable SSL |
## Dependencies
- common
- security
## Example Usage
```yaml
- hosts: webservers
roles:
- role: webserver
vars:
http_port: 8080
ssl_enabled: true
Error Handling
Block Usage
- name: Handle complex deployments
block:
- name: Deploy application
include_role:
name: deployment
rescue:
- name: Rollback deployment
include_role:
name: rollback
always:
- name: Cleanup temporary files
file:
path: /tmp/deployment
state: absent
Monitoring and Logging
Task Logging
- name: Execute critical operation
command: /usr/local/bin/critical_operation
register: operation_result
notify: log_operation
handlers:
- name: log_operation
copy:
content: |
Operation executed at {{ ansible_date_time.iso8601 }}
Result: {{ operation_result.stdout }}
dest: /var/log/ansible/operations.log
Maintenance and Updates
Role Updates
# requirements.yml
---
roles:
- name: geerlingguy.nginx
version: 3.1.0
- src: https://github.com/company/ansible-security
scm: git
version: main
name: security
Version Control
# .gitignore
*.retry
*.pyc
.vault_pass
inventory/*/group_vars/*/vault.yml