home rss

Masterless Puppet with Vagrant

25 Apr 2014


In a previous article, I described how to create a Masterless Puppet workflow with Capistrano. In this article, I'll show how you can use Vagrant for Masterless Puppet.

Since Capistrano works over SSH, it can be used to control any type of server – from bare metal to LXC containers. However, the workflow can be a bit complex. Vagrant provides a simpler workflow, but you're limited to the server providers compatible with Vagrant. Pros and Cons.

Prerequisites and Dependencies

For this article, I'm using Vagrant 1.5.2 and vagrant-openstack-plugin to enable Vagrant to launch virtual machines inside OpenStack. The virtual machine that Vagrant launches will be running Ubuntu 14.04.

Despite these choices, though, the methods described in this article should be applicable to any Vagrant-based server as long as Vagrant's Puppet provisioner is compatible with it.

Vagrant's Puppet Provisioners

Out of the box, Vagrant supports provisioning servers with both Puppet Apply and Puppet Agent. Since this is a Masterless Puppet workflow, I'm going to be using Puppet Apply.

Vagrant's documentation does an excellent job at describing how to use the Puppet Apply provisioner. The only gotcha I found is that Puppet must be installed on the Vagrant-based virtual machine before Puppet Apply can be used. Maybe I just missed that bit in the documentation.

Configuring Vagrant

For this article, I'll create a simple Vagrant environment that will create a virtual machine and have Apache installed onto it. The virtual machine will be launched inside OpenStack, have Puppet installed via the Shell provisioner, and have Apache installed by the Puppet Apply provisioner along with Hiera and the Puppetlabs Apache module.


Below is the completed Vagrantfile:

require 'vagrant-openstack-plugin'

Vagrant.configure("2") do |config|
  # "dummy" box because we're using Glance
  config.vm.box = "dummy"

  # SSH
  config.ssh.private_key_path = "/path/to/id_rsa"

  # Basic OpenStack options
  # Note that an openrc file needs sourced before using
  config.vm.provider :openstack do |os|
    os.username        = ENV['OS_USERNAME']
    os.api_key         = ENV['OS_PASSWORD']
    os.tenant          = ENV['OS_TENANT_NAME']
    os.flavor          = /m1.tiny/
    os.image           = 'cc4a5014-1a99-4d65-811a-8c0184b15dd7'
    os.endpoint        = "#{ENV['OS_AUTH_URL']}/tokens"
    os.keypair_name    = "home"
    os.ssh_username    = "ubuntu"
    os.security_groups = ["default"]

  config.vm.define "p.example.com"

  config.vm.provision "shell", :inline => <<-SHELL
    apt-get update
    apt-get install -y puppet

  config.vm.provision "puppet" do |puppet|
    puppet.hiera_config_path = "hiera.yaml"
    puppet.module_path = "modules"



The Vagrantfile defines a Hiera configuration called hiera.yaml located in the same directory as the Vagrantfile.


Here's the contents of hiera.yaml:

  - yaml

  - "common"

  :datadir: '/vagrant/hiera'

This tells hiera to look for a file called common.yaml in the /vagrant/hiera directory on the newly provisioned virtual machine. On the Vagrant-side, everything is in the same directory as the Vagrantfile.


The contents of common.yaml will simply be:

apache::serveradmin: 'joe@example.com'
my_message: 'Hello, World!'

The first line sets the serveradmin setting for Apache and the second line sets an arbitrary message for later use.


The Vagrant Puppet Apply provisioner supports hosting modules. In the Vagrantfile, the module path is configured as ./modules.

To install and configure Apache via Puppet, I'm going to use the puppetlabs/apache module. This module requires the puppetlabs/stdlib and puppetlabs/concat modules, so in total, three modules will be used:

$ mkdir modules
$ cd modules
$ git clone https://github.com/puppetlabs/puppetlabs-apache apache
$ git clone https://github.com/puppetlabs/puppetlabs-stdlib stdlib
$ git clone https://github.com/puppetlabs/puppetlabs-concat concat


The final component is an actual Puppet manifest. By default, Vagrant looks for ./manifests/default.pp which is exactly what I'll use:

include apache
notify { 'my_message':
  message => hiera('my_message'),

The first line installs and configures apache using the default settings from the puppetlabs/apache module except for the serveradmin setting which was defined in Hiera as joe@example.com.

The second line prints a message stored in Hiera just as another way to show that data is successfully being pulled from Hiera.

The Final Directory Structure

With everything in place, the directory structure should look like this:

$ tree -L 2
├── Vagrantfile
├── hiera
│   └── common.yaml
├── hiera.yaml
├── manifests
│   └── default.pp
└── modules
    ├── apache
    ├── concat
    └── stdlib

Vagrant Up

Now simply run vagrant up and watch Vagrant go to work!

If you're using a provider other than VirtualBox, like the OpenStack provider used in this article, don't forget to specify it on the command line:

$ vagrant up --provider=openstack


This article showed how Vagrant can provide a Masterless Puppet workflow. Besides the non-default OpenStack provider used, all other features shown in this article are native to Vagrant. This makes it very easy and accessible to use Puppet within Vagrant. The downside, though, is that the servers you can provision in this way are limited to the providers available for Vagrant. If this is a blocker for you, consider using something like Capistrano and building your own Masterless Puppet workflow.


comments powered by Disqus