Role Builder
Quick Start
Create well-structured Ansible roles with proper organization, reusability, and idempotency.
Instructions
Step 1: Initialize role structure
bash
# Create role using ansible-galaxy
ansible-galaxy init my_role
# Or create manually
mkdir -p roles/my_role/{tasks,handlers,templates,files,vars,defaults,meta}
Standard role structure:
code
my_role/ ├── tasks/ │ └── main.yml # Main task list ├── handlers/ │ └── main.yml # Handlers ├── templates/ │ └── config.j2 # Jinja2 templates ├── files/ │ └── script.sh # Static files ├── vars/ │ └── main.yml # Role variables ├── defaults/ │ └── main.yml # Default variables ├── meta/ │ └── main.yml # Role metadata and dependencies └── README.md # Role documentation
Step 2: Define tasks
tasks/main.yml:
yaml
---
# Main task file for my_role
- name: Install required packages
ansible.builtin.package:
name: "{{ item }}"
state: present
loop: "{{ required_packages }}"
tags: [packages]
- name: Create application directory
ansible.builtin.file:
path: "{{ app_directory }}"
state: directory
owner: "{{ app_user }}"
group: "{{ app_group }}"
mode: '0755'
tags: [setup]
- name: Deploy configuration file
ansible.builtin.template:
src: config.j2
dest: "{{ app_directory }}/config.yml"
owner: "{{ app_user }}"
group: "{{ app_group }}"
mode: '0644'
notify: restart application
tags: [config]
- name: Ensure service is running
ansible.builtin.service:
name: "{{ service_name }}"
state: started
enabled: true
tags: [service]
Step 3: Create handlers
handlers/main.yml:
yaml
---
# Handlers for my_role
- name: restart application
ansible.builtin.service:
name: "{{ service_name }}"
state: restarted
- name: reload application
ansible.builtin.service:
name: "{{ service_name }}"
state: reloaded
- name: validate configuration
ansible.builtin.command:
cmd: "{{ app_binary }} --validate-config"
changed_when: false
Step 4: Define variables
defaults/main.yml (default values):
yaml
--- # Default variables for my_role app_user: appuser app_group: appgroup app_directory: /opt/myapp service_name: myapp required_packages: - python3 - python3-pip - git
vars/main.yml (role-specific variables):
yaml
--- # Role-specific variables app_binary: /usr/local/bin/myapp config_template: config.j2
Step 5: Add metadata
meta/main.yml:
yaml
---
galaxy_info:
author: Your Name
description: Role for deploying my application
company: Your Company
license: MIT
min_ansible_version: '2.9'
platforms:
- name: Ubuntu
versions:
- focal
- jammy
- name: EL
versions:
- '8'
- '9'
galaxy_tags:
- application
- deployment
dependencies:
- role: common
vars:
common_packages:
- curl
- wget
Role Design Patterns
Pattern 1: Modular tasks
Break tasks into separate files:
yaml
# tasks/main.yml --- - name: Include installation tasks ansible.builtin.include_tasks: install.yml tags: [install] - name: Include configuration tasks ansible.builtin.include_tasks: configure.yml tags: [configure] - name: Include service tasks ansible.builtin.include_tasks: service.yml tags: [service]
Pattern 2: Conditional execution
yaml
- name: Install on Debian-based systems
ansible.builtin.apt:
name: "{{ package_name }}"
state: present
when: ansible_os_family == "Debian"
- name: Install on RedHat-based systems
ansible.builtin.yum:
name: "{{ package_name }}"
state: present
when: ansible_os_family == "RedHat"
Pattern 3: Idempotent operations
yaml
- name: Ensure configuration is present
ansible.builtin.lineinfile:
path: /etc/myapp/config
line: "{{ config_line }}"
state: present
create: true
# Idempotent - only changes if line is missing
- name: Ensure service is running
ansible.builtin.service:
name: myapp
state: started
# Idempotent - only starts if not running
Best Practices
Use fully qualified collection names:
yaml
# Good
- name: Install package
ansible.builtin.package:
name: nginx
# Avoid
- name: Install package
package:
name: nginx
Add descriptive names:
yaml
# Good
- name: Install nginx web server
ansible.builtin.package:
name: nginx
# Avoid
- name: Install
ansible.builtin.package:
name: nginx
Use tags for selective execution:
yaml
- name: Install packages
ansible.builtin.package:
name: "{{ item }}"
loop: "{{ packages }}"
tags: [packages, install]
- name: Configure application
ansible.builtin.template:
src: config.j2
dest: /etc/app/config
tags: [config, configure]
Implement error handling:
yaml
- name: Attempt risky operation
ansible.builtin.command:
cmd: /usr/local/bin/risky-command
register: result
failed_when: false
changed_when: result.rc == 0
tags: [risky]
- name: Handle failure
ansible.builtin.debug:
msg: "Operation failed, continuing anyway"
when: result.rc != 0
Advanced
For detailed information, see:
- •Role Structure - Complete role directory layout and organization
- •Handlers - Handler patterns and best practices
- •Variables - Variable precedence and management