Debugging and Designing Custom .NET Framework PowerShell Providers

Introduction

In a prior article Constructing Your First PowerShell Provider. I explained how to build a basic PowerShell Provider. If you’ve experimented with writing Provider code you’ve probably realized that building a Provider is unlike, for example, a Windows Presentation Foundation (WPF) application. There are no buttons to “wire-up” and no existing pattern for partitioning your code. Most likely, questions abound, like how should I think about the user interface? What should I return to PowerShell? How do I unit test my Provider code?

My goal is to demystify some PowerShell Provider design concepts and offer some debugging techniques. First I’ll provide tips for configuring your development environment and move into design concepts later in the article.

Prepping for 2.0

If you’re still on PowerShell version 1.0, move to version 2.0. A complete review of all the new features in 2.0 is beyond the scope of this article, but it’s enough to say everything is better and easier in 2.0. The accompanying sample code is built on 2.0 and Microsoft Visual Studio 2008. If you’re on Windows 7 or Windows 2008 Server R2, you’re already on 2.0.

You’ll also need the PowerShell SDK. Download and install the PowerShell 2.0 SDK. The sample code requires a default install of the SDK. Included in the SDK are samples you’ll find helpful. Also, remember to call: Set-ExecutionPolicy Unrestricted before you run any of the accompanying scripts. If you’re unsure of the version you are currently running invoke: $Host.Version to get the version number. Don’t be fooled by the Version 1 folder name when you reference the PowerShell assemblies.

Resources below include links to the appropriate downloads.

Now that the environment is configured, it’s time to delve into some code.

Runspace Scaffolding

One of the more difficult aspects of developing a Provider is duplicating the environment the provider executes in. As I mentioned earlier, a PowerShell Provider is different from other .NET applications a developer may be familiar with.

Testing tools can be adopted to do some of the testing, but once a developer has moved beyond unit testing the underlying objects, nothing exists to feed a PowerShell provider the commands it will receive from the PowerShell environment.

In the sample, I built some Scaffolding to feed the PowerShell Provider a script. A developer can use the Scaffolding to do things like test a PowerShell script to exercise the Provider or walk through script execution against the underlying PowerShell Provider code using, for example, the Microsoft Visual Studio Debugger. Also, a novice Provider developer can leverage the Scaffolding to understand how PowerShell invokes the provider, looking at, for example, when, where, and how PowerShell calls the Provider code.

Here are some of the key parts of the Scaffolding followed by an explanation of how it all works.


Collection<PSObject> results = null;

var ps = System.Management.Automation.PowerShell.Create();

try
{
   var cmds = new CommandFileReader(pathToFile);
   foreach (var cmd in cmds)
   {
       var pipeline = ps.Runspace.CreatePipeline();

       WriteMsg(cmd.ToString());
       pipeline.Commands.Add(new Command(cmd.ToString(), true));

       results = pipeline.Invoke();

       foreach (var res in results)
       {
           WriteMsg(res.ToString());
       }
   }


RunSpaces and Pipelines are all part of the underlying data structures that the PowerShell command prompt leverages. So by using the same data structures the Scaffolding is effectively a PowerShell host. PowerShell executes its commands in the context of a RunSpace. The RunSpace must create a Pipeline, add commands to the Pipeline, and invoke the Pipeline.

Commands come from a Script Text File. Each Command is a line of text. The CommandFileReader exposes the underlying script file as a collection of strings. CommandFileReader implements the IEnumerable interface allowing a developer to utilize the foreach statement to iterate through the file. Each time the foreach goes to the next item in the collection it invokes the MoveNext function in the CommandFileReader.

There are more resources with more on RunSpaces and Pipelines at the end of the article.

That covers debugging, now I’m shifting to Provider architecture and design, starting with some “pointers”.

More by Author

Must Read