GuíasGuides · Nivel 3Level 3

Puppet: servidor y agentes en Linux y Windows, certificados, un primer módulo, Hiera por entorno y un agente que deja de aplicar cambiosPuppet: server and agents on Linux and Windows, certificates, a first module, Hiera per environment and an agent that stops applying changes

Puppet 8 u OpenVox 8 tras el cambio de licencia: el servidor en Linux, los agentes en Linux y Windows, la firma de certificados, site.pp, un primer módulo con paquete, plantilla y servicio, roles y perfiles, Hiera por entorno, el código en Git, la ejecución en noop, los informes y un agente que deja de aplicar cambios.Puppet 8 or OpenVox 8 after the licensing change: the server on Linux, agents on Linux and Windows, certificate signing, site.pp, a first module with a package, a template and a service, roles and profiles, Hiera per environment, code in Git, noop runs, reports and an agent that stops applying changes.

Qué vas a conseguir

Montar desde cero una instalación de Puppet que gestiona servidores Linux y Windows: un servidor primario con su autoridad de certificación, dos agentes con el certificado firmado, un primer módulo que instala un paquete, escribe su fichero de configuración desde una plantilla y reinicia el servicio solo cuando el fichero cambia, los datos en Hiera separados por entorno y el código en Git. Y saber qué mirar cuando un agente deja de aplicar los cambios que se suben al repositorio.

Antes de empezar

  • Un servidor Linux para el servidor primario (RHEL 9 o compatible en esta guía; Ubuntu 24.04 también sirve) con 4 GB de RAM como mínimo: Puppet Server es una aplicación Java y arranca con 2 GB de memoria reservada.
  • Un nombre DNS estable para él, por ejemplo puppet.contoso.local, que resuelvan todos los agentes. Los agentes se conectan por TCP 8140.
  • Dos servidores de prueba: srv-app-01.contoso.local (Linux) y srv-win-01.contoso.local (Windows Server 2022 o 2025), con la hora sincronizada. Los certificados fallan con relojes desfasados.
  • Acceso sudo en los Linux y administrador local en el Windows.
  • Un repositorio Git para el código: desde el primer módulo, nada se edita a mano en el servidor.

1. Puppet u OpenVox: qué instalar

Desde 2025 hay dos caminos para la versión 8:

  • Perforce, propietario de Puppet, anunció a finales de 2024 que los paquetes oficiales pasarían a un repositorio privado. Su distribución, Puppet Core, exige aceptar una licencia comercial, con un uso gratuito limitado (25 nodos en el anuncio original) y suscripción por encima. El código abierto publicado sigue bajo licencia Apache 2.0, pero el desarrollo nuevo ya no se hace en público.
  • OpenVox es la bifurcación de la comunidad (Vox Pupuli), con primera versión en enero de 2025. Es compatible con el código de Puppet, publica paquetes en yum.voxpupuli.org y apt.voxpupuli.org y mantiene la rama 8 mientras prepara la 9.

Los paquetes cambian de nombre (openvox-server y openvox-agent frente a puppetserver y puppet-agent), pero los comandos (puppet, puppetserver), las rutas (/etc/puppetlabs) y el lenguaje son los mismos. Esta guía usa los paquetes de OpenVox 8; con Puppet Core solo cambia el repositorio. Revisa las condiciones vigentes de cada uno antes de elegir.

2. El servidor primario

bash · puppet.contoso.local
sudo rpm -Uvh https://yum.voxpupuli.org/openvox8-release-el-9.noarch.rpmsudo dnf install -y openvox-serversource /etc/profile.d/puppet-agent.shsudo /opt/puppetlabs/bin/puppet config set dns_alt_names puppet,puppet.contoso.local --section serversudo /opt/puppetlabs/bin/puppet config set server puppet.contoso.local --section mainsudo firewall-cmd --permanent --add-port=8140/tcp && sudo firewall-cmd --reloadsudo systemctl enable --now puppetserverpuppetserver --versionpuppetserver version: 8.11.0
  • Los ejecutables quedan en /opt/puppetlabs/bin; el script de /etc/profile.d lo añade al PATH. Con sudo hace falta la ruta completa o sudo -i.
  • dns_alt_names va antes del primer arranque: en ese arranque el servidor crea la autoridad de certificación y su propio certificado, y los nombres alternativos quedan grabados en él. Cambiarlo después obliga a regenerar el certificado del servidor.
  • La memoria de Java está en /etc/sysconfig/puppetserver (en Ubuntu, /etc/default/puppetserver): JAVA_ARGS="-Xms2g -Xmx2g". Con cientos de agentes se sube aquí.
  • El código vive en /etc/puppetlabs/code/environments/, un directorio por entorno. El paquete crea production.

3. El agente en Linux y en Windows

En el Linux:

bash · srv-app-01.contoso.local
sudo rpm -Uvh https://yum.voxpupuli.org/openvox8-release-el-9.noarch.rpmsudo dnf install -y openvox-agentsudo /opt/puppetlabs/bin/puppet config set server puppet.contoso.local --section mainsudo /opt/puppetlabs/bin/puppet agent -tInfo: Creating a new RSA SSL key for srv-app-01.contoso.localInfo: Creating a new SSL certificate request for srv-app-01.contoso.localInfo: Certificate Request fingerprint (SHA256): 4F:1A:7C:...:9CInfo: Certificate for srv-app-01.contoso.local has not been signed yetCouldn't fetch certificate from CA server; you might still need to sign this agent's certificate (srv-app-01.contoso.local).

En Ubuntu, el paquete del repositorio es openvox8-release-ubuntu24.04.deb de apt.voxpupuli.org, se instala con dpkg -i y después apt update y apt install openvox-agent.

En el Windows, el MSI de la rama 8 está en downloads.voxpupuli.org/windows/openvox8/. Se instala en silencio desde PowerShell como administrador:

PowerShell · srv-win-01.contoso.local
msiexec /qn /norestart /i openvox-agent-8.28.0-x64.msi PUPPET_MASTER_SERVER=puppet.contoso.local PUPPET_AGENT_STARTUP_MODE=AutomaticGet-Service puppetStatus   Name     DisplayName------   ----     -----------Running  puppet   Puppet Agent
  • El MSI añade C:\Program Files\Puppet Labs\Puppet\bin al PATH; la configuración queda en C:\ProgramData\PuppetLabs\puppet\etc\puppet.conf. Para ejecutar puppet agent -t a mano, consola como administrador.
  • El nombre del certificado (certname) es el FQDN en minúsculas. Si el Windows no tiene sufijo DNS, se queda solo con el nombre corto; PUPPET_AGENT_CERTNAME=srv-win-01.contoso.local lo fija en la instalación.
  • El agente se ejecuta como servicio cada 30 minutos (runinterval). En el Linux, sudo /opt/puppetlabs/bin/puppet resource service puppet ensure=running enable=true lo deja activo al arrancar.

4. Firmar los certificados

Cada agente genera su clave y envía una petición de certificado. Hasta que el servidor la firma, no recibe nada:

bash · puppet.contoso.local
sudo /opt/puppetlabs/bin/puppetserver ca listRequested Certificates:    srv-app-01.contoso.local   (SHA256)  4F:1A:7C:...:9C    srv-win-01.contoso.local   (SHA256)  B2:77:0E:...:E0sudo /opt/puppetlabs/bin/puppetserver ca sign --certname srv-app-01.contoso.local,srv-win-01.contoso.localSuccessfully signed certificate request for srv-app-01.contoso.localSuccessfully signed certificate request for srv-win-01.contoso.localsudo /opt/puppetlabs/bin/puppetserver ca list --all
  • Compara la huella de ca list con la que mostró el agente antes de firmar: así sabes que firmas el equipo que crees.
  • La firma automática (autosign.conf o un script de política) existe y ahorra trabajo en despliegues masivos, pero firmar lo que llega sin comprobarlo da la configuración, y los secretos que lleve, a cualquier equipo que alcance el puerto 8140.

5. site.pp: qué recibe cada nodo

El fichero manifests/site.pp del entorno asigna clases a cada nodo por su certname:

site.pp · environments/production/manifests/
node 'srv-app-01.contoso.local' {  include role::app_linux} node 'srv-win-01.contoso.local' {  include role::base_windows} node /^srv-web-\d+\.contoso\.local$/ {  include role::web} node default {}
  • Se aplica el bloque más concreto que coincida; si ninguno coincide, default. Sin bloque default el agente falla con Could not find node statement.
  • En instalaciones grandes, la clasificación sale de un clasificador externo (Foreman, la consola de la edición comercial) en lugar de site.pp. La idea es la misma: un nodo, un rol.

6. El primer módulo: paquete, plantilla y servicio

El módulo timesync instala chrony, escribe su configuración desde una plantilla con los servidores de hora de la empresa y reinicia el servicio solo si el fichero cambia:

modules/timesync/manifests/init.pp
class timesync (  Array[String] $servers,) {  case $facts['os']['family'] {    'RedHat': {      $conf    = '/etc/chrony.conf'      $service = 'chronyd'    }    'Debian': {      $conf    = '/etc/chrony/chrony.conf'      $service = 'chrony'    }    default: {      fail("timesync: ${facts['os']['family']} no está soportado")    }  }   package { 'chrony':    ensure => installed,  }   file { $conf:    ensure  => file,    owner   => 'root',    group   => 'root',    mode    => '0644',    content => epp('timesync/chrony.conf.epp', { 'servers' => $servers }),    require => Package['chrony'],    notify  => Service[$service],  }   service { $service:    ensure => running,    enable => true,  }}
modules/timesync/templates/chrony.conf.epp
<%- | Array[String] $servers | -%># Gestionado por Puppet (módulo timesync). Los cambios a mano se pierden.<% $servers.each |$s| { -%>server <%= $s %> iburst<% } -%>driftfile /var/lib/chrony/driftmakestep 1.0 3rtcsync
  • require ordena (primero el paquete, luego el fichero); notify ordena y reinicia el servicio cuando el fichero cambia. Si el fichero ya está como debe, no hay reinicio.
  • $servers no tiene valor por defecto: sale de Hiera (apartado 8). Un valor fijo en el código obligaría a cambiar el módulo para cada sede.
  • Las plantillas EPP usan el propio lenguaje de Puppet; las ERB (template(), sintaxis Ruby) siguen funcionando y son las que se verán en módulos antiguos.
  • Antes de subir: puppet parser validate modules/timesync/manifests/init.pp y puppet-lint sobre el módulo.

En Windows los mismos tipos de recurso funcionan con otras rutas. Un perfil base mínimo:

site/profile/manifests/base_windows.pp
class profile::base_windows {  service { 'W32Time':    ensure => running,    enable => true,  }   file { 'C:/ProgramData/Contoso':    ensure => directory,  }}

En las rutas de Windows, la barra normal evita problemas de escape en el código.

7. Roles y perfiles

El patrón habitual separa tres capas:

CapaQué contieneEjemplo
Módulo de componenteCómo se configura una pieza, sin datos de la empresatimesync, módulos de Puppet Forge
PerfilUna pieza de la empresa: qué módulos y con qué ajustesprofile::base_linux incluye timesync y el agente de monitorización
RolQué es el servidor, solo con include de perfilesrole::app_linux = profile::base_linux + profile::app

Cada nodo recibe un solo rol. Si dos servidores necesitan algo distinto, son dos roles. Los roles y perfiles suelen vivir en site/role y site/profile, junto a los módulos.

8. Hiera: los datos fuera del código

Hiera da valores a los parámetros de las clases según el nodo. En la raíz de cada entorno:

hiera.yaml · environments/production/
---version: 5defaults:  datadir: data  data_hash: yaml_datahierarchy:  - name: "Por nodo"    path: "nodes/%{trusted.certname}.yaml"  - name: "Por sistema operativo"    path: "os/%{facts.os.family}.yaml"  - name: "Común"    path: "common.yaml"
data/common.yaml
---timesync::servers:  - dc01.contoso.local  - dc02.contoso.local
  • Puppet busca de arriba abajo y se queda con el primer valor: un fichero en nodes/ gana a common.yaml. La clave timesync::servers rellena el parámetro $servers sin que el código lo pida.
  • Usa trusted.certname (del certificado) para la capa de nodo, no un fact que el propio agente puede falsear.
  • Como cada entorno tiene su hiera.yaml y su data/, staging puede apuntar a otros servidores de hora sin tocar production.
  • Para ver qué valor sale y de dónde: puppet lookup timesync::servers --environment production --explain en el servidor.
  • Las contraseñas no van en claro: hiera-eyaml las cifra dentro del YAML.

9. El código en Git y los entornos

  • Un repositorio de control con manifests/, site/, data/, hiera.yaml y un Puppetfile con los módulos de Puppet Forge y sus versiones.
  • Cada rama es un entorno: la rama production se despliega en /etc/puppetlabs/code/environments/production, la rama staging en .../staging. La herramienta que hace ese despliegue es r10k (o Code Manager en la edición comercial), lanzada desde el pipeline al fusionar.
  • El flujo de un cambio: rama, revisión, fusión a staging, prueba en unos pocos nodos, fusión a production.

Antes de aplicar nada, el ensayo en el agente:

bash · srv-app-01.contoso.local
sudo /opt/puppetlabs/bin/puppet agent -t --noop --environment stagingInfo: Using environment 'staging'Info: Retrieving pluginfactsInfo: Retrieving pluginInfo: Loading factsInfo: Caching catalog for srv-app-01.contoso.localInfo: Applying configuration version '1762160512'Notice: /Stage[main]/Timesync/File[/etc/chrony.conf]/content: current_value '{sha256}3b1f...', should be '{sha256}9ac2...' (noop)Notice: /Stage[main]/Timesync/Service[chronyd]: Would have triggered 'refresh' from 1 eventNotice: Applied catalog in 0.94 seconds

--noop compila el catálogo y dice qué cambiaría sin tocar nada. Si el resultado es el esperado, la misma orden sin --noop aplica el cambio en ese nodo; el resto lo recibe en su siguiente ejecución al fusionar en production.

10. Informes: qué pasó en cada ejecución

  • Con -t, el código de salida dice el resultado: 0 sin cambios, 2 con cambios, 4 con fallos, 6 cambios y fallos. Sirve para un pipeline o un script.
  • En el agente, puppet config print lastrunfile da la ruta del resumen de la última ejecución (hora, recursos cambiados, fallidos). Una comprobación de monitorización que avise si ese fichero tiene más de dos horas detecta agentes parados.
  • El agente envía un informe al servidor en cada ejecución; por defecto se guarda en /opt/puppetlabs/server/data/puppetserver/reports/<certname>/.
  • Con más de unos pocos nodos, PuppetDB (u OpenVoxDB) guarda hechos, catálogos e informes de todos, y un panel como Puppetboard muestra qué nodos fallan o llevan tiempo sin informar.

11. El ticket: "un agente dejó de aplicar los cambios"

El cambio de servidores de hora llegó a todos los Linux menos a srv-app-02.contoso.local, reinstalado la semana pasada. Puppetboard dice que su último informe es de hace nueve días. Lo primero, una ejecución a mano en el agente y leer el primer error:

bash · srv-app-02.contoso.local
sudo /opt/puppetlabs/bin/puppet agent -tError: The certificate for 'CN=srv-app-02.contoso.local' does not match its private key

Los cuatro motivos habituales, por orden de frecuencia:

1. Certificado que no corresponde tras una reinstalación. El equipo nuevo generó otra clave, pero la CA conserva el certificado firmado del equipo anterior con el mismo nombre. Se borra en los dos lados y se vuelve a firmar:

bash · servidor y agente
# en puppet.contoso.localsudo /opt/puppetlabs/bin/puppetserver ca clean --certname srv-app-02.contoso.local# en srv-app-02.contoso.localsudo /opt/puppetlabs/bin/puppet ssl cleansudo /opt/puppetlabs/bin/puppet agent -t# de nuevo en el servidor: comprobar la huella y firmarsudo /opt/puppetlabs/bin/puppetserver ca sign --certname srv-app-02.contoso.local

puppet ssl clean borra la clave y el certificado del agente; en Windows es el mismo comando en una consola de administrador. Para que no se repita, el procedimiento de baja o reinstalación de un servidor incluye puppetserver ca clean y, con PuppetDB, puppet node deactivate.

2. Reloj desfasado. Errores como certificate verify failed (certificate is not yet valid) o un certificado recién firmado que el agente da por caducado. timedatectl en Linux o w32tm /query /status en Windows. Es justo lo que el módulo timesync evita, pero un equipo recién instalado aún no lo tiene: la hora se corrige a mano antes del primer puppet agent -t.

3. El puerto 8140. Failed to open TCP connection to puppet.contoso.local:8140 indica red o cortafuegos, no Puppet. nc -zv puppet.contoso.local 8140 en Linux o Test-NetConnection puppet.contoso.local -Port 8140 en Windows. Si no conecta, revisa el cortafuegos del servidor, el del propio equipo y si el nombre resuelve a la IP correcta.

4. Agente deshabilitado. Alguien lo paró para un cambio manual y no lo reactivó:

bash · srv-app-02.contoso.local
sudo /opt/puppetlabs/bin/puppet agent -tNotice: Skipping run of Puppet configuration client; administratively disabled (Reason: 'INC-4471 prueba manual de chrony');Use 'puppet agent --enable' to re-enable.sudo /opt/puppetlabs/bin/puppet agent --enablesudo /opt/puppetlabs/bin/puppet agent -t --noop

puppet agent --disable "motivo" es la forma correcta de parar un agente: deja el motivo a la vista. Antes de reactivarlo, el --noop enseña qué deshará Puppet de lo que se cambió a mano; si ese cambio debe quedarse, va al código primero.

En el ticket: nodo, primer error, causa, comandos ejecutados en servidor y agente, y la ejecución final con código de salida 0 o 2 y sin fallos.

Siguientes pasos

¿Falta algo o en tu entorno es distinto? Escríbeme y lo corrijo.

What you'll get

Building from scratch a Puppet setup that manages Linux and Windows servers: a primary server with its certificate authority, two agents with signed certificates, a first module that installs a package, writes its configuration file from a template and restarts the service only when the file changes, data in Hiera separated by environment, and the code in Git. And knowing what to look at when an agent stops applying the changes pushed to the repository.

Before you start

  • A Linux server for the primary server (RHEL 9 or compatible in this guide; Ubuntu 24.04 works too) with at least 4 GB of RAM: Puppet Server is a Java application and starts with 2 GB of reserved memory.
  • A stable DNS name for it, for example puppet.contoso.local, that every agent resolves. Agents connect over TCP 8140.
  • Two test servers: srv-app-01.contoso.local (Linux) and srv-win-01.contoso.local (Windows Server 2022 or 2025), with their clocks in sync. Certificates fail with skewed clocks.
  • sudo access on the Linux machines and local administrator on the Windows one.
  • A Git repository for the code: from the first module on, nothing is edited by hand on the server.

1. Puppet or OpenVox: what to install

Since 2025 there are two paths for version 8:

  • Perforce, which owns Puppet, announced at the end of 2024 that the official packages would move to a private repository. Its distribution, Puppet Core, requires accepting a commercial licence, with limited free use (25 nodes in the original announcement) and a subscription above that. The published open source code remains under the Apache 2.0 licence, but new development no longer happens in public.
  • OpenVox is the community fork (Vox Pupuli), first released in January 2025. It is compatible with Puppet code, publishes packages at yum.voxpupuli.org and apt.voxpupuli.org, and maintains the 8 branch while it prepares 9.

The packages are renamed (openvox-server and openvox-agent instead of puppetserver and puppet-agent), but the commands (puppet, puppetserver), the paths (/etc/puppetlabs) and the language are the same. This guide uses the OpenVox 8 packages; with Puppet Core only the repository changes. Check the current terms of each before choosing.

2. The primary server

bash · puppet.contoso.local
sudo rpm -Uvh https://yum.voxpupuli.org/openvox8-release-el-9.noarch.rpmsudo dnf install -y openvox-serversource /etc/profile.d/puppet-agent.shsudo /opt/puppetlabs/bin/puppet config set dns_alt_names puppet,puppet.contoso.local --section serversudo /opt/puppetlabs/bin/puppet config set server puppet.contoso.local --section mainsudo firewall-cmd --permanent --add-port=8140/tcp && sudo firewall-cmd --reloadsudo systemctl enable --now puppetserverpuppetserver --versionpuppetserver version: 8.11.0
  • The executables live in /opt/puppetlabs/bin; the /etc/profile.d script adds it to PATH. With sudo you need the full path or sudo -i.
  • dns_alt_names goes in before the first start: on that start the server creates the certificate authority and its own certificate, and the alternative names are baked into it. Changing it later means regenerating the server certificate.
  • Java memory is set in /etc/sysconfig/puppetserver (on Ubuntu, /etc/default/puppetserver): JAVA_ARGS="-Xms2g -Xmx2g". With hundreds of agents, you raise it here.
  • The code lives in /etc/puppetlabs/code/environments/, one directory per environment. The package creates production.

3. The agent on Linux and on Windows

On the Linux server:

bash · srv-app-01.contoso.local
sudo rpm -Uvh https://yum.voxpupuli.org/openvox8-release-el-9.noarch.rpmsudo dnf install -y openvox-agentsudo /opt/puppetlabs/bin/puppet config set server puppet.contoso.local --section mainsudo /opt/puppetlabs/bin/puppet agent -tInfo: Creating a new RSA SSL key for srv-app-01.contoso.localInfo: Creating a new SSL certificate request for srv-app-01.contoso.localInfo: Certificate Request fingerprint (SHA256): 4F:1A:7C:...:9CInfo: Certificate for srv-app-01.contoso.local has not been signed yetCouldn't fetch certificate from CA server; you might still need to sign this agent's certificate (srv-app-01.contoso.local).

On Ubuntu, the repository package is openvox8-release-ubuntu24.04.deb from apt.voxpupuli.org, installed with dpkg -i followed by apt update and apt install openvox-agent.

On Windows, the 8-branch MSI is at downloads.voxpupuli.org/windows/openvox8/. Install it silently from PowerShell as administrator:

PowerShell · srv-win-01.contoso.local
msiexec /qn /norestart /i openvox-agent-8.28.0-x64.msi PUPPET_MASTER_SERVER=puppet.contoso.local PUPPET_AGENT_STARTUP_MODE=AutomaticGet-Service puppetStatus   Name     DisplayName------   ----     -----------Running  puppet   Puppet Agent
  • The MSI adds C:\Program Files\Puppet Labs\Puppet\bin to PATH; the configuration ends up in C:\ProgramData\PuppetLabs\puppet\etc\puppet.conf. To run puppet agent -t by hand, use a console as administrator.
  • The certificate name (certname) is the lowercase FQDN. If the Windows server has no DNS suffix, it gets only the short name; PUPPET_AGENT_CERTNAME=srv-win-01.contoso.local sets it at install time.
  • The agent runs as a service every 30 minutes (runinterval). On Linux, sudo /opt/puppetlabs/bin/puppet resource service puppet ensure=running enable=true keeps it enabled at boot.

4. Sign the certificates

Each agent generates its key and sends a certificate request. Until the server signs it, the agent gets nothing:

bash · puppet.contoso.local
sudo /opt/puppetlabs/bin/puppetserver ca listRequested Certificates:    srv-app-01.contoso.local   (SHA256)  4F:1A:7C:...:9C    srv-win-01.contoso.local   (SHA256)  B2:77:0E:...:E0sudo /opt/puppetlabs/bin/puppetserver ca sign --certname srv-app-01.contoso.local,srv-win-01.contoso.localSuccessfully signed certificate request for srv-app-01.contoso.localSuccessfully signed certificate request for srv-win-01.contoso.localsudo /opt/puppetlabs/bin/puppetserver ca list --all
  • Compare the fingerprint in ca list with the one the agent showed before signing: that way you know you are signing the machine you think you are.
  • Autosigning (autosign.conf or a policy script) exists and saves work in mass rollouts, but signing whatever arrives without checking hands the configuration, and any secrets in it, to any machine that can reach port 8140.

5. site.pp: what each node gets

The environment's manifests/site.pp file assigns classes to each node by its certname:

site.pp · environments/production/manifests/
node 'srv-app-01.contoso.local' {  include role::app_linux} node 'srv-win-01.contoso.local' {  include role::base_windows} node /^srv-web-\d+\.contoso\.local$/ {  include role::web} node default {}
  • The most specific matching block applies; if none matches, default. Without a default block the agent fails with Could not find node statement.
  • In large setups, classification comes from an external node classifier (Foreman, the commercial edition's console) instead of site.pp. The idea is the same: one node, one role.

6. The first module: package, template and service

The timesync module installs chrony, writes its configuration from a template with the company's time servers, and restarts the service only if the file changes:

modules/timesync/manifests/init.pp
class timesync (  Array[String] $servers,) {  case $facts['os']['family'] {    'RedHat': {      $conf    = '/etc/chrony.conf'      $service = 'chronyd'    }    'Debian': {      $conf    = '/etc/chrony/chrony.conf'      $service = 'chrony'    }    default: {      fail("timesync: ${facts['os']['family']} is not supported")    }  }   package { 'chrony':    ensure => installed,  }   file { $conf:    ensure  => file,    owner   => 'root',    group   => 'root',    mode    => '0644',    content => epp('timesync/chrony.conf.epp', { 'servers' => $servers }),    require => Package['chrony'],    notify  => Service[$service],  }   service { $service:    ensure => running,    enable => true,  }}
modules/timesync/templates/chrony.conf.epp
<%- | Array[String] $servers | -%># Managed by Puppet (timesync module). Manual changes will be lost.<% $servers.each |$s| { -%>server <%= $s %> iburst<% } -%>driftfile /var/lib/chrony/driftmakestep 1.0 3rtcsync
  • require sets the order (package first, then the file); notify sets the order and restarts the service when the file changes. If the file is already as it should be, there is no restart.
  • $servers has no default value: it comes from Hiera (section 8). A value hard-coded in the module would mean changing the module for every site.
  • EPP templates use Puppet's own language; ERB templates (template(), Ruby syntax) still work and are what you will see in older modules.
  • Before pushing: puppet parser validate modules/timesync/manifests/init.pp and puppet-lint on the module.

On Windows the same resource types work with different paths. A minimal base profile:

site/profile/manifests/base_windows.pp
class profile::base_windows {  service { 'W32Time':    ensure => running,    enable => true,  }   file { 'C:/ProgramData/Contoso':    ensure => directory,  }}

In Windows paths, forward slashes avoid escaping problems in the code.

7. Roles and profiles

The usual pattern separates three layers:

LayerWhat it containsExample
Component moduleHow one piece is configured, with no company datatimesync, Puppet Forge modules
ProfileOne company building block: which modules and with which settingsprofile::base_linux includes timesync and the monitoring agent
RoleWhat the server is, only include of profilesrole::app_linux = profile::base_linux + profile::app

Each node gets exactly one role. If two servers need something different, they are two roles. Roles and profiles usually live in site/role and site/profile, next to the modules.

8. Hiera: data out of the code

Hiera gives values to class parameters depending on the node. At the root of each environment:

hiera.yaml · environments/production/
---version: 5defaults:  datadir: data  data_hash: yaml_datahierarchy:  - name: "Per node"    path: "nodes/%{trusted.certname}.yaml"  - name: "Per operating system"    path: "os/%{facts.os.family}.yaml"  - name: "Common"    path: "common.yaml"
data/common.yaml
---timesync::servers:  - dc01.contoso.local  - dc02.contoso.local
  • Puppet searches from top to bottom and keeps the first value: a file in nodes/ beats common.yaml. The timesync::servers key fills the $servers parameter without the code asking for it.
  • Use trusted.certname (from the certificate) for the node layer, not a fact the agent itself could fake.
  • Because each environment has its own hiera.yaml and data/, staging can point at other time servers without touching production.
  • To see which value comes out and where from: puppet lookup timesync::servers --environment production --explain on the server.
  • Passwords are never in clear text: hiera-eyaml encrypts them inside the YAML.

9. Code in Git and environments

  • A control repository with manifests/, site/, data/, hiera.yaml and a Puppetfile listing the Puppet Forge modules and their versions.
  • Each branch is an environment: the production branch is deployed to /etc/puppetlabs/code/environments/production, the staging branch to .../staging. The tool that does that deployment is r10k (or Code Manager in the commercial edition), triggered from the pipeline on merge.
  • A change's flow: branch, review, merge to staging, test on a few nodes, merge to production.

Before applying anything, the dry run on the agent:

bash · srv-app-01.contoso.local
sudo /opt/puppetlabs/bin/puppet agent -t --noop --environment stagingInfo: Using environment 'staging'Info: Retrieving pluginfactsInfo: Retrieving pluginInfo: Loading factsInfo: Caching catalog for srv-app-01.contoso.localInfo: Applying configuration version '1762160512'Notice: /Stage[main]/Timesync/File[/etc/chrony.conf]/content: current_value '{sha256}3b1f...', should be '{sha256}9ac2...' (noop)Notice: /Stage[main]/Timesync/Service[chronyd]: Would have triggered 'refresh' from 1 eventNotice: Applied catalog in 0.94 seconds

--noop compiles the catalogue and says what would change without touching anything. If the result is what you expect, the same command without --noop applies the change on that node; the rest get it on their next run once it is merged into production.

10. Reports: what happened on each run

  • With -t, the exit code tells you the result: 0 no changes, 2 changes, 4 failures, 6 changes and failures. Useful for a pipeline or a script.
  • On the agent, puppet config print lastrunfile gives the path of the last run's summary (time, changed and failed resources). A monitoring check that alerts if that file is more than two hours old catches stalled agents.
  • The agent sends a report to the server on every run; by default it is stored in /opt/puppetlabs/server/data/puppetserver/reports/<certname>/.
  • With more than a handful of nodes, PuppetDB (or OpenVoxDB) stores facts, catalogues and reports for all of them, and a dashboard such as Puppetboard shows which nodes fail or have not reported for a while.

11. The ticket: "an agent stopped applying changes"

The time server change reached every Linux server except srv-app-02.contoso.local, rebuilt last week. Puppetboard says its last report is nine days old. First, a manual run on the agent, reading the first error:

bash · srv-app-02.contoso.local
sudo /opt/puppetlabs/bin/puppet agent -tError: The certificate for 'CN=srv-app-02.contoso.local' does not match its private key

The four usual causes, in order of frequency:

1. A certificate that does not match after a rebuild. The new machine generated a new key, but the CA still holds the signed certificate of the previous machine with the same name. Remove it on both sides and sign again:

bash · server and agent
# on puppet.contoso.localsudo /opt/puppetlabs/bin/puppetserver ca clean --certname srv-app-02.contoso.local# on srv-app-02.contoso.localsudo /opt/puppetlabs/bin/puppet ssl cleansudo /opt/puppetlabs/bin/puppet agent -t# back on the server: check the fingerprint and signsudo /opt/puppetlabs/bin/puppetserver ca sign --certname srv-app-02.contoso.local

puppet ssl clean deletes the agent's key and certificate; on Windows it is the same command in an administrator console. So it does not happen again, the procedure for decommissioning or rebuilding a server includes puppetserver ca clean and, with PuppetDB, puppet node deactivate.

2. Clock skew. Errors such as certificate verify failed (certificate is not yet valid), or a freshly signed certificate the agent treats as expired. timedatectl on Linux or w32tm /query /status on Windows. It is exactly what the timesync module prevents, but a freshly installed machine does not have it yet: fix the time by hand before the first puppet agent -t.

3. Port 8140. Failed to open TCP connection to puppet.contoso.local:8140 points at the network or a firewall, not Puppet. nc -zv puppet.contoso.local 8140 on Linux or Test-NetConnection puppet.contoso.local -Port 8140 on Windows. If it does not connect, check the server's firewall, the machine's own firewall and whether the name resolves to the right IP.

4. Agent disabled. Someone stopped it for a manual change and never re-enabled it:

bash · srv-app-02.contoso.local
sudo /opt/puppetlabs/bin/puppet agent -tNotice: Skipping run of Puppet configuration client; administratively disabled (Reason: 'INC-4471 manual chrony test');Use 'puppet agent --enable' to re-enable.sudo /opt/puppetlabs/bin/puppet agent --enablesudo /opt/puppetlabs/bin/puppet agent -t --noop

puppet agent --disable "reason" is the right way to stop an agent: it leaves the reason in plain sight. Before re-enabling it, --noop shows what Puppet will undo of what was changed by hand; if that change should stay, it goes into the code first.

In the ticket: node, first error, cause, commands run on server and agent, and the final run with exit code 0 or 2 and no failures.

Next steps

Is something missing, or different in your environment? Get in touch and I'll fix it.

Tech Career Lab · newsletter

¿Te ha servido? Cada dos semanas envío una idea práctica para perfiles IT: LinkedIn, proyectos, networking y procesos de selección.Found this useful? Every two weeks I send one practical idea for IT professionals: LinkedIn, projects, networking and hiring processes.

Apúntate y llévate las 33 ideasSign up and get the 33 ideas →