Instantiate Puppet resources

Foreman acts as an external node classifier (ENC) for Puppet, which allows it to pass a list of classes, global and class parameters. It isn't able to instantiate individual resources (such as a file or package), or a defined type through this interface.

Instead, you have a few possible approaches.

Creating a simple wrapper class or module

Resources should generally be kept in a class which can then be included where needed. For static resources, this is the best way.

Basic dynamic resources with arrays

If the resources you're managing are simple and all have the same properties and parameters, you can use Puppet's array resource declaration syntax (array of title docs) to create many resources at once.

In this example to install a list of packages, we create /etc/puppet/environments/production/modules/packages/manifests/init.pp with:

class packages($list = []) {
  package { $list:
    ensure => installed,
  }
}

When you import this "packages" class, it will have a "list" parameter. In Foreman's UI, go to Configure, Puppet classes, "packages", Smart class parameters and tick the Override checkbox on the "list" parameter. Set the Parameter type to json and use JSON syntax to set the list parameter to an array of values, e.g.

["httpd","vim"]

You can then override this per host or host group and supply a new array.

Advanced dynamic resources with create_resources

Puppet ships a create_resources function (since Puppet 2.7) that takes a hash of resources to instantiate, and this hash can again be passed into a class as a parameter.

Use a similar pattern to the example above, but this time we'll pass an entire hash in:

class packages($resources = {}) {
  create_resources('package', $resources)
}

For the parameter value, again set the parameter type to JSON or YAML and then store a hash such as this (JSON example):

{"httpd":{"ensure":"installed"},"vim":{"ensure":"installed"},"emacs":{"ensure":"absent"}}

Note that the keys of the first level of the hash are the names of the resources (what usually goes after the resource type on the first list), and then the values are a full hash of the properties and parameters of the resource. You'll need to supply them all (type documentation).

Also important is that JSON hashes do not have the same syntax as Puppet. JSON uses colons (":") as key/value separators, while Puppet uses "=>". Foreman will give a parse error if you try using "=>"!

Foreman can store complex parameters using YAML or JSON, here's a YAML example which may be easier to read and work with:

---
httpd:
  ensure: installed
vim:
  ensure: installed
emacs:
  ensure: absent

Using defined types to minimise duplication

Using create_resources means all resource properties/params need to be passed in. When they're often the same, or you have multiple resources to instantiate, it's usually simpler to wrap it all in a defined type and then instantiate that. Here's an example to manage services:

/etc/puppet/environments/production/modules/services_wrapper/manifests/single_service.pp (depends on stdlib):

define services_wrapper::single_service($start = true) {
  service { $title:
    ensure => str2bool($start) ? {
      true  => 'running',
      false => 'stopped',
    },
    enable => str2bool($start),
  }
}

/etc/puppet/environments/production/modules/services_wrapper/manifests/init.pp:

class services_wrapper($resources = {}) {
  create_resources('services_wrapper::single_service', $resources)
}

And then in the parameter:

---
httpd:
dbus:
nfs:
  start: false

Arbitrary resource definition with hash_resources

hash_resources is module that acts as a layer above create_resources, so that you don't have to create individual wrappers for each type you want to use.

Override the resources parameter and provide a hash like this:

---
file:
  /tmp/foo:
    ensure: present
    content: test
  /tmp/bar:
    ensure: present
    content: test
logrotate::rule:
  apache:
    path: '/var/log/httpd/*.log'
    rotate: 5
    mail: 'test@example.com'
    size: '100k'
    sharedscripts: true
    postrotate: '/etc/init.d/httpd restart'
service:
  httpd:
    ensure: started