My Crescendo journey

Sean Wheeler

In a recent PowerShell Users Group meeting I was thinking that it might be good to talk about the new Crescendo module and how to use it. I was going to ask Jason Helmick if he would do a presentation for us. Then, in an unrelated conversation, someone mentioned using vssadmin.exe for some project. This got me thinking: vssadmin is a perfect candidate for a Crescendo module and maybe I should just learn it and do the presentation myself.

What is Crescendo?

Crescendo is an experimental module developed by Jim Truher, one of the main developers of PowerShell. Crescendo provides a framework to rapidly develop PowerShell cmdlets that wrap native commands, regardless of platform. The goal of a Crescendo-based module is to create PowerShell cmdlets that use a native command-line tool, but unlike the tool, return PowerShell objects instead of plain text.

How I got started

When I first heard about Crescendo, I thought:

So what. I’ve written wrapper modules like this before. How is this going to help me?

But I knew there must be more to it for Jim to invest this much time and effort into it, and I wanted something to present at the user group meeting.

So, I started by reading the blog posts about Crescendo and looking at some examples in the repository.

How Crescendo works

To create a module using the Crescendo framework you must create two main components:

  • A JSON configuration file that describes the cmdlets you want
  • Output handler functions that parse the output from the native command and return objects

Initially, the parsing code had to be embedded in the JSON file, which made writing and formatting the code very difficult. But, in the Preview 3 release, Jim added the ability to create your output handler code in a function or a script file, making it much easier to manage.

Alright! Writing the PowerShell functions is something I am more comfortable with, so that was my next step.

Writing the output parser functions

To create the parser functions I had to know what the output looked like for all the possible command combinations of vssadmin.exe. I looked at the help provided by vssadmin and captured the output for each subcommand in a separate file. I used these output files to design and implement a parsing function for each subcommand.

Now, on to the configuration file.

Creating the JSON configuration

For this I used the example from the blog post as a template. I also looked at the Get-InstalledPackage example from the Preview 2 blog post to see how the native commands were referenced. For my first cmdlet I started with this JSON configuration:

{
    "$schema": "https://aka.ms/Crescendo/Schema.json",
    "Commands": [
         {
            "Verb": "Get",
            "Noun": "VssProvider",
            "OriginalName": "$env:Windir/system32/vssadmin.exe",
            "OriginalCommandElements": [
                "list",
                "providers"
            ],
            "OutputHandlers": [
                {
                    "ParameterSetName": "Default",
                    "HandlerType": "Function",
                    "Handler": "ParseProvider"
                }
            ],
         }
    ]
}

The ParseProvider function is one of the functions that I had written to parse the output. I repeated this pattern to create a new cmdlet for each of the vssadmin subcommands.

Notice that the first line of the JSON references a schema file. This file comes with the Crescendo module. I used Visual Studio Code (VS Code) to do all my development. With this schema file, VS Code provides IntelliSense for the JSON, making it easy to know which values are required and the type of information needed.

Eventually, I added properties to the configuration for full help with descriptions and examples. And I defined parameter sets for the vssadmin commands that supported parameters.

Creating the new module

Crescendo, itself, is a module. It has cmdlets that help you create your configuration and then uses that configuration to create the module containing your cmdlets. Once I was happy with the configuration file, I used the Export-CrescendoModule cmdlet to create my module.

Export-CrescendoModule -ConfigurationFile .vssadmin.crescendo.config.json -ModuleName VssAdmin.psm1

Crescendo created two new files:

  • The module code file VssAdmin.psm1
  • The module manifest file VssAdmin.psd1

These are the only two files that need to be installed. The VssAdmin.psm1 file contains all the cmdlets that Crescendo generated from the configuration and the Output Handler functions I wrote to parse the output into objects.

The end result was a well-structured, fully documented module.

I still have one cmdlet left to create and I want to add administrative elevation since vssadmin requires it. But I am happy with the results I have so far.

Conclusion

After reading all of this you might still be asking “how is this any easier than just writing the module myself?”

That is a fair question. But here are the conclusions I came to as I went through this process.

  • The entire process, starting from nothing, researching both Crescendo and vssadmin, writing the code, creating the configuration, and generating the module took me about 4 hours. I thought that was pretty fast.
  • Crescendo lets you separate the logic code (your parsing functions) from the cmdlet definition and parameter handling code. I found it easier to describe the cmdlets and their parameters in the JSON file rather than having to write that code myself.
  • Crescendo handles things like CommonParameters and SupportsShouldProcess for you. You don’t have to write that support code in the cmdlets.
  • The configuration file also makes it easy to add help to your cmdlets. You don’t have to remember the comment-based help keywords and structure.
  • Separating the declarative code (the JSON configuration) from the logical code (your parsers) makes it easier to add functionality to your module if the native command-line tool is updated.

Take a few minutes to read the Crescendo blog posts. Then go and look at the VssAdmin module I created. I have included the link to it below. Examine the vssadmin.crescendo.config.json file to see how I defined the cmdlets and the parameter sets. The vssadmin.exe resize shadowstorage command has a /MaxSize= parameter that can take 3 distinct types of values. Look at the definition of the Resize-VssShadowStorage cmdlet to see how I managed that.

Posts in this series