Questy.org

Puppet News and Views

Comprehensive Guide to Puppet Resources

| Permalink

Puppet Primer III – Resources

Welcome back to the Puppet Primer series. As I mentioned in the first installment of this series, we were to cover resources in depth later, and this is that article.

In the context of Puppet, when we say “resources”, we are referring to the fundamental unit we use in modeling system configurations. Puppet says it this way:

Resources are the fundamental unit for modeling system configurations. Each resource describes the desired state for some aspect of a system, like a specific service or package. When Puppet applies a catalog to the target system, it manages every resource in the catalog, ensuring the actual state matches the desired state.1.

But herein there is a disconnect. One might read this and say to themselves… “so what? How does this help me configure this fleet of computers here?” Here is where some amount of remediation and ordering of our thoughts is appropriate.

Infrastructure as Code

The buzzword above is pervasive today. Whether we’re talking about cloud computing resources, local VMs, or even containers and groups of containers, the buzzword “Infrastructure as Code” or “IaC” has been overused and is as loaded as the term “DevOps”. So first, let’s break down IaC. In short:

Infrastructure as code (IaC) is the process of managing and provisioning computer data center resources through machine-readable definition files, rather than physical hardware configuration or interactive configuration tools.[1] The IT infrastructure managed by this process comprises both physical equipment, such as bare-metal servers, as well as virtual machines, and associated configuration resources. 2.

To restate, in the old days we connected to individual systems one at a time to perform a configuration action against that machine. It may be to setup a web server, edit a DNS Zone file, configure a mail server, or even just modify content, but it was a very manual and very laborious process prone to error and misconfiguration, differences between systems, known as “drift”, resulting in improper or incomplete coverage of the environment.

Infrastructure as Code lends us the opportunity to think about our systems in a different way, as code elements rather than physical machines with installed software. One might then consider “All that is great and I can learn how to do that, but what provides me the interface or interaction with the operating system and hardware to see the effective change that I want?”

Your systems need some sort of interface between code and configuration, an interface to the system that can occur programmatically, and a method for applying that code-based work to the operating systems and the underlying hardware. We refer to the language as a “Domain Specific Language”. A Domain Specific Language simply means you have a language construct that is designed to manage one group of things in a particular domain or “grouping of things”.

A domain specific language such as the Puppet language is a “desired state configuration” language or a DSC. Puppet describes the language here:

The “secret sauce” of Puppet is the layer that translates your commands into Operating System configurations. This is known as a “resource abstraction layer” we spoke about in Puppet Primer I.

Puppet has taken the time to write code that recognizes and can configure many different Linux/UNIX platforms as well as MacOS and Windows. Consider the files on a Puppet system that can be found in the following location:

/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/puppet/

In this location, there are hundreds of files, functions, and methods that are designed by Puppet to address the various components of the system they are running on. For instance, in this directory there is a “provider” subdirectory that contains many components of the Puppet language you may be familiar with:

exec
file
group
package
service
user

What Puppet has done here is created code that recognizes the platform you are running on, and accommodates whichever platform it is to perform an action. For example, the “user” directory here has several files:

aix.rb
directoryservice.rb
hpux.rb
ldap.rb
openbsd.rb
pw.rb
useradd.rb
user_role_add.rb
windows_adsi.rb

All of these files accommodate the various circumstances one might encounter when trying to add a user. Let’s look at the “useradd” file. In particular, let’s compare components that add the user in various platforms. Here is a small part of the useradd.rb file:

  if value == :absent                                 
    if Puppet.runtime[:facter].value('os.name') == 'SLES' &&   Puppet.runtime[:facter].value('os.release.major') == "11"
      -1
    else
      ''                                 
    end
  else
    case Puppet.runtime[:facter].value('os.name')
    when 'Solaris'
      # Solaris uses %m/%d/%Y for useradd/usermod
      expiry_year, expiry_month, expiry_day = value.split('-')
      [expiry_month, expiry_day, expiry_year].join('/')                                   
    else
      value
    end
  end
},

As you can see, code has been written here for “useradd” to accommodate SLES and Solaris. There are many more examples where code accommodates special functions features, information, methods, command line switches and much more. When working with customers, I often will bring there here and walk through this particular portion of Puppet even though you never interact with this code or change it in any way. It gives sysadmins and developers a peek under the covers as to how resources are managed, and how (with Ruby) they are “separated” from the underlying hardware and operating system with accommodating code.

Commonly Used Resources

For our purposes here, we will deal with a small subset of resources, namely: “file”, “user”, “exec”, “service”, and “package”. We will also consider “catalogs” and “facts” here since they all work hand-in-hand.

As noted above, we see that code has been written that directly interoperates with the OS, thus shielding you from the idiosyncrasies of the underlying platform. By learning the Puppet DSL instead of each platform upon which you need to work, you only need to learn one command to add a user rather than “useradd”, “adduser”, “smitty”, “sam”, and any number of other platform commands you might need to interact with. Consider the following:

user { 'bob':
  ensure   => 'present',
  uid      => '500',
  gid      => '500',
  comment  => 'Bob from Accounting - x321',
  shell    => '/bin/bash',
  home     => '/home/bob',
  password => '1/A5wIWqKsPYo',
  ...
}

The rest of the attributes you can choose to configure the user “bob” can be found in the reference located here. The great thing about Puppet code is this above block can be applied to any system of any type that Puppet supports. One set of code applied to your entire fleet, whether Ubuntu, RedHat, Windows, Solaris, or AIX. It gives you the opportunity to learn one configuration language to do what you want to do, leaving the specifics of the underlying OS’s user management to Puppet.

In the preceding block, we refer to the “user” portion as the resource. The name “bob” on the same line is the namevar or “name” attribute, but you can also give your resources interesting and descriptive names. The section after “user”, then becomes the “resource title”, then set the namevar itself to the username like so:

user { 'Bob Williams - Accounting, Desk 123AX - Extension 321':
  ensure   => 'present',
  name     => 'bob',
  uid      => '500',
  gid      => '500',
  comment  => 'Bob Williams',
  shell    => '/bin/bash',
  home     => '/home/bob',
  password => '1/A5wIWqKsPYo',
  ...
}

The options are limitless, and are defined by you to be whatever you want.

Resources across the gamut in Puppet follow the same general rules. You can browse the available resource types and links to all the options supported by each one here.

Manifests

The next most common term you’ll hear in Puppet-world is this concept of a “manifest“. Puppet has adopted the idiom of resources collected together into manifests, manifests collected together into modules (along with other components), modules collected together in profiles and profiles collected together into roles.

This sounds like a lot to consume, but in actuality, except for manifests and modules, all of the above are logical organizational units known as a design pattern, which is simply a defined way to organize and arrange code to be used in an environment. We will go into detail on manifests in the next installment.