Puppet Installation and Configuration

Puppet is an infrastructure automation platform that uses a client-server model with a declarative language for defining system state. Puppet enables centralized management of thousands of systems through reusable modules and manifests. This guide covers server and agent installation, writing manifests, creating modules, Hiera for data management, Facter for system discovery, and Puppet Forge for community modules.

Table of Contents

  1. Puppet Overview
  2. Server Installation
  3. Agent Installation
  4. Certificate Management
  5. Writing Manifests
  6. Creating Modules
  7. Hiera Data Management
  8. Facter for System Discovery
  9. Puppet Forge and Community Modules
  10. Conclusion

Puppet Overview

Puppet is a declarative infrastructure automation tool that defines desired system state in Puppet language. The client-server model uses a central Puppet server managing agents on remote systems, enforcing desired configuration state automatically.

Key components:

  • Puppet Server: Central authority managing system state
  • Puppet Agent: Runs on managed nodes, pulls configuration from server
  • Manifests: Files defining infrastructure state (Puppet language)
  • Modules: Reusable collections of manifests and data
  • Hiera: Hierarchical data management for flexibility
  • Facter: System discovery, provides node information
  • Catalog: Compiled configuration sent to agents

Architecture:

┌──────────────────────┐
│   Puppet Server      │
│  (Master Authority)  │
└──────────┬───────────┘
           │ Compile catalogs
           │
     ┌─────┴─────┬──────────┐
     │            │          │
   Agent        Agent      Agent
  (Ubuntu)     (CentOS)   (Debian)
     │            │          │
  Pull config  Pull config  Pull config
     │            │          │
  Enforce     Enforce     Enforce

Server Installation

Install and configure the Puppet server.

Installation:

# Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y puppetserver

# CentOS/RHEL
sudo yum install -y puppetserver

# macOS
brew install puppetserver

# Start server
sudo systemctl start puppetserver
sudo systemctl enable puppetserver

# Verify
sudo puppetserver ca list

Server configuration:

# /etc/puppet/puppet.conf
[master]
  # Server certificate name
  certname = puppet.example.com
  
  # DNS names for certificate
  dns_alt_names = puppet,puppet.example.com
  
  # Manifest directory
  manifest = /etc/puppet/manifests/site.pp
  
  # Module path
  modulepath = /etc/puppet/modules:/usr/share/puppet/modules
  
  # SSL settings
  ssl_protocols = TLSv1.2,TLSv1.3
  
  # Memory settings
  max_requests_per_instance = 1000
  
  # Node definition
  node_terminus = hiera
  hiera_config = /etc/puppet/hiera.yaml
  
  # Reporting
  reports = foreman

[agent]
  # Agent-side settings
  runinterval = 1800  # 30 minutes
  report = true
  pluginsync = true

Create directory structure:

# Puppet directories
sudo mkdir -p /etc/puppet/manifests
sudo mkdir -p /etc/puppet/modules
sudo mkdir -p /etc/puppet/hieradata

# Set permissions
sudo chown -R puppet:puppet /etc/puppet
sudo chmod -R 755 /etc/puppet

Test server connectivity:

# List certificate requests
sudo puppetserver ca list

# Check server status
sudo systemctl status puppetserver

# View logs
sudo journalctl -u puppetserver -f

Agent Installation

Install Puppet agents on managed nodes.

Installation:

# Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y puppet-agent

# CentOS/RHEL
sudo yum install -y puppet-agent

# macOS
brew install puppet-agent

# Start agent
sudo systemctl start puppet
sudo systemctl enable puppet

Agent configuration:

# /etc/puppet/puppet.conf
[agent]
  # Puppet server address
  server = puppet.example.com
  
  # Agent certificate name (defaults to hostname)
  certname = webserver-01
  
  # Environment
  environment = production
  
  # Run interval
  runinterval = 1800
  
  # Report results
  report = true
  
  # Sync plugins from server
  pluginsync = true
  
  # Log level
  loglevel = notice
  
  # Log to syslog
  use_syslog = true

Generate and sign certificates:

# On agent - generate certificate request
sudo puppet agent -t --waitforcert 60

# On server - list requests
sudo puppetserver ca list

# On server - sign certificate
sudo puppetserver ca sign webserver-01

# On server - sign all pending
sudo puppetserver ca sign --all

# View certificates
sudo puppetserver ca list --all

# Revoke certificate (if needed)
sudo puppetserver ca revoke webserver-01
sudo puppetserver ca clean webserver-01

Certificate Management

Manage SSL certificates between server and agents.

Certificate operations:

# List all certificates
sudo puppetserver ca list --all

# List unsigned certificates
sudo puppetserver ca list

# List signed certificates
sudo puppetserver ca list --signed

# View certificate details
sudo puppetserver ca show webserver-01

# Verify certificate chain
sudo puppetserver ca verify webserver-01

# Delete certificate (remove node)
sudo puppetserver ca clean webserver-01

# Revoke certificate
sudo puppetserver ca revoke webserver-01

Automatic certificate signing:

# /etc/puppet/autosign.conf
# Enable autosign for specific pattern
*.example.com
webserver-*.prod.internal

# Or use autosign script
autosign = /usr/local/bin/autosign.sh

Custom autosign script:

#!/bin/bash
# /usr/local/bin/autosign.sh
# Automatically sign certificates from trusted networks

CERTNAME=$1

# Whitelist
if [[ "$CERTNAME" =~ ^(webserver|appserver)-[0-9]+\.prod\.internal$ ]]; then
  exit 0
fi

exit 1

Writing Manifests

Define system state using Puppet language.

Basic manifest:

# /etc/puppet/manifests/site.pp
# Apply to all nodes
node default {
  # Include classes
  include ::nginx
  include ::ntp
  include ::ssh
}

# Specific node
node 'webserver-01' {
  include ::nginx
  include ::php
  
  # Resource examples
  package { 'curl':
    ensure => installed,
  }
  
  service { 'nginx':
    ensure => running,
    enable => true,
  }
  
  file { '/etc/nginx/nginx.conf':
    ensure  => file,
    source  => 'puppet:///modules/nginx/nginx.conf',
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
    require => Package['nginx'],
    notify  => Service['nginx'],
  }
}

# Node pattern matching
node /^webserver-[0-9]+$/ {
  include ::web_server
}

node /^db-(prod|staging)-[0-9]+$/ {
  include ::database
}

Resource examples:

# Package installation
package { 'nginx':
  ensure => '1.18.0-1ubuntu1',
  before => Service['nginx'],
}

# Service management
service { 'nginx':
  ensure    => running,
  enable    => true,
  provider  => 'systemd',
  subscribe => File['/etc/nginx/nginx.conf'],
}

# File management with template
file { '/etc/nginx/sites-available/default':
  ensure  => file,
  content => template('nginx/vhost.erb'),
  owner   => 'www-data',
  group   => 'www-data',
  mode    => '0644',
  require => Package['nginx'],
  notify  => Service['nginx'],
}

# Directory management
file { '/var/www/html':
  ensure => directory,
  owner  => 'www-data',
  group  => 'www-data',
  mode   => '0755',
}

# Command execution
exec { 'configure-nginx':
  command => '/usr/sbin/nginx -t',
  unless  => '/usr/sbin/nginx -t > /dev/null 2>&1',
  require => Package['nginx'],
}

# Conditionals
if $::osfamily == 'Debian' {
  package { 'apt-transport-https':
    ensure => installed,
  }
} elsif $::osfamily == 'RedHat' {
  package { 'yum-plugin-fastestmirror':
    ensure => installed,
  }
}

Creating Modules

Organize reusable Puppet code as modules.

Module structure:

nginx/
├── manifests/
│   ├── init.pp
│   ├── install.pp
│   ├── config.pp
│   └── service.pp
├── templates/
│   ├── nginx.conf.erb
│   └── vhost.erb
├── files/
│   └── mime.types
├── lib/
│   └── puppet/type/nginx_vhost.rb
└── README.md

Module creation:

# Generate module structure
puppet module generate company_name-nginx

# Install module
puppet module install puppetlabs-apache

# List installed modules
puppet module list

# Search Forge
puppet module search apache

Example module:

# nginx/manifests/init.pp
class nginx (
  String $package_name = 'nginx',
  String $service_name = 'nginx',
  String $config_path = '/etc/nginx/nginx.conf',
  String $user = 'www-data',
  Integer $port = 80,
  Boolean $enable_ssl = false,
) {
  include nginx::install
  include nginx::config
  include nginx::service

  Class['nginx::install']
    -> Class['nginx::config']
      -> Class['nginx::service']
}

# nginx/manifests/install.pp
class nginx::install (
  String $package_name = $nginx::package_name,
) {
  package { $package_name:
    ensure => present,
  }
}

# nginx/manifests/config.pp
class nginx::config (
  String $config_path = $nginx::config_path,
  String $user = $nginx::user,
  Integer $port = $nginx::port,
) {
  file { $config_path:
    ensure  => file,
    content => template('nginx/nginx.conf.erb'),
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
    require => Class['nginx::install'],
    notify  => Class['nginx::service'],
  }
}

# nginx/manifests/service.pp
class nginx::service (
  String $service_name = $nginx::service_name,
) {
  service { $service_name:
    ensure    => running,
    enable    => true,
    require   => Class['nginx::config'],
  }
}

Use module in site.pp:

# /etc/puppet/manifests/site.pp
node 'webserver-01' {
  class { 'nginx':
    port    => 8080,
    user    => 'www-data',
  }
}

Hiera Data Management

Separate data from code using Hiera.

Hiera configuration:

# /etc/puppet/hiera.yaml
---
version: 5

defaults:
  datadir: /etc/puppet/hieradata
  data_hash: yaml_data

hierarchy:
  - name: 'Per-node data'
    path: 'nodes/%{::fqdn}.yaml'
  
  - name: 'Per-environment data'
    path: 'env/%{::environment}.yaml'
  
  - name: 'Per-OS data'
    path: 'os/%{::osfamily}.yaml'
  
  - name: 'Common data'
    path: 'common.yaml'

Hiera data files:

# /etc/puppet/hieradata/common.yaml
ntp::servers:
  - 0.ubuntu.pool.ntp.org
  - 1.ubuntu.pool.ntp.org

nginx::package_name: nginx
nginx::service_name: nginx

# /etc/puppet/hieradata/env/production.yaml
environment: production

nginx::port: 80

database::host: db.prod.internal
database::password: secure_password

# /etc/puppet/hieradata/nodes/webserver-01.example.com.yaml
role: webserver
nginx::port: 8080
app::name: myapp

Use Hiera in manifests:

# Access Hiera data
class web_server {
  $app_name = lookup('app::name', String, 'first', 'default_app')
  $db_host = lookup('database::host', String, 'first', 'localhost')
  
  class { 'nginx':
    port => lookup('nginx::port', Integer, 'first', 80),
  }
}

Facter for System Discovery

Leverage Facter for system information in manifests.

Built-in facts:

os
osrelease
osfamily
hostname
fqdn
ipaddress
ipaddress_eth0
netmask
gateway
memory
processorcount
timezone

Use facts in manifests:

# Use facts from Facter
if $::osfamily == 'Debian' {
  package { 'apt-utils':
    ensure => installed,
  }
} elsif $::osfamily == 'RedHat' {
  package { 'yum-utils':
    ensure => installed,
  }
}

# Memory-based configuration
if $::memorysize_mb > 4096 {
  $max_workers = 8
} else {
  $max_workers = 4
}

# Conditional based on hostname
if $::hostname =~ /^prod-/ {
  include ::monitoring::agent
  include ::logging::agent
}

Custom facts:

# /etc/puppet/modules/custom/lib/facter/app_version.rb
Facter.add(:app_version) do
  confine :kernel => 'Linux'
  setcode do
    %x{/opt/app/bin/app --version 2>/dev/null}.chomp
  end
end

View facts:

# List all facts
facter

# Get specific fact
facter hostname
facter osfamily

# JSON format
facter --json

Puppet Forge and Community Modules

Leverage pre-built modules from Puppet Forge.

Install modules:

# Install specific module
puppet module install puppetlabs-apache

# Install specific version
puppet module install puppetlabs-apache -v 5.0.0

# List installed modules
puppet module list

# Update module
puppet module upgrade puppetlabs-apache

# Remove module
puppet module uninstall puppetlabs-apache

# Install from custom source
puppet module install git+https://github.com/example/module.git

Puppetfile for dependency management:

# Puppetfile
forge "https://forge.puppet.com"

# Specify versions
mod 'puppetlabs-apache', '5.0.0'
mod 'puppetlabs-mysql', '10.0.0'
mod 'puppetlabs-postgresql', '7.0.0'

# From GitHub
mod 'custom-module',
  :git => 'https://github.com/myorg/puppet-module.git',
  :branch => 'main'

# Install dependencies
# puppet install --puppetfile=./Puppetfile

Use Forge modules:

# /etc/puppet/manifests/site.pp
node 'database-01' {
  class { 'postgresql::server':
    postgres_password => 'secure_password',
  }
  
  postgresql::server::db { 'myapp':
    user     => 'appuser',
    password => 'app_password',
  }
}

Conclusion

Puppet provides powerful, declarative infrastructure automation through client-server architecture and reusable modules. By mastering manifest writing, module creation, Hiera for flexible data management, and leveraging Puppet Forge for community modules, you can automate configuration management at scale. The combination of declarative language, strong resource modeling, and extensive community support makes Puppet ideal for managing complex infrastructure consistently.