Role Builder
Quick Start
Create well-structured Ansible roles with proper organization, reusability, and idempotency.
Instructions
Step 1: Initialize role structure
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:
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:
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:
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):
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):
Role-specific variables
app_binary: /usr/local/bin/myapp config_template: config.j2
Step 5: Add metadata
meta/main.yml:
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:
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
-
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
-
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:
Good
- name: Install package ansible.builtin.package: name: nginx
Avoid
- name: Install package package: name: nginx
Add descriptive names:
Good
- name: Install nginx web server ansible.builtin.package: name: nginx
Avoid
- name: Install ansible.builtin.package: name: nginx
Use tags for selective execution:
-
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:
-
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