Authoring a Puppet Module

45 minutes
  • 8 Learning Objectives

About this Hands-on Lab

A full Puppet module generally makes use of a number of components: Classes, manifests, files, Hiera data, and sometimes even defined types. In this lab, we’ll be working with all of these things to create a full Nginx module that works across Debian-based and RedHat-based distributions. The module will install Nginx, pull the configuration file under Puppet’s control, ensure the service has been started, and create any virtual hosts, demonstrating how to use classes, metaparameters, Hiera, and defined ttypes.

This lab assumes basic familiarity of Puppet module components, such as resource types and class declarations; for a lab that addresses the very basics, try “Getting Started with Puppet Modules.”

Learning Objectives

Successfully complete this lab by achieving the following learning objectives:

Create the `install` class
  1. Move into the module directory:

    $ cd /etc/puppetlabs/code/environments/production/modules/

  2. Create the nginx module skeleton. You do not need to provide a Puppet Forge user, set the module author to yourself, use the default license, and set the operating systems to RedHat and Debian:

    $ sudo pdk new module nginx

  3. Move into the module directory:

    $ cd nginx

  4. Create a new class:

    $ sudo pdk new class install

  5. Update the resource type and provide related parameters. Note how we add our Hiera data from the start.

    $ sudo vim manifests/install.pp

    class nginx::install {
    package { "${nginx::install_name}":
    ensure => $nginx::install_ensure,

    Save and exit.

  6. Check for syntax errors:

    $ sudo puppet parser validate manifests/install.pp

Create the `config` class
  1. Pull down the configuration files into the files directory:

    $ sudo curl -o files/Debian.conf
    $ sudo curl -o files/RedHat.conf

    Note that we rename the files so we can call them via facts in our class.

  2. Create the config class:

    $ sudo pdk new class config

  3. Use the file resource type and add the related parameters:

    $ sudo vim manifests/config.pp

    class nginx::config {
    file { "${nginx::config_path}":
    source => "puppet:///modules/nginx/${osfamily}.conf",
    ensure => $nginx::config_ensure,

    Save and exit.

  4. Validate for syntax errors:

    $ sudo puppet parser validate manifests/config.pp

Create the `service` class
  1. Create the service class:

    $ sudo pdk new class service

  2. Add the service resource type and add parameters:

    $ sudo vim manifests/service.pp

     service { "${nginx::service_name}":
       ensure     => $nginx::service_ensure,
       enable     => $nginx::service_enable,
       hasrestart => true,

    Save and exit.

  3. Check for syntax errors:

    $ sudo puppet parser validate manifests/service.pp

Create the `vhosts` defined type
  1. We first want to create the template we’ll use for our hosts:

    $ sudo vim templates/vhosts.conf.epp

    The .epp signifies that we’ll be writing this template with embedded Puppet.

  2. Copy in the template:

    server {
    listen 80;
    listen [::]:80;

     root /var/www/;


  3. We want to turn the port values and server name into variables. That said, we won’t be assigning our variables in Hiera as did in our classes, because we’ll be instead creating these configurations via defined type. Instead, give them simple names:

    server {
    listen <%= $port %>;
    listen [::]:<%= $port %>;

     root /var/www/<%= $servername %>;
     server_name <%= $servername %> www.<%= $servername %>;


    Note that we’re only using two variables. Save and exit.

  4. Create the vhosts defined type:

    $ sudo pdk new defined_type vhosts

  5. We first want to define our variables:

    $ sudo vim manifests/vhosts.pp

    define nginx::vhosts (
    Integer $port,
    String[1] $servername,
    ) {

  6. Next, we want to create a file resource that create our website’s root directory:

     file { "/var/www/${servername}":
       ensure => 'directory',
       owner  => $nginx::vhosts_owner,
       group  => $nginx::vhosts_group,
  7. Then generate the configuration file from the template:

     file { "${nginx::vhosts_root}/${servername}.conf":
       ensure  => 'file',
       owner   => $nginx::vhosts_owner,
       group   => $nginx::vhosts_group,
       content => epp('nginx/vhosts.conf.epp', {'port' => $port, 'servername' => $servername}),
       notify  => Service["${nginx::service_name}"],

    Note that we pull our variables into our template in the content parameter, and that we notify our service class so that a restart can occur.

    Save and exit.

  8. Validate the syntax:

    $ sudo puppet parser validate manifests/vhosts.pp

Add Hiera data
  1. Let’s now add our variables into Hiera. First, however, we need to update our Hiera config:

    $ sudo vim hiera.yaml


    • name: ‘Operating System Family’
      path: ‘%{}-family.yaml’

    • name: ‘common’
      path: ‘common.yaml’

  1. Then add our OS-specific variables:

    $ sudo vim data/RedHat-family.yaml

    nginx::vhosts_root: ‘/etc/nginx/conf.d’
    nginx::vhosts_owner: ‘nginx’
    nginx::vhosts_group: ‘nginx’

    $ sudo vim data/Debian-family.yaml

    nginx::vhosts_root: ‘/etc/nginx/sites-available’
    nginx::vhosts_owner: ‘www-data’
    nginx::vhosts_group: ‘www-data’

  2. Next, add any default Hiera data:

    $ sudo vim data/common.yaml

    nginx::install_name: ‘nginx’
    nginx::install_ensure: ‘present’
    nginx::config_path: ‘/etc/nginx/nginx.conf’
    nginx::config_ensure: ‘file’
    nginx::service_name: ‘nginx’
    nginx::service_ensure: ‘running’
    nginx::service_enable: true
    nginx::vhosts_root: ‘/etc/nginx/sites-available’
    nginx::vhosts_owner: ‘www-data’
    nginx::vhosts_group: ‘www-data’

Create the `init` file
  1. Create our final manifest:

    $ sudo pdk new class nginx

  2. Add all Hiera data:

    $ sudo vim manifests/init.pp

    class nginx (
    String $install_name,
    String $install_ensure,
    String $config_path,
    String $config_ensure,
    String $service_name,
    String $service_ensure,
    Boolean $service_enable,
    String $vhosts_root,
    String $vhosts_owner,
    String $vhosts_group,
    ) {

  3. Pull in all classes, but not our defined type:

    ) {

     contain nginx::install
     contain nginx::config
     contain nginx::service
     -> Class['::nginx::config']
     ~> Class['::nginx::service']


    Save and exit.

  4. Validate the syntax:

    $ sudo puppet parser validate manifests/init.pp

Update the main manifest
  1. Open the Main manifest:

    $ sudo vim ../../manifests/site.pp

  1. Add the module to a node1.ec2.internal node definition:

    node node1.ec2.internal {
    include nginx

  2. Call the defined type:

    node node1.ec2.internal {
    include nginx

     nginx::vhosts { 'puppet-project':
       port       => 80,
       servername => '',


  3. Repeat with the second node:

    node node2.ec2.internal {
    include nginx

     nginx::vhosts { 'puppet-project':
       port       => 80,
       servername => '',


    Save and exit.

Test the module

On both additional nodes:

  1. Install Puppet:

    $ curl -k https://puppet.ec2.internal:8140/packages/current/install.bash | sudo bash

On the master:

  1. Approve the nodes:

    $ sudo puppetserver ca sign –all

Return to both nodes:

  1. Test the module:

    sudo puppet agent -t

    Note that on CentOS you may have to switch to root (sudo -i)

Additional Resources

You are a member of the DevOps team at a company whose main product is a web application. Up until this point, Apache was used to serve much of the static content pages, but stress testing a version hosted on Nginx showed improves in load time. As such, you've been tasked with creating an Nginx formula for Puppet so that the company can begin the transition away from Apache. The modules does not need to be incredibly advanced, but it does need to install, configure, and start Nginx, as well as set up a virtual hosts file.

Use the following files as the configuration file for the module:

Red Hat:


And base the "virtual hosts" setup on this example code:

server {
listen 80;
  listen [::]:80;

  root /var/www/;

When finished, ensure the module works by running it against both the Puppet master and the provided additional node.

Bootstrap the provided additional node with:

curl -k https://puppet.ec2.internal:8140/packages/current/install.bash | sudo bash

And be sure to approve the node on the master:

sudo puppetserver ca sign --all

What are Hands-on Labs

Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.

Sign In
Welcome Back!

Psst…this one if you’ve been moved to ACG!

Get Started
Who’s going to be learning?