Resources

Resources are the fundamental unit for modeling system configurations. Resource declarations have a lot of possible features, so your code's readability is crucial.

Resource names

All resource names or titles must be quoted. If you are using an array of titles you must quote each title in the array, but cannot quote the array itself.

Good:

package { 'openssh': ensure => present }

Bad:

package { openssh: ensure => present }

These quoting requirements do not apply to expressions that evaluate to strings.

Arrow alignment

To align hash rockets (=>) in a resource's attribute/value list or in a nested block, place the hash rocket one space ahead of the longest attribute name. Indent the nested block by two spaces, and place each attribute on a separate line. Declare very short or single purpose resource declarations on a single line.

Good:

exec { 'hambone':
  path => '/usr/bin',
  cwd  => '/tmp',
}

exec { 'test':
  subscribe   => File['/etc/test'],
  refreshonly => true,
}

myresource { 'test':
  ensure => present,
  myhash => {
    'myhash_key1' => 'value1',
    'key2'        => 'value2',
  },
}

notify { 'warning': message => 'This is an example warning' }

Bad:

exec { 'hambone':
path  => '/usr/bin',
cwd => '/tmp',
}

file { "/path/to/my-filename.txt":
  ensure => file, mode => $mode, owner => $owner, group => $group,
  source => 'puppet:///modules/my-module/productions/my-filename.txt'
}

Attribute ordering

If a resource declaration includes an ensure attribute, it should be the first attribute specified so that a user can quickly see if the resource is being created or deleted.

Good:

file { '/tmp/readme.txt':
  ensure => file,
  owner  => '0',
  group  => '0',
  mode   => '0644',
}

When using the special attribute * (asterisk or splat character) in addition to other attributes, splat should be ordered last so that it is easy to see. You may not include multiple splats in the same body.

Good:

$file_ownership = {
  'owner' => 'root',
  'group' => 'wheel',
  'mode'  => '0644',
}

file { '/etc/passwd':
  ensure => file,
  *      => $file_ownership,
}

Resource arrangement

Within a manifest, resources should be grouped by logical relationship to each other, rather than by resource type.

Good:

file { '/tmp/dir':
  ensure => directory,
}

file { '/tmp/dir/a':
  content => 'a',
}

file { '/tmp/dir2':
  ensure => directory,
}

file { '/tmp/dir2/b':
  content => 'b',
}

Bad:

file { '/tmp/dir':
  ensure => directory,
}

file { '/tmp/dir2':
  ensure => directory,
}

file { '/tmp/dir/a':
  content => 'a',
}

file { '/tmp/dir2/b':
  content => 'b',
}

Use semicolon-separated multiple resource bodies only in conjunction with a local default body.

Good:

$defaults = { < hash of defaults > }

file {
  default: 
    * => $defaults,;

  '/tmp/testfile':
    content => 'content of the test file',
}

Good: Repeated pattern with defaults:

$defaults = { < hash of defaults > }

file {
  default: 
    * => $defaults,;

  '/tmp/motd':
    content => 'message of the day',;

  '/tmp/motd_tomorrow':
    content => 'tomorrows message of the day',;
}

Bad: Unrelated resources grouped:

file {
  '/tmp/testfile':
    owner    => 'admin',
    mode     => '0644',
    contents => 'this is the content',;

  '/opt/myapp':
    owner  => 'myapp-admin',
    mode   => '0644',
    source => 'puppet://<someurl>',;

  # etc
}

You cannot set any attribute more than one time for a given resource; if you try, Puppet raises a compilation error. This means:

  • If you use a hash to set attributes for a resource, you cannot set a different, explicit value for any of those attributes. For example, if mode is present in the hash, you can’t also set mode => "0644" in that resource body.
  • You can’t use the * attribute multiple times in one resource body, because * itself acts like an attribute.
  • To use some attributes from a hash and override others, either use a hash to set per-expression defaults, or use the + (merging) operator to combine attributes from two hashes (with the right-hand hash overriding the left-hand one).

Symbolic links

Declare symbolic links with an ensure value of ensure => link. To inform the user that you are creating a link, specify a value for the target attribute.

Good:

file { '/var/log/syslog':
  ensure => link,
  target => '/var/log/messages',
}

Bad:

file { '/var/log/syslog':
  ensure => '/var/log/messages',
}

File modes

  • POSIX numeric notation must be represented as 4 digits.
  • POSIX symbolic notation must be a string.
  • You should not use file mode with Windows; instead use the acl module.
  • You should use numeric notation whenever possible.
  • The file mode attribute should always be a quoted string or (unquoted) variable, never an integer.

Good:

file { '/var/log/syslog':
  ensure => file,
  mode   => '0644',
}

Bad:

file { '/var/log/syslog':
  ensure => present,
  mode   => 644,
}

Multiple resources

Multiple resources declared in a single block should be used only when there is also a default set of options for the resource type.

Good:

file {
  default:
    ensure => 'file',
    mode   => '0666',;

  '/owner':
    user => 'owner',;

  '/staff':
    user => 'staff',;
}
Good: Give the defaults a name if used several times:
$our_default_file_attributes = { 
  'ensure' => 'file', 
  'mode'   => '0666', 
}
 
file {
  default:
    * => $our_default_file_attributes,;

  '/owner':
    user => 'owner',;

  '/staff':
    user => 'staff',;
}
Good: Spell out 'magic' iteration:
['/owner', '/staff'].each |$path| {
  file { $path:
    ensure => 'file',
  }
}
Good: Spell out 'magic' iteration:
$array_of_paths.each |$path| {
  file { $path:
    ensure => 'file',
  }
}
Bad:
file {
  '/owner':
    ensure => 'file',
    user   => owner,
    mode   => '0666',;

  '/staff':
    ensure => 'file',
    user   => staff,
    mode   => '0774',;
}

file { ['/owner', '/staff']:
  ensure => 'file',
}
 
file { $array_of_paths:
  ensure => 'file',
}

Legacy style defaults

Avoid legacy style defaults. If you do use them, they should occur only at top scope in your site manifest. This is because resource defaults propagate through dynamic scope, which can have unpredictable effects far away from where the default was declared.

Acceptable: site.pp:

Package {
  provider => 'zypper',
}
Bad: /etc/puppetlabs/puppet/modules/apache/manifests/init.pp:
File {
  owner => 'nobody',
  group => 'nogroup',
  mode  => '0600',
}

concat { $config_file_path:
  notify  => Class['Apache::Service'],
  require => Package['httpd'],
}

Attribute alignment

Resource attributes must be uniformly indented in two spaces from the title.

Good:

file { '/owner':
  ensure => 'file',
  owner  => 'root',
}
Bad: Too many levels of indentation:
file { '/owner':
    ensure => 'file',
    owner  => 'root',
}
Bad: No indentation:
file { '/owner':
ensure => 'file',
owner  => 'root',
}
Bad: Improper and non-uniform indentation:
file { '/owner':
  ensure => 'file',
   owner => 'root',
}
Bad: Indented the wrong direction:
  file { '/owner':
ensure => 'file',
owner  => 'root',
  }
For multiple bodies, each title should be on its own line, and be indented. You may align all arrows across the bodies, but arrow alignment is not required if alignment per body is more readable.
file {
  default:
    * => $local_defaults,;
 
  '/owner':
    ensure => 'file',
    owner  => 'root',
}

Defined resource types

Because defined resource types can have multiple instances, resource names must have a unique variable to avoid duplicate declarations.

Good: Template uses $listen_addr_port:

define apache::listen {
  $listen_addr_port = $name

  concat::fragment { "Listen ${listen_addr_port}":
    ensure  => present,
    target  => $::apache::ports_file,
    content => template('apache/listen.erb'),
  }
}
Bad: Template uses $name:
define apache::listen {

  concat::fragment { 'Listen port':
    ensure  => present,
    target  => $::apache::ports_file,
    content => template('apache/listen.erb'),
  }
}