Ansible Role
In Ansible, a Role is a mechanism for automatically loading certain vars_files, tasks, handlers, and templates based on a known file structure. Instead of writing a massive, single-file playbook (monolithic style), you group your automation assets into a standardized directory structure.
Why Use Roles
- Reusability: Share roles across different projects, environments, or teams.
- Maintainability: Separation of concerns makes it easy to modify specific configurations (e.g., changing a variable or updating a template) without breaking the core logic.
- Readability: Playbooks become small, declarative master orchestration files instead of containing hundreds of lines of code.
Standard Ansible Role Directory Structure
When you create a role using ansible-galaxy role init <role_name>, it creates a standardized skeleton. Here is a breakdown of what each directory represents:
tomcat/
├── defaults/
│ └── main.yml # Low-priority default variables (easily overridden).
├── vars/
│ └── main.yml # High-priority role-specific variables.
├── tasks/
│ └── main.yml # The core sequence of tasks executed by the role.
├── templates/
│ ├── tomcat.service.j2 # Jinja2 templates dynamic text configuration files.
│ ├── tomcat-users.xml.j2
│ └── context.xml.j2
├── files/
│ └── secure-policy.jar # Static files to be copied directly to remote machines.
├── handlers/
│ └── main.yml # Tasks triggered by 'notify' statements (e.g., restarts).
├── meta/
│ └── main.yml # Role metadata (author, description, dependencies).
└── README.md # Documentation for using the role.
Detailed Folder Explanations
| Folder Name | Purpose | Teacher’s Note / Best Practice |
|---|---|---|
defaults/ |
Contains default values for variables used inside the role. These have the lowest precedence and can be easily overridden in playbooks or inventories. | Always place configuration tunables here (e.g., port numbers, version numbers). |
vars/ |
Contains internal variables that shouldn’t be easily modified or overridden by external configs. | Use this for internal logic/constants specific to the operating system or application design. |
tasks/ |
The heart of the role. Contains the sequential task definitions. Can be broken into smaller files and included using import_tasks or include_tasks. |
Keep main.yml clean. If the playbook grows, split tasks into install.yml, configure.yml, and service.yml. |
templates/ |
Holds Jinja2 template files (ending in .j2). These files can contain variables that get evaluated and rendered dynamically on the target node. |
Great for dynamically inserting system configurations based on host variables (like max memory or host IPs). |
files/ |
Stores static files that do not need parsing or variable substitution. They are transferred exactly as-is using the copy module. |
Keep large binary assets out of here; pull them from an artifact repository (like Nexus/Artifactory) if possible. |
handlers/ |
Contains tasks that are only executed when “notified” by a task that reports a state change (e.g., reloading systemd after a template update). | Perfect for stopping/starting services only when configuration files actually change, making playbooks faster and more efficient. |
meta/ |
Defines role metadata, supported platforms, and upstream dependencies (other roles that must run before this one). | Essential if publishing to Ansible Galaxy or defining strict infrastructure pre-requisites. |
Once your role files are in place under a roles/ directory, your main production playbook becomes elegant, minimal, and fully abstract.
Create a file named deploy_tomcat.yml:
---
- name: Deploy Apache Tomcat 10
hosts: all
become: yes
roles:
- role: tomcat
vars:
# You can easily override default configurations inline here if needed
tomcat_version: 10.1.55
java_version: openjdk-21-jdk
Running the Playbook:
ansible-playbook -i inventory.ini deploy_tomcat.yml
