This function was initially created to manage the /etc/sudoers file, but it aims to be more generic.

The function takes two arguments: a content and a command. The content is copied to a tempfile and checked with the command. If the command returns 0, the content is returned, otherwise the function raises a puppet error.

/usr/local/lib/site_ruby/puppet/parser/functions/validate.rb

#!/usr/bin/ruby

module Puppet::Parser::Functions
  newfunction(:validate, :type => :rvalue) do |args|
        content = args[0]
        checkscript = args[1]

        # Test content in a temporary file
        tmpfile = Tempfile.new("puppet")
        tmpfile.write(content)
        tmpfile.close
        checkcmd=checkscript+" "+tmpfile.path
        system(checkcmd)
        if $?==0
               return(content)
        end
        raise Puppet::ParseError, "could not validate content with command "+checkscript
  end
end

Usage

Here is an example of usage with a template to manage an /etc/sudoers file. The sudo.pp file contains the values of the variables to be used in the template, for the purpose of the example.

/var/lib/puppet/templates/sudo.erb

# /etc/sudoers
#
# This file was created and transferred by puppet
# Do not edit manually!

<% host_alias.each do |val| -%>
Host_Alias <%= val %>
<% end -%>

# User alias specification

# Cmnd alias specification

<% cmnd_alias.each do |val| -%>
Cmnd_Alias <%= val %>
<% end -%>

# Defaults

Defaults <%= sudo_defaults %>

# User privilege specification
root    ALL=(ALL) ALL

<% sudo_rules.each do |val| -%>
<%= val %>
<% end -%>

/etc/puppet/manifest/classes/sudo.pp

# /etc/puppet/manifests/classes/sudo.pp

class sudo {

        $host_alias = [ "LOCALNET = 192.168.0.0/24, localhost" ]

        $cmnd_alias = [
                "DEBIAN_TOOLS = /usr/bin/apt-get, /usr/bin/auto-get, \
                /usr/bin/dpkg, /usr/bin/dselect, /usr/sbin/dpkg-reconfigure",
                "PBUILDER = /usr/sbin/pbuilder"
                ]

        $sudo_defaults = "!lecture,tty_tickets,!fqdn"

        $sudo_rules = [
                "%admin  ALL=(ALL) ALL, NOPASSWD: DEBIAN_TOOLS",
                "%pbuilder       LOCALNET = NOPASSWD: PBUILDER",
                "buildd ALL=(ALL) NOPASSWD:ALL"
                ]

        $content = template("sudo.erb")

        file { "/etc/sudoers":
                owner => root,
                group => root,
                mode => 440,
                content => validate($content,"/usr/sbin/visudo -cq -f")
        }
}

With this template and these values in sudo.pp, the file is validated and copied. If an error is introduced in either, puppet will raise an error and use the cache for this file (if any).

Notes

As of version 0.22.4, puppet functions do not support other functions as arguments. When this is fixed, you will be able to call content-generating functions as arguments to validate(), and the current example could become validate(template(“sudo.erb”),“/usr/sbin/visudo -cq -f”).