Puppet for Complete Beginners

Posted by Alexander Todorov on Tue 01 March 2016

I guess everyone knows what Puppet is but probably not everyone knows how to write Puppet modules. This article outlines what I've learned while adding a new module to an existing Puppet configuration without having any previous knowledge beforehand and not reading the official documentation (which I should have done).

Existing setup

The existing setup is a single git repository, which holds all of the Puppet configuration for all hosts and environments. The main directory inside the repo is puppet/modules. I wanted to add a few Python scripts which automate tasks inside YouTrack.

What to do next

First step in understanding Puppet was to figure out what do I need to do ?

  • Have my scripts inside the repository;
  • Provide configuration file for credentials;
  • Configure cron jobs;
  • Install all of this on one of the existing systems.

My directory layout looks like this


files/ is where all the scripts go. They can be accessed from here later on. manifests/init.pp is the definition of this module - what gets installed where and so on. templates/ is where templates go. These are usually config files which use a placeholder for their values.

My files/archive is a simple executable Python script, which queries YouTrack for old issues and archives them. It looks for a youtrack.conf file at a pre-defined location (the location on the host system) or at environment variables for testing purposes.

templates/youtrack.conf.erb looks like this

url  = <%= scope.lookupvar('common::vars::youtrack_url') %>
user = <%= scope.lookupvar('common::vars::youtrack_user') %>
pass = <%= scope.lookupvar('common::vars::youtrack_pass') %>

manifests/init.pp looks like this

class youtrack {
  $youtrack_files =

  file { '/opt/youtrack.conf':
    content => template('youtrack/youtrack.conf.erb'),

  cron { 'Archive issues older than 2 weeks':
    ensure      => present,
    command     => "cd ${youtrack_files} && ${youtrack_files}/archive",
    environment => [ 'MAILTO=devops@example.com' ],
    user        => 'root',
    minute      => 0,
    hour        => 8,

Once Puppet applies this configuration on the host system it will

  • Install the configuration template under /opt/youtrack.conf replacing the placeholder variables with actual values. Notice the argument to template() - it's of the form module_name/file_name;
  • Add a cron job entry for my Python script.

NOTE: The host system is the Puppet master so I don't really need to install my Python scripts into another location. I could if I wanted to but this isn't necessary. Cron is executing the script from inside the git checkout.

Bundle it all together

Our module is done. Now we need to instruct Puppet that we want to use it. I have a puppet/modules/role/manifests/pmaster.pp which defines what modules get used on the Puppet master machine. pmaster matches the hostname of the system (that's how it's been configured to work). The module looks like this

class role::pmaster {
  include youtrack


There is also a puppet/modules/common/manifests/vars-static.pp file which defines all the variables used in the templates. Simply add the necessary ones at the bottom:

@@ -197,4 +197,9 @@
+  # YouTrack automation
+  $youtrack_url  = 'http://example.com'
+  $youtrack_user = 'changeMe'
+  $youtrack_pass = 'changeMe'

NOTE: in reality this file is just a placeholder. The real values are not stored in git but are configured manually on systems which need them. On the Puppet master there are separate XXX-vars.pp files for different environments like devel, staging and production.

Please support this blog by buying something from Amazon with my referral id .

Comments !