Roles — Organizing Playbooks for Reuse
Ansible
Chapter 7 · Roles — Organizing Playbooks for Reuse
Everything so far — tasks, handlers, variables, templates — has lived in one or two files. That works for a small example; it doesn't scale. This chapter is Ansible's real answer: roles, a standard way to package each concern into its own self-contained, reusable unit.
The Problem — One Big Playbook Doesn't Scale
As a playbook grows — more tasks, more handlers, more variables, more templates, all covering genuinely different concerns — it becomes hard to reuse any single piece elsewhere, and hard to find anything in a file that keeps growing. Roles solve this by giving each concern its own directory, structured the same predictable way every time.
The Role Directory Structure
- tasks/main.yml — the role's own task list, loaded automatically by convention, no explicit reference needed anywhere
- handlers/main.yml — handlers scoped to this role, working exactly as
ansible1-6described - templates/ —
.j2files, referenced by relative filename from the role's own tasks - defaults/main.yml — deliberately the lowest-precedence variables per
ansible1-5's own order, meant to be overridden by whoever uses the role - vars/main.yml — higher-precedence variables, meant to stay more fixed, internal to the role's own logic
- files/ — static files for a plain
copytask - meta/main.yml — role metadata, including dependencies on other roles
Using a Role in a Playbook
Every task in every listed role runs, in order, before any play-level tasks. For finer control over ordering — interleaving a role's tasks with ordinary tasks at a specific point — include_role does the same job from inside a normal tasks: list:
Ansible Galaxy — A Registry of Shared Roles
ansible-galaxy install <role> pulls a pre-built, community-maintained role from the public Galaxy registry — the same "why reinvent this" idea as tf1-7's own public Terraform Registry. ansible-galaxy init <rolename> scaffolds the entire directory structure above automatically, so the exact folder names never need to be memorized or typed by hand.
DRY Configuration — Why This Actually Matters Here
Without roles, the capstone's own real target — ws1's HTTPS/Let's Encrypt setup, UFW, fail2ban, SSH hardening, and Apache security headers — would all have to live crammed into one sprawling playbook. With roles, each of those concerns becomes its own independently testable, independently reusable unit. This directly foreshadows how ansible1-10's own capstone is actually structured: one role per ws1 concern, not one giant file.
| Precedence (ansible1-5) | Intended use | |
|---|---|---|
| defaults/main.yml | Lowest — meant to be overridden | Sensible defaults, freely customizable by whoever uses the role |
| vars/main.yml | Higher — closer to fixed | Internal values the role's own logic depends on, not meant for casual overriding |
ansible-galaxy init nginx creates every subdirectory shown above, empty and ready to fill in — no need to remember the exact folder names or create them by hand one at a time.
task/ instead of tasks/ doesn't raise an error; Ansible simply finds nothing there and treats the role as if it has no tasks at all, silently doing nothing. Worth checking directory spelling directly when a role appears to run successfully but does nothing.
Hands-On Exercises
Sketch the directory structure (folder names only, matching this chapter's own layout) for a new role called "fail2ban" that needs its own tasks, handlers, and one default variable.
📄 View solutionExplain why a role variable meant to let users customize the SSH port should live in defaults/main.yml rather than vars/main.yml, referencing this chapter's own precedence table.
📄 View solutionA role named "ufw" runs successfully with no errors, but none of its intended firewall rules are ever applied. Using this chapter's own warn-box, propose the most likely explanation and how to confirm it.
📄 View solutionChapter 7 Quick Reference
- A role packages tasks/handlers/templates/vars/files for one concern into a standard directory structure
- tasks/main.yml auto-loads by convention; defaults/main.yml is deliberately lowest precedence, vars/main.yml higher
roles:at the play level runs all listed roles first;include_roleinterleaves role tasks anywhere in a task list- Ansible Galaxy — a public registry of shared roles, the same idea as Terraform's own Registry
ansible-galaxy init <name>scaffolds the whole directory tree automatically- A typo'd directory name (task/ vs tasks/) fails silently — the role just does nothing, with no error
- The capstone (ansible1-10) is structured as one role per ws1 concern — this chapter's own DRY payoff, made concrete