catalog a compiled, node-specific resource graphdesired state a specification, not a scriptpuppet agent -t request, apply, report--noop simulate convergence, change nothingfacter -p see the facts that shaped meidempotent re-applying me is safeWhat 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.
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 }service { 'nginx': ensure => running, enable => true }file { '/etc/nginx.conf': content => ... }provider => 'systemd'puppet resource service nginxpuppet describe packageThe Compilation Pipeline
I come into existence through an ordered sequence on the master, triggered by an agent's request.
| Stage | What happens | Input |
|---|---|---|
1. Classification | ENC or site.pp assigns classes to the node | node name, ENC output |
2. Fact injection | Agent-supplied facts become top-scope variables | Facter payload |
3. Data lookup | Hiera resolves class parameters from data | hierarchy + facts |
4. Evaluation | Manifests run; logic, loops, templates resolve | Puppet code |
5. Graph output | Resources + relationships become me | compiled catalog |
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']facter -p networking.ip$facts['trusted']['certname']FACTER_role=web facter -p$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.
| Construct | Meaning |
|---|---|
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::config | Pull a class inside this one's containment boundary. |
~>) 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')hiera.yaml hierarchyclass::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: ... }@@).