Vagrant and ansible -- proper way to use ansible with vagrant?

hey folks.
I have been messing around with ansible as part of my vagrant process to build boxes. My current setup is: build packer image, then import that image with vagrant.
Within my vagrantfile, i call the box I created.
That works well.

The second part is leveraging ansible in the next flow.
I can call ansible within the vagrantfile, which I have tested and worked well.
Or.
I can run ansible from the host against the vagrant boxes (i use multi-vm setups)

Some of my boxes are ‘tiered’ setups which require different packages to be installed.
Knowing that, is it safe to say, due to my requirement you can do it one of two ways:

1.) When using ansible as a post provisioner, i would create different playbooks as needed, and call the specific playbooks within the specific BOX creation portion of the vagrant file?

2.) Second option is, do this all within ansible itself once the boxes are up, run it from the host against the boxes.

With option 2, there is a need to manage the ansible inventory file, which is an extra step.

Does that make sense?
Anyone have any thoughts/experiences on the two above (or something different)?

Thanks!

Hey there,
these two options both seem fine. Option 1 will look something like:

Vagrant.configure("2") do |config|
  config.vm.define "box1" do |c|
    c.vm.box = "some/box"
    c.vm.provision "ansible" do |ansible|
      ansible.playbook = "playbook_one.yml"
    end
  end

   config.vm.define "box2" do |c|
    c.vm.box = "some/box"
    c.vm.provision "ansible" do |ansible|
      ansible.playbook = "playbook_two.yml"
    end
  end
end

Option 2 will look something like:

Vagrant.configure("2") do |config|
  config.vm.define "box1" do |c|
    c.vm.box = "some/box"
  end

   config.vm.define "box2" do |c|
    c.vm.box = "some/box"
  end

  c.vm.provision "ansible" do |ansible|
    ansible.playbook = "playbook.yml"
    ansible.inventory_path = "./inventory"
  end
end

Ansible has some best practices about this type of setup (https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html#top-level-playbooks-are-separated-by-role). Following these practices it seems like it’s best to have a playbook for each type of machine that is being configured in addition to a parent playbook that can provision the entire setup. Both setups can be made to adhere to these guidelines.

Another thing you may want to consider is how a user will be interacting with this Vagrant setup. For example, should users be able to do things like re-provision provision a single machine at any time? Something like vagrant provision box1 using setup 1 is probably more straight forward than using option 2 where you will have to run ansible directly and set --limit.

That is very helpful. I think one of the things I overlooked/missed was the ansible.inventory_path option.
with that option, I can have a more granular application of my playbooks to my boxes.
Right now, I was testing using ansible AFTER the boxes are up, but it would be nice to be able to have both options available.

It almost looks like it would be a personal preference? Provision within Vagrant, or after by hand?
Thank you again

Another feature that might be helpful is the provisioner’s :run argument. For example a Vagrantfile that looks something like:

Vagrant.configure("2") do |config|
  config.vm.define "box1" do |c|
    c.vm.box = "some/box"
  end

   config.vm.define "box2" do |c|
    c.vm.box = "some/box"
  end

  config.vm.provision "ansible",  run: "never" do |ansible|
    ansible.playbook = "playbook.yml"
    ansible.inventory_path = "./inventory"
  end

end

Can be used like:

# bring up the boxes (not provisioned)
$ vagrant up
# provision box1
$ vagrant provision box1 --provision-with ansible
# re-provision both boxes (running all available provisioners)
$ vagrant provision box1
$ vagrant provision box2
# provision with ansible 
$ ansible-playbook -i ./inventory playbook.yml

It almost looks like it would be a personal preference?

I would say so, kind of. By design, there are a few ways to make this work to offer users flexibility. I would choose the one that makes life easiest while still solving the problem. I hope this helps!