Puppet – working with define based virtuals

Define Based Virtuals

Define-based virtuals are quite a powerful feature of Puppet, but many people either don’t understand them or don’t know how to apply them effectivly to their manifests. Due to Puppet’s normalized configuration structure, you can’t configure the same resource in two different places. For example, if the user mysql is configured in your database class, and then you realise that your webserver class also needs the mysql user, then, short of including the whhole database class, you cannot just configure the user here. The answer to this lies in resource virtualization.

Resources that are virtualized are effectivly non-real, they won’t be included in any configuration until they are explicitly realized. The good news is you can realize your resource multiple times in your manifests, so by defining the resource as virtual in a seperate class, both your webserver class and databasse class can realize it.

Version 0.23 of Puppet took this one step further, with the ability to virtualize your definitions rather than just native resource types. Now we can define a collection of resources, virtualize them, and realize them at whim in our manifests. I’ve seen a few Puppet installations and this method is far under used, and under appreciated.

To demonstrate, this is an example of how to manage your users with virtual define-based resources.

User management with defines and virtuals.

I’ve seen a few ways of managing users within a Puppet estate. The following is a quick guide to get you started managing users, passwords and ssh keys for your users. My favourite approach is to use define-based virtuals and split it out into several sections

  • Back end module to specify the definition for a user
  • A class with a list of virtualized users
  • Realizing your users ad-hoc in your classes

The advantage of this approach is that you can keep a central place for users whilst still being able to easily pick and choose which users get deployed with which classes. Lets break it down into our components;

Users module

As we want to manage users as well as ssh keys it makes sense to wrap this up into a definition, which we’ll call localuser. We create a module called users::virtual where we define the following class.

1234567891011121314151617181920212223242526class users::virtual {        define localuser ($uid,$gid,$pass,$sshkey=””) {                 user { $title:                        ensure  =>      “present”,                        uid     =>      $uid,                        gid     =>      $gid,                        shell   =>      “/bin/bash”,                        home    =>      “/home/$title”,                        comment =>      $realname,                        password =>     $pass,                        managehome =>   true,                }                 if ( $sshkey != “” ) {                 ssh_authorized_key { $title:                         ensure  =>      “present”,                         type    =>      “ssh-rsa”,                         key     =>      “$sshkey”,                         user    =>      “$title”,                         require =>      User[“$title”],                         name    =>      “$title”,                 }                }        }}

This class sets up our localuser definition in users::virtual, other resources can also be added here that will affect every user.

Creating our users class.

Now you can create a users.pp class that imports your module and defines the specific users you want to configure. We configure these as virtuals to give us the option of realizing them whenever we want.

1234567891011class mysite::users {include users::virtual         @users::virtual::localuser { “bobsmith”:                uid     =>      “3202”,                gid     =>      “users”,                pass    =>      ‘$1$fU8c0mlIjDFYCRu0U+r1’,                sshkey  =>      “AAAAB3NzaC1yc2EAAAABIwAAAQEAtbsafzNX08oT63vnKh6LNYVpFM9U42knt+tUMvhTQaOEGVnsRH6zVQj86PLYo9HD7MCVqYAloKRN6hVvoqU++CSLO0zUYsQ4bX/+DQthtKcOwU76QLFTcXVRIIGMH++GLHGjphEhjPAJc/rPM0YswCetOm3JVGVB9x/WJFOmoT+a7r4IXaULaNTYZOPZ6fr/CvUB/w3NBvPnmLMxwPFOgBLxcQ9Tbpa5sjwi1thlXl1ZfQ8Sh++gg60odTHbAhwZOU70mA8WGOmkuETDQzunQvTK14fGDvFSHJNE5nYse8IPChbfrSMJl1PsWB+SiiGrPVQtly9BEOYi/aOokj3vfQ==”,        } }

The passord should be the fully encrypted string as would appear in the shadow file, don’t forget to use single quotes if your password string contains $ to prevent it from being interpereted as a variable.

sshey is optional (note the conditional in our users module), and, if defined will set up the key in ~/.ssh/authorized_keys. Now you have a central list of users as virtuals that call our custom define, they can be easily included, or realized, at whim in your other classes.

Realize the user

Within your manifests for your server profile, whenever you require the user bobsmith to be part of the configuration, you simply realize it like this

123456class server::web {        include mysite::users        realize (                Users::Virtual::Localuser[“bobsmith”],        )}

The above example realizes the user by the name of “bobsmith”. Another very useful way to realise users is by using Puppet’s collection syntax. For instance, to realize all the users in the users group, you would simply replace the realize statement with:

1        User <| group == users |>

… and all your virtual users that match the gid of users will be included.


What we’ve acheived by using virtual define-based resources here is primarily,

  • An abstracted module for defining what a user is, rather than who
  • A central place to manage all of our virtual users
  • A very flexible and easy way to pull users into our classes based on name or attribute

For more information on working with Puppet virtuals, check out the documentation hereFollow and share if you liked this

Subscribe to Craig Dunn

Sign up now to get access to the library of members-only issues.
Jamie Larson