puppet agent -t trigger a run (test mode, verbose)--noop simulate; report drift, change nothingpuppet catalog compile build the catalog for a nodefacter -p show facts (incl. custom/external)puppet resource service inspect live state as resourcesrequire => declare an ordering edgeWhat I Am
A specification, not a script
I am a compiled, node-specific graph of resources describing desired state. I am not executed top to bottom — I am converged toward. Run me twice and the second run changes nothing.
Per-node and immutable
I am built fresh for one agent at one moment, frozen from its facts and your code. The agent enforces me; the master/compiler authored me. I carry no logic — only declared intent and explicit ordering.
The Resource Type System
I am made of resources: typed, named declarations of desired state. Each has a type, a title, and attributes.
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']ensure => present|absent|...define mymod::thing { ... }The Provider Abstraction
I describe state; providers know how to reach it. The type defines the contract; the provider implements it per platform, chosen from facts.
| Type | Provider examples | Selection |
|---|---|---|
package | apt, yum, dnf, gem, pip | defaulted by os.family; override with provider => |
service | systemd, init, launchd | auto-detected from init system |
file | (single posix/windows) | by kernel |
ensure => installed, not apt-get install.Manifest & Module Structure
Your code is organised into modules; I am assembled from the classes they declare.
mymodule/
├── manifests/
│ ├── init.pp # class mymodule { }
│ ├── config.pp # class mymodule::config { }
│ └── service.pp # class mymodule::service { }
├── templates/ # .epp / .erb
├── files/ # static files served to agents
├── hiera.yaml # module-level data
├── data/ # default parameter data
└── manifests/install.pp
include mymoduleclass { 'mymodule': port => 8080 }contain mymodule::configThe Compilation Pipeline
How I come to exist, from agent request to finished graph.
| Stage | What happens |
|---|---|
| 1. Node classification | The compiler decides which classes apply (ENC, site.pp node blocks, or Hiera classification). |
| 2. Fact injection | The agent's facts arrive as top-scope $facts hash, parameterising evaluation. |
| 3. Code evaluation | Manifests run as a program — conditionals, functions, Hiera lookups, template rendering — producing resources. |
| 4. Relationship resolution | Metaparameters and chaining arrows become edges; containment is flattened. |
| 5. Graph output | I emerge: a validated DAG of resources, serialized and sent to the agent. |
if/case logic resolves at compile time — by the time I reach the agent, every decision is already made.Facter & the Trust Boundary
Facter gathers system facts on the agent and ships them to the compiler before I am built.
$facts['os']['family']$trusted['certname']facter -p custom_factfacts.d/, Ruby facts) the agent will send.$trusted, which the master validates against the certificate.Relationships, Ordering & Refresh
Order is never implied by file position — you declare every edge. I am a DAG, and the agent topologically sorts me.
| Metaparameter | Meaning | Refresh? |
|---|---|---|
before / require | This applies before / after the target. | No |
notify | Apply before target and send it a refresh on change. | Yes (sends) |
subscribe | Apply after source and refresh when source changes. | Yes (receives) |
# chaining arrows express the same edges
Package['nginx'] -> File['/etc/nginx/nginx.conf'] ~> Service['nginx']
# -> ordering, ~> ordering + refresh