Puppet is an enterprise-grade configuration management tool that automates the provisioning, configuration, and compliance of infrastructure across multi-cloud and on-premises environments.
Puppet uses a declarative domain-specific language (DSL) based on Ruby to model system state. It can be deployed in an agent-server (primary/agent) architecture or run standalone using Puppet Apply or Puppet Bolt (agentless task runner).
| Term | Description |
|---|---|
| Manifest | A file containing Puppet DSL code with the .pp file extension. |
| Resource | The basic unit of configuration representing a system element (package, file, service, user). |
| Class | A named, reusable block of Puppet code representing a singleton configuration component. |
| Defined Type | Reusable block that can be instantiated multiple times with unique titles. |
| Module | A structured package containing manifests, templates, files, and metadata. |
| Facts | System metadata automatically gathered from managed nodes using Facter. |
| Catalog | Compiled dependency graph representing the desired state for a specific node. |
| Hiera | Key-value hierarchical data lookup tool separating code from configuration data. |
🟢 Beginner Commands (Click to Expand)
puppet --version
facter --versionpuppet apply manifest.pp
puppet apply --noop manifest.pp # Dry-run mode (no changes applied)puppet parser validate manifest.pp
puppet-lint manifest.pppuppet resource user root
puppet resource service sshd
puppet resource package nginxfacter
facter os.family
facter networking.ip
facter memory.system.available🟡 Intermediate Commands (Click to Expand)
pdk new module my_module
pdk new class my_class
pdk validate # Syntax, linting, metadata check
pdk test unit # Run RSpec unit testspuppet module list
puppet module install puppetlabs-apt
puppet module install puppetlabs-docker
puppet module upgrade puppetlabs-apt# Run agent once and print verbose output
puppet agent -t
# Dry-run agent execution
puppet agent -t --noop
# Enable debug logging
puppet agent -t --debug🔴 Advanced Commands (Click to Expand)
# List all pending certificate signing requests (CSRs)
puppetserver ca list
# Sign an agent certificate
puppetserver ca sign --certname node1.example.com
# Sign all pending certificates
puppetserver ca sign --all
# Revoke and clean a decommissioned node certificate
puppetserver ca revoke --certname node1.example.com
puppetserver ca clean --certname node1.example.com# Query nodes by operating system
puppet query 'inventory[certname] { facts.os.family = "Debian" }'
# Query nodes where a specific package is installed
puppet query 'resources[certname] { type = "Package" and title = "openssl" }'puppet lookup my_module::port --node web1.example.com --explainbolt command run "uptime" --targets linux_servers
bolt task run package action=install name=curl --targets web1.example.com
bolt plan run myplan::deploy --targets all# Manage file
file { '/etc/motd':
ensure => file,
owner => 'root',
group => 'root',
mode => '0644',
content => "Authorized Access Only - Managed by Puppet\n",
}
# Manage package
package { 'nginx':
ensure => installed,
}
# Manage service
service { 'nginx':
ensure => running,
enable => true,
}In Puppet, resources do not execute in linear top-to-bottom order by default. You must declare explicit dependencies using metaparameters or chaining arrows.
| Metaparameter | Behavior |
|---|---|
require |
Current resource applies after specified resource |
before |
Current resource applies before specified resource |
notify |
Current resource applies before and sends a refresh trigger if changed |
subscribe |
Current resource applies after and refreshes if target changed |
package { 'nginx':
ensure => installed,
}
file { '/etc/nginx/nginx.conf':
ensure => file,
source => 'puppet:///modules/nginx/nginx.conf',
require => Package['nginx'],
notify => Service['nginx'],
}
service { 'nginx':
ensure => running,
enable => true,
}Use -> (ordering) and ~> (ordering with notification):
Package['nginx'] -> File['/etc/nginx/nginx.conf'] ~> Service['nginx']- Class: Singleton. Can only be declared once per node.
- Defined Type: Reusable prototype. Can be instantiated multiple times with different titles.
class nginx (
Integer $port = 80,
String $package_name = 'nginx',
) {
package { $package_name:
ensure => installed,
}
file { '/etc/nginx/sites-available/default':
ensure => file,
content => epp('nginx/vhost.epp', { 'port' => $port }),
notify => Service['nginx'],
}
service { 'nginx':
ensure => running,
enable => true,
}
}define nginx::vhost (
Integer $port,
String $docroot,
) {
file { "/etc/nginx/conf.d/${title}.conf":
ensure => file,
content => epp('nginx/vhost_snippet.epp', {
'server_name' => $title,
'port' => $port,
'docroot' => $docroot,
}),
notify => Service['nginx'],
}
}
# Invoking multiple instances:
nginx::vhost { 'blog.example.com':
port => 80,
docroot => '/var/www/blog',
}
nginx::vhost { 'shop.example.com':
port => 8080,
docroot => '/var/www/shop',
}Modern Puppet favors EPP (.epp) templates using native Puppet DSL over legacy ERB.
File: templates/vhost.epp:
<%- | String $server_name, Integer $port, String $docroot | -%>
server {
listen <%= $port %>;
server_name <%= $server_name %>;
root <%= $docroot %>;
}Usage in manifest:
file { '/etc/nginx/conf.d/site.conf':
ensure => file,
content => epp('nginx/vhost.epp', {
'server_name' => 'example.com',
'port' => 80,
'docroot' => '/var/www/html',
}),
}Hiera separates code logic from site-specific data:
version: 5
defaults:
datadir: data
data_hash: yaml_data
hierarchy:
- name: "Per-node secrets (eyaml)"
lookup_key: eyaml_lookup_key
path: "nodes/%{trusted.certname}.eyaml"
- name: "Per-node data"
path: "nodes/%{trusted.certname}.yaml"
- name: "Environment data"
path: "environments/%{server_facts.environment}.yaml"
- name: "Operating System Family"
path: "os/%{facts.os.family}.yaml"
- name: "Common data"
path: "common.yaml"Lookup data directly in Hiera YAML:
# data/common.yaml
nginx::port: 80
app::db_user: 'app_admin'Encrypt passwords directly in your git repository:
# Encrypt a secret string
eyaml encrypt -s 'SuperSecretPassword123'Output stored safely in data/common.eyaml:
app::db_password: >
ENC[PKCS7,MIIBeQYJKoZIhvcNAQcDoIIBajCCAWYCAQAxggEhMIIBHQIBADAFMAACAQEw...==]- Use PDK for Module Authoring: Standardize code style, unit tests, and validation.
- Always Declare Explicit Dependencies: Use
require,notify, or chaining arrows (->,~>) rather than relying on evaluation order. - Prefer EPP over ERB: EPP ensures type validation and syntax consistency with Puppet DSL.
- Use Trusted Facts: Rely on
$trusted['certname']for node identification to prevent fact spoofing. - Keep Manifests Generic: Store all environment-specific values in Hiera data files, not hardcoded in
.ppfiles.
