Puppetry with Vagrant
Streamlining Puppet Development with Vagrant
This tutorial covers how to provision systems using the Vagrant Puppet Apply Provisioner. You’ll learn how to set up a local development environment to create and test Puppet manifests and modules.
Vagrant is widely used for automating the creation and provisioning of virtual machines, offering various provisioners, including Shell and Puppet Apply. By combining Vagrant with your preferred text editor, you can have an IDE-like environment for developing change configuration scripts, or infrastructure-as-code.
In 2018, I explored this topic with Puppet 5 on Ubuntu 16.04 (Xenial Xarus) and CentOS 7. This updated guide uses Puppet 8.9 on Ubuntu 22.04 (Jammy Jellyfish) and Rocky Linux 9, allowing you to test across both systems based on the Debian family or RHEL family operating systems.
Tested Environments
These are the test environments I used for testing this solution.
* **Host**:
* macOS "Monterey" 12.2.1
* Vagrant 2.4.1
* Virtualbox 7.0.18r162988
* **Guests**:
* Ubuntu 22.04.3 "Jammy Jellyfish": `generic/ubuntu2204`
* puppet 8.9.0
* Rocky Linux 9.3 "Blue Onyx": `generic/rocky9`
* puppet 8.9.0
Requirements
These are the tools required for this tutorial.
- Vagrant [
vagrant
]: virtualization management tool - Virtualbox [
vboxmanage
]: virtualization solution that is used by Vagrant - POSIX-compliant shell [
bash
orzsh
]: tutorial uses commands compatible with one of these shells. On Windows, you can run these shells with either MSYS2 or Cygwin environments.
Project Setup
Here’s how you can set up the project directories for this tutorial:
PROJ_HOME=~/vagrant-puppet
# craete directory structure
mkdir -p \
$PROJ_HOME/site/hello_web/{manifests,files} \
$PROJ_HOME/{ubuntu2204,rocky9}/manifests
cd $PROJ_HOME
# create files
touch \
bootstrap.sh \
site/hello_web/{manifests/init.pp,files/index.html,metadata.json} \
{ubuntu2204,rocky9}/{manifests/default.pp,Vagrantfile}
Afterward, the following structure should be created:
Powershell Hints
This is off-topic and beyond the scope of this tutorial, but if you prefer using PowerShell on Windows instead of a POSIX shell, you’ll need to convert some commands. Here are a few tips:
- Replace the tilde (
~
) with$HOME
. - Use
Set-Location
in place ofcd
. - For
pushd
andpopd
, usePush-Location
andPop-Location
. - Replace
mkdir
andtouch
withNew-Item
. - Ensure the
vagrant
command is in your path by adding it to$env:PATH
.
As an example, the PowerShell script below can create the same directory structure for this project.
# Create directory structure
New-Item -ItemType Directory -Force -Path `
"$HOME\vagrant-puppet\site\hello_web\manifests", `
"$HOME\vagrant-puppet\site\hello_web\files", `
"$HOME\vagrant-puppet\ubuntu2204\manifests", `
"$HOME\vagrant-puppet\rocky9\manifests"
Set-Location "$HOME\vagrant-puppet"
# Create files
New-Item -ItemType File -Force -Path `
"$HOME\vagrant-puppet\bootstrap.sh", `
"$HOME\vagrant-puppet\site\hello_web\manifests\init.pp", `
"$HOME\vagrant-puppet\site\hello_web\files\index.html", `
"$HOME\vagrant-puppet\site\hello_web\metadata.json", `
"$HOME\vagrant-puppet\ubuntu2204\manifests\default.pp", `
"$HOME\vagrant-puppet\ubuntu2204\Vagrantfile", `
"$HOME\vagrant-puppet\rocky9\manifests\default.pp", `
"$HOME\vagrant-puppet\rocky9\Vagrantfile"
Vagrant Configuration
In this tutorial, we’ll use VirtualBox as the default provider, which works consistently on Intel-based machines running Windows, macOS, and Linux. We’ll be configuring recent versions of Ubuntu and Rocky Linux.
Vagrant also supports other providers besides VirtualBox. Although this tutorial focuses on VirtualBox, feel free to explore these alternatives based on your system:
- Windows Professional: Hyper-V using the
hyperv
provider - macOS: Hypervisor.framework using the
vagrant-qemu
plugin - Linux: KVM using the
vagrant-libvirt
plugin
Ubuntu 22.04 guest virtual machine
Update the file $HOME/vagrant-puppet/ubuntu2204/Vagrantfile
with the following:
Vagrant.configure('2') do |config|
config.vm.box = 'generic/ubuntu2204'
config.vm.network 'forwarded_port', guest: 80, host: 8085
####### Install Puppet Agent #######
config.vm.provision "bootstrap",
before: :all,
type: "shell",
path: "../bootstrap.sh"
####### Provision #######
config.vm.provision :puppet do |puppet|
puppet.module_path = "../site"
puppet.options = "--verbose --debug"
end
end
You can download this image and start the virtual machine with the following command:
pushd ~/vagrant-puppet/ubuntu2204 && vagrant up --no-provision && popd
Rocket Linux 9 guest virtual machine
Update the file $HOME/vagrant-puppet/rocky9/Vagrantfile
with the following:
Vagrant.configure('2') do |config|
config.vm.box = 'generic/rocky9'
config.vm.network 'forwarded_port', guest: 80, host: 8085
####### Install Puppet Agent #######
config.vm.provision "bootstrap",
before: :all,
type: "shell",
path: "../bootstrap.sh"
####### Provision #######
config.vm.provision :puppet do |puppet|
puppet.module_path = "../site"
puppet.options = "--verbose --debug"
end
end
You can download this image and start the virtual machine with the following command:
pushd ~/vagrant-puppet/rocky9 && vagrant up --no-provision && popd
Installing Puppet
The Vagrant Puppet Apply provisioner does not bootstrap the systems with the Puppet agent, so we have to install this ourselves before we can use Puppet.
First, update the file $HOME/vagrant-puppet/boostrap.sh
with the following code:
#!/bin/sh
command -v puppet > /dev/null \
&& { echo "Puppet is installed! skipping" ; exit 0; }
ID=$(grep -oP '(?<=^ID=).*' /etc/os-release | tr -d '"')
case "${ID}" in
rocky)
VERS=$(grep -oP '(?<=PLATFORM_ID="platform:el).*[0-9]' /etc/os-release)
sudo rpm -Uvh https://yum.puppet.com/puppet8-release-el-${VERS}.noarch.rpm
sudo yum install -y puppet-agent
;;
debian|ubuntu)
wget https://apt.puppetlabs.com/puppet8-release-$(lsb_release -cs).deb
sudo dpkg -i puppet8-release-$(lsb_release -cs).deb
sudo apt-get -qq update
sudo apt-get install -y puppet-agent
;;
*)
echo "ERROR: Distro '${ID}' not supported" 2>&1
exit 1
;;
esac
Ubuntu 22.04
On the Ubuntu 22.04 virtual machine guest, you can install (bootstrap) the puppet agent using the following command:
pushd ~/vagrant-puppet/ubuntu2204 && \
vagrant provision --provision-with "bootstrap" && popd
Rocky 9
On the Rocky 9 virtual machine guest, you can install (bootstrap) the puppet agent using the following command:
pushd ~/vagrant-puppet/rocky9 \
&& vagrant provision --provision-with "bootstrap" && popd
Puppet Configuration
This guide will walk you through setting up all the Infrastructure-as-Code (IaC) scripts used in this project. Puppet consists of several key components:
- Manifests: Puppet’s Domain-Specific Language (DSL) that defines the desired state of a system.
- Modules: Collections of manifests, templates, and files organized to manage system configurations.
- Facts: Information about a system (node) that can be used by manifests and modules.
- Node Definitions: Blocks within manifests that define configurations for specific nodes, based on their hostname or facts.
- Site Manifest: The primary manifest that orchestrates and configures your entire infrastructure, often including node definitions.
Site Manifest
In this tutorial, we won’t use a formal site manifest to list all nodes. Instead, we’ll use a local manifest/default.pp
file, which will apply a default configuration to the single virtual machine guest.
This manifest will call our module hello_web
and optionally pass in parameters if they are different than the defaults.
For Ubuntu, update the file $HOME/vagrant-puppet/ubuntu2204/manifests/default.pp
with:
node default {
class { 'hello_web': }
}
For Rocky Linux, update the file $HOME/vagrant-puppet/rocky9/manifests/default.pp
with:
node default {
class { 'hello_web':
package_name => 'httpd',
service_name => 'httpd',
doc_root => '/var/www/html',
}
}
The hello_web module
This is a simple module to install Apache HTTP Server and a default hello world web page.
Update $HOME/vagrant-puppet/site/hello_web/files/index.html
with the following:
<html>
<body>
<h1>Hello World!</h1>
</body>
</html>
Update $HOME/vagrant-puppet/site/manifests/init.pp
with the following:
class hello_web (
$package_name = 'apache2',
$service_name = 'apache2',
$doc_root = '/var/www/html'
) {
package { $package_name:
ensure => present,
}
service { $service_name:
ensure => running,
enable => true,
}
file { "$doc_root/index.html":
source => "puppet:///modules/hello_web/index.html",
}
}
Update $HOME/vagrant-puppet/site/metadata.json
with the following:
{
"name": "joachim8675309-hello_web",
"version": "0.1.0",
"author": "joachim8675309",
"summary": "Hello World Tutorial",
"license": "Apache-2.0",
"source": "https://github.com/darkn3rd/blog_tutorials",
"dependencies": [],
"operatingsystem_support": [
{
"operatingsystem": "RedHat",
"operatingsystemrelease": ["9"]
},
{
"operatingsystem": "Rocky",
"operatingsystemrelease": ["9"]
},
{
"operatingsystem": "Debian",
"operatingsystemrelease": ["12"]
},
{
"operatingsystem": "Ubuntu",
"operatingsystemrelease": ["22.04"]
}
],
"requirements": [
{
"name": "puppet",
"version_requirement": ">= 7.24 < 9.0.0"
}
]
}
📓 NOTE: This metadata.json
file and the overall module was generated using PDK (Puppet Development Kit), which can scaffold with a single command like pdk new module hello_web
.
Provisioning Automatically
You can run puppet apply
through the following commands below.
Ubuntu 22.04
On the Ubuntu 22.04 virtual machine guest, you can provision using Puppet with the following command:
pushd ~/vagrant-puppet/ubuntu2204 && vagrant provision && popd
Rocky 9
On the Rocky 9 virtual machine guest, you can provision using Puppet with the following command:
pushd ~/vagrant-puppet/rocky9 && vagrant provision && popd
Provisioning Manually
Alternatively, instead of using Vagrant to automate running the puppet
command, you can run this yourself. Here’s how you can do that:
Ubuntu 22.04
First log onto the virtual machine guest:
cd ~/vagrant-puppet/ubuntu2204
vagrant ssh
On the virtual machine guest, run these commands:
MODULES=$(mount | grep -o '/tmp/vagrant-puppet/modules-[^ ]*')
MANIFESTS=$(mount | grep -o '/tmp/vagrant-puppet/manifests-[^ ]*' | uniq)
puppet apply --verbose --debug \
--modulepath "$MODULES:/etc/puppet/modules" \
--detailed-exitcodes \
"$MANIFESTS/default.pp"
# When finished
logout
Rocky 9
First log onto the virtual machine guest:
cd ~/vagrant-puppet/rocky9
vagrant ssh
On the virtual machine guest, run these commands:
MODULES=$(mount | grep -o '/tmp/vagrant-puppet/modules-[^ ]*')
MANIFESTS=$(mount | grep -o '/tmp/vagrant-puppet/manifests-[^ ]*' | uniq)
puppet apply --verbose --debug \
--modulepath "$MODULES:/etc/puppet/modules" \
--detailed-exitcodes \
"$MANIFESTS/default.pp"
# When finished
logout
Testing the Solution
You can use curl
with the command line option to include headers in the response for testing purposes.
Ubuntu 22.04
The Ubuntu guest system, by default, does not have a firewall, making it accessible from outside the guest system. This setup maps port 80
to port 8085
. To test this, you can use the following command from the host system:
curl --include http://localhost:8085
This should show something like the following below:
Rocky 9
By default, the Rocky Linux guest system has a firewall enabled, restricting access from outside the guest system. To allow traffic, you can open the necessary ports using the following command:
cd ~/vagrant-puppet/rocky9
vagrant ssh --command \
'sudo /usr/bin/firewall-cmd --zone=public --add-port=80/tcp'
This vagrant setup maps port 80
to port 8083
. To test this, you can use the following command from the host system:
curl --include http://localhost:8083
This should show something like the following below:
Cleanup
These systems can be cleaned up by using the vagrant destroy
command. This will halt the system and delete the disk image used by the guest VM.
pushd vm_guests/ubuntu2204 && vagrant destroy && popd
pushd vm_guests/rocky9&& vagrant destroy && popd
Conclusion
In many organizations, engineers often joke that “Prod is the new Test,” highlighting the tendency for untested changes to reach production. One major challenge with configuration management tools like Puppet, and infrastructure-as-code in general, is the difficulty of testing changes locally. Vagrant addresses this by reducing the friction, making it easier to develop and test in a local environment before pushing changes to staging or production.
This tutorial serves as a simple introduction to help you quickly get started with Puppet. I’ve intentionally kept the example straightforward — a basic “hello world” scenario — to ease newcomers into Puppet and Vagrant.
In more complex environments, you’d be managing fleets of servers, each interacting with databases, monitoring tools, and services like message queues, search indexing, caching, and reverse proxy load balancers. And that’s just for monolithic applications.
When managing clusters (e.g., ElasticSearch) or microservices, asynchronous service discovery becomes crucial. Tools like Consul or cloud metadata like AWS tags help with this process. For more advanced setups, you’ll also need to integrate an External Node Classifier (ENC) or Hiera plugins to retrieve real-time health information from cluster nodes, which is then used to configure other interdependent nodes accordingly.
Thank you for reading!
Source Code
You can find he source code related to this tutorial at the following location:
Related Articles
Earlier Article
This was the original article written 2018 that has some extra material about using node classifiers, smart parameters with an inherited class, and some comments about Hiera and ENC.
Vagrant with Salt Provisioner
Similar article using the Salt provisioner.
Vagrant with Macbook Mx (arm64)
Article that shows how to run virtual machines using hypervisor.framework
or emulator with q35
using the vagrant-qemu
plugin.
Resources
These are articles that I found through searches (some dated) about introductory material on Puppet.
- Getting Started With Puppet Code: Manifests and Modules by Michel Anicas (Digitial Ocean) on August 21, 2014
- Getting Started with Puppet — Installation and Setup by Unknown Author (Linode) on January 15, 2019 — This covers both Puppet Server and Puppet agents on Ubuntu 18.04 and CentOS 7
- Install and Configure Puppet by Elle Kraut (Akamai) on January 15, 2019
- Get Started with Puppet: A Tutorial for First-Timers by David Sandilands (Puppet) on July 19, 2023 — This is an index to other articles that help install server and client agents and automate jobs.