TT Lab
Get started
Learn Learning paths Courses

Ansible in Practice

Building a Reusable Role

Continue in TT Lab

Goal

You fold playbook pieces into a role to make a reusable unit, and learn to call the same role several times with different values.

Why it matters

The essence of a role is not "splitting code" but "fixing a convention so that it can be reused without configuration." A file placed in templates/ is found without writing a path, and a handler in handlers/main.yml can be used without any registration step. Thanks to this convention, you can guess the structure even of a role someone else wrote. What actually determines reusability is the choice between defaults and vars — if you put a value users should change in vars, its precedence is so high that it cannot be overridden from outside, and the role ends up being copied and edited and diverging again. And the habit of prefixing variable names with the role name pays off the moment you have more than two roles.

Steps

  1. Under /root/ans/roles/roles/webapp, create the tasks, defaults, handlers, templates, and meta directories and tasks/main.yml.
  2. In /root/ans/roles/roles/webapp/defaults/main.yml, define webapp_port: 8080 and webapp_root: /root/ans/roles/artifacts.
  3. In /root/ans/roles/roles/webapp/tasks/main.yml, add at least 2 named tasks, and make running them create the /root/ans/roles/artifacts directory.
  4. Create /root/ans/roles/roles/webapp/templates/webapp.conf.j2 and render it to /root/ans/roles/artifacts/webapp.conf. The result must contain the line port = 8080, and the template must reference the webapp_port variable.
  5. In /root/ans/roles/roles/webapp/handlers/main.yml, define a restart webapp handler and notify it from a task in /root/ans/roles/roles/webapp/tasks/main.yml. The handler creates /root/ans/roles/artifacts/restart.marker.
  6. Create a /root/ans/roles/roles/baseline role (entry point /root/ans/roles/roles/baseline/tasks/main.yml) that leaves /root/ans/roles/artifacts/baseline.stamp, and declare it in dependencies of /root/ans/roles/roles/webapp/meta/main.yml.
  7. In /root/ans/roles/site.yml, call the webapp role twice. Call it once with the defaults (webapp.conf, port 8080), and once with webapp_port: 9443 so that it creates /root/ans/roles/artifacts/staging.conf. Do not copy the role directory.
  8. Save the output of the second playbook run to /root/ans/roles/out/run2.txt and the ansible-lint result to /root/ans/roles/out/lint.txt. The second run must show changed=0 and the handler must not run.

Notes

Create the role skeleton directories

Under /root/ans/roles/roles/webapp, create the tasks, defaults, handlers, templates, and meta directories and tasks/main.yml.

ansible-galaxy role init creates the standard structure for you. The directories you need are tasks/defaults/handlers/templates/meta.

Define two default values

In /root/ans/roles/roles/webapp/defaults/main.yml, define webapp_port: 8080 and webapp_root: /root/ans/roles/artifacts.

Put values that are expected to be overridden in defaults. Prefix the names with the role name.

Write and run the role tasks

In /root/ans/roles/roles/webapp/tasks/main.yml, add at least 2 named tasks, and make running them create the /root/ans/roles/artifacts directory.

tasks/main.yml is the entry point. You need at least 2 tasks, and all of them must have names.

Create a configuration file from a role template

Create /root/ans/roles/roles/webapp/templates/webapp.conf.j2 and render it to /root/ans/roles/artifacts/webapp.conf. The result must contain the line port = 8080, and the template must reference the webapp_port variable.

Files inside templates/ are found by name alone. Do not hard-code the port value; use the variable.

Define a role handler and trigger it

In /root/ans/roles/roles/webapp/handlers/main.yml, define a restart webapp handler and notify it from a task in /root/ans/roles/roles/webapp/tasks/main.yml. The handler creates /root/ans/roles/artifacts/restart.marker.

Handlers in handlers/main.yml are registered automatically. Leave a marker file when it runs.

Add a dependent role so it runs first

Create a /root/ans/roles/roles/baseline role (entry point /root/ans/roles/roles/baseline/tasks/main.yml) that leaves /root/ans/roles/artifacts/baseline.stamp, and declare it in dependencies of /root/ans/roles/roles/webapp/meta/main.yml.

Create the baseline role separately and list it under dependencies in meta/main.yml. The dependent role runs first.

Call the same role twice with different values

In /root/ans/roles/site.yml, call the webapp role twice. Call it once with the defaults (webapp.conf, port 8080), and once with webapp_port: 9443 so that it creates /root/ans/roles/artifacts/staging.conf. Do not copy the role directory.

In the roles section of a play, you can pass variables along with the role name. Do not copy the role.

Pass the rerun idempotency check and the lint

Save the output of the second playbook run to /root/ans/roles/out/run2.txt and the ansible-lint result to /root/ans/roles/out/lint.txt. The second run must show changed=0 and the handler must not run.

On the second run you need changed=0 and a quiet handler. Save the ansible-lint result as well.