<p>Unless I totally missed your question, roles are exactly what you need in this case. I'll use examples from my real world. My code below is just to illustrate my point (so it is not fully runnable, might contain errors and is not bullet proof rocket science: you will probably have to adapt/overcome tools limitations). But at least you'll be able to tell me if I got you wrong.</p>

<p>Let's say your servicing a large community of developers on different techs: java, php, nodejs, scala, python, ruby... needing some tools as well like apache, nginx, postgres, mysql, redis...</p>

<p>You have a role for each of these techs/tools, and possibly use existing ones from galaxy that you don't even have to write and that you can <a href="https://www.vagrantup.com/docs/provisioning/ansible_common.html#galaxy_role_file" rel="nofollow noreferrer">download at provisioning time</a>. These roles can possibly be controlled with vars to choose e.g. versions. (see vagrant provisionner config below)</p>

<p>Now, one way you can activate/deactivate roles in a play is with the use of <a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_tags.html" rel="nofollow noreferrer">tags</a></p>

<p>Your unique playbook could look something like this</p>

<pre><code>- name: Vagrant provisionning playbook
  hosts: all

  pre_tasks:
    # whatever you need to do before roles

  roles:
    - role: php
      tags: [php]
    - role: java
      tags: [java]
    - role: nginx
      tags: [nginx,php] # if you always deploy php with nginx
    - role: redis
      tags: [redis]
    # more roles here

    tasks:
      # Whatever tasks are needed to be played after roles
</code></pre>

<p>With such a structure, you can now configure your ansible provisioinner in vagrant <a href="https://www.vagrantup.com/docs/provisioning/ansible_common.html#tags" rel="nofollow noreferrer">defining the tags</a> and <a href="https://www.vagrantup.com/docs/provisioning/ansible_intro.html#host-variables" rel="nofollow noreferrer">specifying the vars</a> needed to provision with your single playbook.</p>

<pre><code>Vagrant.configure(2) do |config|

  config.vm define "php_application" do |php_application|

    # Some config for vm
    php_application.vm.provision "ansible" do |ansible| 
      ansible.playbook = "playbook.yml"
      ansible.tags = "php,nginx,untagged"
    end

  end

  # Host vars declared globally. You might be able to do
  # it on host by host basis but the doc does not specify it
  config.vm.provision "ansible" do |ansible|
    ansible.host_vars = {
      "php_application" =&gt; {"php_version" =&gt; "7.2"}
    }
  end
end 
</code></pre>

<p>Does this fit your needs ?</p>

<p>Note: if a single playbook is too much of a hassle, you can still use the roles and assemble in each playbook the techs/tools you need and reuse things that you will need in several places.</p>

CC BY-SA 4.0
2019-04-03T00:11:45.137Z
Roles for each application seems sort of heavyweight, no? It seems like I can accomplish the same thing you're suggesting just by including task files. So instead of a PHP role, tag, and playbook, I just have a single PHP task file, and in the main playbook: ``import_tasks: tasks/install/php-7.2.yml``. Doesn't that give me the same reusability with less indirection?
Ryan O.
CC BY-SA 4.0
2019-04-03T00:24:50.540Z
Your choice. Roles are much more reusable accross projects, versionned in their own repo, and can contain in a correctly packaged unity files, templates, handlers, default vars, documentation, etc. They are easily extandable with a custom role that inherit from an other one.
Zeitounator
CC BY-SA 4.0
2019-04-03T01:42:25.850Z
Too late to edit my previous comment... you should read "unit" instead of "unity"
Zeitounator
CC BY-SA 4.0
2019-04-03T13:43:15.673Z
Okay, so if I understand your approach right, instead of having one task file per application ("php-7.2.yml"), I would have one role directory per application. Then my main playbook would specify all of those roles, and I would use tags in Vagrant to specify which roles to include for a specific use case? And the advantage is that I can create more reusable application bundles than by just defining them as task files. Did I understand right?
Ryan O.
CC BY-SA 4.0
2019-04-03T15:48:38.537Z
Well that is at least my point of view. Roles are really the basics of re-usability in ansible. You were asking in your first comment if one role for each application was not heavyweight: in practice day to day I can use several roles for a single application. Just to illustrate: I deploy nexus repository manager on a dozen of servers. [I use an open-source role (that I happen to maintain)](https://github.com/ansible-ThoTeam/nexus3-oss). Reverse proxy settings in this role are not sufficient for my client. We disabled it there and created a custom child role just for this purpose.
Zeitounator
CC BY-SA 4.0
2019-04-02T22:30:21.453Z
2019-04-02T22:30:21.453Z
Zeitounator
<p>I hope this question is not too vague / opinion-based:</p>

<p>I need to create and maintain a handful of virtual machines that get packaged up and deployed out to thousands of users, depending on specific requirements. Everyone is doing roughly the same job, but different people need different tools. This is important because I don't think I can structure things around roles (though I'm happy to be told I'm wrong).</p>

<p>They way I have things structured now is:</p>

<pre><code>vagrant-directory
|-Vagrantfile # Multi-host setup, with one machine per "use-case"
|-scripts/ # Shell scripts run by Vagrant (mostly used to install Ansible prereqs)
|-ansible/ # All Ansible-related files
|----Playbook1.yml # One playbook per use case
|----tasks/ # Contains task files for everything I want to do
|------install/ # All task files related to installing software
|------apt-update.yml # For example, a script to run apt update
|------iptables-flush.yml # Disable iptables
</code></pre>

<p>With this approach, it basically means that when I get a requirement for a new machine, I have to create a new Playbook42.yml, use <code>import_tasks</code> for the tasks that I need, create any machine-specific tasks, and then provision and export.</p>

<p>Is there a more "Ansible official" or industry standard approach to doing this?</p>

<ansible><vagrant>
Structuring Ansible playbooks for low volume, purpose-built images
Ryan O.
# Structuring Ansible playbooks for low volume, purpose-built images

(Asked by: username_0 on 2019-04-02)

I hope this question is not too vague / opinion-based:
I need to create and maintain a handful of virtual machines that get packaged up and deployed out to thousands of users, depending on specific requirements. Everyone is doing roughly the same job, but different people need different tools. This is important because I don't think I can structure things around roles (though I'm happy to be told I'm wrong).
They way I have things structured now is:
vagrant-directory
|-Vagrantfile # Multi-host setup, with one machine per "use-case"
|-scripts/ # Shell scripts run by Vagrant (mostly used to install Ansible prereqs)
|-ansible/ # All Ansible-related files
|----Playbook1.yml # One playbook per use case
|----tasks/ # Contains task files for everything I want to do
|------install/ # All task files related to installing software
|------apt-update.yml # For example, a script to run apt update
|------iptables-flush.yml # Disable iptables

With this approach, it basically means that when I get a requirement for a new machine, I have to create a new Playbook42.yml, use import_tasks for the tasks that I need, create any machine-specific tasks, and then provision and export.
Is there a more "Ansible official" or industry standard approach to doing this?

**Tags:** ansible, vagrant


---

## 1 answers

### Answer by username_1 on 2019-04-02. Score: 2 | Accepted answer

Unless I totally missed your question, roles are exactly what you need in this case. I'll use examples from my real world. My code below is just to illustrate my point (so it is not fully runnable, might contain errors and is not bullet proof rocket science: you will probably have to adapt/overcome tools limitations). But at least you'll be able to tell me if I got you wrong.
Let's say your servicing a large community of developers on different techs: java, php, nodejs, scala, python, ruby... needing some tools as well like apache, nginx, postgres, mysql, redis...
You have a role for each of these techs/tools, and possibly use existing ones from galaxy that you don't even have to write and that you can download at provisioning time. These roles can possibly be controlled with vars to choose e.g. versions. (see vagrant provisionner config below)
Now, one way you can activate/deactivate roles in a play is with the use of tags
Your unique playbook could look something like this
- name: Vagrant provisionning playbook
  hosts: all

  pre_tasks:
    # whatever you need to do before roles

  roles:
    - role: php
      tags: [php]
    - role: java
      tags: [java]
    - role: nginx
      tags: [nginx,php] # if you always deploy php with nginx
    - role: redis
      tags: [redis]
    # more roles here

    tasks:
      # Whatever tasks are needed to be played after roles

With such a structure, you can now configure your ansible provisioinner in vagrant defining the tags and specifying the vars needed to provision with your single playbook.
Vagrant.configure(2) do |config|

  config.vm define "php_application" do |php_application|

    # Some config for vm
    php_application.vm.provision "ansible" do |ansible| 
      ansible.playbook = "playbook.yml"
      ansible.tags = "php,nginx,untagged"
    end

  end

  # Host vars declared globally. You might be able to do
  # it on host by host basis but the doc does not specify it
  config.vm.provision "ansible" do |ansible|
    ansible.host_vars = {
      "php_application" => {"php_version" => "7.2"}
    }
  end
end 

Does this fit your needs ?
Note: if a single playbook is too much of a hassle, you can still use the roles and assemble in each playbook the techs/tools you need and reuse things that you will need in several places.

> **Comments:**
> **username_0**: Roles for each application seems sort of heavyweight, no? It seems like I can accomplish the same thing you're suggesting just by including task files. So instead of a PHP role, tag, and playbook, I just have a single PHP task file, and in the main playbook: ``import_tasks: tasks/install/php-7.2.yml``. Doesn't that give me the same reusability with less indirection?  
> **username_1**: Your choice. Roles are much more reusable accross projects, versionned in their own repo, and can contain in a correctly packaged unity files, templates, handlers, default vars, documentation, etc. They are easily extandable with a custom role that inherit from an other one.  
> **username_1**: Too late to edit my previous comment... you should read "unit" instead of "unity"  
> **username_0**: Okay, so if I understand your approach right, instead of having one task file per application ("php-7.2.yml"), I would have one role directory per application. Then my main playbook would specify all of those roles, and I would use tags in Vagrant to specify which roles to include for a specific use case? And the advantage is that I can create more reusable application bundles than by just defining them as task files. Did I understand right?  
> **username_1**: Well that is at least my point of view. Roles are really the basics of re-usability in ansible. You were asking in your first comment if one role for each application was not heavyweight: in practice day to day I can use several roles for a single application. Just to illustrate: I deploy nexus repository manager on a dozen of servers. [I use an open-source role (that I happen to maintain)](https://github.com/ansible-ThoTeam/nexus3-oss). Reverse proxy settings in this role are not sufficient for my client. We disabled it there and created a custom child role just for this purpose.  

---

