Deploy packages across your Windows estate with Bolt and Chocolatey

With increasing pressure to keep infrastructure secure and install the latest software packages, even the most efficient teams will struggle to manually install packages on hundreds, or even thousands, of ever-changing servers.To meet the needs of internal customers, you might be asked to install a package on all the servers in your infrastructure. To do this manually could mean finding where the package is stored, knowing the version, logging into the system with the right credentials, installing the package with the right credentials (that may or may not be the same as the ones used to login), and in some cases, creating a directory for the packages to be installed in and verifying they were installed.

To do this on one server is doable. However, if you need to do this across a fleet of servers, automation will save you hours. To show you how this can be done in practice, we’ll go through a quick example using a combination tools — Bolt and Chocolatey — that will get your package deployed quickly, securely and at scale.

Example: Deploy a package (in 3 steps!)

In this example, we’re going to write one Puppet apply plan that installs Chocolatey, and uses the Chocolatey package provider to install a package. This is all done using existing content from the Puppet Forge. We’ll go through the following three steps:

Build a project specific configuration using a Bolt Project Directory and Puppet Development Kit (PDK). Download module content from the Puppet Forge. Write a Bolt Plan to apply Puppet code and orchestrate the deployment of a package resource using the Chocolatey provider.

Prerequisites

Install Bolt and PDK. Ensure you have Powershell and WinRM access.

Step 1: Build a project specific configuration using a Bolt project directory

Bolt runs in the context of a project directory. This directory contains all of the configuration, code, and data loaded by Bolt. When you make a project directory you can share Bolt configuration and code between users.

We are going to create a module using the PDK command pdk new module puppet_choc_tap. To make the module a Bolt project directory, we’ll add a bolt.yaml file. There are various items of configuration that can be added to the bolt.yaml, but the important one for our project is:

modulepath: "./modules/:~/.puppetlabs/bolt-code/modules:~/.puppetlabs/bolt-code/site-modules"

This means that the project is going to support itself by deploying all module dependencies to a modules folder in the project. Note that you don’t have to create the folder ./modules.

Next we will make an inventory file to store the following information about our nodes.

groups:
  - name: windows
    nodes:
      - chocowin0.classroom.puppet.com
      - chocowin1.classroom.puppet.com
    config:
      transport: winrm
      winrm:
        user: Administrator
        Password: <ADD PASSWORD>

This is stored as inventory.yaml by default in the project directory.

To make sure our inventory is configured correctly and we can connect to all your nodes, we’ll run the following command from inside the project directory:

bolt command run 'echo hi' --targets windows

Note: The --targets windows argument refers to the group of nodes in the inventory file.

We should get the following output:

Started on chocowin0.classroom.puppet.com
Started on chocowin1.classroom.puppet.com 
Finished on chocowin0.classroom.puppet.com:
  STDOUT:
    hi
Finished on chocowin1.classroom.puppet.com:
  STDOUT:
    hi
Successful on 2 nodes: chocowin0.classroom.puppet.com, chocowin1.classroom.puppet.com
Ran on 2 nodes in 0.20 seconds

Step 2: Download module content from the Puppet Forge

Bolt uses a Puppetfile to install module content from the Forge. The Puppetfile specifies the modules and data that you want in each environment.

We are going to save the following Puppetfile (which describes the Forge content we want to install in our project directory):

 forge 'https://forge.puppet.com'

# Modules from the Puppet Forge
mod 'puppetlabs-chocolatey', '4.1.0'
## dependencies
mod 'puppetlabs-stdlib', '4.13.1' #install latest
mod 'puppetlabs-powershell', '2.3.0'
mod 'puppetlabs-registry', '2.1.0'

# Modules from Git
# Examples: https://github.com/puppetlabs/r10k/blob/master/doc/puppetfile.mkd#examples
#mod 'apache',
#  :git    => 'https://github.com/puppetlabs/puppetlabs-apache',
#  :commit => 'de290646f97e04b4b8e42c70f6e01e860c394ce7'
# In this example we reference our own Bolt project so we can use it in the context of Bolt at run time

mod 'puppet_choco_tap',
    :git    =>  'https://github.com/abuxton/puppet_choco_tap.git'

From inside the project directory, we’ll install the Forge content with the following Bolt command:

bolt puppetfile install

We should now be able to see a modules directory inside the project directory, containing the modules we specified in the Puppetfile.

Step 3: Write a Bolt plan to apply Puppet code and orchestrate the deployment of a package resource using the Chocolatey provider

Plans allow you to run more than one task with a single command.

Now that we have downloaded an existing module, we can now create a plan inside our Bolt project, called puppet_chocolatey_tap::installer in the plans folder.

The folder tree should look like:

├──plan
         └── installer.pp

And here is our plan:

plan puppet_choco_tap::installer(
   TargetSpec                                   $nodes,
   String							     $chocolatey_repo = 'http://internal/odata/repo/check_this/chocolatey.VERSION.nupkg',
   String                                       $package,
   Variant[Enum['absent', 'present'], String ]  $ensure = 'present',
 ){

 apply_prep($nodes)

apply($nodes){
  class {'chocolatey':
    chocolatey_download_url => $chocolatey_repo    
    use_7zip                => false,
}
 }
apply($nodes){
  package { $package :
    ensure    => $ensure,
    provider  => 'chocolatey',
    }
  }
}

Take note of the following features of the plan:

  1. It has three parameters, the nodes TargetSpec, a package string for the package name, and the ensure state of the package which allows for version, absent or present.
  2. It has the apply_prep function call, which is used to install modules needed by apply on remote nodes as well as to gather facts about the nodes.
  3. The first apply block installs the Chocolatey package manager, utilizing an internal repository. The chocolatey provider is also deployed as a library with the Puppet agent in apply_prep.
  4. The second apply block installs the package using the Chocolatey provider

All this in only 18 lines of Bolt code!

To verify that the puppet_chocolatey_tap plan is available, we will run the following command:

bolt plan show

The output should look like:

facts
facts::info
puppet_choco_tap::installer
Puppetdb_fact

Great! Now we can execute the plan with the bolt plan run command:

bolt plan run puppet_choco_tap::installer package=frogsay  --targets=win --password

The output should look like:

Starting: plan puppet_choco_tap::installer
Starting: install puppet and gather facts on chocowin0.classroom.puppet.com, chocowin1.classroom.puppet.com
Finished: install puppet and gather facts with 0 failures in 22.11 sec
Starting: apply catalog on chocowin0.classroom.puppet.com, chocowin1.classroom.puppet.com
Finished: apply catalog with 0 failures in 18.77 sec
Starting: apply catalog on chocowin0.classroom.puppet.com, chocowin1.classroom.puppet.com
Finished: apply catalog with 0 failures in 33.74 sec
Finished: plan puppet_choco_tap::installer in 74.63 sec

To verify the deployment is operating as expected, we’ll use the following frogsay command to see the output:

bolt command run 'frogsay ribbit' --targets windows --password

The result is likely to vary on each server, but it will look something like this:

Started on chocowin1.classroom.puppet.com...
Started on chocowin2.classroom.puppet.com...
Finished on chocowin0.classroom.puppet.com:
  STDOUT:
    
            DO NOT PAINT OVER FROG.
            /
      @..@
     (----)
    ( >__< )
    ^^ ~~ ^^
Finished on chocowin1.classroom.puppet.com:
  STDOUT:
    
            TO PREVENT THE RISK OF FIRE OR ELECTRIC SHOCK, DO NOT ENGAGE WITH FROG
            WHILE AUTOMATIC UPDATES ARE BEING INSTALLED.
            /
      @..@
     (----)
    ( >__< )
    ^^ ~~ ^^
Successful on 2 nodes: chocowin0.classroom.puppet.com,chocowin1.classroom.puppet.com
Ran on 2 nodes in 3.15 seconds

That’s it! In our one plan, we have both installed Chocolatey and deployed the package to two nodes. You can do the same thing on any number of nodes, just by editing the inventory file. Note that Chocolatey will remain installed on your machine.

Next steps

After you have installed your package, with the help of Bolt, you can use Chocolatey to automate all of the package management tasks for upgrades or uninstalls. You can then use Puppet Enterprise to guarantee state across all of your machines and handle configuration drift — and basically make sure no one accidentally uninstalls the package that you just installed.

Bolt, Chocolatey, and Puppet make a great combo when you have a whole fleet to manage. Don’t forget to harness the power of the Forge to manage infrastructure and deploy even more packages!

In case you missed it, check out Rob Reynolds’ Chocolatey and Puppet: Better Together talk at our recent Puppetize PDX conference.

Claire Cadman is a technical writer at Puppet, and Adam Buxton is a principle training solutions engineer at Puppet.

Learn more

  • Read more about using bolt apply in masterless workflows in one of our recent blog post by Bolt developer Michael Smith.
Puppet sites use proprietary and third-party cookies. By using our sites, you agree to our cookie policy.