Developing/Testing Custom Facts for Puppet

Michael Sharpe
4 min readNov 26, 2020

An important part of any puppet deployment are custom facts. Facts identify specific attributes about the machines in your environment. For example, a fact might indicate if a specific application is installed, another may identify the version of that application. Some facts are built in and available by default, for example OS information. Other facts are too specific to your environment and facts must be created and deployed. One of the best ways to create facts in puppet is to use custom facts. These facts are written in Ruby scripts using the Facter API.

I am a senior architect at a SaaS software company. I oversee the monitoring of all of our production hardware, database and other equipment, virtual and physical. We use puppet to automatically deploy all monitoring technology. As soon as new machines register with puppet (part of the initial image), our custom facts run. These facts are used to determine what needs to be monitored, what needs to be deployed to the new machine and what configuration needs to be updated in other places.

Structure of a Custom Fact in Ruby

Custom facts have a specific structure. Adherence to the structure is important as the whole fact does not necessarily execute all at once, or even in a single context. The structure is as follows

Facter.add(<fact name>) do
confine :<someFact> => '<some value>'
confine :<someOtherFact> do |f|
f == '<value 1> || f == '<value 2>'
end
has_weight <some number> setcode do
# code for fact ...
end
end

The puppet help documentation explains how to write a custom fact pretty well. The documentation is available here. There are a couple of key things to note which are easy to miss.

Firstly, there can be multiple confine statements. Confine statements are actually a block, and that is how you can confine the fact to any of multiple possible values. It is documented, but easy to miss initially.

Secondly, it is important to realize that the setcode block does not necessarily run when the Facter.add() block runs. Instead it is possible for puppet to take a reference to the setcode block and execute it later. This important, as sometimes, there are multiple implementations for the same fact, and puppet has to process all of them and determine which one applies. Puppet tries to only execute the setcode…

--

--

Michael Sharpe

Senior Software Architect located in Houston, Texas