NETWAYS Training

Lab 2.1: Installation


Install the Puppet agent provided by Puppet


Make Puppet Collection available by installing the release repository

$ sudo yum install http://yum.puppet.com/puppet5/puppet5-release-el-7.noarch.rpm -y

Inspect what version is installed on your master

$ sudo rpm -aq |grep puppet-agent
puppet-agent-x.y.z-n.el7.x86_64

Install puppet-agent using yum

$ sudo yum install puppet-agent-x.y.z-n.el7.x86_64 -y

Extend the secure path variable to include the puppet command

$ sudo visudo
Defaults    secure_path = /sbin:/bin:/usr/sbin:/usr/bin:/opt/puppetlabs/bin

$ exec bash
NETWAYS Training

Lab 3.1: Puppet Resource


Use Puppet Resource Command to query and change resources


Query all users

$ sudo puppet resource user

Query state of the file "/etc/passwd"

$ sudo puppet resource file /etc/passwd 

Ensure package "vim-enhanced" is installed

$ sudo puppet resource package vim-enhanced ensure=present
NETWAYS Training

Lab 4.1: User

Create yourself a user


Create a manifest "user.pp" to manage your user

Change to the directory "manifests" and create "user.pp"

$ cd ~/puppet/manifests/
$ vim user.pp

Declare your user including its shell as "/bin/bash"

Declare your user with your typically username, ensure that he is present and set its shell to "/bin/bash"

$ ~/puppet/manifests/user.pp
user { 'myuser':
  ensure => present,
  shell  => '/bin/bash',
}

Set it to also manage your home directory

Enforcing the user now would create your home directory depending on the default of your operatingsystem, to enforce its creation set the attribute "managehome" to true.

$ vim ~/puppet/manifests/user.pp
user { 'myuser':
  ensure     => present,
  shell      => '/bin/bash',
  managehome => true,
}

Apply the manifest with "puppet apply user.pp"

Save the manifest and then use "puppet apply" to enforce it. Add "--noop" if you want to simulate first, add "-d" if you want to see the commands executed. If you are not sure about the syntax run the syntax validation before.

$ puppet parser validate ~/puppet/manifests/user.pp
$ sudo puppet apply --noop ~/puppet/manifests/user.pp
$ sudo puppet apply -d ~/puppet/manifests/user.pp

To verify the user's existence you can use "id" or "getent", also verify the creation of the home directory with "ls -la".

$ sudo id myuser
$ sudo getent passwd myuser
$ sudo ls -la /home/myuser/
NETWAYS Training

Lab 4.2: Group


Manage the group membership of your user


Add a group definition for your user's private group to your manifest (a group with the same name)

Open your manifest user.pp and add a group resource.

$ vim ~/puppet/manifests/user.pp
group { 'myuser':
  ensure => present,
}

Add a group definition for a administrative group "admins" to your manifest

Add another group resource.

$ vim ~/puppet/manifests/user.pp
group { 'admins':
  ensure => present,
}

Change your user definition to set its private group as its primary group and the administrative group as secondary

Add the groups to your user definition

$ vim ~/puppet/manifests/user.pp
user { 'myuser':
  ensure     => present,
  shell      => '/bin/bash',
  gid        => 'myuser',
  groups     => [ 'admins' ],
  managehome => true,
}

Apply the manifest with "puppet apply user.pp"

Save the manifest and then use "puppet apply" to enforce it. Add "--noop" if you want to simulate first, add "-d" if you want to see the commands executed.

$ sudo puppet apply --noop ~/puppet/manifests/user.pp
$ sudo puppet apply -d ~/puppet/manifests/user.pp

To verify the user's group membership you can use "id".

$ sudo id myuser
NETWAYS Training

Lab 4.3: SSH Authorized Key


Grant your user access to the system authenticated by a ssh key


Generate a ssh key on your laptop using "ssh-keygen"

Open a terminal on your laptop and execute "ssh-keygen". Keep the passphrase empty if you like.

$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/training/.ssh/id_rsa): /home/training/.ssh/training_ssh
Created directory '/home/training/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/training/.ssh/training_ssh.
Your public key has been saved in /home/training/.ssh/training_ssh.pub.

$ cat ~/.ssh/training_ssh.pub

Add a ssh_authorized_key resource to distribute your public key

Add a ssh_authorized_key resource to distribute your public key. The first part of the output of the command above is the type, the second one is the key itself and the third is the comment which you are not required to keep.

$ vim ~/puppet/manifests/user.pp
ssh_authorized_key { 'myuser':
  ensure => present,
  type   => 'ssh-rsa',
  key    => '...',
  user   => 'myuser',
}

Apply the manifest with "puppet apply user.pp"

Save the manifest and then use "puppet apply" to enforce it. Add "--noop" if you want to simulate first, add "-d" if you want to see the commands executed.

$ sudo puppet apply --noop ~/puppet/manifests/user.pp
$ sudo puppet apply -d ~/puppet/manifests/user.pp

To verify the login try to connect to the virtual machine from your laptop.

$ ssh myuser@agent-centos.localdomain
NETWAYS Training

Lab 4.4: Sudoers


Grant your user administrative privileges using sudo


Ensure package "sudo" is installed

Edit your manifest to include a definition ensuring the package "sudo" is installed.

$ vim ~/puppet/manifests/user.pp
package { 'sudo':
  ensure => present,
}

Manage a file in "/etc/sudoers.d" for your user allow to execute all commands as root

Add a file resource "/etc/sudoers.d/myuser" with owner "root", group "root", mode "0400" and content "myuser ALL=(ALL) NOPASSWD: ALL".

$ vim ~/puppet/manifests/user.pp
file { '/etc/sudoers.d/myuser': 
  ensure  => file,
  owner   => 'root',
  group   => 'root',
  mode    => '0400',
  content => "myuser ALL=(ALL) NOPASSWD: ALL\n",
}

Apply the manifest with "puppet apply user.pp"

Save the manifest and then use "puppet apply" to enforce it. Add "--noop" if you want to simulate first, add "-d" if you want to see the commands executed.

$ sudo puppet apply --noop ~/puppet/manifests/user.pp
$ sudo puppet apply -d ~/puppet/manifests/user.pp

You can verify the sudo permissions by displaying them with sudo or testing it as the user.

$ sudo -U myuser -l

myuser$ sudo -l
myuser$ sudo id
NETWAYS Training

Lab 4.5: Package-File-Service Pattern


Install and run the Apache webserver


Ensure package "httpd" is installed

Create a new manifest "apache.pp" and declare a package resource "httpd" to be installed.

$ vim ~/puppet/manifests/apache.pp
package { 'httpd':
  ensure => present,
}

Manage a configuration file "/etc/httpd/conf.d/local.conf"

Add a new file to the "puppet/files" directory in your home directory as a source and then add a file resource managing "/etc/httpd/conf.d/local.conf" to your manifest. To get the file install the apache package manually oder run the apply command on your manifest before doing this step.

$ vim ~/puppet/files/local.conf
ServerAdmin info@netways.de

$ vim ~/puppet/manifests/apache.pp
file { '/etc/httpd/conf.d/local.conf':
  ensure  => file,
  owner   => 'root',
  group   => 'root',
  mode    => '0644',
  source  => '/home/training/puppet/files/local.conf',
}

Enable and start the service "httpd"

Add a service resource which enables and starts "httpd".

$ vim ~/puppet/manifests/apache.pp
service { 'httpd':
  ensure    => running,
  enable    => true,
}

Add dependencies to make sure it is installed before you configure it and config changes restart the service

With Puppet 4's manifest based ordering the three resource would already be applied in correct order, but no refresh would happen. Also understanding the code is easier if dependencies are explicitly declared.

$ vim ~/puppet/manifests/apache.pp
package { 'httpd':
  ...
}

file { '/etc/httpd/conf.d/local.conf':
  ...
  require => Package['httpd'],
}

service { 'httpd':
  ...
  subscribe => File['/etc/httpd/conf.d/local.conf'],
}

Apply the manifest

Save the manifest and then enforce it.

$ sudo puppet apply ~/puppet/manifests/apache.pp

Add the "Servername" to the configuration and apply again to see the service being restarted

Open your copy of the configuration file and add the "Servername" directive, then apply again to if the service gets restarted after changing its configuration.

$ vim ~/puppet/files/local.conf
ServerName agent-centos.localdomain:80

$ sudo puppet apply ~/puppet/manifests/apache.pp
NETWAYS Training

Lab 4.6: Exec Resource


Add an exec resource to change restart behaviour


Add an exec resource to restart apache using systemctl

$ vim ~/puppet/manifests/apache.pp
exec { 'apache-restart':
  command     => 'systemctl restart httpd',
  path        => '/usr/bin:/usr/sbin:/bin:/sbin',
  refreshonly => true,
}

Change the service resource to reload instead of restart with a systemctl command in the restart attribute

$ vim ~/puppet/manifests/apache.pp
service { 'httpd':
  ...
  #subscribe => File['/etc/httpd/conf.d/local.conf'],
  restart   => '/usr/bin/systemctl reload httpd',
}

Change the file resource to notify the exec instead of the service

$ vim ~/puppet/manifests/apache.pp
file { '/etc/httpd/conf.d/local.conf':
  ...
  require => Package['httpd'],
  notify  => Exec['apache-restart'],
}

Apply the manifest

Change something in the configuration file and apply the manifest.

$ sudo puppet apply ~/puppet/manifests/apache.pp

Now you can decide in which cases a restart is required and when a reload is enough.

NETWAYS Training

Lab 4.7: Resource Defaults


Add a vhost definition to your webserver


Set the resource defaults for file resources to provide owner "root", group "root" and mode "0644"

$ vim ~/puppet/manifests/vhost.pp
File {
  owner => 'root',
  group => 'root',
  mode  => '0644',
}

Add a host entry "vhost.localdomain" for your vhost on your server

$ vim ~/puppet/manifests/vhost.pp
host { 'vhost.localdomain':
  ensure       => present,
  ip           => '127.0.0.1',
  host_aliases => ['vhost'],
}

Add a configuration file for your vhost creating a name based vhost

$ vim ~/puppet/manifests/vhost.pp
file { '/etc/httpd/conf.d/vhost.conf':
  ensure => file,
  source => '/home/training/puppet/files/vhost.conf',
}
$ vim ~/puppet/files/vhost.conf
<VirtualHost *:80>
    DocumentRoot "/var/www/vhost.localdomain"
    ServerName vhost.localdomain
</VirtualHost>

Create the document root "/var/www/vhost.localdomain" and an index file saying "Hello World"

$ vim ~/puppet/manifests/vhost.pp
file { '/var/www/vhost.localdomain/':
  ensure => directory,
}

file { '/var/www/vhost.localdomain/index.html':
  ensure => file,
  content => '<h1>Hello World</h1>',
}

Apply the manifest and restart the service

$ sudo puppet apply ~/puppet/manifests/vhost.pp
$ sudo service httpd reload

Optional: Add an additional vhost for your default servername to provide different content based on the servername

$ vim ~/puppet/manifests/apache.pp
file { '/etc/httpd/conf.d/default.conf':
  ensure => file,
  source => '/home/training/puppet/files/default.conf',
  notify => Service['httpd'],
}

$ vim ~/puppet/files/default.conf
<VirtualHost *:80>
    DocumentRoot "/var/www"
    ServerName agent-centos.localdomain
</VirtualHost>

$ sudo puppet apply ~/puppet/manifests/apache.pp
NETWAYS Training

Lab 4.8: Classes


Rework your apache manifest to provide a class apache


Rework your apache manifest to provide a class apache

Simply add the class definition around your Puppet code.

$ vim ~/puppet/manifests/apache.pp 
class apache {
  ...
}

During apply have a look on what Puppet is actually doing

Use the debug option to have a look on what Puppet is actually doing while you try to apply your class definition. You will notice it is not managing your resources.

$ sudo puppet apply --debug ~/puppet/manifests/apache.pp 
NETWAYS Training

Lab 5.1: Variables


Rework your vhost manifest to include variables


Add variables for hostname, fqdn, ip address of your vhost

Because of the facts hostname, fqdn and ipaddress you can not use these names for your variables.

$ vim ~/puppet/manifests/vhost.pp
$shortname = 'vhost'
$fullname = "${shortname}.localdomain"
$ip = '127.0.0.1'

Add variables for apache's configuration directory and your vhost's document root

$ vim ~/puppet/manifests/vhost.pp
$confdir = '/etc/httpd'
$documentroot = "/var/www/${fullname}"

Incorporate the variables in your resource declarations

$ vim ~/puppet/manifests/vhost.pp
host { $fullname:
  ip           => $ip,
  host_aliases => [ $shortname ],
}

file { "${confdir}/conf.d/${shortname}.conf":
  ensure => file,
  source => "/home/training/puppet/files/${shortname}.conf",
}

file { $documentroot:
  ensure => directory,
}

file { "${documentroot}/index.html":
  ensure  => file,
  content => '<h1>Hello World</h1>',
}
NETWAYS Training

Lab 5.2: Case Statement


Add Debian support to your apache manifest by using a case statement


Move your package, config file and service name to variables

$ vim ~/puppet/manifests/apache.pp
class apache {
  $packagename = 'httpd'
  $configdir   = '/etc/httpd'
  $localconfig  = "${configdir}/conf.d/local.conf"
  $servicename = 'httpd'
  ...
}

Add the variables as value for the namevar of your resources

$ vim ~/puppet/manifests/apache.pp
package { 'httpd':
  ...
  name => $packagename,
}

file { 'httpd local.conf':
  ...
  path => $localconfig,
}

service { 'httpd':
  ...
  name => $servicename,
}

Add a case statement setting the variables depending on the "osfamily"

$ vim ~/puppet/manifests/apache.pp
class apache {
  case $::osfamily {
    'RedHat': {
      $packagename = 'httpd'
      $configdir   = '/etc/httpd'
      $servicename = 'httpd'
    }
    'Debian': {
      $packagename = 'apache2'
      $configdir   = '/etc/apache2'
      $servicename = 'apache2'
    }
    default: {
      fail('Your platform is not supported')
    }
  }

  $confd  = "${configdir}/conf.d"
  $localconfig  = "${confd}/local.conf"
  ...
}
NETWAYS Training

Lab 5.3: If Statement


Allow to purge the Apache webserver


Create a if statement based on a variable "$ensure" to set the state of your resources

Add a variable "$ensure" to the top of your manifest and then set values corresponding to the resource types. Explicitly requiring absent ensures the default is the more common use case.

$ vim ~/puppet/manifests/apache.pp
class apache {
  $ensure = 'present'

  if $ensure == 'absent' {
    $ensure_package = 'purged'
    $ensure_file    = 'absent'
    $ensure_service = 'stopped'
    $enable_service = false
  } else {
    $ensure_package = 'present'
    $ensure_file    = 'file'
    $ensure_service = 'running'
    $enable_service = true
  }
  ...
}

Use this variables in your resource declarations

$ vim ~/puppet/manifests/apache.pp
package { 'httpd':
  ensure => $ensure_package,
  ...
}

file { 'httpd local.conf':
  ensure => $ensure_file,
  ...
}

service { 'httpd':
  ensure => $ensure_service,
  enable => $enable_service,
  ...
}
continued...

Adjust the dependency chain based on "ensure" to bring also purging in correct order

Adjusting the dependency chain based on the value of "ensure" is required that purge it completely works fine. This is a very good example for the usage of the arrow syntax for declaring dependencies, but not a typical example for common use in modules. In most public modules only small parts like features will also have a deactivation routine.

$ vim ~/puppet/manifests/apache.pp
file { 'httpd local.conf':
  ...
  #require => Package['httpd'],
}

if $ensure == "absent" {
  Service['httpd'] -> File['httpd local.conf'] -> Package['httpd']
} else {
  Package['httpd'] -> File['httpd local.conf'] ~> Service['httpd']
}
NETWAYS Training

Lab 5.4: ERB Template


Move your vhost configuration to an erb template


Create a erb template for your vhost by copying your static file and replace the values with erb

The file extension .erb is not required but it enables syntax checking on templates based on it.

$ cp ~/puppet/files/vhost.conf ~/puppet/templates/vhost.conf.erb

$ vim ~/puppet/templates/vhost.conf.erb
<VirtualHost *:80>
  DocumentRoot "<%= @documentroot %>"
  ServerName <%= @fullname %>
</VirtualHost>

Change the resource to use the template function for content instead of a static file as source

Make sure to comment the source attribute because it is mutually exclusive with content.

$ vim ~/puppet/manifests/vhost.pp
file { "${confdir}/conf.d/${shortname}.conf":
  ensure  => file,
  #source => "/home/training/puppet/files/${shortname}.conf",
  content => template('/home/training/puppet/templates/vhost.conf.erb'),
}
NETWAYS Training

Lab 5.5: EPP Template


Create your webserver content from an epp template


Create a epp template to represent your webserver content

In this example both parameters get a default value for being optional. The greeting message must be a string and falls back to "Hello World". The parameter from is per default undefined and can be this value or a string.

$ vim ~/puppet/templates/index.html.epp
<%- | String           $greeting = "Hello World",
      Optional[String] $from     = undef
| -%>
<h1><%= $greeting %> <% unless $from =~ Undef { -%> from <%= $from %><% } -%></h1>

Change the resource to use the template instead of fixed string

This example defines only the from parameter and uses the default for the greeting message.

$ vim ~/puppet/manifests/vhost.pp
file { "${documentroot}/index.html":
  ensure   => file,
  #content => '<h1>Hello World</h1>',
  content  => epp('/home/training/puppet/templates/index.html.epp', {
    'from' => "${fullname}"
  }),
}
NETWAYS Training

Lab 5.6: Iteration


Add multiple vhosts with an iteration


Add a hash containing shortnames and ip addresses to the top of your manifest

Add at least another vhost to this hash, simply use local ip addresses for this example.

$ vim ~/puppet/manifests/vhost.pp
$vhosts = {
  'vhost' => '127.0.0.1',
  'foo'   => '127.0.0.2',
  'bar'   => '127.0.0.3',
}

Add an each loop based on this hash around your resources

Iterate over the vhost hash with the each function to create your resources. Make sure to not include your resource defaults in this loop.

$ vim ~/puppet/manifests/vhost.pp
#$shortname = 'vhost'
#$fullname = "${shortname}.localdomain"
#$ip = '127.0.0.1'

#$confdir = '/etc/httpd'
#$documentroot = "/var/www/${fullname}"

$vhosts.each | $shortname, $ip | {
  $fullname = "${shortname}.localdomain"
  $confdir = '/etc/httpd'
  $documentroot = "/var/www/${fullname}"
...
}
NETWAYS Training

Lab 6.1: Module


Build a module to handle your apache class


Create the structure for your apache module in ~/puppet/modules

It requires a directory apache with subdirectories manifests, files, templates and examples. Make sure to use the plural, the most common mistake is using the singular which will prevent autoloading from working.

$ mkdir -p ~/puppet/modules/apache/{manifests,files,templates,examples}

Move your manifest and all required files to this structure

Move your class manifest to be your modules default class and your configuration file to the location for static files.

$ mv ~/puppet/manifests/apache.pp ~/puppet/modules/apache/manifests/init.pp
$ mv ~/puppet/files/local.conf ~/puppet/modules/apache/files/

Adjust the source of your file resource

Use the URL puppet:// to serve the configuration file. Note the URL starting with puppet:/// because of the omitted server. If no server is given the agent will load the file from the server it contacted for requesting a catalog.

$ vim ~/puppet/modules/apache/manifests/init.pp
file {'httpd local.conf':
  ...
  source  => 'puppet:///modules/apache/local.conf',
}

Create a smoke test including your apache class

Simple create a manifest using the include function on your apache class named like the manifest defining it in the modules examples directory.

$ vim ~/puppet/modules/apache/examples/init.pp
include apache

Now you can use apply on this file but you have to provide the modulepath.

$ sudo puppet apply --debug --modulepath=~/puppet/modules/ ~/puppet/modules/apache/examples/init.pp
Debug: /Stage[main]/Apache/Package[httpd]/before: requires File[httpd local.conf]
Debug: /Stage[main]/Apache/File[httpd local.conf]/notify: subscribes to Service[httpd]
NETWAYS Training

Lab 6.2: Parameterized Class


Parameterize your apache class


Promote the variable "$ensure" to be a parameter

Change your class to provide a parameter "$ensure" which takes "running" and "stopped" with a default of "running". And a Boolean parameter named "$enable" with a default to "true". Both have to manage the service.

$ vim ~/puppet/modules/apache/manifests/init.pp
class apache (
  Enum['running','stopped'] $ensure = 'running',
  Boolean                   $enable = true,
) {
  ...
  #$ensure = 'present'

  if $ensure == 'absent' {
    ...
    #$enable_service = false
  } else {
    ...
    #$enable_service = true
  }

  service { 'httpd':
    ensure => $ensure_service,
    enable => $enable,
  }
}

Add a boolean parameter "$ssl" managing ssl support

Add a boolean parameter "$ssl". Its default is "false", but if changed to "true" on CentOS the package "mod_ssl" should be installed and the service "httpd" should be notified. You can also handle the case of "absent" but package dependencies would be enough in this simple setup.

$ vim ~/puppet/modules/apache/manifests/init.pp
class apache (
  ...
  Boolean                   $ssl = false,
) {
  if $::osfamily == 'RedHat' {
    package { 'mod_ssl':
      ensure => $ssl ? {
        true    => installed,
        default => absent,
      }
    }
  }
  ...
}
continued...

Add a smoke test for running and to enable ssl

Creating a smoke test allows you to validate your parameterized class.

$ vim ~/puppet/modules/apache/examples/init_stopped.pp 
class { 'apache':
  ensure => stopped,
}

$ sudo puppet apply --modulepath ~/puppet/modules/ ~/puppet/modules/apache/examples/init_stopped.pp

$ vim ~/puppet/modules/apache/examples/init_ssl.pp 
class { 'apache':
  ssl => true,
}

$ sudo puppet apply --modulepath ~/puppet/modules/ ~/puppet/modules/apache/examples/init_ssl.pp
NETWAYS Training

Lab 6.3: Params.pp Pattern


Create a params class providing your default parameters


Create a params class providing your default parameters

$ vim ~/puppet/modules/apache/manifests/params.pp
class apache::params {
  case $::osfamily {
    'RedHat': {
      $packagename = 'httpd'
      $configdir   = '/etc/httpd'
      $confd       = "${configdir}/conf.d"
      $servicename = 'httpd'
      $ssl         =  true
    }
    'Debian': {
      $packagename = 'apache2'
      $configdir   = '/etc/apache2'
      $confd       = "${configdir}/conf.d"
      $servicename = 'apache2'
      $ssl         =  false
    }
    default: {
      fail('Your platform is not supported.')
    }
  }
}

Remove the parameter calculation based on "$::osfamily" from your main class

$ vim ~/puppet/modules/apache/manifests/init.pp
class apache (
  ...
) {
  #case $::osfamily {
  #  ...
  #}
  ...
}

Inherit your params class and use its values as default

$ vim ~/puppet/modules/apache/manifests/init.pp
class apache (
  Enum['running','stopped'] $ensure = 'running',
  Boolean           $enable = true,
  Boolean                   $ssl    = $apache::params::ssl,
) inherits apache::params {
  ...
}
NETWAYS Training

Lab 6.4: Defined Resources


Create a defined resource apache::vhost from your manifest


Create a defined resource apache::vhost

Create a defined resource apache::vhost based on your manifest. Use the title for the shortname of your vhost.

$ vim ~/puppet/modules/apache/manifests/vhost.pp
define apache::vhost (
  String $ip,
  String $shortname    = $title,
  String $fullname     = "${shortname}.localdomain",
  String $confdir      = '/etc/httpd',
  String $documentroot = "/var/www/${fullname}",
) {
  ...
}

The ip address will be mandatory with this definition, all other have default values and are optional because of this.

Change the path of the templates from absolute to relative

Copy the template files to the templates directory and change the path so the templates are found through autoloading.

$ cp -r ~/puppet/templates/ ~/puppet/modules/apache/
$ vim ~/puppet/modules/apache/manifests/vhost.pp
...
#content => template('/home/training/puppet/templates/vhost.conf.erb'),
content => template('apache/vhost.conf.erb'),
...
#content  => epp('/home/training/puppet/templates/index.html.epp', { 'from' => "${fullname}" }),
content  => epp('apache/index.html.epp', { 'from' => "${fullname}" }),
...

Add a smoke test to apply multiple vhosts

For testing defined resources always create a test declaring multiple resources with different parameters to ensure included resources are unique.

This example uses an iteration, but simply declaring multiple resources or using create_resources would be fine.

 $ vim ~/puppet/modules/apache/examples/vhost.pp
 $vhosts = {
   'vhost' => { ip => '127.0.0.1'},
   'foo'   => { ip => '127.0.0.2'},
   'bar'   => { ip => '127.0.0.3', fullname => 'bar.example.com' },
   'baz'   => { ip => '127.0.0.4', documentroot => '/var/www/bazinga'},
 }

 $vhosts.each | String $name, Hash $vhost | {
   apache::vhost { $name :
     * => $vhost,
   }
 }

Optional: Expand your defined resource to notify the service.

Add a notify to your defined resource vhost to restart the service on changes to the vhost configuration. This requires the smoke test to also include the main class.

$ vim ~/puppet/modules/apache/manifests/vhost.pp
file { "${confdir}/conf.d/$shortname.conf":
  ...
  notify => Service['httpd'],
}

$ vim ~/puppet/modules/apache/examples/vhost.pp
include apache
...
NETWAYS Training

Lab 7.1: Communication


Establish the communication between agent and master


Configure the agent to connect to the master

Set the server parameter in the agent section of the configuration /etc/puppetlabs/puppet/puppet.conf

$ sudo vim /etc/puppetlabs/puppet/puppet.conf
...
[agent]
server = puppet.localdomain

Execute a puppet agent run

On the first run it will inform you about creation of key and certificate request and that it failed to retrieve certificate.

$ sudo puppet agent -t

Sign the certificate

Login to the master and run the following command to sign the certificate.

$ sudo puppet cert list
$ sudo puppet cert sign agent-centos.localdomain

Execute another run

On this run it will get the certificate and an empty catalog which applies without errors.

$ sudo puppet agent -t
NETWAYS Training

Lab 7.2: Git Workflow


Bring your puppet code under version control


Use git status to find pending changes

Git status will show you which files have changes no already commited which will be all files, but it will only print to top most directory.

$ git status

Add them with git add to the staging area

Run git add on the files printed.

$ git add modules

Create your initial commit using git commit

Running git commit will create a commit with all content of the staging area and ask you for a commit message which you can also directly add with parameter -m.

$ git commit -m "initial commit"

Push it to the Puppet Master with git push

You should always tell git what exactly it should push if you do not want to accidently push some local pending changes.

$ git push origin master

In this case we only have one remote target named "origin" and only one branch "master". This represents the "production" environment on the Puppet master.

If you get some error message from the validation hooks, fix them in the files, add them again and create a new commit to push.

NETWAYS Training

Lab 7.3: Node Declaration


Create a node declaration for your agent


Create a node declaration for your agent 'agent-centos.localdomain'

Add a node declaration to a manifest called site.pp for 'agent-centos.localdomain' including apache.

$ sudo vim ~/puppet/manifests/site.pp
node 'agent-centos.localdomain' {
  include apache
}

Create a default node declaration printing a notice 'Node not configured'

Add a default node declaration printing a notice 'Node not configured' which will be printed if you run the agent with verbose or debug output. If you want it also included in a report use a notify resource instead which will also a changed resource on every run.

$ sudo vim ~/puppet/manifests/site.pp
node default {
  notice('Node not configured')
}

Commit and push it to your server

Pushing the node declaration is required to use it.

$ cd ~/puppet/
$ git add manifests/site.pp
$ git commit -m "adds default node declaration and agent-centos.localdomain"
$ git push origin master
NETWAYS Training

Lab 7.4: Environment Data


Create a parameterized apache configuration using hiera


Enable Hiera for environment data

$ vim ~/puppet/hiera.yaml
---
version: 5
defaults:
  datadir: data
  data_hash: yaml_data
hierarchy:
  - name: "Per-node data (yaml version)"
    path: "nodes/%{trusted.certname}.yaml"

  - name: "Other YAML hierarchy levels"
    paths:
      - "common.yaml"

$ mkdir -p ~/puppet/data/nodes

Enable ssl per default

$ vim ~/puppet/data/common.yaml
---
apache::ssl: true

Add a new parameter to accept a hash of vhosts

Make also sure templates used in apache::vhost point to a relative path if not already changed before.

$ vim ~/puppet/modules/apache/manifests/init.pp
class apache (
  ...
  Hash                     $vhosts      = {},
) inherits apache::params {
...
  $vhosts.each | String $name, Hash $vhost | {
    apache::vhost { $name :
      * => $vhost,
    }
  }
}

Define the vhost hash for your client

$ vim data/nodes/agent-centos.localdomain.yaml
---
apache::vhosts:
  foobar:
    ip: 127.0.0.5

Do not forget to commit and push these changes before testing!

NETWAYS Training

Lab 7.5: Params.pp pattern


Move the params.pp pattern to data in modules


Create a hiera config for your module

$ vim ~/puppet/modules/apache/hiera.yaml
---
version: 5

defaults:
  datadir: 'data'
  data_hash: 'yaml_data'

hierarchy:
  - name: 'Full Version'
    path: '%{facts.os.name}-%{facts.os.release.full}.yaml'

  - name: 'Major Version'
    path: '%{facts.os.name}-%{facts.os.release.major}.yaml'

  - name: 'Distribution Name'
    path: '%{facts.os.name}.yaml'

  - name: 'Operating System Family'
    path: '%{facts.os.family}-family.yaml'

  - name: 'common'
    path: 'common.yaml'

Create hiera data for your supported operating systems

$ mkdir ~/puppet/modules/apache/data
$ vim ~/puppet/modules/apache/data/common.yaml
---
apache::ensure: 'stopped'
apache::enable: false
apache::package_name: 'httpd'
apache::config_dir: '~'
apache::main_config: '~/httpd.conf'
apache::conf_d: '~'
apache::service_name: 'httpd'
apache::ssl: false
$ vim ~/puppet/modules/apache/data/RedHat-family.yaml
---
apache::ensure: 'running'
apache::enable: true
apache::package_name: 'httpd'
apache::config_dir: '/etc/httpd'
apache::main_config: '/etc/httpd/conf/httpd.conf'
apache::conf_d: '/etc/httpd/conf.d'
apache::service_name: 'httpd'
apache::ssl: true
$ vim ~/puppet/modules/apache/data/Debian-family.yaml
---
apache::ensure: 'running'
apache::enable: true
apache::package_name: 'apache2'
apache::config_dir: '/etc/apache2'
apache::main_config: '/etc/apache2/apache2.conf'
apache::conf_d: '/etc/apache2/conf.d'
apache::service_name: 'apache2'
apache::ssl: false

Adjust your base class

$ vim ~/puppet/modules/apache/manifests/init.pp
class apache (
  Enum['running','stopped'] $ensure,
  Boolean                   $enable,
  String                    $package_name,
  String                    $config_dir,
  String                    $main_config,
  String                    $conf_d,
  String                    $service_name,
  Boolean                   $ssl,
) {
...
NETWAYS Training

Lab 8.1: Forge


Install the stdlib module provided by puppetlabs


Search the Forge

$ puppet module search stdlib

Install the module

$ puppet module install --modulepath ~/puppet/modules/ puppetlabs-stdlib

List all installed modules

$ puppet module list --modulepath ~/puppet/modules/
NETWAYS Training

Lab 8.2: PuppetDB


Install PuppetDB using the Puppet module


Install the PuppetDB module

On the puppet master use the puppet module tool to install "puppetlabs-puppetdb" into environment master.

$ sudo puppet module install puppetlabs-puppetdb --modulepath /etc/puppetlabs/code/environments/master/modules

Assign it to your master

Disable the module firewall management and bind it to your public ip address. The master is named "puppetmaster" and we want to have reports stored in puppetdb.

$ sudo vim /etc/puppetlabs/code/environments/master/manifests/site.pp
node 'puppet.localdomain' {
  class { 'puppetdb':
    manage_firewall => false,
    listen_address  => '192.168.56.101',
  }
  class { 'puppetdb::master::config':
    puppet_service_name     => 'puppetmaster',
    manage_report_processor => true,
    enable_reports          => true,
  }
}

Instead of typing all the configuration use the documentation of the module to copy most of it.

Run the puppet agent to install it

$ sudo puppet agent -t
NETWAYS Training

Lab 8.3: Puppet Explorer


Install Puppet Explorer using the Puppet module


Install the Puppet Explorer module

On the puppet master use the puppet module tool to install "spotify-puppetexplorer" and "puppetlabs-apache" into environment "master". The apache module is only an optional dependency so not automatically installed.

Note: PuppetExplorer was first developed by the team at Spotify, and is now maintained by GitHub user "dalen".

$ sudo puppet module install spotify-puppetexplorer -i /etc/puppetlabs/code/environments/master/modules
$ sudo puppet module install puppetlabs-apache -i /etc/puppetlabs/code/environments/master/modules

Assign it to your master

Puppet Explorer simply proxies the query issued to the PuppetDB using Apache. PuppetDB 4.x also introduced a breaking change by moving some query location, compatibilty for this change can be restored by a rewrite rule (https://github.com/dalen/puppetexplorer/issues/49).

$ sudo vim /etc/puppetlabs/code/environments/master/manifests/site.pp
node 'puppet.localdomain' {
  ...
  class { 'puppetexplorer':
    proxy_pass => [
      { 'path' => '/api/pdb/query', 'url' => 'http://192.168.56.101:8080/pdb/query' },
      { 'path' => '/api/pdb/meta', 'url' => 'http://192.168.56.101:8080/pdb/meta' },
      { 'path' => '/api/metrics', 'url' => 'http://192.168.56.101:8080/metrics' }
    ],
    vhost_options => {
      rewrites  => [ { rewrite_rule => ['^/api/metrics/v1/mbeans/puppetlabs.puppetdb.query.population:type=default,name=(.*)$  https://%{HTTP_HOST}/api/metrics/v1/mbeans/puppetlabs.puppetdb.population:name=$1 [R=301,L]'] } ],
    },
  }
}

Instead of typing all the configuration use the documentation of the module and the provided issue to copy most of it.

Run the puppet agent to install it

$ sudo puppet agent -t