Skip to Content

Puppet: Evaluating defined types for each element in an array in a non-brittle way

Posted on    2 mins read

In one of the JourneyMonitor puppet manifests, we have a defined type that allows us to dynamically create cronjob files on target machines:

define createCronjobFile {
  file { "/etc/cron.d/journeymonitor-${name}":
    owner   => "root",
    group   => "root",
    mode    => "0644",
    content => template("cronjobs/etc/cron.d/journeymonitor-${name}.erb"),

Let’s assume that we have a system where we want to end up having cronjobs /etc/cron.d/journeymonitor-foo and /etc/cron.d/journeymonitor-bar.

This is achieved by defining an array in Hiera like this:

  - foo
  - bar

and then passing it to createCronjobFile upon declaration:

$cronjobs = hiera_array("cronjobs");

createCronjobFile { $cronjobs: }

The nice thing here is that Puppet unfolds the array for us, that is, we do not need to loop over the array entries explicitly – instead, createCronjobFile is automatically evaluated once for each entry in the array.

(In case this isn’t clear: passing a value before the colon when declaring a defined type makes this value available as parameter $name in the defined type.)

However, this implementation is brittle. If, for a given target system, no cronjobs array has been defined in the Hiera structure, then the above manifest will fail with Could not find data item cronjobs in any Hiera data file and no default supplied.

The solution is to make hiera_array return an empty array if cronjobs is not defined (by providing the empty array [] as the second parameter to hiera_array), and to check for an empty array before declaring createCronjobFile:

$cronjobs = hiera_array("cronjobs");

if $cronjobs != [] {
  createCronjobFile { $cronjobs: }

The if clause is necessary because an empty array unfortunately doesn’t result in createCronjobFile being declared zero times, as one might expect – it is declared with the empty string for parameter $name, which isn’t what we want.