# Role to encapsulate several OS specific "sub-roles"

**URL:** <https://forum.ansible.com/t/role-to-encapsulate-several-os-specific-sub-roles/14133>\
**Category:** Ansible Project\
**Created:** [December 11, 2013, 10:01pm UTC](https://forum.ansible.com/t/role-to-encapsulate-several-os-specific-sub-roles/14133 "2013-12-11T22:01:19Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![sebbrochet](https://avatars.discourse-cdn.com/v4/letter/s/a9a28c/32.png) [@sebbrochet](https://forum.ansible.com/u/sebbrochet)\
**Post date:** [December 11, 2013, 10:01pm UTC](https://forum.ansible.com/t/role-to-encapsulate-several-os-specific-sub-roles/14133/1 "2013-12-11T22:01:19Z")

</div>

Hello,

I’ve written a role to install and configure Apache, the role is at the moment specific to Debian.  
This means my role/apache/tasks/main.yml contains corresponding tasks.

I need also to support CentOS.  
I could add corresponding tasks to the same file and add a when clause for each task, based on OS version.  
But it makes more sense to create a specific role for each os: apache-debian and apache-centos

My main playbook, site.yml include the apache role:  
role:

- apache

I’d like my playbook to describe I want Apache installed and the apache role to install the right role based on the host OS.  
As a task file can’t conditionally include (yet!) a role, the only option (not tested) I’ve come so far to implement it, is to use role dependency and when clause by putting in role/apache/meta/main.yml:  
dependencies:

- { role: apache-debian, when: “ansible\_os\_family == ‘Debian’” }

- { role: apache-centos, when: “ansible\_os\_family == ‘CentOS’” }

and role/apache/tasks/main.yml would be empty and just acts as a dispatcher.

This way my apache role encapsulates the apache-debian and apache-centos roles, so far so good.  
But I’d like these sub-roles to be packaged with the apache role to be shared easily.  
This means they could be defined in a “roles” sub-directory of the apache role.

I don’t think it’s currently possible so the sub-roles should be located at the same level than my shared apache role, which is far from perfect 🙂

Do you see other options to achieve clean role encapsulation and packaging?

Thanks

@sebbrochet

---

<div class="post-metadata">

**Author:** ![sebbrochet](https://avatars.discourse-cdn.com/v4/letter/s/a9a28c/32.png) [@sebbrochet](https://forum.ansible.com/u/sebbrochet)\
**Post date:** [December 12, 2013, 1:02pm UTC](https://forum.ansible.com/t/role-to-encapsulate-several-os-specific-sub-roles/14133/2 "2013-12-12T13:02:50Z")

</div>

Replying to myself…

It’s possible to reference a role using a path, i.e apache/roles/debian  
Then I can create my sub-roles directly under my apache role:

apache  
\_ meta  
\_ main.yml  
\_ roles  
\_ centos  
\_ tasks  
\_ main.yml  
\_ debian

\_ tasks  
\_ main.yml

And put in apache/meta/main.yml:

dependencies:

- { role: apache/roles/debian, when: “ansible\_os\_family == ‘Debian’” }
- { role: apache/roles/centos, when: “ansible\_os\_family == ‘RedHat’” }

Only minor drawback is that with the **when** clause, all tasks are listed even if they are not executed.  
This could clutter the output if many tasks are not executed…

root@machine ~/devops/ansible2/projects/test-role-os $ ansible-playbook -i centos.hosts site.yml

PLAY [test-role-hosts] \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*

GATHERING FACTS \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
ok: [vps-centos]

TASK: [testrole/roles/debian | say hello from your os] \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
skipping: [vps-centos]

TASK: [testrole/roles/centos | say hello from your os] \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
changed: [vps-centos]

PLAY RECAP \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
vps-centos : ok=2 changed=1 unreachable=0 failed=0

root@machine ~/devops/ansible2/projects/test-role-os $ ansible-playbook -i debian.hosts site.yml

PLAY [test-role-hosts] \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*

GATHERING FACTS \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
ok: [vps-debian]

TASK: [testrole/roles/debian | say hello from your os] \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
changed: [vps-debian]

TASK: [testrole/roles/centos | say hello from your os] \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
skipping: [vps-debian]

PLAY RECAP \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  
vps-debian : ok=2 changed=1 unreachable=0 failed=0

What about a task with a **special when clause** that doesn’t output anything if it’s not executed ?

Thanks

@sebbrochet

---

<div class="post-metadata">

**Author:** ![James\_Martin2](https://avatars.discourse-cdn.com/v4/letter/j/b9e5f3/32.png) [@James\_Martin2](https://forum.ansible.com/u/James_Martin2)\
**Post date:** [December 12, 2013, 2:11pm UTC](https://forum.ansible.com/t/role-to-encapsulate-several-os-specific-sub-roles/14133/3 "2013-12-12T14:11:45Z")

</div>

Here's what I do --

Prior to all my roles, I do a role called bootstrap across all of my  
nodes. That role performs a group\_by task:

- name: Create groups based on distribution  
&nbsp;&nbsp;group\_by: key={{ ansible\_distribution }}

then I execute my other roles -- first I execute the OS specific  
role--assigned to the intersection of my group in inventory and the  
group created by group\_by task above--followed by the common role,  
that works on both OSes.

- hosts: CentOS:RedHat:&riak\_cluster  
&nbsp;&nbsp;gather\_facts: no  
&nbsp;&nbsp;sudo: True  
&nbsp;&nbsp;roles:

&nbsp;&nbsp;- { role: riak/redhat, tags: ["riak"] }  
&nbsp;&nbsp;- { role: riak/common, tags: ["riak"] }

- hosts: Ubuntu:&riak\_cluster  
&nbsp;&nbsp;gather\_facts: no  
&nbsp;&nbsp;sudo: True

&nbsp;&nbsp;roles:

&nbsp;&nbsp;- { role: riak/ubuntu, tags: ["riak"] }  
&nbsp;&nbsp;- { role: riak/common, tags: ["riak"] }

- James

---

<div class="post-metadata">

**Author:** ![Brian\_Candler](https://avatars.discourse-cdn.com/v4/letter/b/5f9b8f/32.png) [@Brian\_Candler](https://forum.ansible.com/u/Brian_Candler)\
**Post date:** [December 16, 2013, 2:50pm UTC](https://forum.ansible.com/t/role-to-encapsulate-several-os-specific-sub-roles/14133/4 "2013-12-16T14:50:31Z")

</div>

You can include tasks conditionally within a role:

[roles/apache/tasks/main.yml]

- include: debian.yml  
when: ansible\_os\_family == ‘Debian’
- include: redhat.yml  
when: ansible\_os\_family == ‘RedHat’

# Now do stuff which is both common to Debian and RedHat

As well as doing OS-specific tasks in there, you can use set\_fact to set variables conditionally. e.g.

[roles/apache/tasks/debian.yml]

- set\_fact: service\_name=apache2

[roles/apache/tasks/redhat.yml]

- set\_fact: service\_name=httpd

# In the common config: service name={{service\_name}} state=started enabled=yes

(Aside: this is messy compared to dynamically pulling in a vars file as you can do in a playbook, but that feature isn’t available within a role AFAICS. But I’m with you: I believe this stuff belongs in the role, not the playbook)

You will still have the annoying thing that all RedHat tasks will be shown as “skipped” on a Debian host and vice versa. You can reduce the noise a bit using

> **[The Manifesto for Agentic Automation](https://aiagentmanifesto.org)**
>
> Governing the Shift from Automation to Autonomy. The standard for Autonomous Workflows.

although if _all_ your hosts are the same type, I believe the task header is still shown.
