Using Variables In File Paths With Puppet

LoopedNetwork
3 min readMar 20, 2022

--

Lately I’ve been working on beefing up my Puppet deployment at work. To avoid boiling the ocean, I initially started with the “minimum viable product” approach and slowly building out from there. Previously, I quickly discovered that, while trying to accomplish everything that I wanted out of the gate, I was getting nowhere fast with having my Puppet infrastructure in a place where I felt comfortable enrolling existing, established hosts. While working on customizing some LibreNMS deployments, I wanted to control the logo and favicon in use. While these are largely the same across the majority of the servers I manage, there are a handful with one-off imagery, so I wanted to figure out how to best handle that scenario. In the course of researching the solution, I found some conflicting information, which seemed to be mostly related to different versions of Puppet itself. At any rate, I figured a post on how this can be done in 2022 seemed pertinent.

Initially, I had just hard-coded my module for LibreNMS (I’m not using a Forge module today because the majority of the configuration is handled in our base image… for now) to copy the header and favicon content to the the host at: /opt/librenms/html/images/custom/. Then I used file_line in Puppet to ensure the lines starting with $config[’favicon’] and $config[’title_image’] pointed to those files. Here’s a sample of just the parts for the favicon in my config.pp file:

file { '/opt/librenms/html/images/custom/favicon.ico':
source => 'puppet:///modules/librenms/favicon.ico',
owner => $::librenms::libre_user,
group => $::librenms::libre_group,
mode => $::librenms::libre_permission,
require => File['/opt/librenms/html/images/custom'],
}file_line { 'libre_favicon':
path => '/opt/librenms/config.php',
match => '^\$config\[\'favicon\'\] = *',
line => '$config[\'favicon\'] = "images/custom/favicon.ico";',
require => File['/opt/librenms/html/images/custom/favicon.ico'],
}

Note: I was originally hung up for way longer than I care to admit getting my match line to work properly. Remember that square brackets [ ] are also special characters in regular expressions which need to be escaped!

As you can see, I’m already using variables when copying the favicon file over so that I can customize things like the user, group, and permissions on the file. These are done by making the overall class parameterized, with default values I can override as needed. I figured I could simply do the same with the files I needed to copy and apply. Here is my original init.pp file:

class librenms (
$libre_user = 'librenms',
$libre_group = 'librenms',
$libre_permission = '0664'
){
include librenms::config
}

It was easy enough to add another couple of parameters I could override in my node definition if needed for the imagery:

class librenms (
$libre_user = 'librenms',
$libre_group = 'librenms',
$libre_permission = '0664',
$favicon_name = 'favicon.ico',
$title_image_name = 'default_logo.png'
){
include librenms::config
}

That just left me with a need to figure out how to reference that new variable inside of another string so they could be concatenated together. In other parts of my config.pp file, I was able to use those values in a standalone fashion via:

$::librenms::libre_user

It turns out that — from a syntax perspective — string interpolation in Puppet is exactly the same as in Groovy, which I also use heavily at work. The curly braces ${ } are the key for denoting an expression is contained within which should be evaluated. The other important part is that, subconsciously, I tend to use single quotes in strings without any expressions so the interpreter knows it has nothing to look for, such as in the lines above where I copied the hard-coded files from the Puppet master to the target hosts. These need to be switched to double-quotes for interpolation to happen. The variables simply need to then be referenced inside of the curly braces in the exact same format I would use when calling them by themselves. Here’s an example of what the favicon copy and application in config.php looks like after the fact:

file { "/opt/librenms/html/images/custom/${::librenms::favicon_name}":
source => "puppet:///modules/librenms/${::librenms::favicon_name}",
owner => $::librenms::libre_user,
group => $::librenms::libre_group,
mode => $::librenms::libre_permission,
require => File['/opt/librenms/html/images/custom']
}
file_line { 'favicon':
line => "\$config[\'favicon\'] = \"images/custom/${::librenms::favicon_name}\";",
match => '^\$config\[\'favicon\'\]',
path => '/opt/librenms/config.php',
require => File["/opt/librenms/html/images/custom/${::librenms::favicon_name}"]
}

This does the job of ensuring that I only copy the appropriate file and set that same file to be used in the LibreNMS configuration.

--

--

LoopedNetwork
LoopedNetwork

No responses yet