Use when structuring and reusing code with Ansible roles for modular, maintainable automation and configuration management.
Limited to specific tools
Additional assets for this skill
This skill is limited to using the following tools:
Structure and reuse automation code with Ansible roles for modular, maintainable infrastructure.
A well-organized Ansible role follows a standardized directory structure:
roles/
└── webserver/
├── README.md
├── defaults/
│ └── main.yml
├── files/
│ ├── nginx.conf
│ └── ssl/
│ ├── cert.pem
│ └── key.pem
├── handlers/
│ └── main.yml
├── meta/
│ └── main.yml
├── tasks/
│ ├── main.yml
│ ├── install.yml
│ ├── configure.yml
│ └── security.yml
├── templates/
│ ├── nginx.conf.j2
│ └── site.conf.j2
├── tests/
│ ├── inventory
│ └── test.yml
└── vars/
└── main.yml
---
# Main task file for webserver role
- name: Include OS-specific variables
include_vars: "{{ ansible_os_family }}.yml"
- name: Import installation tasks
import_tasks: install.yml
tags:
- install
- webserver
- name: Import configuration tasks
import_tasks: configure.yml
tags:
- configure
- webserver
- name: Import security tasks
import_tasks: security.yml
tags:
- security
- webserver
- name: Ensure nginx is running
service:
name: "{{ nginx_service_name }}"
state: started
enabled: yes
tags:
- service
- webserver
---
# Installation tasks for webserver role
- name: Install nginx and dependencies (Debian/Ubuntu)
apt:
name:
- nginx
- nginx-extras
- python3-passlib
state: present
update_cache: yes
cache_valid_time: 3600
when: ansible_os_family == "Debian"
- name: Install nginx and dependencies (RedHat/CentOS)
yum:
name:
- nginx
- nginx-mod-stream
- python3-passlib
state: present
update_cache: yes
when: ansible_os_family == "RedHat"
- name: Create nginx directories
file:
path: "{{ item }}"
state: directory
owner: "{{ nginx_user }}"
group: "{{ nginx_group }}"
mode: '0755'
loop:
- "{{ nginx_conf_dir }}/sites-available"
- "{{ nginx_conf_dir }}/sites-enabled"
- "{{ nginx_log_dir }}"
- "{{ nginx_cache_dir }}"
- /var/www/html
- name: Install certbot for SSL
apt:
name: certbot
state: present
when:
- nginx_ssl_enabled
- ansible_os_family == "Debian"
---
# Configuration tasks for webserver role
- name: Deploy main nginx configuration
template:
src: nginx.conf.j2
dest: "{{ nginx_conf_dir }}/nginx.conf"
owner: root
group: root
mode: '0644'
validate: 'nginx -t -c %s'
backup: yes
notify:
- Reload nginx
tags:
- config
- name: Deploy site configurations
template:
src: site.conf.j2
dest: "{{ nginx_conf_dir }}/sites-available/{{ item.name }}.conf"
owner: root
group: root
mode: '0644'
validate: 'nginx -t -c {{ nginx_conf_dir }}/nginx.conf'
loop: "{{ nginx_sites }}"
when: nginx_sites is defined
notify:
- Reload nginx
- name: Enable sites
file:
src: "{{ nginx_conf_dir }}/sites-available/{{ item.name }}.conf"
dest: "{{ nginx_conf_dir }}/sites-enabled/{{ item.name }}.conf"
state: link
loop: "{{ nginx_sites }}"
when:
- nginx_sites is defined
- item.enabled | default(true)
notify:
- Reload nginx
- name: Disable default site
file:
path: "{{ nginx_conf_dir }}/sites-enabled/default"
state: absent
when: nginx_disable_default_site
notify:
- Reload nginx
- name: Configure log rotation
template:
src: logrotate.j2
dest: /etc/logrotate.d/nginx
owner: root
group: root
mode: '0644'
---
# Security tasks for webserver role
- name: Generate dhparam file
command: openssl dhparam -out {{ nginx_conf_dir }}/dhparam.pem 2048
args:
creates: "{{ nginx_conf_dir }}/dhparam.pem"
when: nginx_ssl_enabled
- name: Set secure permissions on dhparam
file:
path: "{{ nginx_conf_dir }}/dhparam.pem"
owner: root
group: root
mode: '0600'
when: nginx_ssl_enabled
- name: Configure firewall rules (ufw)
ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop:
- "80"
- "443"
when:
- nginx_configure_firewall
- ansible_os_family == "Debian"
- name: Configure firewall rules (firewalld)
firewalld:
service: "{{ item }}"
permanent: yes
state: enabled
immediate: yes
loop:
- http
- https
when:
- nginx_configure_firewall
- ansible_os_family == "RedHat"
- name: Create basic auth file
htpasswd:
path: "{{ nginx_conf_dir }}/.htpasswd"
name: "{{ item.username }}"
password: "{{ item.password }}"
owner: root
group: "{{ nginx_group }}"
mode: '0640'
loop: "{{ nginx_basic_auth_users }}"
when: nginx_basic_auth_users is defined
no_log: yes
---
# Default variables for webserver role
# These can be overridden in playbooks or inventory
# Package and service names
nginx_package_name: nginx
nginx_service_name: nginx
# User and group
nginx_user: www-data
nginx_group: www-data
# Directories
nginx_conf_dir: /etc/nginx
nginx_log_dir: /var/log/nginx
nginx_cache_dir: /var/cache/nginx
nginx_pid_file: /var/run/nginx.pid
# Main configuration
nginx_worker_processes: auto
nginx_worker_connections: 1024
nginx_keepalive_timeout: 65
nginx_client_max_body_size: 10m
# Performance tuning
nginx_sendfile: on
nginx_tcp_nopush: on
nginx_tcp_nodelay: on
nginx_gzip: on
nginx_gzip_types:
- text/plain
- text/css
- application/json
- application/javascript
- text/xml
- application/xml
# Security
nginx_ssl_enabled: no
nginx_ssl_protocols: "TLSv1.2 TLSv1.3"
nginx_ssl_ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256"
nginx_ssl_prefer_server_ciphers: on
nginx_disable_default_site: yes
nginx_configure_firewall: yes
nginx_server_tokens: off
# Sites configuration
nginx_sites: []
# Example:
# nginx_sites:
# - name: example.com
# server_name: example.com www.example.com
# root: /var/www/example.com
# enabled: yes
# ssl: yes
# Basic authentication
# nginx_basic_auth_users:
# - username: admin
# password: secretpassword
---
# Variables that should not be overridden
nginx_conf_path: "{{ nginx_conf_dir }}/nginx.conf"
nginx_error_log: "{{ nginx_log_dir }}/error.log"
nginx_access_log: "{{ nginx_log_dir }}/access.log"
# OS-specific overrides loaded via include_vars
---
nginx_user: www-data
nginx_group: www-data
nginx_conf_dir: /etc/nginx
nginx_service_name: nginx
---
nginx_user: nginx
nginx_group: nginx
nginx_conf_dir: /etc/nginx
nginx_service_name: nginx
---
# Handlers for webserver role
- name: Reload nginx
service:
name: "{{ nginx_service_name }}"
state: reloaded
listen: "reload nginx"
- name: Restart nginx
service:
name: "{{ nginx_service_name }}"
state: restarted
listen: "restart nginx"
- name: Validate nginx config
command: nginx -t
changed_when: false
listen: "validate nginx"
- name: Reload firewall
service:
name: ufw
state: reloaded
when: ansible_os_family == "Debian"
listen: "reload firewall"
# {{ ansible_managed }}
user {{ nginx_user }};
worker_processes {{ nginx_worker_processes }};
pid {{ nginx_pid_file }};
events {
worker_connections {{ nginx_worker_connections }};
use epoll;
multi_accept on;
}
http {
##
# Basic Settings
##
sendfile {{ nginx_sendfile | ternary('on', 'off') }};
tcp_nopush {{ nginx_tcp_nopush | ternary('on', 'off') }};
tcp_nodelay {{ nginx_tcp_nodelay | ternary('on', 'off') }};
keepalive_timeout {{ nginx_keepalive_timeout }};
types_hash_max_size 2048;
server_tokens {{ nginx_server_tokens | ternary('on', 'off') }};
client_max_body_size {{ nginx_client_max_body_size }};
include {{ nginx_conf_dir }}/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
{% if nginx_ssl_enabled %}
ssl_protocols {{ nginx_ssl_protocols }};
ssl_ciphers {{ nginx_ssl_ciphers }};
ssl_prefer_server_ciphers {{ nginx_ssl_prefer_server_ciphers | ternary('on', 'off') }};
ssl_dhparam {{ nginx_conf_dir }}/dhparam.pem;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
{% endif %}
##
# Logging Settings
##
access_log {{ nginx_access_log }};
error_log {{ nginx_error_log }};
##
# Gzip Settings
##
gzip {{ nginx_gzip | ternary('on', 'off') }};
{% if nginx_gzip %}
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types {{ nginx_gzip_types | join(' ') }};
{% endif %}
##
# Virtual Host Configs
##
include {{ nginx_conf_dir }}/sites-enabled/*;
}
# {{ ansible_managed }}
# Site: {{ item.name }}
{% if item.ssl | default(false) %}
# Redirect HTTP to HTTPS
server {
listen 80;
listen [::]:80;
server_name {{ item.server_name }};
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name {{ item.server_name }};
ssl_certificate {{ item.ssl_cert | default('/etc/letsencrypt/live/' + item.name + '/fullchain.pem') }};
ssl_certificate_key {{ item.ssl_key | default('/etc/letsencrypt/live/' + item.name + '/privkey.pem') }};
{% else %}
server {
listen 80;
listen [::]:80;
server_name {{ item.server_name }};
{% endif %}
root {{ item.root | default('/var/www/' + item.name) }};
index {{ item.index | default('index.html index.htm') }};
{% if item.access_log is defined %}
access_log {{ item.access_log }};
{% endif %}
{% if item.error_log is defined %}
error_log {{ item.error_log }};
{% endif %}
{% if item.basic_auth | default(false) %}
auth_basic "Restricted Access";
auth_basic_user_file {{ nginx_conf_dir }}/.htpasswd;
{% endif %}
location / {
{% if item.proxy_pass is defined %}
proxy_pass {{ item.proxy_pass }};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
{% else %}
try_files $uri $uri/ =404;
{% endif %}
}
{% if item.locations is defined %}
{% for location in item.locations %}
location {{ location.path }} {
{% if location.proxy_pass is defined %}
proxy_pass {{ location.proxy_pass }};
{% endif %}
{% if location.alias is defined %}
alias {{ location.alias }};
{% endif %}
{% if location.return is defined %}
return {{ location.return }};
{% endif %}
}
{% endfor %}
{% endif %}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
}
---
galaxy_info:
role_name: webserver
author: Your Organization
description: Install and configure nginx web server
company: Your Company
license: MIT
min_ansible_version: 2.9
platforms:
- name: Ubuntu
versions:
- focal
- jammy
- name: Debian
versions:
- buster
- bullseye
- name: EL
versions:
- 7
- 8
- 9
galaxy_tags:
- web
- nginx
- webserver
- http
dependencies:
- role: common
vars:
common_packages:
- curl
- wget
- role: firewall
when: nginx_configure_firewall
allow_duplicates: no
---
- name: Configure web servers
hosts: webservers
become: yes
roles:
- webserver
---
- name: Configure web servers with custom settings
hosts: webservers
become: yes
roles:
- role: webserver
vars:
nginx_worker_processes: 4
nginx_ssl_enabled: yes
nginx_sites:
- name: example.com
server_name: example.com www.example.com
root: /var/www/example.com
ssl: yes
ssl_cert: /etc/ssl/certs/example.com.crt
ssl_key: /etc/ssl/private/example.com.key
---
- name: Configure infrastructure
hosts: all
become: yes
roles:
- role: webserver
tags:
- web
- nginx
- role: database
tags:
- db
- postgres
---
- name: Deploy web application
hosts: webservers
become: yes
pre_tasks:
- name: Announce deployment
debug:
msg: "Starting deployment to {{ inventory_hostname }}"
- name: Check disk space
command: df -h /
register: disk_space
changed_when: false
roles:
- webserver
- monitoring
post_tasks:
- name: Verify nginx is responding
uri:
url: http://localhost
status_code: 200
retries: 3
delay: 5
- name: Notify completion
debug:
msg: "Deployment completed successfully"
---
- name: Configure servers
hosts: all
tasks:
- name: Import webserver role
import_role:
name: webserver
vars:
nginx_worker_processes: 2
tags:
- webserver
---
- name: Configure servers based on conditions
hosts: all
tasks:
- name: Include webserver role for web servers
include_role:
name: webserver
when: "'webservers' in group_names"
- name: Include database role for db servers
include_role:
name: database
when: "'databases' in group_names"
---
- name: Custom installation workflow
hosts: webservers
tasks:
- name: Run pre-installation checks
import_role:
name: webserver
tasks_from: preflight
- name: Install nginx
import_role:
name: webserver
tasks_from: install
- name: Configure nginx
import_role:
name: webserver
tasks_from: configure
# Create role structure
ansible-galaxy init roles/myapp
# Create role with custom template
ansible-galaxy init --init-path roles/ myapp
# List role files
tree roles/myapp
# Install a role
ansible-galaxy install geerlingguy.nginx
# Install specific version
ansible-galaxy install geerlingguy.nginx,2.8.0
# Install from requirements file
ansible-galaxy install -r requirements.yml
---
# Install from Ansible Galaxy
- name: geerlingguy.nginx
version: 2.8.0
- name: geerlingguy.postgresql
version: 3.4.0
# Install from Git repository
- name: custom-app
src: https://github.com/yourorg/ansible-role-custom-app.git
scm: git
version: main
# Install from local path
- name: internal-role
src: /path/to/roles/internal-role
# roles/webserver/tasks/main.yml
---
- name: Default task flow
import_tasks: "{{ webserver_task_flow | default('standard') }}.yml"
# roles/webserver/tasks/standard.yml
---
- import_tasks: install.yml
- import_tasks: configure.yml
- import_tasks: security.yml
# roles/webserver/tasks/minimal.yml
---
- import_tasks: install.yml
- import_tasks: configure.yml
---
- name: Configure servers with conditional roles
hosts: all
become: yes
roles:
- role: webserver
when:
- ansible_os_family == "Debian"
- inventory_hostname in groups['webservers']
- role: webserver-nginx
when: webserver_type == "nginx"
- role: webserver-apache
when: webserver_type == "apache"
# roles/application/meta/main.yml
---
dependencies:
- role: webserver
vars:
nginx_sites:
- name: "{{ app_name }}"
server_name: "{{ app_domain }}"
proxy_pass: "http://localhost:{{ app_port }}"
- role: database
vars:
db_name: "{{ app_db_name }}"
db_user: "{{ app_db_user }}"
- role: monitoring
vars:
monitor_services:
- nginx
- "{{ app_name }}"
Use the ansible-roles skill when you need to: