I want to create a more generic Ansible role where you could refine a prefix for all variables you might want to access (just so you won't have to overwrite variables). For this, I had the following scenario:

  1. define the prefix as an ansible variable (e.g. my_role),
  2. define a bunch of other ansible variables having the same prefix (e.g. my_role_message) and
  3. Use my_role_message in the role itself programmatically.

How?

I found that you can use lookup('vars') to get your name programmarically like so:

# tasks for my generic_role
- name: Display message based on prefix variable
  ansible.builtin.debug:
    msg: "Text is: [{{ lookup('vars', my_role ~ '_message') }}]"

This is a simple task which displays the content in a dynamic variable. if my_role is for example "app", then the role would expect to find a variable named app_message and will display its content.

Results

This allows me to define a set of variables:

---
# localhost.yml
super_app_text: "Super App message"
buggy_app_text: "I don't have bugs!"

... and use the role in an ansible playbook like so:

---

- name: Use generic_role with super_app
  hosts: localhost
  vars:
    my_role: "super_app"
  roles:
    - role: generic_role

- name: Use generic_role with buggy_app
  hosts: localhost
  vars:
    my_role: "buggy_app"
  roles:
    - role: generic_role

The result should be something along the lines:

PLAY [Converge] ***********************************************************

TASK [Include laurivan.generic_test] **************************************

TASK [laurivan.generic_test : Display message based on prefix variable] ***
ok: [localhost] => {
    "msg": "Default text for generic_test role"
}

TASK [Include laurivan.generic_test] **************************************

TASK [laurivan.generic_test : Display message based on prefix variable] ***
ok: [localhost] => {
    "msg": "Super App message"
}

TASK [Include laurivan.generic_test] **************************************

TASK [laurivan.generic_test : Display message based on prefix variable] ***
ok: [localhost] => {
    "msg": "I don't have bugs!"
}

PLAY RECAP ****************************************************************
localhost: ok=3    ...

Conclusion

With the above, you can dynamically change the behaviour of a role and make it 'portable' across different instantiations, but this also has some disadvantages:

  • This is another abstraction layer
  • You have to cater for all different use cases
  • Testing is more difficult (multiple use cases)
  • If you have optional steps (e.g. use templates if defined), the role implementation becomes quite complex

With such a generic approachm you practically move behaviour from role description to role configuration.

In the end, you'll have to decide if it's worth writing a generic role or just use dedicated ones, which would be much clearer.

You can find a sample code here. Please look into the molecule's converge.yml to see a bit more details

HTH,