Using Puppet for Account Management

It’s been over four months since my last post on open source Puppet, when I posted some updated multi-OS Puppet configurations. I’m back from a long Puppet hiatus with some information on using Puppet for managing user accounts.

It was pretty tough coming back to Puppet after a four-month break (during which time I’ve been spending plenty of time on KVM, Open vSwitch, and libvirt), and it was almost like learning Puppet again from scratch. Fortunately, with the help of a few folks from Twitter and the #puppet IRC channel, I was able to get managing user accounts with Puppet to work (and learned a little bit in the process).

The key to making it work is the idea of a virtual resource. In Puppet, you are normally only allowed to define a resource once; you’d then apply that same resource against multiple systems. However, if you define a user resource for one system, you’re then prevented from defining the same user resource for another system—the resource has already been defined. With a virtual resource, though, you can define the resource once (as a virtual resource; denoted with an “@” in front of the resource). Once a virtual resource has been defined, you can then realize (or instantiate) that resource as many times as needed across multiple systems. (At least, that’s how I understand it. I’m still wrapping my head around some of this stuff.) The Puppet Labs site does a much better job of explaining virtual resources.

Now that you’re armed with that little bit of background information, let’s get started on making this work. There are 2 basic steps involved:

  1. Create a class with a defined type that creates the virtual user resources.
  2. Realize the virtual user resources where needed.

Let’s tackle that first step: creating the class and defined type.

Essentially what you’re going to do to create the class and defined type is create a module. As such, we’ll want to follow Puppet’s guidelines for module structures. In my case, I created a directory for the class (I called mine accounts), with all the suggested subdirectories for a module, even if most of them weren’t needed. From there, I only need to create two files, both in the accounts/manifests directory: init.pp and virtual.pp.

I’ll come to the init.pp in a moment; somewhat counter-intuitively, we’ll start with the virtual.pp manifest first. Here’s the contents of that manifest:

I used the latest version of puppet-lint to ensure that all stylistic recommendations were followed properly. I derived this code from this example, which—once I wrapped my head around the concept—I found quite helpful. Note that there are no virtual resources here; those come in just a moment.

Now that the defined type is done, we can use it to actually create the virtual user resources. We’ll actually do that in the accounts/manifests/init.pp file, like this:

You’ll just repeat that snippet of test as many times as necessary to create a virtual “accounts::virtual” resource for each user account you want to manage within Puppet. Each “accounts::virtual” resource includes a user account, a group account, and a home directory, but we manage all those individual resources as one object through the class definition.

Once you’re ready to actually instantiate an virtual resource, you do that with a snippet of code like this:

Let’s break that down real quick:

  • This is a standard node definition, typically found in nodes.pp or similar.
  • Note that the accounts class (module) is included; this is the class (module) we created earlier, where we created the virtual user resources. This makes the virtual user resources “available” to this node.
  • Then we realize (or actually instantiate/create) the user account on this node with the realize (Accounts::Virtual['johndoe']) statement. Pay attention to the capitalized Accounts::Virtual in this code—this refers back to a resource that has already been defined. In this case, it’s the virtual resource you defined in the init.pp file in the class you established earlier.

With this structure in place, when the node named “” runs Puppet and connects to the Puppet master, it will create the realized resources—user account, group account, and home directory—specified in the node definition. Pretty cool, huh?

I freely admit that I’m still relatively new to Puppet, so I’m sure there are numerous ways this approach could be improved. I tested this code on both CentOS 6.3 as well as Ubuntu 12.04, and it seems to work fine on both platforms. Feel free to submit suggestions for improvement, corrections, or clarifications in the comments below.

Tags: , , ,

  1. Andrew’s avatar

    What is the use case here?

    If you do this, you will need to edit your puppet entries to change passwords or user details.

    Woudn’t it be better to use LDAP? (managing your nssswitch etc through Puppet of course ;))

  2. Brent Jones’s avatar

    LDAP is good, I use it extensively for user accounting on Linux.
    However, having local users/groups is nice for service accounts, should you have a problem with LDAP, at least your local services are running fine (and maybe have a local administrative account to come in on)

  3. mkruger’s avatar

    I picked up a few puppet books to beginn cutting my teeth on Puppet as well. Finding the time to read those books is my current challenge. We use LDAP as well, and in other places use NIS or active directory. If it was up to me, I would ditch them all and move to something like FreeIPA.

  4. Tiago’s avatar

    Hey! Nice article.

    How do I add more than one group to a certain user?

  5. Traver’s avatar

    Nice article.

  6. mandm’s avatar

    Did you ever manage to add the user to the the sudoers file?

    LEt me know

  7. Riccardo’s avatar

    Hi, the password is not set for the user, do you have an idea why?

  8. Axel’s avatar

    Hi their,

    what could we change in this config, to set the password only once?? Only in the time of first creating, thereby the user could change his password later on…


  9. slowe’s avatar

    Axel, you could use Puppet without a Puppet master abd just apply the manifest manually on the client. I don’t know of any other way.

  10. Axel’s avatar

    Thanks for answering me, slowe.

    This could be a solution:!topic/puppet-users/4ObpgEz_Qs8
    The 2nd post from Ygor i mean. I’ld test this later… and there are other hints and opinions…

  11. Anthony’s avatar

    I added this but in the node definition section, how do I group my needed users per different nodes so as not to fill up site.pp file.

    node ‘prod-web5′ {
    include base
    include users
    realize (Users::Virtual['anthony'])

    node ‘dev-web1′ {
    include base
    include users
    realize (Users::Virtual['admin'])

    I use different users for different groups of servers. I have production web and dev web, taxware, solr, backend, etc. I should have different users per group of servers, is there a way to add groups of users and then import to this node def?


Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>