Thursday, June 5, 2014

Managing users with Puppet (aka creating a custom resource type)

I have to admit I've become something of a puppet fanatic.  I now have a policy of no changes to systems outside of puppet, no exceptions, and for the most part that works well.   The only exceptions I have at this stage are significant file system changes, and they don't happen all that often.

It takes a while to get your head around puppet and how to make use of the features it provides, one that I really like is user management where centralised authentication isn't an option.

Puppet provides a convenient resource type for managing users which can be used to create your initial template:

$/usr/local/bin/puppet resource user FOO
 user { 'FOO':
  ensure           => 'present',
  comment          => 'User FOO',
  gid              => '56666',
  groups           => ['support'],
  home             => '/home/FOO',
  password         => '<its a secret>',
  password_max_age => '99999',
  password_min_age => '0',
  shell            => '/bin/bash',
  uid              => '56666',
}


This is an easy way to create an initial user resource for central management, however I've often found that I want to be able to manage more than the basic account entries.   What if we want to install config files and manage permissions for each user in a generic way ?

The simple answer is to create our own resource type.  For example I want to be able to manage users, their group memberships, uid/gid and ssh key essentially a subset of the default user resource.   I also want to enforce file permissions and install some 'standard' config files when user accounts are created.

So we define a custom virtual resource type 'account':

class virtual::define::account {
}
define account::virtual ($uid,
                         $realname,
                         $unixgroups,
                         $key,
                         $ensure) {
  case $osfamily {
    /RedHat/: {
      case $ensure {
        /present/: {
          user { $title:
            ensure => present,
            uid => $uid,
            gid => $title,
            groups => $unixgroups,
            membership => inclusive,  
            shell => '/bin/bash',
            home => "/home/${title}",
            comment => $realname,
            managehome => true,
            require => Group[$title],
          }
          group { $title:
            gid    => $uid,
          }
          file { "/home/$title":
            ensure => directory,
            owner => $title,
            group => $title,
            mode => "0750",
            require => [ User[$title], Group[$title] ],
          }
          file { "/home/$title/.ssh":
            ensure => directory,
            owner => $title,
            group => $title,
            mode => "0700",
            require => [ User[$title], Group[$title], File["/home/$title"] ],
          }
    file { "/home/$title/.ssh/authorized_keys":
            ensure => file,
            content => $key,
            owner => $title,
            group => $title,
            mode => "0600",
            replace => false,
            backup => false,
            require => [ User[$title], Group[$title], File["/home/$title/.ssh"] ],
          }
          exec { "${title}-etc-skel":
            command => "/bin/cp /etc/skel/.* /home/${title}/; chown -R ${title}:fxsupport /home/${title}",
            require => [ User["${title}"], File["/home/$title"] ],
            unless => "/usr/bin/test -f /home/$title/.bash_profile",
          }
          file_line{"$title-umask":
            ensure => "present",
            path => "/home/$title/.bash_profile",
            line => "umask 0022",
            require => [ User["$title"]],
          }

          file { "/home/$title/.deploytool":
            ensure => file,
            source => "puppet:///modules/virtual/default-deploytool",
            owner => $title,
            group => $title,
            mode => "0600",
            replace => "true",
            backup => "true",
            require => [ User[$title], Group[$title], File["/home/$title"]],
          }
        } # present
        /absent/: {
          user { $title: ensure => absent, uid => $uid, gid => $title, before => Group["$title"]}
          group { $title: gid  => $uid, ensure => absent }
          file { "/home/$title": ensure => absent, force => true }
        } # absent
        default: {
        }
      } # case $ensure
    } # case RedHat
 default: {
    }
  }
}
I put this class into the 'modules/virtual/manifests/define/account.pp' directory.   Once created I can define the actual user accounts in 'modules/virtual/manifests/account.pp:
 
class virtual::accounts {    include virtual::define::account
            @account::virtual { 'FOO':                uid => 501,                realname => 'User FOO',                unixgroups => ["support"],                key => "<ssh key>",                ensure => present,        }}
Declaring a virtual account resource just as you would the standard 'user' resources.

Getting puppet to create the accounts uses the same mechanism you would use for a 'user' resource.  For example to create all user accounts in with the 'support' group:
class usermgmt::createaccounts {
  case $::osfamily {
    /RedHat/: {
        include virtual::groups
        include virtual::accounts
        realize(Group['support'])
        Account::Virtual <| unixgroups == 'support' |>
    } # Redhat
    default: {}
  } # case
}



And your done.   For bonus points you can use the 'resources' class to purge any accounts that are not explicitly managed by puppet, although do use care with this one as the results could be disastrous if your not careful...
    resources { 'user':
            purge => true
    }

No comments:

Post a Comment