Roles
Role Structure
Standard Role Directory Structure
roles/
└── webserver/
├── defaults/
│ └── main.yml
├── files/
│ └── index.html
├── handlers/
│ └── main.yml
├── meta/
│ └── main.yml
├── tasks/
│ └── main.yml
├── templates/
│ └── nginx.conf.j2
├── tests/
│ ├── inventory
│ └── test.yml
└── vars/
└── main.yml
Role Creation
# Create role structure
ansible-galaxy init webserver
# Create role in specific directory
ansible-galaxy init --init-path ./roles webserver
# Create role with specific structure
mkdir -p roles/webserver/{tasks,handlers,templates,files,vars,defaults,meta}
Role Components
Tasks (tasks/main.yml)
---
- name: Install web server
package:
name: '{{ webserver_package }}'
state: present
become: yes
- name: Create web directory
file:
path: '{{ webserver_document_root }}'
state: directory
owner: '{{ webserver_user }}'
group: '{{ webserver_group }}'
mode: '0755'
- name: Configure web server
template:
src: '{{ webserver_config_template }}'
dest: '{{ webserver_config_path }}'
owner: root
group: root
mode: '0644'
backup: yes
notify: restart webserver
- name: Start and enable web server
service:
name: '{{ webserver_service }}'
state: started
enabled: yes
become: yes
- name: Install custom index page
template:
src: index.html.j2
dest: '{{ webserver_document_root }}/index.html'
owner: '{{ webserver_user }}'
group: '{{ webserver_group }}'
mode: '0644'
Handlers (handlers/main.yml)
---
- name: restart webserver
service:
name: '{{ webserver_service }}'
state: restarted
become: yes
- name: reload webserver
service:
name: '{{ webserver_service }}'
state: reloaded
become: yes
- name: restart nginx
service:
name: nginx
state: restarted
become: yes
listen: 'restart webserver'
- name: restart apache
service:
name: apache2
state: restarted
become: yes
listen: 'restart webserver'
Variables (vars/main.yml)
---
# Internal role variables (high precedence)
webserver_config_template: nginx.conf.j2
webserver_service_status: started
webserver_packages:
- nginx
- nginx-extras
# OS-specific variables
webserver_config_path: '{{ _webserver_config_path[ansible_os_family] }}'
webserver_user: '{{ _webserver_user[ansible_os_family] }}'
webserver_group: '{{ _webserver_group[ansible_os_family] }}'
_webserver_config_path:
Debian: /etc/nginx/nginx.conf
RedHat: /etc/nginx/nginx.conf
_webserver_user:
Debian: www-data
RedHat: nginx
_webserver_group:
Debian: www-data
RedHat: nginx
Default Variables (defaults/main.yml)
---
# Default role variables (low precedence)
webserver_package: nginx
webserver_service: nginx
webserver_port: 80
webserver_ssl_port: 443
webserver_document_root: /var/www/html
webserver_server_name: '{{ ansible_fqdn }}'
webserver_worker_processes: auto
webserver_worker_connections: 1024
webserver_keepalive_timeout: 65
webserver_client_max_body_size: 1m
# SSL configuration
webserver_ssl_enabled: false
webserver_ssl_certificate: /etc/ssl/certs/server.crt
webserver_ssl_certificate_key: /etc/ssl/private/server.key
# Logging
webserver_access_log: /var/log/nginx/access.log
webserver_error_log: /var/log/nginx/error.log
webserver_log_level: warn
# Security
webserver_server_tokens: 'off'
webserver_hide_version: true
Meta Information (meta/main.yml)
---
galaxy_info:
author: Your Name
description: Web server configuration role
company: Your Company
license: MIT
min_ansible_version: 2.9
platforms:
- name: Ubuntu
versions:
- 18.04
- 20.04
- 22.04
- name: CentOS
versions:
- 7
- 8
- name: Debian
versions:
- 10
- 11
galaxy_tags:
- web
- nginx
- apache
- server
dependencies:
- role: firewall
vars:
firewall_rules:
- port: 80
protocol: tcp
source: any
- port: 443
protocol: tcp
source: any
- role: ssl
when: webserver_ssl_enabled
Using Roles
Basic Role Usage
---
- name: Configure web servers
hosts: webservers
become: yes
roles:
- webserver
Role with Variables
---
- name: Configure web servers
hosts: webservers
become: yes
roles:
- role: webserver
vars:
webserver_port: 8080
webserver_ssl_enabled: true
webserver_server_name: example.com
Conditional Role Execution
---
- name: Configure servers
hosts: all
become: yes
roles:
- role: webserver
when: "'webservers' in group_names"
- role: database
when: "'databases' in group_names"
Role Dependencies
---
- name: Configure application servers
hosts: app_servers
become: yes
roles:
- common
- firewall
- ssl
- webserver
- application
Advanced Role Features
Role Tasks Organization
# tasks/main.yml
---
- name: Include OS-specific variables
include_vars: '{{ ansible_os_family }}.yml'
- name: Include installation tasks
include_tasks: install.yml
tags: installation
- name: Include configuration tasks
include_tasks: configure.yml
tags: configuration
- name: Include SSL tasks
include_tasks: ssl.yml
when: webserver_ssl_enabled
tags: ssl
Separate Task Files
# tasks/install.yml
---
- name: Install web server package
package:
name: '{{ webserver_package }}'
state: present
- name: Install additional packages
package:
name: '{{ item }}'
state: present
loop: '{{ webserver_additional_packages | default([]) }}'
# tasks/configure.yml
---
- name: Create web directory
file:
path: '{{ webserver_document_root }}'
state: directory
owner: '{{ webserver_user }}'
group: '{{ webserver_group }}'
mode: '0755'
- name: Configure web server
template:
src: '{{ webserver_config_template }}'
dest: '{{ webserver_config_path }}'
backup: yes
notify: restart webserver
OS-Specific Variables
# vars/Debian.yml
---
webserver_package: nginx
webserver_service: nginx
webserver_user: www-data
webserver_group: www-data
webserver_config_path: /etc/nginx/nginx.conf
# vars/RedHat.yml
---
webserver_package: nginx
webserver_service: nginx
webserver_user: nginx
webserver_group: nginx
webserver_config_path: /etc/nginx/nginx.conf
Role Templates
Nginx Configuration Template
# templates/nginx.conf.j2
user {{ webserver_user }};
worker_processes {{ webserver_worker_processes }};
pid /run/nginx.pid;
events {
worker_connections {{ webserver_worker_connections }};
use epoll;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout {{ webserver_keepalive_timeout }};
client_max_body_size {{ webserver_client_max_body_size }};
server_tokens {{ webserver_server_tokens }};
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/javascript;
access_log {{ webserver_access_log }};
error_log {{ webserver_error_log }} {{ webserver_log_level }};
server {
listen {{ webserver_port }};
server_name {{ webserver_server_name }};
root {{ webserver_document_root }};
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
{% if webserver_ssl_enabled %}
listen {{ webserver_ssl_port }} ssl;
ssl_certificate {{ webserver_ssl_certificate }};
ssl_certificate_key {{ webserver_ssl_certificate_key }};
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
{% endif %}
}
}
Dynamic Configuration Template
# templates/vhost.conf.j2
{% for vhost in webserver_vhosts %}
server {
listen {{ vhost.port | default(80) }};
server_name {{ vhost.server_name }};
root {{ vhost.document_root }};
{% if vhost.ssl_enabled | default(false) %}
listen {{ vhost.ssl_port | default(443) }} ssl;
ssl_certificate {{ vhost.ssl_certificate }};
ssl_certificate_key {{ vhost.ssl_certificate_key }};
{% endif %}
{% for location in vhost.locations | default([]) %}
location {{ location.path }} {
{% for directive in location.directives %}
{{ directive }};
{% endfor %}
}
{% endfor %}
}
{% endfor %}
Role Testing
Test Playbook (tests/test.yml)
---
- name: Test webserver role
hosts: localhost
remote_user: root
roles:
- webserver
post_tasks:
- name: Check if web server is running
service:
name: nginx
state: started
register: service_status
- name: Verify web server is accessible
uri:
url: http://localhost:{{ webserver_port }}
return_content: yes
register: web_response
- name: Assert web server is working
assert:
that:
- service_status.state == "started"
- web_response.status == 200
fail_msg: 'Web server is not working correctly'
Role Testing with Molecule
# molecule/default/molecule.yml
---
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: webserver-ubuntu
image: ubuntu:20.04
pre_build_image: true
provisioner:
name: ansible
inventory:
host_vars:
webserver-ubuntu:
webserver_port: 8080
verifier:
name: ansible
Role Dependencies
Complex Dependencies
# meta/main.yml
---
dependencies:
- role: common
vars:
common_packages:
- curl
- wget
- unzip
- role: firewall
vars:
firewall_rules:
- port: '{{ webserver_port }}'
protocol: tcp
source: any
- port: '{{ webserver_ssl_port }}'
protocol: tcp
source: any
when: webserver_ssl_enabled
- role: ssl
when: webserver_ssl_enabled
vars:
ssl_certificate_path: '{{ webserver_ssl_certificate }}'
ssl_key_path: '{{ webserver_ssl_certificate_key }}'
Conditional Dependencies
# meta/main.yml
---
dependencies:
- role: mysql
when: webserver_backend == "mysql"
- role: postgresql
when: webserver_backend == "postgresql"
- role: redis
when: webserver_cache_enabled
Role Distribution
Ansible Galaxy
# Initialize role for Galaxy
ansible-galaxy init --init-path ./roles my-role
# Build role package
ansible-galaxy collection build
# Install role from Galaxy
ansible-galaxy install username.rolename
# Install role from Git
ansible-galaxy install git+https://github.com/username/role.git
# Install specific version
ansible-galaxy install username.rolename,1.0.0
Requirements File
# requirements.yml
---
# From Galaxy
- src: geerlingguy.nginx
version: 2.8.0
# From Git
- src: https://github.com/username/role.git
scm: git
version: master
name: custom-role
# From local path
- src: /path/to/local/role
name: local-role
Install Requirements
# Install roles from requirements file
ansible-galaxy install -r requirements.yml
# Install to specific directory
ansible-galaxy install -r requirements.yml -p ./roles/
# Force reinstall
ansible-galaxy install -r requirements.yml --force
Best Practices
Role Design Principles
# Good role design
- name: Configure web server
hosts: webservers
roles:
- role: webserver
vars:
webserver_port: 80
webserver_ssl_enabled: true
webserver_vhosts:
- server_name: example.com
document_root: /var/www/example
ssl_enabled: true
- server_name: test.com
document_root: /var/www/test
ssl_enabled: false
Variable Organization
# defaults/main.yml - User-configurable defaults
webserver_port: 80
webserver_ssl_enabled: false
webserver_worker_processes: auto
# vars/main.yml - Internal role variables
webserver_config_template: nginx.conf.j2
webserver_service_commands:
start: systemctl start nginx
stop: systemctl stop nginx
restart: systemctl restart nginx
Role Documentation
# README.md
# Webserver Role
This role installs and configures a web server.
## Requirements
- Ansible 2.9+
- Python 3.6+
## Role Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `webserver_port` | `80` | HTTP port |
| `webserver_ssl_enabled` | `false` | Enable SSL |
| `webserver_ssl_port` | `443` | HTTPS port |
## Dependencies
- firewall
- ssl (when SSL enabled)
## Example Playbook
```yaml
- hosts: webservers
roles:
- role: webserver
vars:
webserver_port: 8080
webserver_ssl_enabled: true
License
MIT
Author
Your Name
### Security Considerations
```yaml
# Secure role practices
- name: Configure web server securely
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
backup: yes
notify: restart nginx
no_log: "{{ webserver_no_log | default(false) }}"
- name: Install SSL certificate
copy:
content: "{{ webserver_ssl_certificate_content }}"
dest: "{{ webserver_ssl_certificate }}"
owner: root
group: root
mode: '0644'
when: webserver_ssl_enabled
no_log: true
Performance Optimization
# Optimized role tasks
- name: Install packages efficiently
package:
name: '{{ webserver_packages }}'
state: present
# Better than installing packages one by one
- name: Use appropriate modules
file:
path: '{{ webserver_document_root }}'
state: directory
owner: '{{ webserver_user }}'
group: '{{ webserver_group }}'
mode: '0755'
# Better than using shell module
- name: Template with minimal changes
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
backup: yes
notify: restart nginx
# Use backup for safety, notify for efficiency