Puppet Bolt with a Puppet control repository

March 30, 2020 

NOTE: this article is somewhat outdated. Please refer to Serverless Puppet with control repo, Hiera, roles and profiles and Puppet Bolt instead.

Puppet Bolt is an orchestration tool similar to Ansible. It is suitable for multiple use-cases:

  1. Running ad hoc commands on target nodes. We use this approach for testing Puppet feature branches before approving and merging them.
  2. One-off configurations written in the Puppet language
  3. Masterless Puppet environments (look here). You may not always have enough privileges, trust or will to manage a complete Puppet infrastructure including Puppetserver, PuppetDB and Puppet Agents.
  4. Orchestration, i.e. running tasks on multiple servers (example here).

There is quite a bit of documentation on Puppet Bolt, but cobbling it all together takes a bit of effort. Starting with Bolt Hands-on Lab and the official documentation is advised. This article shows how to accomplish steps 1-3, above, in the context of an existing Puppet control repository and assumes a fair bit of Puppet knowledge.

The first step is to install Puppet Bolt. After that you need to add bolt.yaml and inventory.yaml to the root of your control repository. A minimal bolt.yaml might look like this:

concurrency: 3
format: human
ssh:
  host-key-check: false

The presence of bolt.yaml makes the "bolt" executable realize that the root of the control repository is the Boltdir.

The next step is to add inventory.yaml which can be quite simple:

---
version: 2
groups:
  - name: vagrant
    targets:
    - bolt

Here we define a single group which contain a single (Vagrant) node. Setting up a Vagrant environment for Bolt testing is covered in the hands-on lab. If you can do "ssh bolt-test" you are ready to try Bolt for ad hoc commands from the root of the control-repository (a.k.a. "Boltdir"):

bolt command run "date" --target vagrant
 Started on bolt…
 Finished on bolt:
   STDOUT:
     Mon Mar 30 11:02:17 UTC 2020
 Successful on 1 target: bolt
 Ran on 1 target in 0.34 sec

Now we know that connectivity and Bolt in general is working fine. The next step is to apply an ad hoc manifest. Let's add site/profile/plans/simple_file.pp with this content:

plan profile::simple_file
(
  TargetSpec $nodes,
  String     $file_content,
)
{
  $nodes.apply_prep

  apply($nodes) {
    file { '/foobar':
      content => $file_content,
    }
  }
}

The TargetSpec parameters gets populated by Bolt based on the --target parameter. The $file_content parameter needs to be passed on the Bolt command-line:

$ bolt plan run profile::simple_file file_content=foobar --target vagrant --run-as root
Starting: plan profile::simple_file
 Starting: install puppet and gather facts on bolt
 Finished: install puppet and gather facts with 0 failures in 4.63 sec
 Starting: apply catalog on bolt
 Finished: apply catalog with 0 failures in 6.81 sec
 Finished: plan profile::simple_file in 11.46 sec
 Plan completed successfully with no result

In this particular case we can easily check the results with an ad hoc command:

$ bolt command run "cat /foobar" --target vagrant
 Started on bolt…
 Finished on bolt:
   STDOUT:
     foobar
 Successful on 1 target: bolt
 Ran on 1 target in 0.35 sec

As we can see, the file was created successfully. However, we will immediately run into trouble if we try to use external Puppet modules. While Bolt includes Puppet's core resources and modules, it depends on a Puppetfile for the rest. For example this plan would not work out of the box:

plan profile::bash_prompt
 (
   TargetSpec $nodes,
   String     $prefix,
 )
 {
   $nodes.apply_prep
   apply($nodes) {
     class { '::bash_prompt':
       prefix => $prefix,
     }
   }
 }

If we try to apply this we get a failure:

$ bolt plan run profile::bash_prompt prefix=foobar --target vagrant --run-as root
Starting: plan profile::bash_prompt
Starting: install puppet and gather facts on bolt
Finished: install puppet and gather facts with 0 failures in 1.91 sec
Starting: apply catalog on bolt
Finished: apply catalog with 1 failure in 1.93 sec
Finished: plan profile::bash_prompt in 3.86 sec
Failed on bolt:
  Apply failed to compile for bolt: Could not find declared class
  ::bash_prompt (file: /home/samuli/opt/bolt-test/site/profile/plans/bash_prompt.pp,
line: 10, column: 5)
Failed on 1 target: bolt
Ran on 1 target

Fortunately we already have a Puppetfile in our control repository, so all we have to do is to install the external modules:

$ bolt puppetfile install

That command installs all the modules in the Puppetfile locally, after which you can use them in your plans.

Bolt can also use data from Hiera - you just need to do lookups from within the apply block and ensure that you Hiera levels are useful in conjunction with nodes that don't have certificates and hence $trusted.certname.

Samuli Seppänen
Samuli Seppänen
Author archive
menucross-circle