Automate System Administration Tasks with Puppet

Use Puppet for configuration management.
Introducing Classes

Although powerful, these resource definitions can become unwieldy. Puppet has ways around this too. Create a directory under manifests called services, and create a file in this directory called ntpclient.pp with the following contents:

class ntpclient {
    package {
            ensure => installed

    file {
            mode => 644,
            owner => root,
            group => root,
            path => "/etc/ntp.conf",
            source => "puppet://",

    service {
            ensure => true,
            enable => true,
            subscribe => [ File["ntp.conf"], Package [ntp] ],

This new file contains the three resources you created earlier, surrounded by a class definition. A class groups several resources, which simplifies your configuration and promotes manifest sharing.

Now, replace your site.pp with this simplified manifest:

import "services/*"

include ntpclient

The import line reads in all the files inside the services directory. The include line evaluates the class, which means that the class will be applied to the node. This configuration has the same effect as the one before, except the NTP client functionality now has been bundled into the class.

Getting Selective

So far, the manifest has assumed that all clients get the same configuration. The easiest way to give different configurations to different clients is with a node definition. A node definition applies a series of configuration directives to a given set of nodes. Replace your site.pp as follows:

import "services/*"

node test2, test3 {
    include ntpclient

node default {


With this policy in place, only test2 and test3 will have the ntp client class applied. Any other client will be caught by the default statement, which has no resources defined.

Facter is another way to differentiate hosts. Facter generates facts about a machine, such as the operating system, hostname and processor. Simply type facter to see a list of the currently known facts. Here is a subset of the facts generated on one of my test machines:

architecture => i386
domain =>
facterversion => 1.3.8
fqdn =>
hardwareisa => i686
hardwaremodel => i686
hostname => test2
id => root
ipaddress =>
ipaddress_eth0 =>
kernel => Linux
kernelrelease => 2.6.18-8.el5xen
lsbdistcodename => Final
lsbdistdescription => CentOS release 5 (Final)
lsbdistid => CentOS
lsbdistrelease => 5
macaddress => 00:16:3E:5D:22:17
macaddress_eth0 => 00:16:3E:5D:22:17
memoryfree => 159.17 MB
memorysize => 256.17 MB
operatingsystem => CentOS
operatingsystemrelease => 2.6.18-8.el5xen
processor0 => Intel(R) Pentium(R) 4 CPU 1.80GHz
processorcount => 1
ps => ps -ef
puppetversion => 0.24.2

Facts are exposed in the manifest as variables. The operatingsystem fact is seen as $operatingsystem. A common use of this is to make the same resource behave differently, depending on the operating system:

file { "foo" 
    name => $operatingsystem ? {
        solaris => "/usr/local/etc/foo.conf",
        default => "/etc/foo.conf"

The above example uses a Puppet selector to set the name attribute instead of a static string. A selector is much like a case statement in that it can return different values depending on the input. This file resource refers to /usr/local/etc/foo.conf on Solaris systems and /etc/foo.conf on other systems. The system type is determined from the input to the selector, which is the $operatingsystem Facter variable.

You can add your own facts by writing a Ruby script. See Resources for links to documentation for adding custom facts.

Puppet vs. the Alternatives

My first experience with configuration management was with a product called cfengine. With cfengine, I was able to manage a Web cluster of 14 servers easily and reduce the time to install a new node from several hours to a matter of minutes. Puppet's author has a great deal of cfengine experience and built Puppet to address many shortcomings of cfengine.

Given that cfengine has a much wider install base than Puppet, why would one choose Puppet? After comparing the two, I've discovered several reasons. First, Puppet has a much cleaner configuration than cfengine. In the cfengine world, you are concerned with the ordering of certain operations, whereas Puppet handles ordering with the subscribe attribute (and some others).

Cfengine has many commands for adding and removing lines from files, which don't exist natively in Puppet. Puppet addresses this by providing native resource types for many of the systems that I found myself editing by hand, such as mountpoints. Using a dedicated resource type means the manifest is clear and simple.

Cfengine is open source, but it has a more closed community than Puppet. You can extend cfengine through modules, much akin to Puppet's recipes and facts, but it is nowhere near as integrated. Puppet seems designed from the start to be extensible, where cfengine feels like an afterthought. Puppet also promotes recipe sharing by making them modular, where sharing cfengine code is more difficult because the resources are in different parts of the cfengine policy.

Puppet is written in Ruby, and cfengine is written in C. Initially, I thought this was an advantage for cfengine, but after getting into Puppet, I realized it's not a big deal. Puppet's author takes great pains to abstract Puppet's configuration from the Ruby language, so no knowledge of Ruby is needed.

I found the learning curve for cfengine to be the steepest. Granted, I had no understanding of configuration management when starting with cfengine, and I had some cfengine experience by the time I started with Puppet, but many of my stumbling blocks have been fixed in Puppet.

Both projects offer support over their IRC channels. Cfengine has an extensive on-line manual and a fair bit of third-party documentation on other Web sites. Puppet has an excellent wiki and a comparable amount of third-party documentation.

Although Puppet is younger compared to cfengine, its openness and extensibility are what make it a better choice than cfengine.



Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Puppet Tutorial

John Arundel's picture

Excellent write-up! I'm a Puppet consultant and have just published the first article in a Puppet tutorial series up to date with Puppet 0.25.0 and the 'best practices' module layout:

Puppet Tutorial: Powering Up With Puppet

I would love to hear from any Puppet / Linux beginners whether they found the article helpful or not, or if anyone has suggestions for how it might be improved.

Great write-up

mschenck's picture

I really like your organization. Puppet is a great tool. One more area for people to look into is the use of templates. They're a very powerfully addition to puppet, leveraging ruby's erb files.

Erb files probably get the greatest exposure in ruby on rails, but can prove to be a very powerful tool for system administrator in lending a hand to remove human error when managing the differences in configuration between different environments.

Speaking of environments, that's also great asset puppet provides and should be considered another area of interest for potential adopters of puppet to look into.