Managing Linux Using Puppet

Listing 5. Re-running Puppet

cd /etc/puppet/linuxjournal
git pull
puppet apply /etc/puppet/linuxjournal/manifests

Creating Modules

Putting all this code inside the node isn't very reusable. Let's move the user into a developer_pc module and call that from your node. To do this, create the file modules/developer_pc/manifests/init.pp in the git repository as per Listing 6. This creates a new module called developer_pc that accepts a parameter called developer name and uses it to define the user.

Listing 6. /modules/developer_pc/manifests/init.pp

class developer_pc ($developer) {
    user { "$developer":
        ensure => present,
        comment => "Developer $developer",
        shell => '/bin/bash',
        managehome => true,

You then can use the module in your node as demonstrated in Listing 7. Note how you pass the developer parameter, which is then accessible inside the module.

Listing 7. /manifests/puppet-test.pp

node 'puppet-test' {
    package { 'vim':
        ensure => 'present'

    package { 'emacs':
        ensure => 'absent'

    class { 'developer_pc': developer => 'david' }

Apply the changes again, and there shouldn't be any change. All you have done is refactored the code.

Creating Static Files

Say you would like to standardize your vim config for all the developers and stop word wrapping by setting up their .vimrc file. To do this in Puppet, you create the file you want to use in /modules/developer_pc/files/vimrc as per Listing 8, and then add a file resource in /modules/developer_pc/manifests/init.pp as per Listing 9. The file resource can be placed immediately below the user resource.

Listing 8. /modules/developer_pc/files/vimrc

# Managed by puppet in developer_pc

set nowrap

Listing 9. /modules/developer_pc/manifests/init.pp

file { "/home/$developer/.vimrc":
    source => "puppet:///modules/developer_pc/vimrc",
    owner => "$developer",
    group => "$developer",
    require => [ User["$developer"] ]

The file resource defines a file /home/$developer/.vimrc, which will be set from the vimrc file you created just before. You also set the owner and group on the file, since Puppet typically is run as root.

The require clause on the file takes an array of resources and states that those resources must be processed before this file is processed (note the uppercase first letter; this is how Puppet refers to resources rather than declaring them). This dependency allows you to stop Puppet from trying to create the .vimrc file before the user has been created. When resources are adjacent, like the user and the file, they also can be "chained" using the -> operator.

Apply the changes again, and you now can expect to see your custom .vimrc set up. If you run puppet apply later, if the source vimrc file hasn't changed, the .vimrc file won't change either, including the modification date. If one of the developers changes .vimrc, the next time puppet apply is run, it will be reverted to the version in Puppet.

A little later, say one of the developers asks if they can ignore case as well in vim when searching. You easily can roll this out to all the desktops. Simply change the vimrc file to include set ignorecase, commit and run puppet apply on each machine.

Creating Dynamically Generated Files

Often you will want to create files where the content is dynamic. Puppet has support for .erb templates, which are templates containing snippets of Ruby code similar to jsp or php files. The code has access to all of the variables in Puppet, with a slightly different syntax.

As an example, our build process uses a file called $HOME/Projects/ that contains the name of the build root. This is typically just the user's home directory. You can set this up in Puppet using an .erb template as shown in Listing 10. The erb template is very similar to the static file, except it needs to be in the template folder, and it uses <%= %> for expressions, <% %> for code, and variables are referred to with the @ prefix.

Listing 10. /modules/developer_pc/templates/

# Managed by Puppet

dir.home=/home/<%= @developer %>/

You use the .erb template by adding the rules shown in Listing 11. First, you have to ensure that there is a Projects directory, and then you require the file itself. The -> operator is used to ensure that you create the directory first and then the file.

Listing 11. /modules/developer_pc/manifests/init.pp

file { "/home/$developer/Projects":
    ensure => 'directory',
    owner => "$developer",
    group => "$developer",
    require => [ User["$developer"] ]


file { "/home/$developer/Projects/":
    content => template('developer_pc/'),
    owner => "$developer",
    group => "$developer",


David Barton is Managing Director of OneIT, a company specializing in custom business software development. He's been using Linux since 1998 and managing OneIT's Linux servers for more than 10 years.