Skip to main content

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