Convert a hash into JSON in Puppet

This article shows you how to convert a hash into JSON in Puppet using a simple ERB template that gets its data from Hiera.
Suppose you have this data in Hiera:
myhash:
mykey:
foo: one
bar: two
Converting a hash into a JSON file on the target node is surprisingly easy. First look up the data:
$myhash = lookup('myhash', Hash)
Then create a simple ERB template (here: mymodule/templates/myhash.erb)
Using .sync.yml in Puppet Development Kit (PDK)
Puppet Development Kit is probably the best thing since sliced bread if you work a lot with Puppet. It makes adding basic validation and unit tests trivial with help from rspec-puppet. It also makes it very easy to build module packages for the Puppet Forge.
That said, there is a minor annoyance with it: whenever you run “pdk update” to update PDK to the latest version, all your local changes to files such as .gitignore get destroyed, because PDK creates those files from PDK templates and any local changes are mercilessly wiped. Fortunately there is a way to sync your local changes to the files generated from the templates with .sync.yml. While .sync.yml is is pretty well documented, documentation is lots and lots of words with very few examples. So, here’s a fairly trivial .sync.yml example that ensures local changes to .pdkignore and .gitignore persist across PDK updates:
Use content of a file as a variable with Puppet
Every now and then a need to use the content of a file as a variable on an agent node arises. Here’s one way to do it with the help of a custom fact.
First create a custom fact on the puppet server:
Facter.add(:my_file_content) do
setcode do
path = "/path/to/my_file"
File.read(path) if File.exist?(path)
end
end
You can confine this to restrict it to be available only on selected nodes, for example:
Debugging Puppet Bolt inventory plugins
Puppet Bolt handles inventories in a very flexible and powerful manner: you can combine static target definitions and different targets into a single inventory. For example, you can have an inventory which defines some static node names combined with the AWS inventory, or one that combines static nodes with the Vagrant inventory.
Puppet Bolt inventory plugins do their magic and then output what is essentially a static inventory generated dynamically. When dynamic inventory plugins do not work (as in my case) it can be challenging to figure out what is wrong, let alone how to fix it. However, there’s is a very handy way to print out a static version of the entire inventory, including the dynamically created content:
Hiera lookups in rspec-puppet
While rspec-puppet documentation is quite decent, it does not really explain how to test classes that get their parameters via Hiera lookups, such as profiles in the roles and profiles pattern. Several parameters related to Hiera are listed in the rspec-puppet configuration reference, but that’s all. The other documentation you find on the Internet is generally outdated and not applicable for modules converted with PDK.
In this article we show how to test an “ipa_client” class that uses lookups to get its parameter from Hiera. It used to be a profile that was later separated from the control-repo into a separate module, which means that rspec-puppet does not have access to the control-repo’s Hiera data.
Serverless Puppet with control repo, Hiera, roles and profiles and Puppet Bolt
The traditional way of managing systems with Puppet is to install Puppet agent on the nodes being managed and point those agents to a Puppet server (more details here). This approach works well for environments with tens or hundreds of nodes, but is an overkill for small environments with just a handful of nodes. Fortunately nowadays Puppet Bolt enables you to push configurations to the managed nodes via SSH and WinRM. When using Bolt the catalog (desired state) is created on the computer running Puppet Bolt using facts from managed nodes and then sent to the managed nodes to be applied.
Creating Puppet Bolt groups based on AWS tags
The Using tags in Puppet Bolt aws_inventory target_mapping showed how to use AWS “Name” tag as the target name for Puppet Bolt. Use of tags can be extended to creating Bolt target groups:
groups:
- name: webservers
targets:
- _plugin: aws_inventory
region:
_plugin: env_var
var: AWS_DEFAULT_REGION
filters:
- name: tag:Type
values: [webserver]
target_mapping:
name: tags.Name
uri: public_ip_address
All you need to do is add a “filter” section with one filter. The “name” parameter tells Bolt that the filter is based on tags and what tag to look for. The “Type” that follows “tag” is the name of the tag you want to filter by. The “values” array lists acceptable values for that tag. Any nodes that don’t have the tag (here: “Type”) or where tag value is not acceptable (e.g. “db”) will not be shown in the group.
Testing Puppet feature environments with Puppet Bolt
Puppet feature environments are an excellent way to test code before deploying it, typically to production. They allow testing Puppet runs on no-operation mode across the whole node population managed by Puppet. There are sometimes cases where your code changes potentially impact many nodes and you’re not exactly sure of their scope or effect. In that case it helps to test your code on all Puppet-managed nodes. The challenge is that Puppet open source does not have any built-in mechanism to do that. This is where we can make good use of Puppet Bolt.
Using tags in Puppet Bolt aws_inventory target_mapping
We’re migrating away from Ansible to Puppet Bolt and the fact that Ansible updates broke the old ec2.py inventory script expedited that process. While that inventory script was quite rudimentary, it was able to automatically add human-readable names to the EC2 instance names. So, for example, you could target a node using “server_example_org” if the Name tag was set to “server.example.org”.
Puppet Bolt’s aws_inventory seemed to lack that feature, but after taking a good look at its code this was just a case of missing documentation, which I fixed in this PR. Here’s an example of inventory.yaml for Bolt that maps the value of the “Name” tag to the name of the Bolt target:
Puppet Bolt: taking action based on exit codes
Puppet resources like Package, File or Service are idempotent. In other words, you define the state of the resource and Puppet enforces it. The resources can be used in Bolt plans as well, which frees you from having to do state checks yourself. However, if the thing you’re managing is not a Puppet resource you have to check its state manually and use that information to drive login in your plan. Here’s a crude but functional PoC for checking if a node’s certificate is present on the Puppetmaster and acting accordingly: