// learn Β· linux Β· rhce Β· 11 min

Ansible roles.

A role bundles tasks, handlers, templates, files, and variables into one reusable directory layout β€” and Ansible auto-loads each subdir's main.yml. Watch the tree light up subdir by subdir, then pull a community role off Galaxy with a requirements.yml lockfile.

Ansible roles animated tutorial. The standard role directory layout β€” tasks, handlers, templates, files, vars, defaults, and meta main.yml files that Ansible auto-loads. Using roles in a play with parameters, import_role versus include_role, defaults versus vars variable precedence, installing community roles and collections from Ansible Galaxy, pinning them in requirements.yml, role dependencies in meta/main.yml, and Fully-Qualified Collection Names (FQCN). RHCE EX294 ready.
// ansible Β· roles & collections

A role is just a fixed directory layout Ansible knows how to auto-load. Watch a role's tree light up subdir by subdir β€” tasks, handlers, templates, defaults vs vars β€” then pull one off Galaxy with requirements.yml.

// before you start
you should know
  • Β·Comfortable writing Ansible playbooks and tasks
  • Β·Know what handlers and templates (.j2) are
  • Β·Basic ansible.cfg / inventory familiarity
by the end you'll

Scaffold a role with the standard layout, use it in a play with parameters, place variables correctly in defaults vs vars, and install community roles and collections from Galaxy via requirements.yml.

pace: 11 minutes

ansible β€” step 1 / 6 Β· the idea
πŸ“¦ roles/webserver/
β”œβ”€ tasks/main.ymlauto-loaded
the work β€” runs when the role is applied
β”œβ”€ handlers/main.ymlauto-loaded
handlers you notify (restart httpd …)
β”œβ”€ templates/
.j2 files β€” referenced by basename
β”œβ”€ files/
static files β€” src for the copy module
β”œβ”€ vars/main.ymlauto-loaded
HIGH-precedence vars (hard to override)
β”œβ”€ defaults/main.ymlauto-loaded
LOWEST-precedence vars (user-overridable)
β”œβ”€ meta/main.ymlauto-loaded
metadata + role dependencies
β”‚ auto-loaded into the play β”‚
site.yml β€” a play
- hosts: webservers
  roles:
    - webserver
↑ each subdir's main.yml is auto-loaded β€” no include paths

Why roles β€” stop copy-pasting

A role is a self-contained, reusable bundle of tasks, handlers, templates, files, and variables that follows a STANDARD directory layout. Drop a role into a project and Ansible auto-loads main.yml from each subdir β€” there are no include paths to wire up by hand. Roles keep your playbooks short: a 200-line web setup collapses to one line, `roles: [webserver]`. And because the layout is a fixed convention, a role you write and a role pulled off Ansible Galaxy plug in exactly the same way.

$ansible-galaxy init webserver
$ls roles/webserver/
$roles:\n - webserver
// key insight

A role is just a convention β€” a fixed directory layout where Ansible auto-loads tasks, handlers, templates, and defaults from each subdir's main.yml. Learn the layout once and every role β€” yours or one pulled off Galaxy β€” drops into a play with a single line: roles: [webserver].

// exam-ready Β· role layout, galaxy & collections
$ansible-galaxy init NAME
$roles/NAME/tasks/main.yml
$handlers/main.yml
$templates/ (by basename)
$files/ (copy src)
$vars/main.yml
$defaults/main.yml
$meta/main.yml
$roles:\n - role: NAME\n k: v
$- import_role: name=NAME
$- include_role: name=NAME
$ansible-galaxy role install X
$ansible-galaxy install -r requirements.yml
$ansible-galaxy collection install ns.coll
$ns.coll.module (FQCN)
// check yourself
4 quick questions
Q1

Which role subdir is auto-loaded as the role's main task list?

Q2

You want a role variable users can easily override from inventory. Put it in defaults/ or vars/?

Q3

What does `ansible-galaxy init webserver` do?

Q4

import_role vs include_role β€” what's the difference?

These aren't graded β€” they're just for active recall, which is what actually makes the lesson stick.

πŸ”’
// next: secrets

Ansible Vault

Roles and group_vars often hold passwords and keys. Learn to encrypt sensitive data right in your repo with Ansible Vault β€” so secrets ship safely beside your playbooks.

open β†’

Read this lesson as text6 chapters Β· quiz answers

  1. 01Why roles β€” stop copy-pasting

    A role is a self-contained, reusable bundle of tasks, handlers, templates, files, and variables that follows a STANDARD directory layout. Drop a role into a project and Ansible auto-loads main.yml from each subdir β€” there are no include paths to wire up by hand. Roles keep your playbooks short: a 200-line web setup collapses to one line, roles: [webserver]. And because the layout is a fixed convention, a role you write and a role pulled off Ansible Galaxy plug in exactly the same way.

    • $ ansible-galaxy init webserver β€” scaffold the standard skeleton
    • $ ls roles/webserver/ β€” see the standard subdirs
    • $ roles:\n - webserver β€” use it β€” one line in a play
  2. 02The directory layout

    Every role is the same set of subdirs, each with a main.yml that Ansible loads automatically. tasks/ holds the work, handlers/ holds handlers you notify, templates/ holds .j2 files (referenced by basename), files/ holds static files for copy, vars/ holds high-precedence vars, defaults/ holds the lowest-precedence (overridable) vars, and meta/ holds metadata and role dependencies. You never write include paths β€” being in the right subdir IS the wiring. A template at templates/httpd.conf.j2 is reached as just httpd.conf.j2; a static file under files/ is the src= for the copy module.

    • $ roles/webserver/tasks/main.yml β€” the work runs from here
    • $ template: src=httpd.conf.j2 β€” templates/ ref by basename
    • $ copy: src=index.html β€” files/ is the copy source dir
  3. 03Using roles in a play

    The simplest way is a static roles: list on the play β€” those roles run after pre_tasks and before the play's own tasks. Pass parameters by giving the role as a mapping with extra keys (they become role vars). For finer control, call a role from inside tasks: import_role is STATIC (parsed at parse time, so tags and when apply to all its tasks), while include_role is DYNAMIC (evaluated at runtime, so it can be looped or made conditional). Reach for the dynamic include only when you actually need run-time behaviour.

    • $ roles:\n - role: webserver\n http_port: 8080 β€” run with a parameter
    • $ - import_role:\n name: webserver β€” static β€” parsed at parse time
    • $ - include_role:\n name: webserver β€” dynamic β€” runtime, loopable
  4. 04defaults vs vars β€” precedence

    These two subdirs look alike but sit at opposite ends of variable precedence. defaults/main.yml is the LOWEST precedence in all of Ansible β€” it exists to be overridden by inventory vars, group_vars, play vars, or -e extra-vars. vars/main.yml is HIGH precedence and is genuinely hard to override. The rule: put tunables a user might change (ports, versions) in defaults; put internal constants the role relies on (package name, config path) in vars. Get this backwards and your role becomes painful to reuse.

    • $ defaults/main.yml\nhttp_port: 80 β€” lowest β€” meant to be overridden
    • $ vars/main.yml\npkg_name: httpd β€” high β€” internal constant
    • $ group_vars/web.yml\nhttp_port: 8080 β€” beats the default cleanly
  5. 05ansible-galaxy & requirements.yml

    You rarely write everything from scratch β€” Ansible Galaxy hosts community roles and collections you can install. Pin exactly what a project needs in a requirements.yml and install it all in one command, so every teammate and CI runner gets the same versions. Where the content lands is governed by roles_path and collections_path in ansible.cfg (defaults include ./roles and ~/.ansible). Treat requirements.yml like a lockfile: it makes a project reproducible.

    • $ ansible-galaxy role install geerlingguy.nginx β€” grab one role
    • $ ansible-galaxy install -r requirements.yml β€” install everything pinned
    • $ ansible-galaxy collection install ansible.posix β€” grab a collection
  6. 06Dependencies & collections (FQCN)

    meta/main.yml can declare role dependencies: β€” those roles run before this role, so a webserver role can depend on a baseline role and trust it has already run. Collections are the bigger packaging unit: they bundle roles, modules, and plugins under a namespace. You reference content from a collection by its Fully-Qualified Collection Name, namespace.collection.module β€” for example ansible.posix.firewalld. A collections: key on a play lets you use short names, but FQCN is unambiguous and is what the exam expects.

    • $ meta/main.yml\ndependencies:\n - role: baseline β€” runs before this role
    • $ - ansible.posix.firewalld:\n service: http β€” module by FQCN
    • $ collections:\n - ansible.posix β€” play-level short names

Quiz β€” answers

Which role subdir is auto-loaded as the role's main task list?
β†’tasks/main.yml β€” Ansible runs this automatically when the role is applied.tasks/main.yml is the entry point Ansible loads for the role's work. handlers/main.yml holds notified handlers, meta/main.yml holds metadata and dependencies, and vars/main.yml holds high-precedence variables β€” none of those are the task list.
You want a role variable users can easily override from inventory. Put it in defaults/ or vars/?
β†’defaults/main.yml β€” it is the lowest precedence in Ansible, so inventory, group_vars, play vars, and -e all beat it cleanly.defaults/main.yml is the lowest-precedence source of all, designed to be overridden. vars/main.yml is HIGH precedence and is hard to override, so it's the wrong place for anything a user should tune.
What does ansible-galaxy init webserver do?
β†’Scaffolds the standard role directory skeleton (tasks/, handlers/, templates/, files/, vars/, defaults/, meta/) under roles/webserver/.init creates an empty role with the standard layout already in place, each subdir holding a stub main.yml. Installing a published role is a different command, ansible-galaxy role install NAME.
import_role vs include_role β€” what's the difference?
β†’import_role is static (parsed at parse time, so tags apply to all its tasks); include_role is dynamic (evaluated at runtime, so it can be looped or made conditional).Static import_role is resolved when the playbook is parsed, so tags and when on the import apply to every task inside. Dynamic include_role is resolved at runtime, which is what lets you loop over it or guard it with a condition β€” at the cost of tags not propagating the same way.
// more in systems

keep going β€” these pair well with what you just learned.

see all systems β†’

See also

back to RHCSA / RHCE trackall lessons