puppet agent -t request, compile & apply mepuppet catalog compile $node build me on the master--noop simulate convergence, change nothingfacter -p see the facts that shape mepuppet lookup key resolve Hiera datadesired state not a script — a specificationWhat I Am
I am a compiled, node-specific directed acyclic graph of resources. I describe what your system should look like, never the steps to get there. The agent reads me and converges reality toward my declarations — adding, changing, or leaving alone whatever is needed.
Declarative, not imperative
Order of resources in source is irrelevant; only explicit relationships establish sequence. I am evaluated to a state, not executed line by line.
Idempotent by design
Applying me twice produces no second change. Each resource is checked against current state and only acted on if it diverges.
Resources, Types & Providers
Resources are my atoms: a type, a title, and attributes describing desired state.
package { 'nginx':
ensure => installed,
}
service { 'nginx':
ensure => running,
enable => true,
require => Package['nginx'],
}
typeproviderpuppet resource service nginxpuppet describe packageensure => running and let the agent pick systemd or upstart.How I Come to Exist — The Compilation Pipeline
| Stage | What happens |
|---|---|
1. Request | Agent submits its certname and plugin-synced facts to the master. |
2. Classify | Node classifier / site.pp / ENC assigns classes to the node. |
3. Inject facts | Facts become top-scope $facts variables available to manifests. |
4. Evaluate | Puppet DSL is parsed and evaluated; Hiera lookups resolve; classes & defines expand into resources. |
5. Build graph | Relationships and containment edges produce a DAG — that is me. |
6. Serialize | I am sent to the agent as JSON and cached locally. |
Facter & the Trust Boundary
Facter runs on the agent and reports system inventory (OS, IP, memory, custom facts). I am shaped by these values during compilation.
$facts['os']['family']facter -p networking.ip$trusted['certname']$trusted data, extracted from the cert, is safe for security-sensitive classification decisions.Relationships, Ordering & Containment
Because I am declarative, sequence must be stated explicitly. Metaparameters and chaining arrows define my edges.
require => Package['x']before => Service['x']notify => Service['x']subscribe => File['conf']File['a'] -> Service['b']File['a'] ~> Service['b']Data Separation — Hiera
Code declares structure; Hiera supplies the values. Lookups resolve hierarchically by node-specific facts during compilation, keeping me free of hardcoded data.
# hiera.yaml hierarchy
hierarchy:
- name: "Per-node"
path: "nodes/%{trusted.certname}.yaml"
- name: "Per-OS"
path: "os/%{facts.os.family}.yaml"
- name: "Common"
path: "common.yaml"
lookup('ntp::servers')class profile::ntp ($servers) {}profile::ntp::servers from Hiera.Module Structure & Roles/Profiles
Module layout
mymod/
manifests/init.pp
templates/conf.epp
files/static.txt
lib/facter/
data/
hiera.yaml
Roles & Profiles
A profile wraps and configures component modules for one technology. A role composes profiles to describe a machine's business purpose. Each node gets exactly one role.
class role::webserver {
include profile::base
include profile::nginx
include profile::monitoring
}
site.pp trivial: classify nodes to a single role and let composition do the rest.Exported Resources & PuppetDB
Resources declared on one node can be collected by another, enabling cross-node orchestration like load-balancer pools and monitoring registration. PuppetDB stores exported resources, facts, and my reports.
@@nagios_host { $hostname: ... }