Back in 2017, we had a customer who wanted to use Puppet to deploy the Nanitor Agent across their Linux estate. My good friend Sverrir Arason, a true Puppet expert, quickly got a working module up and running.
Fast forward 8 years, the module had seen usage but needed maintenance to keep up with modern Puppet practices and packaging changes. It was about time to give it some love, clean it up, and ensure it worked on modern Debian and RHEL-based systems.
I decided to get my hands dirty, set up a development environment, and get it up and running again. It was also a great opportunity to learn more about Puppet by working hands-on and getting feedback from Sverrir as we went.
You can find the module on GitHub.
The easiest way to work with Puppet modules is inside a clean, reproducible environment.
We created a Dockerfile.dev:
FROM debian:bullseye
ENV PATH="/opt/puppetlabs/bin:$PATH"
RUN apt-get update && \
apt-get install -y locales curl gnupg lsb-release git vim wget && \
locale-gen C.UTF-8 && \
echo 'en_US.UTF-8 UTF-8' >> /etc/locale.gen && \
locale-gen && \
curl -O https://apt.puppet.com/puppet7-release-bullseye.deb && \
dpkg -i puppet7-release-bullseye.deb && \
apt-get update && \
apt-get install -y puppet-agent && \
puppet module install puppetlabs-apt && \
rm -rf /var/lib/apt/lists/*
WORKDIR /etc/puppetlabs/code/environments/production/modules/nanitor_agent
We then run:
docker build -t puppet-dev -f Dockerfile.dev .
docker run -it --rm puppet-dev /bin/bash
Inside the container, we test the module in a clean environment without messing up our workstation.
In production, customers typically use Puppet Master, which automatically distributes modules and enforces state across all Linux devices.
For local testing and development, we use masterless Puppet:
This is faster for development, while Puppet Master will handle dependencies and ordering automatically in production.
To install and sign up the Nanitor Agent to your Nanitor server, create /root/signup_test.pp:
class { 'nanitor_agent':
signup_url => 'https://myinstance.nanitor.net/api/agent_get_signup_key/REST_OF_KEY_HERE',
}
You will need a valid signup URL from your Nanitor instance.
On a Debian 12 test VM:
apt updates
apt install -y puppet git
puppet module install puppetlabs-apt
mkdir -p /etc/puppet/environments/production/modules/
git clone https://github.com/Nanitor/nanitor-agent-puppet.git /etc/puppet/environments/production/modules/nanitor_agent
puppet apply /root/signup_test.pp --modulepath=/etc/puppet/environments/production/modules:/etc/puppet/code/modules
This will:
One learning from this process was understanding how Puppet handles apt sources. While notify_update => true triggers apt-get update, it does not guarantee ordering with your package { 'nanitor-agent': } resource.
To avoid “package not found” errors, we explicitly chained:
Apt::Source['nanitor-agent'] -> Package['nanitor-agent']
This ensures Puppet waits for apt-get update before attempting to install the package, following best practices instead of using exec hacks.
On an Alma Linux 9 test VM:
dnf update
dnf install -y https://yum.puppet.com/puppet7-release-el-9.noarch.rpm
dnf install -y git puppet
mkdir -p /etc/puppetlabs/code/environments/production/modules
git clone https://github.com/Nanitor/nanitor-agent-puppet.git /etc/puppetlabs/code/environments/production/modules/nanitor_agent
puppet apply /root/signup_test.pp --modulepath=/etc/puppetlabs/code/environments/production/modules:/etc/puppetlabs/code/modules
This will:
The workflow is nearly identical, which is the beauty of using Puppet to handle OS differences cleanly within the same module.
It was rewarding to revamp the Nanitor Puppet module and bring it up to modern standards while learning more about real-world Puppet dependency management along the way.
Thanks to guidance from Sverrir Arason, the master, the module is now production-ready, tested on Debian 12 and Alma Linux 9, and supports clean deployments of the Nanitor Agent for our customers.
This ensures customers can deploy the Nanitor Agent seamlessly at scale using Puppet, reinforcing our philosophy of automation, clarity, and minimalism.