Providing help documentation for PowerShell cmdlets

We’ve been working on a new product that involves writing Windows PowerShell cmdlets (pronounced “commandlets”) for our customers to use and, to make their lives easy, we want to provide rich built-in help documentation. There’s no easy way to do this out of the box, so I’ve written a tool that allows us to write the documentation directly within the XML doc comments of the cmdlet source code. Now our technical author, Beth, only writes the documentation once, and the developers keep it up to date as the API of the cmdlets evolves, with only minor subsequent reviews by Beth. We’ve posted the source code for the tool on GitHub for anyone else to use.

The problem

My new team has just started working on a product that uses, at its core, a series of Windows PowerShell cmdlets. While we expect that most of our users won’t directly interact with them, it’s clear that there’ll be a sizeable number of power users who will want to invoke our cmdlets in their own PowerShell scripts. With that in mind, we’re trying to make our cmdlets as easy to understand and use as possible, and one important way to achieve this is to provide rich help documentation that integrates with the interactive help facilities built into PowerShell.

It turns out that there are two distinct styles of PowerShell modules that can be used to make cmdlets available to users – script modules and binary modules – and they have distinctly different ways of providing integrated help documentation.

Script modules

A PowerShell script module is written in the PowerShell scripting language. Help documentation for the cmdlets in the module is defined within the scripts’ special comments fields, which is helpful for several reasons:

  1. The help system automatically derives the syntax of the cmdlets from their definitions, so we can concentrate more on writing useful descriptive text rather than on making sure the syntax described in the help content matches the actual syntax of our cmdlets.
  2. Keeping the help documentation close to the definition of the cmdlets maximises the chance of it being kept up-to-date by our developers when they make changes to the source code. There’s no guarantee that the developers will actually do this, but it’s much more likely to serve as a prompt or be picked up in code reviews than if the help documentation is defined somewhere else.
  3. Once our technical author, Beth, has written the initial help content, minor changes can be easily made by developers as they change the source code. This means that we’ll only need to carry out relatively small documentation reviews between releases. Our developers also have a good habit of tagging source code to indicate when a change they have made requires a review by Beth.

Basically this is our preferred way of dealing with the help documentation – or rather, it would be if we were actually writing a script module, but we’re not. We’re writing a binary module, where things are less rosy.

Binary modules

A PowerShell binary module is simply a .NET assembly dll that contains one or more cmdlet classes. Help documentation for a binary module needs to be provided in an XML file alongside the module. For example, the help text for MyModule.dll would be found in a file named MyModule.dll-Help.xml. This situation is problematic for several reasons:

  1. The help documentation is separate from the source code, so it’s much more likely to go out of sync with the cmdlets, and requires more checking.
  2. There’s nothing to ensure that the syntax described in the help files is actually correct, so there’s an additional maintenance burden involved in having to check this.
  3. The help file’s XML format is not well documented. Microsoft have provided some guidance for writing XML help files for cmdlets, but in practice we quickly realised that it’s very superficial. We’ve had to resort to reverse engineering help files that accompany various Microsoft-written PowerShell modules, with mixed success.
  4. There’s lots of repetition in the XML help file, which doesn’t make it a good primary source for defining the help documentation.

All in all, I’m rather disappointed that the state of play for providing help documentation for binary modules is so much worse than for script modules.

The solution

I spent a fair bit of time searching for solutions that might improve the situation. The big hope was that I’d find something that would allow us to define the help documentation in the source code of our cmdlets, and use it to auto-generate the cmdlet XML help file. Alas, and with no great surprise, I didn’t find anything.

However, during the research I did begin to formulate an idea of what the solution would look like. This was influenced hugely by there already being an existing solution for script modules, and my having worked recently with Jolt.NET, a library that can be used to programmatically access the XML doc comments of an assembly. In the end, I rolled my own solution over the course of a few evenings.

XmlDoc2CmdletDoc

So I picked the hugely imaginative title of XmlDoc2CmdletDoc, thus clearly demonstrating why I shouldn’t work in marketing! Currently it takes the form of a simple console application that we invoke in the AfterBuild target of our PowerShell module project, like this:

<Target Name="AfterBuild">
<Exec Command="$(SolutionDir)tools\XmlDoc2CmdletDoc\XmlDoc2CmdletDoc.exe $(TargetPath)" />
</Target>

The tool runs every time our PowerShell module is built, and the cmdlet help documentation is always up to date with respect to the source code. So far, the tool has been quick enough that no one on my team has noticed any increase in build time. Marking up the cmdlet source code is relatively simple, too. For the most part, help documentation is derived from XML doc <para> elements that have a type=description” or type=synopsis” attribute added to them, like this:

/// <summary>
/// <para type="synopsis">
/// This is the cmdlet's synopsis.
/// </para>
/// <para type="description">
/// This is the cmdlet's description.
/// </para>
/// <para type="description">
/// This is another paragraph of the cmdlet’s description.
/// </para>
/// </summary>

[Cmdlet("New", "MyExample")]
public class NewMyExampleCommand : Cmdlet
{
/// <summary>
/// <para type="description">This is the parameter's description.</para>
/// </summary>

public string Name { get; set; }


}

For more detail about how to write cmdlet documentation in the XML doc comments, you can view the project’s website – the source code is freely available under the Redgate GitHub account.

We’re still in the early days of documenting our cmdlets using this tool, so I expect there to be several updates in the near future as we iron out any kinks and add features that we don’t yet realize that we need. The most likely upcoming features will be:

  • Add NuGet support, so that you can simply add an XmlDoc2CmdletDoc package to a project and it will automatically modify the project file to ensure that the tool runs correctly.
  • Better testing integration. There’s currently a -strict option which will turn warnings into errors. This is being used in an NUnit test to make sure that our cmdlets all have the right level of documentation, where the tool can be rerun in strict mode and trigger a test failure if there are warnings about missing documentation.

Well, that’s it. If you’re interested in using this tool, I’ll be very pleased to hear what you think, either through GitHub or simply by contacting me directly.