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.
Links to resources
- The blog posts
- The Crescendo repository on GitHub
- The Microsoft.PowerShell.Crescendo module on the PowerShell Gallery
Posts in this series
- My Crescendo journey – this post
- My VssAdmin module
- Converting string output to objects
- A closer look at a Crescendo Output Handler
- A closer look at a Crescendo configuration file
Hi,
Quick question: is the generated code natively compatible with Constrained Language Mode ?
Thanks!
No. It was not designed with that goal in mind. The wrapper code, generated from the JSON, uses classes and that is not allowed in Constrained Mode.
Great post; thanks for putting this together.
I hadn’t come across Crescendo yet but will definitely take a look as I too find myself doing this type of approach to call native commands. Perhaps I may try my hand at some nmap commands.
Great post.
The link to your VssAdmin module is wrong. I think the correct link is: https://github.com/sdwheeler/tools-by-sean/tree/master/modules/vssadmin
Waiting for new post about your design.
Thanks, I have fixed the link.
I have just come across Crescendo and this post is very timely.
One thing missing from this post is the details of the output handlers. I get the basic approach (write some JSON and some PowerShell functions) but it’s the details that get me. Can you please consider doing another post describing how to use output handlers??
The output handlers are just the parsing functions. The output of the native command is passed to the the function as input, either by pipeline or as an argument value. Your handler function needs to be written to accept the input. Take a look at the my parser functions in my GitHub repo to see how they work.
I am considering another post to go into the details of my design. Stay tuned for more.
I have looked at the sample you provided! Some further explanation might be useful (at least for me!).
I may be missing something, but output handelers look very hard to write.