Vagrant Puppet love

02 December 2014

At work we use Puppet to keep our server configuration, and therefore ourselves, sane. We've recently been playing around with the venerable Vagrant to help in testing new configurations, without having to push to test hosts in the cloud to do so.


There are advantages galore in this approach. A couple of the biggest wins are easier branching of configuration, and reducing the turnaround time for testing configurations since we no longer need to push a configuration to Puppet masters then have a client apply it.

Our starting point was puppet-sandbox, which will very kindly configure you up a Puppet master and a couple of clients. This works great on a fresh module but if you're wanting to play around with an existing one it gets a little tiresome copying files between your live Puppet configuration and the sandbox configuration. Much better to just point it at our configuration and have it use that.

We needed to make a few tweaks to the set-up to do that. If you're in a rush, you can stop reading now and just go check out our fork on GitHub. If you're not, keep reading to find out what changes we made and why, to see if this fork is for you.

Wherefore art thou, config?

The first step was to point the Vagrant Puppet master at a local checkout of our Puppet repository. Since each engineer may have it somewhere different, an environment variable seemed the best way to letting Vagrant know where to find it. So, setting PUPPET_ROOT to the appropriate place will then result it being mounted at /puppet on the virtual machines:

config.vm.synced_folder ENV['PUPPET_ROOT'], "/puppet"

Some changes were needed in the provisioning of the virtual Puppet master to have it use this configuration. We symlinked a few directories in /puppet to /etc/puppet, since it meant we could use the puppet.conf from our real configuration without modification. I'll not copy the code here, but it can all be seen over here if you so desire.

Then, to test a particular module or manifest, all that remains is to add a node definition in site.pp for client1 and client2 and you're on your way.

Don't do that

It turns out that there are some things we really don't want to do when running locally. Firstly, we don't want to install our own SSH or Puppet modules, since they break the set-up done by Vagrant1 and aren't really of any use in this local environment. We also (usually) don't want a host being configured with Sensu checks, to avoid pestering the on-call engineer about problems with the test clients. Before you ask: yes, we learnt that one the hard way.</a>

So we added a Puppet fact, vagrant, which can then be tested in modules. For example, a snippet from our baseline package manifest:

  if !str2bool($::vagrant) {
    include sshd
    include puppet


The one thing we've not addressed yet is the secret stuff, which is stored encrypted using hiera-gpg. We don't want to copy keys onto the virtual machines, but I'm thinking that using gpg-agent on the host workstation may work nicely. If you've done something similar do let me know what worked for you.

Up, up and away!

And now, with the wonders of Vagrant, we can create a local virtual Puppet environment that mirrors our real configuration with just one simple command:

> vagrant up
.... many lines of initialisation later ...
notice: Finished catalog run in 22.92 seconds
> vagrant ssh puppet

Last login: Fri Sep 14 06:23:18 2012 from

If this all sounds like something that would be useful to you, go look at our fork over on GitHub.

1 The sugar that is vagrant ssh puppet breaks because our SSH config only allows certain groups to log in, and the Vagrant Puppet configuration uses self-signed client certificates, which works nicely for Vagrant but we do something different in our live configuration.

Picture credit (cropped): Gido

blog comments powered by Disqus