0
votes

I'm creating a Vagrantfile that calls the Ansible playbook. Which is running well.

My issue is that, as I'm using an "each" loop on vagrant for the deploy, the Ansible playbook is run every time a machine is created. The expected behaviour is that the Ansible provider is just run after all VMs are started.

My Vagrantfile is coded as bellow:

    # -*- mode: ruby -*-
# vi: set ft=ruby :
# Define Vagrant API version, the virtualization provider and the location of the param file and its name:
VAGRANTFILE_API_VERSION = "2"
VIRT_PROVIDER="virtualbox"
VM_PARAM_FILE = 'params.yml'
ANSIBLE_PLAYBOOK = "/Users/dzarpelon/Documents/DevOps/Projects/Confluence_Lab/ansible/playbook.yml"
#define secure ssh key location
ssh_pub_key = File.readlines("#{Dir.home}/.ssh/devops_key.pub").first.strip
# Require Yaml module, this is needed to read the 'params.yml' file.
require 'yaml'
# Read 'params.yml' file
servers = YAML.load_file(File.join(File.dirname(__FILE__), VM_PARAM_FILE))
# Create boxes as needed
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.provision "ansible" do |ansible|
      ansible.playbook = ANSIBLE_PLAYBOOK
      ansible.force_remote_user = TRUE
  end #end ansible provisioning
  # ensure that all Vagrant machines will use the same SSH key pair.
  config.ssh.insert_key = false
  if Vagrant.has_plugin?("vagrant-cachier")
    config.cache.scope = :box
  end #end of cache plugin configuration
  if Vagrant.has_plugin?("vagrant-hostmanager")
    config.hostmanager.enabled = true
    config.hostmanager.manage_host = true
    config.hostmanager.ignore_private_ip = false
    config.hostmanager.include_offline = true
  end # end of hostmanager plugin
  #execute for each entry on the params file
  servers.each do |servers|
    config.vm.define servers["name"] do |srv|
      srv.vm.box = servers["box"]
      srv.vm.network servers["network_type"], ip: servers["ip"]
      srv.vm.hostname = servers["name"]
      srv.hostsupdater.aliases = [servers["name"]]
      srv.vm.provider VIRT_PROVIDER do |v|
        v.name = servers["name"]
        v.memory = servers["ram"]
      end #end provider
    end # end config loop
  end # end servers loop


end #end of Vagrant.Configure

As you can see the Ansible provision step is done outside the "each" loop and still it runs after each one of the VMs are created.

Any ideas on how to run Ansible only after all VMs are created by that loop?

Thanks a lot for the help!

1

1 Answers

0
votes

The fine manual seems to address your very concern, and their suggestion is to guard the .provision block to only run on the last machine, thus ensuring -- according to their documentation -- that the provisioning will run after the final machine is up.

According to your usage, you'd need to switch to .each_index in order to identify when you have reached the last one:

  servers_length = servers.length
  servers.each_index do |s_idx|
     # your code snippet was also shadowing the global `servers`
     # by your block variable, which is a bug waiting to happen
     server = servers[s_idx]
     is_last = s_idx + 1 == servers_length

     config.vm.define servers["name"] do |srv|
       # ... all your other srv statements ...
       if is_last
         srv.vm.provision "ansible" do |ansible|
           ansible.limit = "all"
           ansible.playbook = ANSIBLE_PLAYBOOK
           ansible.force_remote_user = TRUE
         end
       end
     end
  end