How to Create Ansible Roles for Reusable Automation
Ansible roles let you organize playbooks into modular, reusable components. Instead of monolithic playbooks, roles break your Breeze server configuration into self-contained units that can be shared across projects and teams.
Role Directory Structure
Initialize a new role with the ansible-galaxy command:
ansible-galaxy role init roles/webserver
# This creates the standard structure:
roles/webserver/
├── defaults/
│ └── main.yml # Default variables (lowest priority)
├── files/
│ └── # Static files to copy
├── handlers/
│ └── main.yml # Handler definitions (e.g., restart services)
├── meta/
│ └── main.yml # Role metadata and dependencies
├── tasks/
│ └── main.yml # Main task list
├── templates/
│ └── # Jinja2 templates
├── tests/
│ ├── inventory
│ └── test.yml
└── vars/
└── main.yml # Role variables (higher priority)
Writing Role Tasks
Define your tasks in roles/webserver/tasks/main.yml:
---
# roles/webserver/tasks/main.yml
- name: Install Nginx
apt:
name: nginx
state: present
update_cache: yes
- name: Deploy Nginx configuration
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
notify: Restart Nginx
- name: Deploy site configuration
template:
src: site.conf.j2
dest: "/etc/nginx/sites-available/{{ site_domain }}"
notify: Reload Nginx
- name: Enable site
file:
src: "/etc/nginx/sites-available/{{ site_domain }}"
dest: "/etc/nginx/sites-enabled/{{ site_domain }}"
state: link
notify: Reload Nginx
- name: Ensure Nginx is running
service:
name: nginx
state: started
enabled: yes
Defining Handlers
Handlers run only when notified by tasks, typically for service restarts:
---
# roles/webserver/handlers/main.yml
- name: Restart Nginx
service:
name: nginx
state: restarted
- name: Reload Nginx
service:
name: nginx
state: reloaded
Setting Default Variables
Provide sensible defaults in roles/webserver/defaults/main.yml:
---
# roles/webserver/defaults/main.yml
site_domain: "example.com"
nginx_worker_processes: auto
nginx_worker_connections: 1024
nginx_client_max_body_size: "64m"
ssl_enabled: true
ssl_certificate_path: "/etc/letsencrypt/live/{{ site_domain }}/fullchain.pem"
ssl_key_path: "/etc/letsencrypt/live/{{ site_domain }}/privkey.pem"
Using the Role in a Playbook
Apply roles to your Breeze servers in a playbook:
---
# site.yml
- hosts: breeze_web_servers
become: yes
roles:
- role: webserver
vars:
site_domain: "myapp.com"
nginx_worker_connections: 2048
- role: firewall
- role: monitoring
Role Dependencies
Declare role dependencies in meta/main.yml so prerequisite roles run automatically:
---
# roles/webserver/meta/main.yml
dependencies:
- role: common
- role: firewall
vars:
firewall_open_ports:
- 80
- 443
Best Practices
- Keep roles focused — each role should handle one concern (web server, database, monitoring)
- Use defaults generously — provide defaults so roles work out of the box
- Tag your tasks — add tags to allow selective execution with
--tags - Document variables — list all variables and their defaults in the role README
- Test roles independently — use the
tests/directory or tools like Molecule - Version roles — use Git tags to version roles used across multiple projects
Well-structured roles make your Breeze infrastructure automation modular, testable, and easy to maintain across growing environments.