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:

|-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?

1 Answer 1


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

    # whatever you need to do before 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

      # 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"


  # 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"}

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.

  • 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.
    Apr 3, 2019 at 0:11
  • 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. Apr 3, 2019 at 0:24
  • Too late to edit my previous comment... you should read "unit" instead of "unity" Apr 3, 2019 at 1:42
  • 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.
    Apr 3, 2019 at 13:43
  • 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). 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. Apr 3, 2019 at 15:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.