Vagrant and Ansible are a great match: using Vagrant it’s very easy to work with virtual machines. Creating, updating, and removing VMs is just a short command away. Vagrant provides various provisioners to configure the VM, and Ansible is one of these. This article covers the ansible
provisioner as opposed to ansible_local
.
Earlier articles I wrote might be of interest in this context:
- Testing and debugging Ansible scripts using Vagrant
- Provision multiple machines in parallel with Vagrant and Ansible
The post was written using Ubuntu 22.04 patched to 230306, I used Ansible and Vagrant as provided by the distribution:
- Ansible 2.10.8
- Vagrant 2.2.19
Configuring the Ansible Inventory
Very often the behaviour of an Ansible playbook is controlled using variables. Providing variables to Ansible from a Vagrantfile is quite neat and subject of this article.
Let’s have a look at the most basic Vagrantfile
:
Vagrant.configure("2") do |config| config.vm.box = "debianbase" config.vm.hostname = "debian" config.ssh.private_key_path = "/home/martin/.ssh/debianbase" config.vm.provider "virtualbox" do |vb| vb.cpus = 2 vb.memory = "2048" vb.name = "debian" end config.vm.provision "ansible" do |ansible| ansible.playbook = "provisioning/blogpost.yml" ansible.verbose = "v" end end
I frequently use a flag indicating if the Ansible script should reboot the VM after the update of all packages completed. Within the provisioning folder I store group_vars
, roles, and the main playbook as per the recommendation in the docs:
$ tree provisioning/ provisioning/ ├── blogpost.yml ├── group_vars │ └── all.yml └── roles └── role1 └── tasks └── main.yml
All global variables I don’t necessarily expect to change are stored in group_vars/all.yml
. This includes the reboot_flag
flag that defaults to false. The playbook does not need to list the variable in its own vars
section, in fact doing so would grant the variable a higher precedence and my way of providing a variable to Ansible via Vagrant would fail. Here is the playbook:
- hosts: default become: true tasks: - debug: var: reboot_flag - name: reboot ansible.builtin.reboot: when: reboot_flag | bool
Since rebooting can be a time consuming task I don’t want to do this by default, which is fine by me as I understand that I have to reboot manually later.
Let’s see what happens when the VM is provisioned:
PLAY [default] ***************************************************************** TASK [Gathering Facts] ********************************************************* ok: [default] TASK [debug] ******************************************************************* ok: [default] => { "reboot_flag": false } TASK [reboot] ****************************************************************** skipping: [default] => { "changed": false, "skip_reason": "Conditional result was False" } PLAY RECAP ********************************************************************* default : ok=2 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
Overriding variables
In case I want to override the flag I can do so without touching my Ansible playbook only by changing the Vagrantfile
. Thanks to host_vars I can pass variables to Ansible via the inventory. Here’s the changed section in the Vagrantfile:
config.vm.provision "ansible" do |ansible| ansible.playbook = "provisioning/blogpost.yml" ansible.verbose = "v" ansible.host_vars = { "default" => { "reboot_flag" => true } } end
All host_vars for my default VM are then appended to the inventory in .vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory
.
Next time I run vagrant provision the flag is changed to true, and the VM is rebooted:
PLAY [default] ***************************************************************** TASK [Gathering Facts] ********************************************************* ok: [default] TASK [debug] ******************************************************************* ok: [default] => { "reboot_flag": "true" } TASK [reboot] ****************************************************************** changed: [default] => { "changed": true, "elapsed": 20, "rebooted": true } PLAY RECAP ********************************************************************* default : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Summary
Vagrant offers a very neat way of creating an Ansible inventory on the fly. If your Ansible playbooks are written in a way that different execution paths/options are configurable via variables a single playbook is highly flexible and can be used for many things. In the age of version control it’s very convenient not having to touch the source code of an Ansible playbook as that might interfere with other projects. Variables, passed at runtime, are much better suited to create flexible automation scripts.