A Rule based Node Classifier using Hiera




4,205 latest version

2.7 quality score

Version information

  • 0.1.1 (latest)
  • 0.1.0
  • 0.0.11
  • 0.0.10
  • 0.0.9
  • 0.0.8
  • 0.0.7
  • 0.0.6
  • 0.0.5
  • 0.0.4
  • 0.0.3
  • 0.0.2
  • 0.0.1
released Jan 22nd 2020
This version is compatible with:
  • Puppet Enterprise 2021.0.x, 2019.8.x, 2019.7.x, 2019.5.x, 2019.4.x, 2019.3.x, 2019.2.x, 2019.1.x, 2019.0.x, 2018.1.x, 2017.3.x
  • Puppet >= 5.0.0

Start using this module


ripienaar/classifier — version 0.1.1 Jan 22nd 2020


An attempt to port and expand to pure Puppet 4 DSL.

This is a node classifier where you can write classification rules that perform boolean matches on fact data and should a rule match some classes will be included. Any number of classification rules can match on a given node and they can all contribute classes.

Different data tiers can contribute rules and it has a way to just add classes statically like the old hiera_include() approach.

NOTE: I'm using semver and as this is still less than 1.0.0 expect breaking changes to happen.

Sample Data

Sticking this in Hiera will create a classification that on RedHat VMs will include the classes centos::vm and on the node it will also include ntp in addition.

# common.yaml
 - sensu
  RedHat VMs:
    match: all
      - fact: "%{}"
        operator: ==
        value: RedHat
      - fact: "%{facts.is_virtual}"
        operator: ==
        value: "true"
      redhat_vm: true
      - centos::vm
# remove sensu and install nagios instead, also add extra stuff
  - --sensu
  - nagios
# clients/acme.yaml
# adjust the RedHat VMs rule to add ntp on node
# these machines but also to remove centos::vm and
# install centos::core instead
  RedHat VMs:
      client_redhat: true
      - ntp
      - --centos::vm
      - centos::core

# add acme client team
  - acme::sysadmins
node default {
  include classifier

The extra_classes parameter lets you specify a array of additional classes without having to construct rules and they are setup with a knockout of -- so that you can remove lower down results.

At present you can't knockout classes included by rules using a knockout prefix on extra_classes, this os something that's planned

You can have many classification rules and which ever match can contribute classes to add

Other classes can access detail about the classification result:

  • $classifier::classification - a array of Hashes with matching Rule names and classes
  • $classifier::classification_classes - just the classes extracted from the classification
  • $classifier::extra_classes - the extra classes resolved from hiera
  • $classifier::classes - the list of classes that will be included
  • $classifier::data[...] - hash of all the data created by the tiers
  • $classifier::enc_used - boolean indicating if the ENC was used
  • $classifier::enc_source - path to the data file that was matched and supplied the environment, undef when not used
  • $classifier::enc_environment - the environment the ENC set, undef when not used


The full type description of a rule is:

    match    => Optional[Enum["all", "any"]],
    rules    => Array[
        fact     => Optional[Data],
        operator => Enum["==", "=~", ">", " =>", "<", "<=", "in", "has_ip_network", "has_mac", "has_ip_address", "has_ip_netmask"],
        value    => Data,
        invert   => Optional[Boolean]
    data     => Optional[Hash[Pattern[/\A[a-z0-9_][a-zA-Z0-9_]*\Z/], Data]],
    classes  => Optional[Array[Pattern[/\A([a-z][a-z0-9_]*)?(::[a-z][a-z0-9_]*)*\Z/]]]
] $rules = {},

A number of custom types are defined for things like the list of valid operators, valid variable and class names, matches, individual rule and the whole classification. Should you wish to build additional classes that consume data from this tool please validate the input using them

  • Classifier::Classification - a single classification made up of rules, classes, data and match type
  • Classifier::Classifications - a collection of classifications
  • Classifier::Matches - the list of valid match types
  • Classifier::Rule - a single rule inside a classification
  • Classifier::Data - valid data items
  • Classifier::Operators - valid operators
  • Classifier::Node - classification result for a node

A few notes:


Match can be either any or all and it means that in the case where you have many rules they must either all match a node or at least one.


Use Hiera interprolation to put any fact or trusted data into the rule set. Take note in hiera to interpolate data you have to quote things like this "${facts.thing}" which coherse the data into a string. In the example rule above a boolean fact is cohersed to a string in this manner and so the match value has to be "true" as well.

The fact is optional, since some times like in the case of has_ip_network for example it does not make sense since it checks a range of facts from the node.


Valid operators are "==", "=~", ">", " =>", "<", "<=", "in", "has_ip_network", "has_mac", "has_ip_address", most of these comparisons are done using the versioncmp function so you should probably understand it to really grasp what these will do.

There are a special ones planned like the current has_ip_network, when using that the fact is optional, so something like:

Development Servers:
    - operator: has_ip_network
    - development


This inverts the match so setting it true just swaps the whole comparison around, so there is no != operator for example, but you can achieve that using the == one and inverting it


This is an optional hash of data items kind of like facts, these are accessible in a hash calledcw $classification::data[..] after classification

Setting The Environment?

You can set the environment of a node with the help of a ENC included in bin, see

Future Plans?

I want to expand the rules so you can use other functions to do evaluation, things like checking if a node IP belongs to a certain subnet for example, this could be done by adding functions to the classifier and them into the classifier::evaluate_rule function as operators perhaps.

At the moment there is has_ip_network, I am not really sure if this is a good fit so that's experimental while I figure it out. It might be nice to support any function call there not just ones that's hardcoded in classifier::evaluate_rule in order to make the classifier user extendible at their site using any functions they might have. If we only supported a bunch of hard coded ones I can imaginet his becoming a huge nightmare to support in the long term as users might want to add many such matchers. A more extendible approach makes more sense.

But down that road lies basically doing eval() in puppet and this is just a terrible terrible idea so I am not sure what's best. It would be very easy to write a function dispatch for any function that exists, but really it would not be a good idea.


R.I.Pienaar / / / @ripienaar