What I Am

Life as a Puppet catalogintermediate · standard · comprehensive | model: claude-opus-4-8 | 2026-06-19
I am
catalog a compiled, node-specific resource graph
desired state a specification, not a script
puppet agent -t request, apply, report
--noop simulate convergence, change nothing
facter -p see the facts that shaped me
idempotent re-applying me is safe

What I Am

I am the catalog: the master compiles me from your manifests and a node's facts into a directed graph of resources, each describing a desired end state rather than the steps to reach it. The agent reads me and converges the system toward what I declare — I am intent made concrete, scoped to exactly one node at one moment.

Declarative, not imperative

I say what should be true, never how to do it. Providers translate my declarations into commands; I stay agnostic.

Ordering is explicit

My file order means nothing. Relationships between resources, not text position, determine the order the agent applies me.

Note I am node-specific and ephemeral. Two agents requesting compilation with different facts receive different catalogs from identical code.

Resources, Types & Providers

My atoms are resources: a type, a title, and attributes. The type defines the model; the provider implements it for a given platform.

package { 'nginx': ensure => installed }
Declares a desired state; provider picks apt/yum/etc. automatically.
service { 'nginx': ensure => running, enable => true }
Runtime and boot state, abstracted across init systems.
file { '/etc/nginx.conf': content => ... }
Content, ownership, and mode declared as one unit.
provider => 'systemd'
Force a specific provider when auto-selection is wrong.
puppet resource service nginx
Reverse-engineer current state into resource syntax.
puppet describe package
List a type's attributes and available providers.
Tip The provider abstraction is why your manifests stay portable: declare the model once, let providers reconcile the platform differences.

The Compilation Pipeline

I come into existence through an ordered sequence on the master, triggered by an agent's request.

StageWhat happensInput
1. ClassificationENC or site.pp assigns classes to the nodenode name, ENC output
2. Fact injectionAgent-supplied facts become top-scope variablesFacter payload
3. Data lookupHiera resolves class parameters from datahierarchy + facts
4. EvaluationManifests run; logic, loops, templates resolvePuppet code
5. Graph outputResources + relationships become mecompiled catalog
Warning All conditional logic and iteration run at compile time on the master. By the time I reach the agent, every if, loop, and lookup is already resolved into static resources.

Facter & the Trust Boundary

Facter discovers system facts on the node and ships them to the compiler, where they parameterise my construction. These facts cross a trust boundary you must respect.

$facts['os']['family']
Structured fact access used to branch logic during compilation.
facter -p networking.ip
Query a single fact including Puppet's custom facts.
$facts['trusted']['certname']
Certificate-derived facts the agent cannot forge.
FACTER_role=web facter -p
External fact via environment variable.
Warning Ordinary facts are self-reported by the agent and can be spoofed. Only $trusted data, signed into the certificate, is safe for authorization decisions.

Relationships, Ordering & Refresh

My edges define application order and signalling. Metaparameters express dependencies; chaining arrows do the same inline.

ConstructMeaning
require => Package['nginx']Apply the named resource before this one.
before => Service['nginx']Apply this resource before the named one.
notify => Service['nginx']Order before, and send a refresh if I changed.
subscribe => File['nginx.conf']Order after, and refresh when the source changes.
Package['nginx'] -> File['conf'] ~> Service['nginx']Chain: order then order-with-refresh.
contain ::nginx::configPull a class inside this one's containment boundary.
Note Refresh signals (~>) are how a changed config restarts a service. Without containment, relationships to a class may not reach the resources inside it.

Modules, Hiera & Roles/Profiles

Code that builds me is organised into modules; data that parameterises me lives in Hiera, separated from logic. The roles/profiles pattern keeps both maintainable.

Module layout

mymod/
  manifests/init.pp
  manifests/config.pp
  templates/app.conf.epp
  files/cert.pem
  data/common.yaml
  hiera.yaml
  metadata.json

Roles & profiles

A profile wraps a technology with site config; a role composes profiles to describe a machine's purpose. A node gets exactly one role.

class role::webserver {
  include profile::base
  include profile::nginx
  include profile::monitoring
}
lookup('profile::nginx::port')
Explicit Hiera lookup with merge and default control.
hiera.yaml hierarchy
Ordered layers (node, environment, common) resolved by facts.
Tip Keep logic in profiles and values in Hiera. Automatic parameter lookup binds class::param keys to class parameters without writing lookup by hand.

Exported Resources & PuppetDB

Some of my resources are declared on one node but realised on another, brokered through PuppetDB across compilations.

@@nagios_host { $fqdn: ... }
Export a resource for collection elsewhere (@@).