I Automated Our Entire Office Network with Puppet — Here's How It Works
So at work I got tasked with figuring out how to manage software and configurations across all the machines on our internal network without having to touch each one manually. You know the deal — IT nightmare stuff. Someone needs Chrome installed, someone else needs VLC, and before you know it everything is inconsistent chaos and you’re the one getting blamed for it.
The answer? Puppet. And honestly, it’s pretty sick once it’s actually running — emphasis on “once it’s actually running” because getting there was a journey.
This post is a breakdown of what I built, how it works, and the several moments where I questioned my life choices.
What Even Is Puppet?
Puppet is a configuration management tool. The idea is simple: instead of logging into every machine and doing stuff manually, you declare what state you want each machine to be in — what software should be installed, what services should be running, what files should exist — and Puppet makes it happen. Automatically. On a schedule.
It’s been around forever and is battle-tested in proper enterprise environments. Think of it like writing a contract for your machines and having Puppet be the one enforcer who actually shows up.
Sounds great, right? It is. But first, you have to install it.
Step 0 — The Distro Situation (A Moment of Grief)
I run Arch. I love Arch. Arch is my home. But Puppet, in its infinite wisdom, decided that its official package support would be for Debian and Red Hat based systems only. No properly maintained AUR package for the server. No official repo. Just vibes and disappointment.
So I had to spin up Ubuntu Server for the Puppet Server. On a ThinkPad. Like some kind of normie.
It’s fine. Ubuntu is fine. I’m fine. We don’t need to talk about this anymore.
The Setup
The whole thing runs on a server–agent model. One Puppet Server holds all the rules (called catalogs), and every machine on the network runs a Puppet Agent that checks in every 30 minutes and does whatever it’s told. It’s essentially a very obedient robot army, which is exactly what we needed.
Here’s the hardware:
Puppet Server — a Lenovo ThinkPad T460 running Ubuntu Linux (pours one out for Arch)
- Intel Core i5-6200U, 8 GB RAM, 256 GB SSD
- Puppet 8.10.0
- Static IP:
192.168.1.100 - Hostname:
thinkmad.local
Agent Node — a Windows machine called ergode-mu-515
All communication is SSL/TLS encrypted over port 8140. Puppet has its own built-in Certificate Authority that handles this — every node gets a signed cert before it’s allowed to talk to the server. Properly secure.
Setting Up the Puppet Server (A Guided Tour Through Dependency Hell)
Here’s the thing nobody tells you upfront: puppetserver is a Java application. That means before you even get to Puppet itself, you’re pulling in the JVM, a mountain of Ruby gems, and enough dependencies to make apt momentarily question its own existence. You run apt-get install puppetserver and then you just… wait. And watch. And start wondering if anything is actually happening.
# Add the Puppet 8 repo
wget https://apt.puppet.com/puppet8-release-$(lsb_release -cs).deb
sudo dpkg -i puppet8-release-$(lsb_release -cs).deb
sudo apt-get update
# Install (go make tea, seriously)
sudo apt-get install puppetserver -y
Watch apt confidently download half the internet and then act like that’s completely normal. It is, apparently. This is fine.
Once that marathon finishes, edit /etc/puppetlabs/puppet/puppet.conf to set DNS alt names before you start the server for the first time. This part is load-bearing — I’ll explain exactly why in the Rough Edges section:
[server]
dns_alt_names = puppet, thinkmad.local, 192.168.1.100
Then start it:
sudo systemctl start puppetserver
sudo systemctl enable puppetserver
The JVM Memory Situation
Puppet Server runs on the JVM and by default allocates 2 GB of heap memory. On an 8 GB machine, that’s a quarter of your RAM just sitting there being Java about things. Startup was slow enough that I thought it had crashed. Twice.
The fix is simple — edit /etc/default/puppetserver:
JAVA_ARGS="-Xms512m -Xmx512m"
512 MB works fine for a small environment. The server comes up in a reasonable time and stops eating RAM like it’s at a buffet. Not really Puppet’s fault — JVM gonna JVM.
Connecting the Windows Agent
On the Windows side, Puppet provides an .msi installer which is probably the least painful part of this whole project. Silent install via Command Prompt (run as Administrator):
msiexec /qn /norestart /i puppet-agent-8.x.x-x64.msi ^
PUPPET_MASTER_SERVER=192.168.1.100
Edit the agent config at C:\ProgramData\PuppetLabs\puppet\etc\puppet.conf:
[agent]
server = 192.168.1.100
Then run the agent to send a certificate signing request to the server:
"C:\Program Files\Puppet Labs\Puppet\bin\puppet.bat" agent --test
Yeah, that full path. Every single time. Because Puppet doesn’t add itself to the Windows PATH, presumably as a character-building exercise.
Back on the server, sign the cert:
sudo /opt/puppetlabs/bin/puppetserver ca list
sudo /opt/puppetlabs/bin/puppetserver ca sign --certname ergode-mu-515
Run the agent one more time and the node is enrolled. You now own that machine.
The Good Part — Actually Managing Stuff
This is where it genuinely gets fun. All the configuration lives in a file called site.pp on the server:
/etc/puppetlabs/code/environments/production/manifests/site.pp
(yes the file extension is .pp. no I don’t know why. yes it works. that’s enough for me.)
You match nodes by hostname and declare what you want. Here’s the full manifest for the Windows agent — software, services, a user account, files, and registry keys all in one go:
node 'ergode-mu-515' {
# Bootstrap Chocolatey (basically apt but for Windows, bless)
include chocolatey
# Install software
package { 'googlechrome': ensure => installed, provider => chocolatey, }
package { '7zip': ensure => installed, provider => chocolatey, }
package { 'notepadplusplus': ensure => installed, provider => chocolatey, }
package { 'vlc': ensure => installed, provider => chocolatey, }
# Enforce service states
service { 'wuauserv': ensure => running, enable => true, } # Windows Update
service { 'MpsSvc': ensure => running, enable => true, } # Windows Firewall
service { 'RemoteRegistry': ensure => stopped, enable => false, } # Disabled
# Create a local admin user — no touching, Puppet's got it
user { 'puppetadmin':
ensure => present,
password => 'StrongPass@123',
groups => ['Administrators'],
comment => 'Managed by Puppet',
}
# Managed directories and files
file { 'C:/PuppetManaged': ensure => directory, }
file { 'C:/PuppetManaged/config.txt':
ensure => present,
content => '# Managed by Puppet - do not edit manually',
require => File['C:/PuppetManaged'],
}
file { 'C:/PuppetManaged/scripts':
ensure => directory,
require => File['C:/PuppetManaged'],
}
# Registry keys — telemetry off, and a little "Puppet woz here" stamp
registry_key { 'HKLM\SOFTWARE\Policies\Microsoft\Windows\DataCollection': ensure => present, }
registry_value { 'HKLM\SOFTWARE\...\AllowTelemetry': ensure => present, type => dword, data => 0, }
registry_key { 'HKLM\SOFTWARE\PuppetManaged': ensure => present, }
registry_value { 'HKLM\SOFTWARE\PuppetManaged\ManagedBy': type => string, data => 'Puppet', }
registry_value { 'HKLM\SOFTWARE\PuppetManaged\Environment': type => string, data => 'production', }
}
What this manifest is doing in plain English:
| Category | What’s happening |
|---|---|
| Package manager | Chocolatey bootstrapped automatically |
| Software | Chrome, 7-Zip, Notepad++, VLC installed |
| Services | Windows Update + Firewall forced on; Remote Registry forced off |
| User account | puppetadmin created as local admin, zero manual steps |
| Files | C:/PuppetManaged/ directory, config file, scripts folder |
| Registry | Telemetry disabled; environment info stamped into registry |
The agent applies all of this in 6.58 seconds with no manual steps on the Windows machine. That’s the payoff — you sit there and watch things configure themselves like some kind of sysadmin wizard.
Does It Actually Work?
Yes, and it’s satisfying every time:
systemctl status puppetserver— active, running since boot, JVM behaving itselfpuppetserver ca list --all— both nodes with signed SHA256 certs, nicepuppet agent --teston Windows — contactsthinkmad.local:8140, compiles catalog, applies cleanly in 6.58 secondschoco liston Windows — all four packages sitting there like they were always meant to be- Windows Local Users and Groups —
puppetadminin Administrators, created entirely by Puppet, no human involved
That last one is genuinely funny to watch the first time. You didn’t open the Windows machine. You didn’t touch it. You just wrote some code on a completely different computer and a user account appeared on another machine across the network. It feels wrong in the best way.
The Rough Edges (A Comedy of Errors)
No honest writeup skips this section.
The DNS alt names ambush. Puppet Server must have dns_alt_names configured in puppet.conf before you start it for the first time. Not after. Before. If you forget — and you will forget, because nothing in the install process loudly warns you about this — agents connecting via IP get SSL errors and you get zero useful information about why. The fix is to stop the server, delete the entire CA, and regenerate all certificates from scratch. I found this out the way everyone finds things out: by doing it wrong first and spending a good chunk of time staring at error messages that made no sense.
Manual certificate signing. Every new node has to have its cert manually signed on the server before it can do anything. Autosigning exists but comes with security tradeoffs, so it stays manual for now. It’s not terrible, just the kind of thing that makes onboarding ten machines at once feel like a chore.
Puppet and clocks — a passive aggressive relationship. Puppet’s SSL validation is extremely sensitive to clock drift between nodes. If the clocks on your machines are even slightly out of sync, agents throw certificate errors that look completely unrelated to clocks. NTP wasn’t configured as part of this setup, which means there’s a ticking time bomb somewhere in this network. Literally. It’s on the to-do list.
Windows PATH, or the complete absence of it. Puppet doesn’t add its binaries to the Windows PATH by default. So every single command is:
"C:\Program Files\Puppet Labs\Puppet\bin\puppet.bat" agent --test
instead of just puppet agent --test. Every. Time. It’s not a dealbreaker — just one of those small daily frictions that adds up. You get used to it the same way you get used to a slightly wobbly chair.
Passwords hardcoded in the manifest. The puppetadmin password is sitting right there in site.pp in plain text. This is absolutely a demo shortcut and something Hiera exists specifically to fix — you’d pull config values like that out into a separate data layer. It’s on the improvement list, it just didn’t make it into this version.
What’s Next
The demo works well but there’s a clear list of things to fix before this scales to anything real:
- Hiera — get passwords and config values out of the manifest
- PuppetDB — enable run history and resource querying
- Autosigning with a shared secret — speed up node onboarding
- NTP enforcement — stop waiting for the clock drift disaster
- CI/CD pipeline — stop pushing code changes manually like it’s 2010
- More hardware — the ThinkPad is a trooper but it has limits
Wrapping Up
Puppet is genuinely powerful once you’ve wrestled it into submission. The dependency install is heavy, the JVM needs babysitting, the DNS alt names thing will catch you off guard, and yes — it forced me off Arch. I’m not bitter. I’m totally not bitter.
But when you’re watching a Windows machine configure itself from across the network while you sit completely hands-off? That feeling makes up for all of it.
If you’re doing any kind of sysadmin or infra work and haven’t looked at config management tools yet, this is a great rabbit hole. Just read the docs before you start the server for the first time. And set up NTP. And check your DNS alt names. And maybe have some tea ready for the install.
You’ll be fine.