Language: Using templates

Templates are documents that combine code, data, and literal text to produce a final rendered output. The goal of a template is to manage a complicated piece of text with simple inputs.

In Puppet, you’ll usually use templates to manage the content of configuration files (via the content attribute of the file resource type).

Templating languages

Templates are written in a templating language, which is specialized for generating text from data.

Puppet supports two templating languages:

  • Embedded Puppet (EPP) uses Puppet expressions in special tags. It’s easy for any Puppet user to read, but only works with newer Puppet versions. (≥ 4.0, or late 3.x versions with future parser enabled.)
  • Embedded Ruby (ERB) uses Ruby code in tags. You need to know a small bit of Ruby to read it, but it works with all Puppet versions.

Using templates

Once you have a template, you can pass it to a function that will evaluate it and return a final string.

The actual template can be either a separate file or a string value. You’ll use different functions depending on the template’s language and how the template is stored.

Template Language File String
Embedded Puppet (EPP) epp inline_epp
Embedded Ruby (ERB) template inline_template

With a template file: template and epp

You can put template files in the templates directory of a module. EPP files should have the .epp extension, and ERB files should have the .erb extension.

To use a template file, evaluate it with the template (ERB) or epp function:

# epp(<FILE REFERENCE>, [<PARAMETER HASH>])
file { '/etc/ntp.conf':
  ensure  => file,
  content => epp('ntp/ntp.conf.epp', {'service_name' => 'xntpd', 'iburst_enable' => true}),
  # Loads /etc/puppetlabs/code/environments/production/modules/ntp/templates/ntp.conf.epp
}

# template(<FILE REFERENCE>, [<ADDITIONAL FILES>, ...])
file { '/etc/ntp.conf':
  ensure  => file,
  content => template('ntp/ntp.conf.erb'),
  # Loads /etc/puppetlabs/code/environments/production/modules/ntp/templates/ntp.conf.erb
}

Referencing files

The first argument to these functions should be a string like '<MODULE>/<FILE>', which will load <FILE> from <MODULE>’s templates directory.

File Reference Actual File
ntp/ntp.conf.epp <MODULES DIRECTORY>/ntp/templates/ntp.conf.epp
activemq/amq/activemq.xml.erb <MODULES DIRECTORY>/activemq/templates/amq/activemq.xml.erb

EPP parameters

EPP templates can declare parameters, and you can provide values for them by passing a parameter hash to the epp function.

The keys of the hash must be valid local variable names (minus the $). Inside the template, Puppet will create variables with those names and assign their values from the hash. (With a parameter hash of {'service_name' => 'xntpd', 'iburst_enable' => true}, an EPP template would receive variables called $service_name and $iburst_enable.)

  • If a template declares any mandatory parameters, you must set values for them with a parameter hash.
  • If a template declares any optional parameters, you can choose to provide values or let them use their defaults.
  • If a template declares no parameters, you can pass any number of parameters with any names; otherwise, you can only choose from the parameters requested by the template.

Extra ERB files

The template function can take any number of additional template files, and will concatenate their outputs together to produce the final string.

With a template string: inline_template and inline_epp

If you have a string value that contains template content, you can evaluate it with the inline_template (ERB) or inline_epp functions as follows:

$ntp_conf_template = @(END)
...template content goes here...
END

# inline_epp(<TEMPLATE STRING>, [<PARAMETER HASH>])
file { '/etc/ntp.conf':
  ensure  => file,
  content => inline_epp($ntp_conf_template, {'service_name' => 'xntpd', 'iburst_enable' => true}),
}

# inline_template(<TEMPLATE STRING>, [<ADDITIONAL STRINGS>, ...])
file { '/etc/ntp.conf':
  ensure  => file,
  content => inline_template($ntp_conf_template),
}

In older versions of Puppet, inline_template was mostly used to get around limitations — tiny Ruby fragments were useful for transforming and manipulating data before Puppet had iteration functions like map or puppetlabs/stdlib functions like chomp and keys.

In modern versions of Puppet, inline templates are usable in some of the same situations template files are. Since the heredoc syntax makes it easy to write large and complicated strings in a manifest, you can use inline_template and inline_epp to reduce the number of files needed for a simple module that manages a small config file.

EPP parameters

EPP templates can declare parameters, and you can provide values for them by passing a parameter hash to the epp function.

The keys of the hash must be valid local variable names (minus the $). Inside the template, Puppet will create variables with those names and assign their values from the hash. (With a parameter hash of {'service_name' => 'xntpd', 'iburst_enable' => true}, an EPP template would receive variables called $service_name and $iburst_enable.)

  • If a template declares any mandatory parameters, you must set values for them with a parameter hash.
  • If a template declares any optional parameters, you can choose to provide values or let them use their defaults.
  • If a template declares no parameters, you can pass any number of parameters with any names; otherwise, you can only choose from the parameters requested by the template.

Extra ERB strings

The inline_template function can take any number of additional template strings, and will concatenate their outputs together to produce the final value.

Validating and previewing templates

Before deploying a template, you should validate its syntax and render its output to ensure it’s producing the results you expect.

Puppet includes the puppet epp command-line tool for EPP templates, while Ruby can check ERB syntax after trimming the template with its erb command.

EPP validation

puppet epp validate <TEMPLATE NAME>

The puppet epp command includes an action that checks EPP code for syntax problems. The <TEMPLATE NAME> can be a file reference or can refer to a <MODULE NAME>/<TEMPLATE FILENAME> as the epp function. If a file reference can also refer to a module, Puppet validates the module’s template instead.

You can also pipe EPP code directly to the validator:

cat example.epp | puppet epp validate

The command is silent on a successful validation. It reports and halts on the first error it encounters; to modify this behavior, check the command’s man page.

EPP rendering

puppet epp render <TEMPLATE NAME>

You can render EPP from the command line with puppet epp render. If Puppet can evaluate the template, it outputs the result.

If your template relies on specific parameters or values to function, you can simulate those values by passing a hash to the --values option:

puppet epp render example.epp --values '{x => 10, y => 20}'

You can also render inline EPP by using the -e flag or piping EPP code to puppet epp render, and even simulate facts using YAML. For details, see the command’s man page.

ERB validation

erb -P -x -T '-' example.erb | ruby -c

You can use Ruby to check the syntax of ERB code by piping output from the erb command into ruby. The -P switch ignores lines that start with ‘%’, the -x switch outputs the template’s Ruby script, and the -T '-' sets the trim mode to be consistent with Puppet’s behavior. This output gets piped into Ruby’s syntax checker (-c).

If you need to validate many templates quickly, you can implement this command as a shell function in your shell’s login script, such as .bashrc, .zshrc, or .profile:

validate_erb() {
  erb -P -x -T '-' $1 | ruby -c
}

You can then use validate_erb example.erb to validate an ERB template.

When to use (and not use) templates

Templates are more powerful than normal strings, and less powerful than modeling individual settings as resources.

Strings in the Puppet language already allow you to interpolate variables and expressions into text. So if you’re managing a short and simple config file, you can often use a heredoc and interpolate a few variables, or even something like ${ $my_array.join(', ') }.

However, if you’re doing complex transformations (especially iterating over collections) or working with very large config files, you might need the extra capabilities of a template.

Finally: Sometimes you end up in a situation where several different modules need to manage parts of the same config file, which is incredibly difficult to coordinate with either templates or interpolated strings. For shared configuration like this, you should try to model each setting in that file as an individual resource, with either a custom resource type or an Augeas, concat, or file_line resource. (This approach is similar to how core resource types like ssh_authorized_key and mount work.)