Puppet Code by Example: Part 3

John Tucker
codeburst
Published in
5 min readNov 20, 2020

--

More on Hiera and a quick bit on files/templates.

This is part of a series of articles starting with Puppet Code by Example: Part 1. The final body of Puppet Code developed through this series is available for download.

Parameterized Modules

In the previous article, we parameterized a class, my_parameters::my_class, in a module. The obvious problem with this approach is that this class is an implementation detail of the module and as such should not be exposed outside of the module, i.e., we had to supply the parameter using Hiera as
my_parameters::my_class::greeting: Hola Mundo.

Here we will rather parameterize the module and use that parameter in the class. Let us first create a parametrized module by creating a my_parameters_refactor module with the classes my_class and the usual main (my_parameters_refactor) class.

We update modules/my_parameters_refactor/manifests/my_class.pp:

Things to observe:

  • Notice that the class itself is not parameterized; rather it refers to a variable in the module itself (see below)

We update modules/my_parameters_refactor/manifests/init.pp:

As usual, we include the module in manifests/site.pp.

Finally, we supply the data for the module’s parameter in data/common.yaml

The Hierarchy

In the previous examples, our use of parameters did not really do terribly much; i.e., we simply moved the string constant from within the class out to a Hiera data file. Every agent system, in our case the Debian and the Red Hat systems, got the same data and thus the same files created in their tmp folders.

Here we will explore how we can leverage Hiera’s hierarchy to provide different data to different systems. The behavior of the production environment’s Hiera behavior is defined in the file; hiera.yaml:

Hiera’s behavior is defined by the ordered list of hierarchy entries.

After Hiera replaces the variables to make a list of concrete data sources, it checks those data sources in the order they were written.

Generally, if a data source doesn’t exist, or doesn’t specify a value for the current key, Hiera skips it and moves on to the next source, until it finds one that exists — then it uses it.

— Puppet — About Hiera

Here we can create files named with the certificate names of the agent system to supply per-system (node) data values. To obtain these certificate names, we can execute the following command on the Puppet server:

$ puppetserver ca list --all
Signed Certificates:
agent-rh.us-central1-a.c.bustling-tree-294319.internal (SHA256) E4:06:CB:B1:38:E8:5B:FF:DB:8E:F3:0F:AA:9D:4F:35:24:5E:8D:D3:DC:9E:10:8E:A4:10:50:97:BC:83:18:14
server.us-central1-a.c.bustling-tree-294319.internal (SHA256) 47:38:0B:FF:65:73:A7:37:F4:8E:02:1C:68:7C:37:96:1E:F6:B5:CE:AC:BF:9F:7E:C4:02:4F:D7:01:AC:DF:43 alt names: ["DNS:puppet", "DNS:server.us-central1-a.c.bustling-tree-294319.internal"] authorization extensions: [pp_cli_auth: true]
agent-deb.us-central1-a.c.bustling-tree-294319.internal (SHA256) 5E:B1:2A:88:E4:23:43:B3:86:79:7D:EE:E7:A9:E2:C3:9A:F1:1B:1C:34:24:9F:D6:F2:8C:58:1C:77:15:BB:DC

In my particular case, I created a file; data/nodes/agent-deb.us-central1-a.c.bustling-tree-294319.internal.yaml:

and the file; data/nodes/agent-rh.us-central1-a.c.bustling-tree-294319.internal.yaml:

With this in place, each agent system gets a different value and thus different content in the file /tmp/my_parameters_refactor.

More on the Hierarchy

In the previous example, we simply used the existing Hiera production environment configuration, hiera.yaml, that was scaffolded as part of the Puppet server installation. The true power of Hiera is realized when one configures Hiera based on organization-specific hierarchies as illustrated by the following more complex example hiera.yaml file provided by Puppet.

Things to observe:

  • In addition to targeting individual agent systems (nodes) as we did in the previous example, this example illustrates the use of facts (both built-in and custom) to target data

Also, so far we have been focused only on the production environment Hiera configuration. In fact, Puppet has three layers of configuration.

Hiera uses three independent layers of configuration. Each layer has its own hierarchy, and they’re linked into one super-hierarchy before doing a lookup.

The three layers are searched in the following order: global => environment => module. Hiera searches every data source in the global layer’s hierarchy before checking any source in the environment layer.

— Puppet — About Hiera

Files

Changing gears a bit, let us focus back on the file resource type. In the previous examples, we have been supplying the contents inline; this time we will supply it using a file.

Let us first create a module, my_files_templates, with a class my_files and the usual main (my_files_templates) class.

We update modules/my_files_templates/manifests/my_files.pp:

Things to observe:

  • Notice that the file is referenced using a Puppet URL. The URL resolves to a named file, hello-file, in the module’s files folder

We round out this feature by creating the file modules/my_file_templates/files/hello_file:

Templates

In this example, we will create an Embedded Puppet (EPP) template as the content for our file resource.

We add a class, my_templates, to the my_files_templates module. We also include the new class in the module’s main class.

We update modules/my_files_templates/manifests/my_templates.pp:

Things to observe:

  • Interestingly enough here, we do not use the Puppet URL format, but the format <MODULE>/<TEMPLATE>; this time referencing an EPP template in the module’s template folder

We round out this feature by creating the file modules/my_file_templates/templates/hello_template.epp:

Things to observe:

  • Here we used a simple EPP template expression to insert a fact; hostname

Roles and Profiles

So far we have only used modules in their classes to deliver a focused set of resources; let’s call these component modules. One common approach to allocating an increasingly complex set of component modules to agent systems (nodes) is to introduce additional layers of modules.

The roles and profiles method is the most reliable way to build reusable, configurable, and refactorable system configurations.

Roles and profiles are two extra layers of indirection between your node classifier and your component modules.

The roles and profiles method separates your code into three levels:
- Component modules — Normal modules that manage one particular technology, for example puppetlabs/apache.
- Profiles — Wrapper classes that use multiple component modules to configure a layered technology stack.
- Roles — Wrapper classes that use multiple profiles to build a complete system configuration.

— Puppet — The Roles and Profiles Method

I will leave it to the reader to further explore this topic on their own.

Wrap Up

Obviously, there is more to learn, for example, we only used one resource type throughout this series. At the same time, looking back at the Puppet documentation we can see that we touched on most of the core topics through this series of articles.

--

--