Chapter Eight
Roles & Reuse
The flat Larkspur web playbook has outgrown itself — a dozen tasks, three handlers, four templates. This chapter packages it into the reusable larkspur_web role: the standard directory layout, defaults as a public interface, dependencies and composition, the static and dynamic ways to apply it, and how Galaxy and collections distribute the result. By the end the scattered tasks are one named, idempotent, publishable unit.
A role is the unit Ansible gives you for reuse: a packaged bundle of tasks, handlers, templates, files, and defaults in a fixed directory layout the engine knows how to load by convention. It is the Ansible analog of a Terraform module — a named, versioned thing you drop into any playbook instead of copying a wall of YAML between projects. The whole chapter is the same worked refactor: the Layer A web stack, scattered across a flat playbook, becomes larkspur_web.
The order matters. First the anatomy and the auto-load convention that makes a role "just work," then where variables live and why defaults/ versus vars/ is a precedence decision, then dependencies and the static-versus-dynamic application split that mirrors import versus include. The last three pages move outward — Galaxy for community roles, collections and FQCN for the modern packaging model, and the discipline that separates a role that runs from a role worth reusing.
Topics in This Chapter
main.yml auto-load convention, and why templates resolve relatively inside a role. Splitting a large tasks/main.yml into an imported index, and what a role is concretely — a unit addressed by name, not by path.defaults/main.yml is the role's public interface at the bottom of precedence; vars/main.yml is high-precedence internal constants. Where each lands in the 22 levels, why role variables must be namespaced, and the interface contract that makes a role safe to reuse.meta/main.yml carries — galaxy_info and dependencies — and how a declared dependency runs before the dependent role. Why dependency chains hide execution order, and when explicit composition in the playbook beats a buried meta dependency.roles: keyword, static import_role, dynamic include_role, passing per-invocation vars:, and why only include_role can loop.ansible-galaxy role install, and requirements.yml as a lockfile for role dependencies. Vetting a community role before it lands in your deploy path, and why you pin a version instead of floating HEAD.namespace.collection name. Why ansible.builtin.copy is now how you address a module, which collection ansible-core ships, and how requirements.yml declares both roles and collections.argument_specs that fail fast — the assembled larkspur_web role.