The recent HashiCorp Vagrant 2.2.6 that was released came with a couple of new and interesting features. Dependency provisioners offer a new way to control when your provisioners run for a given guest. Additionally, Vagrant and Vagrant Cloud now offer support for validating the checksum for a published box. In this post, we’ll outline these two new features of Vagrant.
» Dependency Provisioners
Provisioning has always been a core feature of Vagrant. There are many different kinds of provisioners today that use different tools to help set up and configure a Vagrant guest. However, until the introduction of Dependency Provisioners, Vagrant was only able to run provisioners in the "linear" order that they are defined within a Vagrantfile. Vagrant does not evaluate Vagrantfiles strictly top to bottom which can result in some surprising behavior. Take this Vagrantfile for example:
Vagrant.configure("2") do |config| config.vm.box = “hashicorp/bionic64” config.vm.provision "A", type: "shell", inline:<<-SHELL echo 'A' SHELL config.vm.define “guest” do |guest| guest.vm.provision "B", type: "shell", inline:<<-SHELL echo 'B' SHELL end config.vm.provision "C", type: "shell", inline:<<-SHELL echo 'C' SHELL end
If provisioners truly ran in the order they are defined in a Vagrantfile, you would expect the output of running this Vagrantfile to print: A -> B -> C. Due to how Vagrant parses Vagrantfiles, however, the globally defined provisioners
"C" actually run before
"B". While this is just a basic example, dependency provisioners allow users to have finer grained control over exactly when they want a provisioner to run during the provision step. Here’s how to use a dependency provisioner:
# A dependency provisioner that runs before “Configure” config.vm.provision "Hello", before: “Configure”, type: "shell", inline:<<-SHELL echo 'The first provisioner!' SHELL # A dependency provisioner that runs after the configure config.vm.provision "Finished", after: “Configure”, type: "shell", inline:<<-SHELL echo 'Vagrant finished updating your system!' SHELL config.vm.provision "Configure", type: "shell", inline:<<-SHELL apt-get update apt-get install vim tmux tree -y apt-get upgrade -y SHELL
If a provisioner has been configured using the
after options, it is considered a Dependency Provisioner. This means it has been configured to run before or after a Root Provisioner, which does not have the
after options configured. Root Provisioners can be thought of as a traditional Vagrant provisioner prior to the 2.2.6 release, and behave the way provisioners have prior to this release.
By giving the before or after options a string value "Name" that refers to a Root Provisioner, a dependency provisioner can be defined to run before or after that named provisioner. Not only will these keys allow users to modify the run order for their provisioners, they will also allow users to specify if a provisioner should run more than once. Instead of giving a "Name" string, users will also be able to provide an array of strings which contain the names of multiple provisioners. This allows the provisioner to be defined once, but run before or after each of the defined named provisioners. Dependency provisioners also have two valid shortcuts for the before and after keys:
# This will run before any defined provisioner for a given Vagrant guest config.vm.provision "Hello", before: :all, type: "shell", inline:<<-SHELL echo 'The first provisioner!' SHELL # This will run after every defined provisioner for a given Vagrant guest config.vm.provision "Next", after: :each, type: "shell", inline:<<-SHELL echo 'The provisioner finished!' SHELL
Now that we’ve introduced Dependency Provisioners, let’s fix our original example and have the provisioners run in A -> B -> C order:
Vagrant.configure("2") do |config| config.vm.box = “hashicorp/bionic64” config.vm.provision "A", type: "shell", inline:<<-SHELL echo 'A' SHELL config.vm.define “guest” do |guest| guest.vm.provision "B", after, “A”, type: "shell", inline:<<-SHELL echo 'B' SHELL end config.vm.provision "C", type: "shell", inline:<<-SHELL echo 'C' SHELL end
Because this feature is experimental, you will need to enable it using the
VAGRANT_EXPERIMENTAL environment variable. For example, on Linux or macOS you would run:
VAGRANT_EXPERIMENTAL="dependency_provisioners" vagrant up guest
If you are on Windows using Powershell, you would enable this feature by exporting the same environment variable:
$env:VAGRANT_EXPERIMENTAL = "dependency_provisioners" vagrant up guest
Running this will now make our Vagrantfile provisioners run in A -> B -> C order. It should be noted that without the experimental flag, provisioner
"B" would run like a normal provisioner from our first example.
For more information on how to use Dependency Provisioners, please check out the documentation on Vagrant Up.
Dependency provisioners introduce two new top-level keys for the provisioner class: before and after. Because of how the class was designed, it means that when older Vagrant clients try to parse a Vagrantfile with dependency triggers defined, it will fail to parse the dependency provisioners properly and result in a stack trace to the user.
Additionally, as of Vagrant 2.2.6, dependency provisioners cannot rely on other dependency provisioners and is considered a configuration state error in Vagrant. If you must order dependency provisioners, you can still order them by the order they are defined inside your Vagrantfile.
» Vagrant Box Checksum Validation
With this release, we have added support for Vagrant box checksums to Vagrant Cloud. When the checksum value is set, Vagrant will automatically validate that the downloaded box matches the checksum before unpacking. Vagrant versions prior to 2.2.6 will only validate MD5 and SHA1 checksums. Support for SHA256, SHA384, and SHA512 were added in the 2.2.6 release.
Checksum information can be added to boxes directly on the Vagrant Cloud website. Alternatively, they can be added using the Vagrant Cloud API, or by using the
vagrant cloud commands when creating or updating boxes.