DEV Community

Cover image for New Puppet Bolt PowerShell Cmdlets
James Pogran
James Pogran

Posted on

New Puppet Bolt PowerShell Cmdlets

Puppet Bolt now ships (as of version 2.20.0) with PowerShell native cmdlets that are auto-generated from Bolt source code with correct PowerShell verb-noun semantics, familiar PowerShell parameter conventions, and identical Bolt help documentation. All of these features work together to surface Bolt commands in a discoverable and understandable manner and enable using powerful PowerShell language features with Bolt.

What to do

How do you find out what tool to use, if you don't know what tools are available? One of the first things you learn when using PowerShell is how to get help using Get-Command and Get-Help. These are two very powerful cmdlets that enable intuitive discovery of the available tooling and how to use the chosen tool.

Get-Command is a versatile tool that can find any PowerShell command on the system using a variety of filters, and returns information on both the command and where the command comes from. Previously we only shipped the bolt command with Bolt:

PS > Get-Command bolt

CommandType     Name  Version    Source
-----------     ----  -------    ------
Function        bolt  2.23.0     PuppetBolt

This doesn't do much for discoverability and requires you to run the bolt command to find out what it can do. If we instead start to surface Bolt commands as PowerShell cmdlets, we can start to take advantage of both Get-Command and PowerShell's cmdlet conventions to increase discoverability:

PS > Get-Command *bolt*

CommandType     Name                          Version    Source
-----------     ----                          -------    ------
Function        bolt                          2.23.0     PuppetBolt
Function        Convert-BoltPlan              2.23.0     PuppetBolt
Function        Get-BoltGroup                 2.23.0     PuppetBolt
Function        Get-BoltInventory             2.23.0     PuppetBolt
Function        Get-BoltPlan                  2.23.0     PuppetBolt
Function        Get-BoltPuppetfileModules     2.23.0     PuppetBolt
Function        Get-BoltTask                  2.23.0     PuppetBolt
Function        Install-BoltPuppetfile        2.23.0     PuppetBolt
Function        Invoke-BoltApply              2.23.0     PuppetBolt
Function        Invoke-BoltCommand            2.23.0     PuppetBolt
Function        Invoke-BoltPlan               2.23.0     PuppetBolt
Function        Invoke-BoltScript             2.23.0     PuppetBolt
Function        Invoke-BoltTask               2.23.0     PuppetBolt
Function        New-BoltPlan                  2.23.0     PuppetBolt
Function        New-BoltProject               2.23.0     PuppetBolt
Function        New-BoltSecretKey             2.23.0     PuppetBolt
Function        Protect-BoltSecret            2.23.0     PuppetBolt
Function        Receive-BoltFile              2.23.0     PuppetBolt
Function        Register-BoltPuppetfileTypes  2.23.0     PuppetBolt
Function        Send-BoltFile                 2.23.0     PuppetBolt
Function        Unprotect-BoltSecret          2.23.0     PuppetBolt
Function        Update-BoltProject            2.23.0     PuppetBolt

Now we can see a large list of commands using PowerShell's verb-noun syntax. Even if you aren't familiar with PowerShell's verb-noun conventions, reading the list of commands gives you an immediate general idea of what's possible with Bolt. There's plans and scripts, tasks and projects, concepts like invoking commands and getting information from the system. Scanning the list gets you immediate information you can use to choose what tool to complete your task.

If you're familiar with PowerShell's verb-noun conventions, you can start to drill down and quickly decide what command will do what you want. For example, you may want to find out how to get a list of Bolt tasks on the system. You know that PowerShell's verb-noun conventions dictate that all commands that return information use Get as the verb, and that the noun should contain the word related to the information returned, Task. Using this information, you run Get-Command and tell it to search the PuppetBolt module for all commands that use the verb Get and have a noun with the word task in it using a wildcard:

PS > Get-Command -Module PuppetBolt -Verb Get -Noun *task*

CommandType     Name          Version    Source
-----------     ----          -------    ------
Function        Get-BoltTask  2.23.0     PuppetBolt

With Get-Command we found Get-BoltTask quickly in one attempt, whereas we would have to call bolt --help, then bolt task --help to find the same information. We didn't really need to read documentation to know which command to run, we already knew from PowerShell's conventions that any Get command would return a one or more items and that a command with a noun BoltTask returns information about tasks.

All of this is great for figuring out which command we need to run, but how do we know how to use it ? In the next section we'll explore using Get-Help for this purpose, but before we do we can use Get-Command one last time to find out some quick bits of syntax:

PS > Get-Command Get-BoltTask -Syntax

Get-BoltTask [[-Name] <string>] [-LogLevel <string>] [-Modulepath <string>] [-Project <string>] [-Configfile <string>] [-Filter <string>] [-Format <string>] [-Version] [<CommonParameters>]

The output returned by Get-Command calls back to the opening paragraphs of this post. We used Bolt source code to generate these PowerShell cmdlets, so each cmdlet is populated with the parameters and help text for each Bolt command. Since they are formatted using PowerShell conventions, commands like Get-Command and Get-Help can parse these new Bolt PowerShell cmdlets and return structured information. When we ran Get-Command Get-BoltTask -Syntax, PowerShell parsed the Bolt cmdlets and returned structured parameter information for us. This is immensely powerful for command discovery.

Getting Help

We now know the command we want to run, but how do we find out how to run it? We can use Get-Help for that. While we can get far with a few conventions using Get-Command, the cmdlet Get-Help really shines in helping us understand how to use them. Since Get-BoltTask is a PowerShell cmdlet and contains PowerShell comment based help, we can use Get-Help to read more detailed information about how to use it:

PS > Get-Help Get-BoltTask

NAME
    Get-BoltTask

SYNOPSIS
    bolt task show [task] [options]


SYNTAX
    Get-BoltTask [[-Name] <String>] [-LogLevel <String>] [-Modulepath <String>] [-Project
    <String>] [-Configfile <String>] [-Filter <String>] [-Format <String>] [-Version]
    [<CommonParameters>]


DESCRIPTION
    Show available tasks and task documentation. Omitting the name of a task will display a
    list of tasks available in the Bolt project. Providing the name of a task will display
    detailed documentation for the task, including a list of available parameters.


RELATED LINKS
    https://puppet.com/products/bolt
    https://puppet.com/docs/bolt/latest/bolt_command_reference.html

REMARKS
    To see the examples, type: "get-help Get-BoltTask -examples".
    For more information, type: "get-help Get-BoltTask -detailed".
    For technical information, type: "get-help Get-BoltTask -full".
    For online help, type: "get-help Get-BoltTask -online"

We get formatted help immediatly with one command using Get-Help. Looking through the output using Get-Help Get-BoltTask -Detailed, you can see each Bolt PowerShell cmdlet contains all the Bolt help text for each command and parameter:

PARAMETERS
    -Name <String>
        The task to show

    -LogLevel <String>
        Set the log level for the console. Available options are
        debug, info, notice, warn, error, fatal, any.

    -Modulepath <String>
        List of directories containing modules, separated by ';'
        Directories are case-sensitive

    -Project <String>
        Specify what project to load config from (default: autodiscovered from current working
        dir)

    -Configfile <String>
        Specify where to load config from (default: ~/.puppetlabs/bolt/bolt.yaml).
        Directory containing bolt.yaml will be used as the project directory.

This shows that the generated the PowerShell cmdlets have typed parameters. Parameters that accept strings are cast to [string], parameters that accept a range of values now have [ValidateSet()], and so on. Most of Bolt's parameters translate naturally to PowerShell conventions. We spent a lot of time making sure these translation fit PowerShell semantics. A small list of considerations for parameters follows:

  • Required parameters are detected and set as Mandatory
  • Detects when Validation attributes can be applied at the PowerShell level
  • Detects common parameter sets for commands
  • Added new name parameters to commands like command, task, or plan and set as Mandatory and positional
  • Boolean parameters (--[no]-ssl-verify) are collapsed into one PowerShell switch parameter --SslVerify

All of this shows up in the Get-Help output, using commands you're familiar with, without having to page through several pages of bolt command run --help to find out what parameter will do what.

Using what you have learned

Putting all this effort into making these Bolt commands and parameters have PowerShell types and validation pays dividends not only with the PowerShell help system, but also enables some very powerful ways to invoke Bolt commands.

We can see how by using what we've covered so far in a real world example. Let's say we want to get the status of the Print Spooler service on a remote target. Using bolt --help, then bolt task --help, then bolt task show, then bolt task show service we can see that we need to run bolt task run -t localhost service name=spooler action=status in order to check the status of the Print Spooler.

That was around 3 commands and alot of reading through pages of parameters to find out what to do. If the params we want to pass to the service task become complicated, we can use JSON instead but that is yet another line to write.

If we remember that the new Bolt PowerShell cmdlets follow PowerShell verb-noun conventions, we can use the same discovery process we saw earlier in this post to find the command to use. We know that Invoke is the PowerShell verb convention for executing something and we know BoltTask is the noun we need by applying PowerShell noun conventions. Without having to think about it much or run a series of commands we know to use Invoke-BoltTask to execute tasks. To find out how to use Invoke-BoltTask we run Get-Help Invoke-BoltTask -detailed and see the list of parameters needed to run the command and come up with:

PS > Invoke-BoltTask -Targets localhost -Name service -Params @{ name = 'spooler'; action = 'status' }
Started on localhost...
Finished on localhost:
  {
    "status": "running",
    "enabled": "true"
  }
Successful on 1 target: localhost
Ran on 1 target in 9.24 sec

This shows the power of using PowerShell language constructs to make passing input to Bolt easier. The -params parameter accepts a PowerShell hash and converts it under the covers to a JSON string to pass to Bolt. This means we can get as simple or as complicated as we want defining it and PowerShell will handle the complexity of sending it to Bolt.

We can take it one step further and use PowerShell parameter splatting inside a PowerShell script, so that we can reuse the same data for multiple commands in our scripts:

PS > gc .\getserviceinfo.ps1
$spoolerParams = @{
  Name = 'service'
  Params = @{
    name = 'spooler'
    action = 'status'
  }
  Targets = 'localhost'
}
Invoke-BoltTask @spoolerParams
PS > .\getserviceinfo.ps1
Started on localhost...
Finished on localhost:
  {
    "status": "running",
    "enabled": "true"
  }
Successful on 1 target: localhost
Ran on 1 target in 9.68 sec

Wrapping Up

We think taking the time and effort to surface Bolt commands as native PowerShell cmdlets is proven effective by the ease of use demonstrated above. Using some simple scenarios we have shown how PowerShell cmdlets increase discoverability of our toolset and make it easier to understand how to use it. Then we covered how the PowerShell language also makes it easier to express complicated parameter sets to Bolt and enables powerful scripting scenarios.

Discussion (0)