How Can We Help?

How To Use Puppet

You are here:
< All Topics

Prerequisite is to have Puppet installed and running, both on the Puppet master server and on the Puppet clients.


See my IT Knowledge Base article on How To Install Puppet Server and Clients  


NOTE: a good source of practical Puppet tips and commands can be found at


Another tip: you can run your Puppet code through a quick syntax check at:



Some basic Puppet concepts




Puppet code is composed mainly of resource declarations which describe the desired state of the system. This can specify such aspects as that a particular user or file should exist, or that a specific software package should be installed.


Resource declarations are formatted thus:


resource_type { ‘resource_name’
attribute => value



For example, we may specify the following user resource declaration:


user { ‘smith’:
ensure => present,
uid => ‘1000’,
gid => ‘1000’,
shell => ‘/bin/bash’,
home => ‘/home/smith’



To list all default Puppet resource types:


puppet resource –types


root@gemini:/opt/puppetlabs/bin# puppet resource –types





Puppet programs are known as manifests. Manifests are composed of puppet code and are saved as files with the .pp filename extension.


The default main manifest in Puppet is located at /etc/puppetlabs/code/environments/production/manifests/site.pp  (for older versions of Puppet at /etc/puppet/manifests/site.pp.)




Puppet classes are code blocks which can be called in code from elsewhere.


Classes allows you re-use Puppet code and make it easier to read manifests.


Class Definition


A class definition contains the code that composes a class.


Defining a class makes the class available for use in manifests.


The format for a class definition is:


class example_class {





This defines a class with the name “example_class”.


Class Declaration


A class declaration occurs when a class is referenced in a manifest. There are two types of class declaration: normal and resource-like.


Puppet evaluates the code within the class.


A normal class declaration occurs when the “include” keyword is referenced in Puppet code, for example:


include example_class


This instructs Puppet to evaluate the code in example_class.


A resource-like class declaration occurs when a class is declared like a resource, thus:


class { ‘example_class’: }


“resource-like” class declarations allow you to specify class parameters. These override the default values of class attributes.





A Puppet module is a collection of manifests and data (which includes such items as facts, files, and templates). These have a specific directory structure.


Modules are used for organizing Puppet code, since they allow you to split up your Puppet code into manageable individual manifests.  Modules are the preferred method for organizing Puppet manifests.


To add a module to Puppet, save it in the /etc/puppetlabs/code/modules directory.


Manifests are defaultwise located under /etc/puppetlabs/code/environments/production/manifests


root@gemini:/etc/puppetlabs/code/environments/production# ll
total 28
drwxr-xr-x 5 root root 4096 Jul 20 15:08 ./
drwxr-xr-x 3 root root 4096 Jul 20 15:08 ../
drwxr-xr-x 2 root root 4096 Jun 14 09:16 data/
-rw-r–r– 1 root root 865 Jun 14 09:16 environment.conf
-rw-r–r– 1 root root 518 Jun 14 09:16 hiera.yaml
drwxr-xr-x 2 root root 4096 Jun 14 09:16 manifests/
drwxr-xr-x 2 root root 4096 Jun 14 09:16 modules/


By default, the main manifest for an environment is set to <ENVIRONMENTS DIRECTORY>/<ENVIRONMENT>/manifests 


For example /etc/puppetlabs/code/environments/production/manifests as above.


You can configure the manifest on a per-environment basis or you can configure a default for all environments.


To determine its main manifest, an environment uses the manifest setting in environment.conf. This can be an absolute path or a path relative to the environment’s main directory.


If the environment.conf manifest setting is absent, then it references the value of the default_manifest setting found in the puppet.conf file. The default_manifest setting defaults to ./manifests.



NOTE: The current puppet environment location is now /etc/puppetlabs. This replaces the older location /etc/puppet which is still found in older Puppet documentation and may be in use on older versions of Puppet.


Resource declarations


Resource declarations are deployed in manifests. Some basic essential resource declarations include:

exec:  for executing commands, such as apt-get
package:  for installing packages via apt
service: to ensure that a service is running
file: to ensure certain files are present




Writing a Puppet manifest


The following is a simple basic example of a configuration manifest.


This will ensure the package hello is available on a puppet client node (puppetclient_intelvpn)



Open a default manifest with the name /etc/puppetlabs/code/environments/production/manifests/site.pp file on the master server and add the following configuration:


nano /etc/puppetlabs/code/environments/production/manifests/site.pp


package { “hello”:
ensure => “installed”



Our agent on client node puppetclient_intelvpn is set by default to retrieve the puppet master configuration every 30 minutes.


If we do not wish to wait for this time period, we can trigger the configuration request manually:



Package hello is currently unavailable on the client. Trigger new configuration request manually with puppet agent –test


First set a symlink for puppet in order to avoid having to enter the full path to puppet for each command:


root@len:~# ln -s /opt/puppetlabs/puppet/bin/puppet /usr/local/sbin/puppet


then do:


root@len:~# puppet agent –test
Info: Using configured environment ‘production’
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Caching catalog for puppetclient_lenvpn
Info: Applying configuration version ‘1626883804’
Notice: /Stage[main]/Main/Package[hello]/ensure: created
Notice: Applied catalog in 18.89 seconds



From the above output we can see that new configuration was applied and that the package “hello” has now been made available by Puppet on the client machine:


root@len:~# hello
Hello, world!



A module to keep ubuntu systems updated:


Sourced from the official puppet forge site at




root@gemini:~# puppet module install puppet-unattended_upgrades –version 5.1.0
Notice: Preparing to install into /etc/puppetlabs/code/environments/production/modules …
Notice: Downloading from …
Notice: Installing — do not interrupt …
└─┬ puppet-unattended_upgrades (v5.1.0)
├─┬ puppetlabs-apt (v7.7.1)
│ └── puppetlabs-translate (v2.2.0)
└── puppetlabs-stdlib (v6.6.0)


then on the clients do:



root@len:~# puppet agent –test
Info: Using configured environment ‘production’
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Loading facts
Info: Caching catalog for puppetclient_lenvpn
Info: Applying configuration version ‘1626886951’
Notice: Applied catalog in 1.97 seconds


then add following to /etc/puppetlabs/code/environments/production/manifests/site.pp

root@gemini:~# nano /etc/puppetlabs/code/environments/production/manifests/site.pp



node ‘puppetclient_lenvpn’ {


include unattended_upgrades




node ‘puppetclient_asusvpn’ {


include unattended_upgrades




node ‘puppetclient_intelvpn’ {


include unattended_upgrades




then run puppet agent –test


on each client:


eg on puppetclient_lenvpn:


then run puppet agent –test


on puppetclient_lenvpn:


root@len:/etc/apt# puppet agent –test


Info: Using configured environment ‘production’
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Loading facts
Info: Caching catalog for puppetclient_lenvpn
Info: Applying configuration version ‘1626888562’

Notice: /Stage[main]/Apt/File[preferences]/ensure: created
Info: /Stage[main]/Apt/File[preferences]: Scheduling refresh of Class[Apt::Update]
Notice: /Stage[main]/Apt/Apt::Setting[conf-update-stamp]/File[/etc/apt/apt.conf.d/15update-stamp]/content:
— /etc/apt/apt.conf.d/15update-stamp 2013-07-12 19:07:51.000000000 +0200
+++ /tmp/puppet-file20210721-829078-1iei017 2021-07-21 19:29:35.053955262 +0200
@@ -1 +1,2 @@
+// This file is managed by Puppet. DO NOT EDIT.
APT::Update::Post-Invoke-Success {“touch /var/lib/apt/periodic/update-success-stamp 2>/dev/null || true”;};

Notice: /Stage[main]/Apt/Apt::Setting[conf-update-stamp]/File[/etc/apt/apt.conf.d/15update-stamp]/content: content changed ‘{sha256}174cdb519fd06372847e23c20db869b2ff4b593252c0d2a6274d770eae2d92c9’ to ‘{sha256}2e6eb1f5f20262bfc6b7dfb26a302f00b4ab5fee803abd9e07ad8378cce067d5’
Info: /Stage[main]/Apt/Apt::Setting[conf-update-stamp]/File[/etc/apt/apt.conf.d/15update-stamp]: Scheduling refresh of Class[Apt::Update]
Info: Class[Apt::Update]: Scheduling refresh of Exec[apt_update]
Notice: /Stage[main]/Apt::Update/Exec[apt_update]: Triggered ‘refresh’ from 1 event
Notice: /Stage[main]/Unattended_upgrades/Apt::Conf[unattended-upgrades]/Apt::Setting[conf-unattended-upgrades]/File[/etc/apt/apt.conf.d/50unattended-upgrades]/content:
— /etc/apt/apt.conf.d/50unattended-upgrades 2020-04-14 00:37:21.000000000 +0200
+++ /tmp/puppet-file20210721-829078-px7grn 2021-07-21 19:29:56.030091041 +0200
@@ -1,3 +1,4 @@
+// This file is managed by Puppet. DO NOT EDIT.
// Automatically upgrade packages from these (origin:archive) pairs
// Note that in Ubuntu security updates may pull in new dependencies
@@ -6,126 +7,49 @@
Unattended-Upgrade::Allowed-Origins {
– // Extended Security Maintenance; doesn’t necessarily exist for
– // every release and this system may not have it installed, but if
– // available, the policy for updates is such that unattended-upgrades
– // should also install from here by default.
– “${distro_id}ESMApps:${distro_codename}-apps-security”;
– “${distro_id}ESM:${distro_codename}-infra-security”;
-// “${distro_id}:${distro_codename}-updates”;
-// “${distro_id}:${distro_codename}-proposed”;
-// “${distro_id}:${distro_codename}-backports”;

-// Python regular expressions, matching packages to exclude from upgrading
+// List of packages to not update (regexp are supported)
Unattended-Upgrade::Package-Blacklist {
– // The following matches all packages starting with linux-
-// “linux-“;

– // Use $ to explicitely define the end of a package name. Without
– // the $, “libc6” would match all of them.
-// “libc6$”;
-// “libc6-dev$”;
-// “libc6-i686$”;

– // Special characters need escaping
-// “libstdc\+\+6$”;

– // The following matches packages like xen-system-amd64, xen-utils-4.1,
– // xenstore-utils and libxenstore3.0
-// “(lib)?xen(store)?”;

– // For more information about Python regular expressions, see
– //

-// This option controls whether the development release of Ubuntu will be
-// upgraded automatically. Valid values are “true”, “false”, and “auto”.
-Unattended-Upgrade::DevRelease “auto”;
+// List of days in the week that updates should be applied.
+// The days can be specified as localized abbreviated or full names.
+// Or as integers where “0” is Sunday, “1” is Monday etc.
+// Require Unattended-upgrades version >=0.91 to work, else it is ignored
+Unattended-Upgrade::Update-Days {

// This option allows you to control if on a unclean dpkg exit
-// unattended-upgrades will automatically run
+// unattended-upgrades will automatically run
// dpkg –force-confold –configure -a
// The default is true, to ensure updates keep getting installed
-//Unattended-Upgrade::AutoFixInterruptedDpkg “true”;
+Unattended-Upgrade::AutoFixInterruptedDpkg “true”;

// Split the upgrade into the smallest possible chunks so that
// they can be interrupted with SIGTERM. This makes the upgrade
// a bit slower but it has the benefit that shutdown while a upgrade
// is running is possible (with a small delay)
-//Unattended-Upgrade::MinimalSteps “true”;
+Unattended-Upgrade::MinimalSteps “true”;

-// Install all updates when the machine is shutting down
-// instead of doing it in the background while the machine is running.
-// This will (obviously) make shutdown slower.
-// Unattended-upgrades increases logind’s InhibitDelayMaxSec to 30s.
-// This allows more time for unattended-upgrades to shut down gracefully
-// or even install a few packages in InstallOnShutdown mode, but is still a
-// big step back from the 30 minutes allowed for InstallOnShutdown previously.
-// Users enabling InstallOnShutdown mode are advised to increase
-// InhibitDelayMaxSec even further, possibly to 30 minutes.
-//Unattended-Upgrade::InstallOnShutdown “false”;

-// Send email to this address for problems or packages upgrades
-// If empty or unset then no email is sent, make sure that you
-// have a working mail setup on your system. A package that provides
-// ‘mailx’ must be installed. E.g. “”
-//Unattended-Upgrade::Mail “”;

-// Set this value to one of:
-// “always”, “only-on-error” or “on-change”
-// If this is not set, then any legacy MailOnlyOnError (boolean) value
-// is used to chose between “only-on-error” and “on-change”
-//Unattended-Upgrade::MailReport “on-change”;

-// Remove unused automatically installed kernel-related packages
-// (kernel images, kernel headers and kernel version locked tools).
-//Unattended-Upgrade::Remove-Unused-Kernel-Packages “true”;
+// Install all unattended-upgrades when the machine is shuting down
+// instead of doing it in the background while the machine is running
+// This will (obviously) make shutdown slower
+Unattended-Upgrade::InstallOnShutdown “false”;

-// Do automatic removal of newly unused dependencies after the upgrade
-//Unattended-Upgrade::Remove-New-Unused-Dependencies “true”;

-// Do automatic removal of unused packages after the upgrade
+// Do automatic removal of new unused dependencies after the upgrade
// (equivalent to apt-get autoremove)
-//Unattended-Upgrade::Remove-Unused-Dependencies “false”;
+Unattended-Upgrade::Remove-Unused-Dependencies “true”;

-// Automatically reboot *WITHOUT CONFIRMATION* if
-// the file /var/run/reboot-required is found after the upgrade
-//Unattended-Upgrade::Automatic-Reboot “false”;

-// Automatically reboot even if there are users currently logged in
-// when Unattended-Upgrade::Automatic-Reboot is set to true
-//Unattended-Upgrade::Automatic-Reboot-WithUsers “true”;
+// Automatically reboot *WITHOUT CONFIRMATION*
+// if the file /var/run/reboot-required is found after the upgrade
+Unattended-Upgrade::Automatic-Reboot “false”;

// If automatic reboot is enabled and needed, reboot at the specific
// time instead of immediately
// Default: “now”
-//Unattended-Upgrade::Automatic-Reboot-Time “02:00”;

-// Use apt bandwidth limit feature, this example limits the download
-// speed to 70kb/sec
-//Acquire::http::Dl-Limit “70”;

-// Enable logging to syslog. Default is False
-// Unattended-Upgrade::SyslogEnable “false”;

-// Specify syslog facility. Default is daemon
-// Unattended-Upgrade::SyslogFacility “daemon”;

-// Download and install upgrades only on AC power
-// (i.e. skip or gracefully stop updates on battery)
-// Unattended-Upgrade::OnlyOnACPower “true”;

-// Download and install upgrades only on non-metered connection
-// (i.e. skip or gracefully stop updates on a metered connection)
-// Unattended-Upgrade::Skip-Updates-On-Metered-Connections “true”;

-// Verbose logging
-// Unattended-Upgrade::Verbose “false”;

-// Print debugging information both in unattended-upgrades and
-// in unattended-upgrade-shutdown
-// Unattended-Upgrade::Debug “false”;
+Unattended-Upgrade::Automatic-Reboot-Time “now”;

-// Allow package downgrade if Pin-Priority exceeds 1000
-// Unattended-Upgrade::Allow-downgrade “false”;


Notice: /Stage[main]/Unattended_upgrades/Apt::Conf[unattended-upgrades]/Apt::Setting[conf-unattended-upgrades]/File[/etc/apt/apt.conf.d/50unattended-upgrades]/content: content changed ‘{sha256}84fb166f00cc1a0dab73075e6fc9cdbc265ef47dc6d0fe9bdfb054c76cf94947’ to ‘{sha256}06e08c106ff275615b3211d9079c389de4c0dc141d76596acf04197977e00360’
Notice: /Stage[main]/Unattended_upgrades/Apt::Conf[periodic]/Apt::Setting[conf-periodic]/File[/etc/apt/apt.conf.d/10periodic]/content:
— /etc/apt/apt.conf.d/10periodic 2013-07-12 19:07:51.000000000 +0200
+++ /tmp/puppet-file20210721-829078-1t795c6 2021-07-21 19:29:56.082091378 +0200
@@ -1,3 +1,49 @@
+// This file is managed by Puppet. DO NOT EDIT.
+APT::Periodic::Enable “1”;
+# – Enable the update/upgrade script (0=disable)
+APT::Periodic::BackupArchiveInterval “0”;
+# – Backup after n-days if archive contents changed.(0=disable)
+APT::Periodic::BackupLevel “3”;
+# – Backup level.(0=disable), 1 is invalid.
+APT::Periodic::MaxAge “0”;
+# – Set maximum allowed age of a cache package file. If a cache
+# package file is older it is deleted (0=disable)
+APT::Periodic::MinAge “2”;
+# – Set minimum age of a package file. If a file is younger it
+# will not be deleted (0=disable). Usefull to prevent races
+# and to keep backups of the packages for emergency.
+APT::Periodic::MaxSize “0”;
+# – Set maximum size of the cache in MB (0=disable). If the cache
+# is bigger, cached package files are deleted until the size
+# requirement is met (the biggest packages will be deleted
+# first).
APT::Periodic::Update-Package-Lists “1”;
+# – Do “apt-get update” automatically every n-days (0=disable)
APT::Periodic::Download-Upgradeable-Packages “0”;
+# – Do “apt-get upgrade –download-only” every n-days (0=disable)
+APT::Periodic::Download-Upgradeable-Packages-Debdelta “1”;
+# – Use debdelta-upgrade to download updates if available (0=disable)
+APT::Periodic::Unattended-Upgrade “1”;
+# – Run the “unattended-upgrade” security upgrade script
+# every n-days (0=disabled)
+# Requires the package “unattended-upgrades” and will write
+# a log in /var/log/unattended-upgrades
APT::Periodic::AutocleanInterval “0”;
+# – Do “apt-get autoclean” every n-days (0=disable)
+APT::Periodic::Verbose “0”;
+# – Send report mail to root
+# 0: no report (or null string)
+# 1: progress report (actually any string)
+# 2: + command outputs (remove -qq, remove 2>/dev/null, add -d)
+# 3: + trace on


Notice: /Stage[main]/Unattended_upgrades/Apt::Conf[periodic]/Apt::Setting[conf-periodic]/File[/etc/apt/apt.conf.d/10periodic]/content: content changed ‘{sha256}82d9ce0d3f5d2c66945d8a1267445e9955409d1ef3cb9934107adccb46bf5f6a’ to ‘{sha256}2219f593d53546c3025c126a2afb1e799dcb073bb51a2d8cd2648070bbbf8d5f’
Notice: /Stage[main]/Unattended_upgrades/Apt::Conf[auto-upgrades]/Apt::Setting[conf-auto-upgrades]/File[/etc/apt/apt.conf.d/20auto-upgrades]/ensure: removed
Notice: /Stage[main]/Unattended_upgrades/Apt::Conf[options]/Apt::Setting[conf-options]/File[/etc/apt/apt.conf.d/10options]/ensure: defined content as ‘{sha256}fbee4ce1e047bf061a71232f5a0f6e373beec970e5afa52ae5bae984cb7d6f61’
Notice: Applied catalog in 22.72 seconds




To instruct Puppet to ensure a systemd service is running


In this example we want to make sure the sshd server service is active:


first of all, we set the following in the site.pp:


root@gemini:~# nano /etc/puppetlabs/code/environments/production/manifests/site.pp


node default {
# include module_name
# include ssh


service {‘ssh’:
ensure => running,
enable => true,
hasstatus => true,
hasrestart => true,


then install a relevant puppet module for ssh.


We obtain this from the official forge puppet repository site


We are going to use the module from ghoneycutt, available from


So on our puppet master server we do:


puppet module install ghoneycutt-ssh –version 3.62.0   


(the command can be found on the URL page above)


this will download and install the ssh puppet module directly onto our server.


Then, on the clients we can do the following:


Note: to test that the manifest does actually start ssh if it is not running we have first stopped ssh on the client.


As can be seen in the output result, puppet has started ssh on the client successfully:


root@asus:~# puppet agent –test
Info: Using configured environment ‘production’
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Loading facts
Info: Caching catalog for puppetclient_asusvpn
Info: Applying configuration version ‘1626892938’
Notice: /Stage[main]/Main/Node[default]/Service[ssh]/ensure: ensure changed ‘stopped’ to ‘running’ (corrective)
Info: /Stage[main]/Main/Node[default]/Service[ssh]: Unscheduling refresh on Service[ssh]
Notice: Applied catalog in 0.38 seconds



root@asus:~# systemctl status ssh
● ssh.service – OpenBSD Secure Shell server
Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
Active: active (running) since Wed 2021-07-21 20:42:19 CEST; 8min ago
Docs: man:sshd(8)
Process: 2757294 ExecStartPre=/usr/sbin/sshd -t (code=exited, status=0/SUCCESS)
Main PID: 2757295 (sshd)
Tasks: 1 (limit: 21459)
Memory: 4.3M
CGroup: /system.slice/ssh.service
└─2757295 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups

Table of Contents