What I Am

Life as a Puppet catalogintermediate · standard · comprehensive | model: claude-opus-4-8 | 2026-06-21
Quick ref
puppet agent -t trigger a run: fetch facts, compile me, apply
puppet agent -t --noop simulate, report drift, change nothing
puppet catalog compile build me on the master
facter -p see the facts that parameterise me
puppet parser validate file.pp syntax-check a manifest
puppet resource service nginx introspect live state as Puppet sees it

What I Am

I am the catalog: a compiled, node-specific directed graph of resources expressing desired state. I am not a script. I declare what should be true, never a sequence of imperative steps. Two runs of me on the same node produce the same end state because every resource is idempotent.

Declarative consequences

Order between resources is not implied by file position — it must be declared explicitly with relationships. The agent converges the node to my specification regardless of starting state.

Per-node, per-run

I am compiled fresh for one node from its facts plus shared code and data. Change the facts or the data, and a different catalog is born.

Note A resource appearing once means "this state, full stop." Declaring the same resource title twice is a compile error — there is one source of truth per resource.

Resource Types & Providers

Every node in my graph is a resource: a type, a title, and a set of attributes describing desired state.

package { 'nginx':
  ensure => installed,
}
service { 'nginx':
  ensure  => running,
  enable  => true,
  require => Package['nginx'],
}
file { '/etc/nginx/nginx.conf':
  ensure  => file,
  content => template('nginx/nginx.conf.erb'),
  notify  => Service['nginx'],
}
type { 'title': attr => value }
Universal resource syntax; title is the namevar unless overridden.
provider
The platform-specific implementation (apt, yum, systemd) that realises a type's desired state.
ensure
The most common attribute: present/absent, installed, running, file/directory/link.
puppet describe service
List a type's attributes and available providers.
Tip The provider abstraction is why I am portable: the same package declaration converges on Debian or RHEL because providers map intent to local tooling.

Manifests, Modules & the Code I'm Built From

Module layout

mymodule/
  manifests/
    init.pp        # class mymodule
    config.pp      # mymodule::config
  templates/
  files/
  data/            # module-level Hiera
  hiera.yaml
  metadata.json

Classes & defined types

A class is a singleton bundle of resources, declared once. A define is a reusable resource macro you can instantiate many times with different titles.

ConstructPurposeCardinality
classGroup resources, included once per nodeSingleton
defineParameterised, repeatable resource groupMany
nodeMatch a certname to classificationPer node
include / containDeclare a class into the catalog

How I Am Compiled

The pipeline that produces me on the master:

1. Node classification
An ENC, site.pp node blocks, or the role/profile entry point decides which classes apply.
2. Fact injection
The agent's facts arrive as top-scope $facts and trusted data; they parameterise evaluation.
3. Code evaluation
Manifests run top-down: conditionals resolve, Hiera lookups fire, resources are declared.
4. Relationship & graph build
Resources and their edges form a DAG; cycles are a compile error.
5. Catalog output
I am serialised and shipped to the agent — a static specification, code already evaluated.
Warning All language logic runs at compile time on the master. By the time the agent has me, there is no more if/case — only a fixed resource graph. Functions like generate run server-side, not on the node.

Facter & the Trust Boundary

Facter discovers node facts (OS, networking, hardware) and presents them as input to my compilation. But facts are self-reported by the agent — a security boundary.

Data sourceVariableTrust
Facter / custom facts$facts['os']['family']Spoofable by the node
Cert-derived data$trusted['certname']Validated from the SSL cert
Cert extensions$trusted['extensions']Signed at cert issuance
Warning Never gate security-sensitive classification on ordinary facts — a compromised node can forge them. Use $trusted hash data, which derives from the agent's signed certificate.

Relationships, Ordering & Refresh

Because I am declarative, edges in my graph are explicit. Metaparameters and chaining arrows build them.

require => Resource['x']
This resource applies after x.
before => Resource['x']
This resource applies before x.
notify => Service['x']
Order, plus send a refresh signal if this resource changes.
subscribe => File['x']
Order, plus refresh self when x changes.
File['a'] -> Service['b']
Chaining arrow: ordering edge.
File['a'] ~> Service['b']
Chaining arrow with refresh (notify).
Note Containment: resources inside a class are cont