# Ansible Roles in 1.8.4 - Still inferior to include files for complex playbooks?

**URL:** <https://forum.ansible.com/t/ansible-roles-in-1-8-4-still-inferior-to-include-files-for-complex-playbooks/17603>\
**Category:** Ansible Project\
**Created:** [February 23, 2015, 9:41pm UTC](https://forum.ansible.com/t/ansible-roles-in-1-8-4-still-inferior-to-include-files-for-complex-playbooks/17603 "2015-02-23T21:41:55Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![jack.martin8912](https://avatars.discourse-cdn.com/v4/letter/j/7ba0ec/32.png) [@jack.martin8912](https://forum.ansible.com/u/jack.martin8912)\
**Post date:** [February 23, 2015, 9:41pm UTC](https://forum.ansible.com/t/ansible-roles-in-1-8-4-still-inferior-to-include-files-for-complex-playbooks/17603/1 "2015-02-23T21:41:55Z")

</div>

From [Ansible doc](http://docs.ansible.com/playbooks_roles.html#introduction):

> Roles in Ansible build on the idea of include files and combine them to _form clean, reusable abstractions_

I really want to love roles and use them extensively in our system, but so far I’m getting a heap of frustration instead. I am trying to position Ansible at work as the preferred automation platform to manage a complex system with several internally developed apps, plus db, web server, and more. (together this is one product/system) Multiple environments too, as usual.

I’d think it makes sense to have roles like:

- appX (for X in 1 through N)
- web
- db
- etc (more… skipped)

One seemingly basic thing I need to do is to restart this entire system occasionally. I would like to use Ansible to do that. Here’s a simplified pseudo-code recipe of a playbook:

- web.startMaintenance()
- app1.stop()
- app2.stop()
- app2.start()
- app1.start()
- web.endMaintenance()

**I want to run a _single_ playbook command to do this.** That seems clearly possible with include files but not possible with roles. The following doesn’t work:

ansible-playbook --tags “app1.stop,app1.start,app2.start,app2.stop,web.startMaintenance,web.endMaintenance” playbooks/testRoleDeps.yml

That doesn’t work because all plays in the playbook are filtered by the tags provided, and each play needs to have a particular tag applied (first stop _then_ start later), not all of them.

My conclusion? **As of 1.8.4, roles are inadequate for complex playbooks and include files need to be used instead.**

That’s unfortunate for code reuse reasons, etc. I would love to hear that this is not the case and I have overlooked something. Maybe a feature to resolve this is in the release plan?

I can’t figure out how to do this even after reading the documentation repeatedly, reading the ansible O’Reilly book (pre-release copy), the code in ansible-examples, and various other resources.

---

<div class="post-metadata">

**Author:** ![Timothy\_Gerla](https://avatars.discourse-cdn.com/v4/letter/t/977dab/32.png) [@Timothy\_Gerla](https://forum.ansible.com/u/Timothy_Gerla)\
**Post date:** [February 23, 2015, 10:17pm UTC](https://forum.ansible.com/t/ansible-roles-in-1-8-4-still-inferior-to-include-files-for-complex-playbooks/17603/2 "2015-02-23T22:17:34Z")

</div>

Some replies inline!

---

<div class="post-metadata">

**Author:** ![jack.martin8912](https://avatars.discourse-cdn.com/v4/letter/j/7ba0ec/32.png) [@jack.martin8912](https://forum.ansible.com/u/jack.martin8912)\
**Post date:** [February 24, 2015, 2:30am UTC](https://forum.ansible.com/t/ansible-roles-in-1-8-4-still-inferior-to-include-files-for-complex-playbooks/17603/3 "2015-02-24T02:30:53Z")

</div>

> Some replies inline!
> 
> Thanks for all the help! I'll reply inline too.

> > From Ansible doc  
> > \<[http://docs.ansible.com/playbooks\_roles.html#introduction&gt;:](http://docs.ansible.com/playbooks_roles.html#introduction&gt;:)
> > 
> > Roles in Ansible build on the idea of include files and combine them to \*form
> > 
> > > clean, reusable abstractions\*
> > 
> > I really want to love roles and use them extensively in our system, but  
> > so far I'm getting a heap of frustration instead. I am trying to position  
> > Ansible at work as the preferred automation platform to manage a complex  
> > system with several internally developed apps, plus db, web server, and  
> > more. (together this is one product/system) Multiple environments too, as  
> > usual.
> > 
> > I'd think it makes sense to have roles like:  
> > - appX (for X in 1 through N)  
> > - web  
> > - db  
> > - etc (more.. skipped)
> 
> That's usually the case for simple stuff, but for each app or component of  
> the system, I usually end up with multiple roles. Maybe a role, a role to  
> configure, a role to restart. When you start wanting to call individual  
> parts of a single role separately, that's a good sign that you need to  
> break that role up into multiple parts. For organizational purposes, you  
> can definitely do:
> 
> roles/web/setup/\*  
> roles/web/install/\*  
> roles/web/config/\*  
> roles/web/restart/\*

This is very helpful. I have provided some sample code below showing how I  
have used this to do what I intended.

> > One seemingly basic thing I need to do is to restart this entire system  
> > occasionally. I would like to use Ansible to do that. Here's a simplified  
> > pseudo-code recipe of a playbook:  
> > - web.startMaintenance()  
> > - app1.stop()  
> > - app2.stop()  
> > - app2.start()  
> > - app1.start()  
> > - web.endMaintenance()
> > 
> > \*I want to run a single playbook command to do this.\* That seems clearly  
> > possible with include files but not possible with roles. The following  
> > doesn't work:
> > 
> > ansible-playbook --tags  
> > "app1.stop,app1.start,app2.start,app2.stop,web.startMaintenance,web.endMaintenance"  
> > playbooks/testRoleDeps.yml
> > 
> > It seems like a single playbook for all of these different configurations
> 
> might not be realistic. Why not have multiple playbooks, one per general  
> operation, and call the roles as needed?

The entire mission of Ansible is "simple IT automation", right? 🙂 Wherever  
possible, my preference would be for a single Ansible playbook to handle  
the execution of a single high-level "goal"/"user request". If that isn't  
the case, then one needs to somehow bundle those externally, typically in a  
script. That requires the local "Ansible team" to decide on and maintain  
knowledge in such a scripting language, and requires implementing error  
checking and possibly error recovery in the scripting language. Sure.. it  
isn't strictly necessary, but I would rather just call a script  
"restart\_system.bash" that makes a call to a single ansible playbook  
instead of seeing the bash script make a call to 6 ansible playbooks. What  
happens if the playbooks need to pass state or other data between each  
other? Much cleaner if it occurs in a single call, no?

> > That doesn't work because all plays in the playbook are filtered by the  
> > tags provided, and each play needs to have a particular tag applied (first  
> > stop \*then\* start later), not all of them.
> > 
> > My conclusion? \*As of 1.8.4, roles are inadequate for complex playbooks  
> > and include files need to be used instead.\*
> > 
> > Roles are pretty simple--for more complex purposes, you may want to mix
> 
> in playbook includes too. That's fine! I wouldn't get too hung up on  
> getting all of the behavior for a certain part of your app in a single  
> role. That's going to be confusing and complicated to use.
> 
> That's unfortunate for code reuse reasons, etc. I would love to hear that
> 
> > this is not the case and I have overlooked something. Maybe a feature to  
> > resolve this is in the release plan?
> > 
> > I can't figure out how to do this even after reading the documentation  
> > repeatedly, reading the ansible O'Reilly book (pre-release copy), the code  
> > in ansible-examples, and various other resources.
> > 
> > I'm not sure if I understand any specific requests for improvement
> 
> here--besides being able to use a single playbook to drive multiple  
> operations, which is not really Ansible-style. If I've misunderstood, feel  
> free to let me know! 🙂
> 
> One request would be that more complex cases/uses of Ansible be documented

more clearly, and specifically what you wrote above regarding the use of  
"sub-roles": web/setup, web/restart, etc.

> -Tim
> 
> --  
> You received this message because you are subscribed to the Google Groups  
> "Ansible Project" group.  
> To unsubscribe from this group and stop receiving emails from it, send an  
> email to ansible-project+unsubscribe@googlegroups.com.  
> To post to this group, send email to ansible-project@googlegroups.com.  
> To view this discussion on the web visit  
> [https://groups.google.com/d/msgid/ansible-project/CAH4wdVWc73Hc9x19AmiZ2CbZNJZPW2FyKv%3D7g-w88qnQtQNeQQ%40mail.gmail.com](https://groups.google.com/d/msgid/ansible-project/CAH4wdVWc73Hc9x19AmiZ2CbZNJZPW2FyKv%3D7g-w88qnQtQNeQQ%40mail.gmail.com)  
> \<[https://groups.google.com/d/msgid/ansible-project/CAH4wdVWc73Hc9x19AmiZ2CbZNJZPW2FyKv%3D7g-w88qnQtQNeQQ%40mail.gmail.com?utm\_medium=email&utm\_source=footer&gt](https://groups.google.com/d/msgid/ansible-project/CAH4wdVWc73Hc9x19AmiZ2CbZNJZPW2FyKv%3D7g-w88qnQtQNeQQ%40mail.gmail.com?utm_medium=email&utm_source=footer&gt);  
> .
> 
> For more options, visit [https://groups.google.com/d/optout](https://groups.google.com/d/optout).

I wrote some test roles to verify what you described would work:

> ansible$ find roles/{web,app1,app2} -type f | sort  
> roles/app1/startServer/tasks/main.yml  
> roles/app1/stopServer/tasks/main.yml  
> roles/app2/startServer/tasks/main.yml  
> roles/app2/stopServer/tasks/main.yml  
> roles/web/startMaintenance/tasks/main.yml  
> roles/web/startServer/tasks/main.yml  
> roles/web/stopMaintenance/tasks/main.yml  
> roles/web/stopServer/tasks/main.yml

All of those files have what amounts to the the following code:

> ansible$ cat roles/web/startMaintenance/tasks/main.yml  
> ---  
> - name: Run first task for startMaintenance  
> &nbsp;&nbsp;debug: msg="Doing first task"  
> - name: Run second task for startMaintenance  
> &nbsp;&nbsp;debug: msg="Doing second task"

Each role gets a playbook for testing and when separate execution happens  
to be needed:

> ansible$ find playbooks/test/ -type f | grep -v restart | sort  
> playbooks/test/app1\_startServer.yml  
> playbooks/test/app1\_stopServer.yml  
> playbooks/test/app2\_startServer.yml  
> playbooks/test/app2\_stopServer.yml  
> playbooks/test/web\_startMaintenance.yml  
> playbooks/test/web\_stopMaintenance.yml

Each of those has some trivial, essentially identical code:

> ansible$ cat playbooks/test/web\_startMaintenance.yml  
> ---  
> - hosts: 127.0.0.1 # Imagine 'web'  
> &nbsp;&nbsp;connection: local
> 
> &nbsp;&nbsp;roles:  
> &nbsp;&nbsp;- web/startMaintenance

Finally.. the restart playbook:

> ansible$ find playbooks/test/ -type f | grep restart  
> playbooks/test/restart\_system.yml  
> ansible$ cat playbooks/test/restart\_system.yml  
> ---  
> - include: web\_startMaintenance.yml  
> - include: app1\_stopServer.yml  
> - include: app2\_stopServer.yml  
> - include: app2\_startServer.yml  
> - include: app1\_startServer.yml  
> - include: web\_stopMaintenance.yml

Is there something about the way restart\_system.yml is written above that  
makes it "not Ansible-style" or that should be expected to lead to  
problems? Thanks again.

---

<div class="post-metadata">

**Author:** ![Timothy\_Gerla](https://avatars.discourse-cdn.com/v4/letter/t/977dab/32.png) [@Timothy\_Gerla](https://forum.ansible.com/u/Timothy_Gerla)\
**Post date:** [February 24, 2015, 3:36am UTC](https://forum.ansible.com/t/ansible-roles-in-1-8-4-still-inferior-to-include-files-for-complex-playbooks/17603/4 "2015-02-24T03:36:46Z")

</div>

Hey Jack,

---

<div class="post-metadata">

**Author:** ![Tom\_Bamford](https://avatars.discourse-cdn.com/v4/letter/t/c2a13f/32.png) [@Tom\_Bamford](https://forum.ansible.com/u/Tom_Bamford)\
**Post date:** [February 24, 2015, 8:25am UTC](https://forum.ansible.com/t/ansible-roles-in-1-8-4-still-inferior-to-include-files-for-complex-playbooks/17603/5 "2015-02-24T08:25:38Z")

</div>

I also find that smaller, job-oriented roles work well in scenarios like  
this. It makes playbooks more maintainable and they read better when you  
come back to them 3 months later.

---

<div class="post-metadata">

**Author:** ![Brian\_Coca](https://avatars.discourse-cdn.com/v4/letter/b/6de8d8/32.png) [@Brian\_Coca](https://forum.ansible.com/u/Brian_Coca)\
**Post date:** [February 24, 2015, 2:05pm UTC](https://forum.ansible.com/t/ansible-roles-in-1-8-4-still-inferior-to-include-files-for-complex-playbooks/17603/6 "2015-02-24T14:05:23Z")

</div>

you can have it both ways, you can have smaller playbooks that do part  
of those functions and a wrapper one that includes the smaller ones,  
this is a common pattern with site.yml (run everything).

---

<div class="post-metadata">

**Author:** ![jack.martin8912](https://avatars.discourse-cdn.com/v4/letter/j/7ba0ec/32.png) [@jack.martin8912](https://forum.ansible.com/u/jack.martin8912)\
**Post date:** [February 24, 2015, 3:48pm UTC](https://forum.ansible.com/t/ansible-roles-in-1-8-4-still-inferior-to-include-files-for-complex-playbooks/17603/7 "2015-02-24T15:48:26Z")

</div>

> Hey Jack,
> 
> > The entire mission of Ansible is “simple IT automation”, right? 🙂 Wherever possible, my preference would be for a single Ansible playbook to handle the execution of a single high-level “goal”/“user request”. If that isn’t the case, then one needs to somehow bundle those externally, typically in a script. That requires the local “Ansible team” to decide on and maintain knowledge in such a scripting language, and requires implementing error checking and possibly error recovery in the scripting language. Sure… it isn’t strictly necessary, but I would rather just call a script “restart\_system.bash” that makes a call to a single ansible playbook instead of seeing the bash script make a call to 6 ansible playbooks. What happens if the playbooks need to pass state or other data between each other? Much cleaner if it occurs in a single call, no?
> 
> Sorry, I think I misunderstood your intent here. I definitely agree with a single playbook for a single high level user request. I jumped to the conclusion that you wanted to encode a bunch of different user requests in a single playbook.
> 
> > I wrote some test roles to verify what you described would work:
> > 
> > > ansible$ find roles/{web,app1,app2} -type f | sort  
> > > roles/app1/startServer/tasks/main.yml  
> > > roles/app1/stopServer/tasks/main.yml  
> > > roles/app2/startServer/tasks/main.yml  
> > > roles/app2/stopServer/tasks/main.yml  
> > > roles/web/startMaintenance/tasks/main.yml  
> > > roles/web/startServer/tasks/main.yml  
> > > roles/web/stopMaintenance/tasks/main.yml  
> > > roles/web/stopServer/tasks/main.yml
> > 
> > All of those files have what amounts to the the following code:
> > 
> > > ## ansible$ cat roles/web/startMaintenance/tasks/main.yml
> > > 
> > > - name: Run first task for startMaintenance  
> > > debug: msg=“Doing first task”
> > > - name: Run second task for startMaintenance  
> > > debug: msg=“Doing second task”
> > 
> > Each role gets a playbook for testing and when separate execution happens to be needed:
> > 
> > > ansible$ find playbooks/test/ -type f | grep -v restart | sort  
> > > playbooks/test/app1\_startServer.yml  
> > > playbooks/test/app1\_stopServer.yml  
> > > playbooks/test/app2\_startServer.yml  
> > > playbooks/test/app2\_stopServer.yml  
> > > playbooks/test/web\_startMaintenance.yml  
> > > playbooks/test/web\_stopMaintenance.yml
> > 
> > Each of those has some trivial, essentially identical code:
> > 
> > > ## ansible$ cat playbooks/test/web\_startMaintenance.yml
> > > 
> > > - hosts: 127.0.0.1 # Imagine ‘web’  
> > > connection: local
> > > 
> > > roles:
> > > 
> > > - web/startMaintenance
> > 
> > Finally… the restart playbook:
> > 
> > > ## ansible$ find playbooks/test/ -type f | grep restart playbooks/test/restart\_system.yml ansible$ cat playbooks/test/restart\_system.yml
> > > 
> > > - include: web\_startMaintenance.yml
> > > - include: app1\_stopServer.yml
> > > - include: app2\_stopServer.yml
> > > - include: app2\_startServer.yml
> > > - include: app1\_startServer.yml
> > > - include: web\_stopMaintenance.yml
> > 
> > Is there something about the way restart\_system.yml is written above that makes it “not Ansible-style” or that should be expected to lead to problems? Thanks again.
> 
> I would probably structure this a little bit differently, going back a little bit on what I said above about a single playbook per user request. This might actually be a really good use case for tags.
> 
> playbooks/test/restart\_system.yml:
> 
> * * *
> 
> - hosts: 127.0.0.1  
> connection: local
> 
> roles:
> 
> - { role: web/startMaintenance, tags: [‘web’] }
> 
> - { role: app1/stopServer, tags: [‘app1’] }
> 
> - { role: app2/stopServer, tags: [‘app2’] }
> 
> - { role: app1/startServer, tags: [‘app1’] }
> 
> - { role: app2/startServer, tags: [‘app2’] }
> 
> - { role: web/stopMaintenance, tags: [‘web’] }

This is a fine refactor of my demo code, but the real production code can’t do this because app1 is on “app1 server cluster” and app2 is on “app2 server cluster”, etc You need a separate play for each call so that you can use the “hosts” field to indicate which server set it is being applied to. Right?

> Now you can invoke it with:
> 
> ansible-playbook restart\_system.yml --tags app1

Thanks for pointing this out.

> …to only restart app1, or without tags to restart everything. (Maybe the web start/stop should be untagged, so it runs every time–not sure. Depends on your app.)
> 
> This way you lose the intermediary playbooks, and end up with a single playbook to do all of your restart tasks.

I don’t dislike the intermediary playbooks, since they can be used by themselves and more easily composed together.

> If app1 and app2 roles are really close together, maybe you could parameterize them, too:
> 
> - { role: genericApp/stopServer, appName: “app1”, tags: [“app1”] }

I will try to find an appropriate place to use this technique. Thanks!
