<< All versions
Skill v1.0.1
currentAutomated scan100/100thebushidocollective/han/ansible-roles
4 files
──Details
PublishedSeptember 28, 2026 at 08:55 PM
Content Hashsha256:776a823d3b3d96e7...
Git SHA19caa51d5fd1
Bump Typepatch
──Files
Files (1 file, 21.1 KB)
SKILL.md21.1 KBactive
SKILL.md · 918 lines · 21.1 KB
version: "1.0.1" name: ansible-roles user-invocable: false description: Use when structuring and reusing code with Ansible roles for modular, maintainable automation and configuration management. allowed-tools: [Bash, Read]
Ansible Roles
Structure and reuse automation code with Ansible roles for modular, maintainable infrastructure.
Role Directory Structure
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
Basic Role Example
tasks/main.yml
yaml
---# Main task file for webserver role- name: Include OS-specific variablesinclude_vars: "{{ ansible_os_family }}.yml"- name: Import installation tasksimport_tasks: install.ymltags:- install- webserver- name: Import configuration tasksimport_tasks: configure.ymltags:- configure- webserver- name: Import security tasksimport_tasks: security.ymltags:- security- webserver- name: Ensure nginx is runningservice:name: "{{ nginx_service_name }}"state: startedenabled: yestags:- service- webserver
tasks/install.yml
yaml
---# Installation tasks for webserver role- name: Install nginx and dependencies (Debian/Ubuntu)apt:name:- nginx- nginx-extras- python3-passlibstate: presentupdate_cache: yescache_valid_time: 3600when: ansible_os_family == "Debian"- name: Install nginx and dependencies (RedHat/CentOS)yum:name:- nginx- nginx-mod-stream- python3-passlibstate: presentupdate_cache: yeswhen: ansible_os_family == "RedHat"- name: Create nginx directoriesfile:path: "{{ item }}"state: directoryowner: "{{ 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 SSLapt:name: certbotstate: presentwhen:- nginx_ssl_enabled- ansible_os_family == "Debian"
tasks/configure.yml
yaml
---# Configuration tasks for webserver role- name: Deploy main nginx configurationtemplate:src: nginx.conf.j2dest: "{{ nginx_conf_dir }}/nginx.conf"owner: rootgroup: rootmode: '0644'validate: 'nginx -t -c %s'backup: yesnotify:- Reload nginxtags:- config- name: Deploy site configurationstemplate:src: site.conf.j2dest: "{{ nginx_conf_dir }}/sites-available/{{ item.name }}.conf"owner: rootgroup: rootmode: '0644'validate: 'nginx -t -c {{ nginx_conf_dir }}/nginx.conf'loop: "{{ nginx_sites }}"when: nginx_sites is definednotify:- Reload nginx- name: Enable sitesfile:src: "{{ nginx_conf_dir }}/sites-available/{{ item.name }}.conf"dest: "{{ nginx_conf_dir }}/sites-enabled/{{ item.name }}.conf"state: linkloop: "{{ nginx_sites }}"when:- nginx_sites is defined- item.enabled | default(true)notify:- Reload nginx- name: Disable default sitefile:path: "{{ nginx_conf_dir }}/sites-enabled/default"state: absentwhen: nginx_disable_default_sitenotify:- Reload nginx- name: Configure log rotationtemplate:src: logrotate.j2dest: /etc/logrotate.d/nginxowner: rootgroup: rootmode: '0644'
tasks/security.yml
yaml
---# Security tasks for webserver role- name: Generate dhparam filecommand: openssl dhparam -out {{ nginx_conf_dir }}/dhparam.pem 2048args:creates: "{{ nginx_conf_dir }}/dhparam.pem"when: nginx_ssl_enabled- name: Set secure permissions on dhparamfile:path: "{{ nginx_conf_dir }}/dhparam.pem"owner: rootgroup: rootmode: '0600'when: nginx_ssl_enabled- name: Configure firewall rules (ufw)ufw:rule: allowport: "{{ item }}"proto: tcploop:- "80"- "443"when:- nginx_configure_firewall- ansible_os_family == "Debian"- name: Configure firewall rules (firewalld)firewalld:service: "{{ item }}"permanent: yesstate: enabledimmediate: yesloop:- http- httpswhen:- nginx_configure_firewall- ansible_os_family == "RedHat"- name: Create basic auth filehtpasswd:path: "{{ nginx_conf_dir }}/.htpasswd"name: "{{ item.username }}"password: "{{ item.password }}"owner: rootgroup: "{{ nginx_group }}"mode: '0640'loop: "{{ nginx_basic_auth_users }}"when: nginx_basic_auth_users is definedno_log: yes
Role Variables
defaults/main.yml
yaml
---# Default variables for webserver role# These can be overridden in playbooks or inventory# Package and service namesnginx_package_name: nginxnginx_service_name: nginx# User and groupnginx_user: www-datanginx_group: www-data# Directoriesnginx_conf_dir: /etc/nginxnginx_log_dir: /var/log/nginxnginx_cache_dir: /var/cache/nginxnginx_pid_file: /var/run/nginx.pid# Main configurationnginx_worker_processes: autonginx_worker_connections: 1024nginx_keepalive_timeout: 65nginx_client_max_body_size: 10m# Performance tuningnginx_sendfile: onnginx_tcp_nopush: onnginx_tcp_nodelay: onnginx_gzip: onnginx_gzip_types:- text/plain- text/css- application/json- application/javascript- text/xml- application/xml# Securitynginx_ssl_enabled: nonginx_ssl_protocols: "TLSv1.2 TLSv1.3"nginx_ssl_ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256"nginx_ssl_prefer_server_ciphers: onnginx_disable_default_site: yesnginx_configure_firewall: yesnginx_server_tokens: off# Sites configurationnginx_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
vars/main.yml
yaml
---# Variables that should not be overriddennginx_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
vars/Debian.yml
yaml
---nginx_user: www-datanginx_group: www-datanginx_conf_dir: /etc/nginxnginx_service_name: nginx
vars/RedHat.yml
yaml
---nginx_user: nginxnginx_group: nginxnginx_conf_dir: /etc/nginxnginx_service_name: nginx
Role Handlers
handlers/main.yml
yaml
---# Handlers for webserver role- name: Reload nginxservice:name: "{{ nginx_service_name }}"state: reloadedlisten: "reload nginx"- name: Restart nginxservice:name: "{{ nginx_service_name }}"state: restartedlisten: "restart nginx"- name: Validate nginx configcommand: nginx -tchanged_when: falselisten: "validate nginx"- name: Reload firewallservice:name: ufwstate: reloadedwhen: ansible_os_family == "Debian"listen: "reload firewall"
Role Templates
templates/nginx.conf.j2
jinja2
# {{ 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/*;}
templates/site.conf.j2
jinja2
# {{ ansible_managed }}# Site: {{ item.name }}{% if item.ssl | default(false) %}# Redirect HTTP to HTTPSserver {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 headersadd_header X-Frame-Options "SAMEORIGIN" always;add_header X-Content-Type-Options "nosniff" always;add_header X-XSS-Protection "1; mode=block" always;}
Role Dependencies
meta/main.yml
yaml
---galaxy_info:role_name: webserverauthor: Your Organizationdescription: Install and configure nginx web servercompany: Your Companylicense: MITmin_ansible_version: 2.9platforms:- name: Ubuntuversions:- focal- jammy- name: Debianversions:- buster- bullseye- name: ELversions:- 7- 8- 9galaxy_tags:- web- nginx- webserver- httpdependencies:- role: commonvars:common_packages:- curl- wget- role: firewallwhen: nginx_configure_firewallallow_duplicates: no
Using Roles in Playbooks
Simple Role Usage
yaml
---- name: Configure web servershosts: webserversbecome: yesroles:- webserver
Role with Variables
yaml
---- name: Configure web servers with custom settingshosts: webserversbecome: yesroles:- role: webservervars:nginx_worker_processes: 4nginx_ssl_enabled: yesnginx_sites:- name: example.comserver_name: example.com www.example.comroot: /var/www/example.comssl: yesssl_cert: /etc/ssl/certs/example.com.crtssl_key: /etc/ssl/private/example.com.key
Role with Tags
yaml
---- name: Configure infrastructurehosts: allbecome: yesroles:- role: webservertags:- web- nginx- role: databasetags:- db- postgres
Pre and Post Tasks
yaml
---- name: Deploy web applicationhosts: webserversbecome: yespre_tasks:- name: Announce deploymentdebug:msg: "Starting deployment to {{ inventory_hostname }}"- name: Check disk spacecommand: df -h /register: disk_spacechanged_when: falseroles:- webserver- monitoringpost_tasks:- name: Verify nginx is respondinguri:url: http://localhoststatus_code: 200retries: 3delay: 5- name: Notify completiondebug:msg: "Deployment completed successfully"
Role Inclusion Methods
Static Import
yaml
---- name: Configure servershosts: alltasks:- name: Import webserver roleimport_role:name: webservervars:nginx_worker_processes: 2tags:- webserver
Dynamic Include
yaml
---- name: Configure servers based on conditionshosts: alltasks:- name: Include webserver role for web serversinclude_role:name: webserverwhen: "'webservers' in group_names"- name: Include database role for db serversinclude_role:name: databasewhen: "'databases' in group_names"
Import with Task Files
yaml
---- name: Custom installation workflowhosts: webserverstasks:- name: Run pre-installation checksimport_role:name: webservertasks_from: preflight- name: Install nginximport_role:name: webservertasks_from: install- name: Configure nginximport_role:name: webservertasks_from: configure
Creating Roles with Ansible Galaxy
Initialize a New Role
bash
# Create role structureansible-galaxy init roles/myapp# Create role with custom templateansible-galaxy init --init-path roles/ myapp# List role filestree roles/myapp
Install Roles from Galaxy
bash
# Install a roleansible-galaxy install geerlingguy.nginx# Install specific versionansible-galaxy install geerlingguy.nginx,2.8.0# Install from requirements fileansible-galaxy install -r requirements.yml
requirements.yml
yaml
---# Install from Ansible Galaxy- name: geerlingguy.nginxversion: 2.8.0- name: geerlingguy.postgresqlversion: 3.4.0# Install from Git repository- name: custom-appsrc: https://github.com/yourorg/ansible-role-custom-app.gitscm: gitversion: main# Install from local path- name: internal-rolesrc: /path/to/roles/internal-role
Advanced Role Patterns
Role with Multiple Entry Points
yaml
# roles/webserver/tasks/main.yml---- name: Default task flowimport_tasks: "{{ webserver_task_flow | default('standard') }}.yml"
yaml
# roles/webserver/tasks/standard.yml---- import_tasks: install.yml- import_tasks: configure.yml- import_tasks: security.yml
yaml
# roles/webserver/tasks/minimal.yml---- import_tasks: install.yml- import_tasks: configure.yml
Conditional Role Execution
yaml
---- name: Configure servers with conditional roleshosts: allbecome: yesroles:- role: webserverwhen:- ansible_os_family == "Debian"- inventory_hostname in groups['webservers']- role: webserver-nginxwhen: webserver_type == "nginx"- role: webserver-apachewhen: webserver_type == "apache"
Nested Role Dependencies
yaml
# roles/application/meta/main.yml---dependencies:- role: webservervars:nginx_sites:- name: "{{ app_name }}"server_name: "{{ app_domain }}"proxy_pass: "http://localhost:{{ app_port }}"- role: databasevars:db_name: "{{ app_db_name }}"db_user: "{{ app_db_user }}"- role: monitoringvars:monitor_services:- nginx- "{{ app_name }}"
When to Use This Skill
Use the ansible-roles skill when you need to:
- Structure reusable automation code across multiple playbooks and projects
- Implement modular infrastructure as code with clear separation of concerns
- Share automation logic between teams or projects
- Create distributable automation packages for common infrastructure patterns
- Organize complex playbooks into manageable, testable components
- Implement role-based configuration management with variable precedence
- Build layered infrastructure with role dependencies
- Version control automation logic independently from playbooks
- Create standardized infrastructure components for consistency
- Implement security and compliance requirements through reusable roles
- Build internal automation libraries for your organization
- Contribute to or consume community automation from Ansible Galaxy
- Test infrastructure components in isolation before integration
- Implement different configurations for development, staging, and production
- Create self-documenting infrastructure through role metadata
Best Practices
- Follow standard directory structure - Use ansible-galaxy init to create roles with proper organization
- Use defaults wisely - Place overridable variables in defaults/main.yml, non-overridable in vars/main.yml
- Document thoroughly - Include comprehensive README.md with usage examples and variable documentation
- Keep roles focused - Each role should have a single, well-defined purpose
- Use role dependencies - Declare dependencies in meta/main.yml rather than in playbooks
- Tag appropriately - Apply meaningful tags to tasks for selective execution
- Implement idempotency - Ensure roles can be run multiple times safely
- Version your roles - Use semantic versioning for role releases
- Test roles independently - Include test playbooks in tests/ directory
- Use templates for configuration - Prefer Jinja2 templates over static files for flexibility
- Implement OS detection - Use ansible_os_family for cross-platform compatibility
- Secure sensitive data - Use ansible-vault for passwords and secrets in role variables
- Use handlers correctly - Only notify handlers when configuration changes
- Validate configurations - Use validate parameter in template/copy modules
- Name tasks clearly - Use descriptive names that explain what each task does
Common Pitfalls
- Overly complex roles - Trying to make one role do too many things
- Poor variable naming - Using generic names that conflict with other roles
- Missing role prefix - Not prefixing role variables with role name
- Ignoring variable precedence - Not understanding how Ansible resolves variable conflicts
- Hard-coded values - Embedding environment-specific values instead of using variables
- Missing dependencies - Not declaring role dependencies in meta/main.yml
- No validation - Deploying configurations without validation checks
- Skipping tests - Not including test playbooks or scenarios
- Poor handler design - Restarting services unnecessarily or not at all
- Missing OS support - Assuming all target systems are the same
- No backup strategy - Not backing up configurations before changes
- Ignoring idempotency - Using command/shell modules without proper guards
- Missing tags - Not tagging tasks for selective execution
- Poor template practices - Complex logic in templates instead of variables
- No version control - Not versioning roles or tracking changes