Custom facts in Puppet are added by writing snippets of Ruby code within your Puppet modules, enabling you to gather specific information from your nodes that isn't provided by default. Puppet then efficiently distributes these custom facts to client nodes via its plug-in synchronization mechanism, making them available for use in your manifests.
Understanding Custom Facts in Puppet
Puppet relies on Facter, a core library, to discover system information (facts) about nodes, such as operating system, IP address, and memory. However, scenarios often arise where you need to collect unique data specific to your environment or applications. This is where custom facts become indispensable. They allow you to extend Facter's capabilities, providing dynamic, tailored data for more precise configuration management.
The Core Method: Writing Ruby Code
The primary way to add custom facts is by creating Ruby code snippets. These snippets define new facts and the logic for how their values are determined.
Where to Place Your Custom Facts
For effective distribution and organization, custom facts should always reside within a Puppet module's dedicated directory structure. Specifically, you place your .rb
fact files in the lib/facter/
subdirectory of your module.
your_module/
├── manifests/
│ └── init.pp
└── lib/
└── facter/
├── my_custom_fact.rb
└── another_fact.rb
When Puppet applies a module to a node, it automatically synchronizes the contents of the lib/facter
directory to the node's Facter search path, making the new facts immediately available.
Example: A Simple Custom Fact
Let's say you want a fact to check if a specific application's configuration file exists.
# your_module/lib/facter/app_config_exists.rb
Facter.add('app_config_exists') do
setcode do
File.exist?('/etc/my_app/config.conf')
end
end
Once this module is deployed to a node, Facter will discover a new fact named app_config_exists
, which will return true
or false
based on the file's presence. You can then use this fact in your Puppet manifests:
# In your_module/manifests/init.pp
if $facts['app_config_exists'] {
notify { 'App config file found!': }
} else {
notify { 'App config file missing.': }
}
Distributing Custom Facts with Modules
The strength of Puppet's custom fact mechanism lies in its distribution model. Instead of manually copying files to each node, you simply place your Ruby fact files within a module.
How Module Plug-ins Work
- Module Creation: You create a Puppet module and add your custom fact Ruby files to
lib/facter/
. - Catalog Compilation: When you compile a catalog for a node that includes this module, Puppet identifies the plug-ins (including custom facts) within the module.
- Plug-in Sync: Puppet's plug-in synchronization (often called "plug-in sync") mechanism automatically pushes these custom fact files from the Puppet primary server to the client node's Facter search path before the catalog is applied. This ensures that the facts are available before Puppet tries to evaluate any conditions or resources that depend on them.
This automated distribution is robust and centralizes the management of your custom data collection.
Practical Steps for Creating Custom Facts
To create and deploy a custom fact, follow these steps:
- Plan Your Fact: Determine what information you need and how it can be reliably obtained (e.g., command output, file content, system calls).
- Create Your Module: If you don't have one, create a new Puppet module using
puppet module generate <your_username>-<your_module_name>
. - Create the
lib/facter/
Directory: Inside your module's root directory, create the pathlib/facter/
. - Write the Ruby File: Create a
.rb
file (e.g.,my_fact.rb
) insidelib/facter/
. - Define the Fact: Use
Facter.add('fact_name') do ... end
to define your fact.setcode
block: Contains the Ruby logic to determine the fact's value. This block can execute shell commands, read files, or perform other Ruby operations.confine
(Optional): Useconfine :osfamily => 'RedHat'
to make the fact run only on specific operating systems, improving performance and avoiding errors.weight
(Optional): If multiple facts have the same name, Facter usesweight
to decide which one to use (higher weight wins).
- Deploy the Module: Place your module in your Puppet environment's module path.
- Run Puppet: Execute
puppet agent -t
on the client node. The plug-in sync will transfer the fact, and Facter will discover it. - Verify: You can test your fact directly on the node using
facter -p fact_name
or by inspecting the facts in the PuppetDB.
Why Use Custom Facts?
- Dynamic Information: Collect real-time, node-specific data not covered by standard Facter facts.
- Platform-Specific Logic: Implement different logic for fact values based on the operating system or hardware.
- Configuration Tailoring: Use custom facts to make intelligent decisions in your Puppet manifests, leading to more flexible and robust configurations.
- Application-Specific Data: Retrieve settings, versions, or status of applications unique to your environment.
Best Practices for Custom Facts
- Keep them Simple: Facts should be lightweight and execute quickly. Avoid complex or time-consuming operations.
- Idempotency: Fact resolution should not alter the state of the system.
- Error Handling: Include error handling within your Ruby code to prevent fact failures from disrupting Puppet runs.
- Confine when Possible: Use
confine
to restrict facts to relevant operating systems or architectures, optimizing performance. - Test Your Facts: Use
rspec-puppet
or simply runfacter -p <fact_name>
on a node to test. - Unique Naming: Ensure your custom fact names are unique to avoid conflicts.
Key Elements of Custom Fact Management
Aspect | Description |
---|---|
Method | Writing Ruby code snippets. |
Location | <MODULE_NAME>/lib/facter/ directory within a Puppet module. |
Definition | Using Facter.add('fact_name') do setcode { ... } end . |
Distribution | Via Puppet's plug-in synchronization (plugin-sync) mechanism, pushing from server to client. |
Usage | Accessed in Puppet manifests as $facts['fact_name'] . |
Benefits | Dynamic data collection, platform-specific logic, enhanced configuration tailoring. |
Best Practices | Keep simple, test thoroughly, use confine , handle errors. |
By leveraging custom facts, you can significantly enhance the intelligence and flexibility of your Puppet infrastructure, allowing your configurations to adapt precisely to the unique characteristics of each managed node.