Sunday, January 28, 2007

PowerShell: Designing a CmdLet


Since the early PowerShell beta days I have been excited about how PowerShell allows us to develop and add to its richness.  By adding custom CmdLets to the PowerShell shell we can add functionality to support third party products, legacy systems or just add basic functionality to simplify common administrative tasks.

Since the release of the first PowerShell SDK I have been considering the usefulness of a group of CmdLets that would abstract common administrative objects such as “user” and “computer”.  Here I am going to record, at some basic level of detail, some of the issues I have been presented with while attempting to come up with a final design for a CmdLet that abstracts a computer.

Let me begin by noting that I am not a PowerShell Guru and I have only limited experience with building CmdLets.  My reason for doing this is twofold.  First, I want to explore the limits and behaviors of a CmdLet, and second, I would like to document as much of my experience as possible so others, primarily administrator types, can benefit.

This series of short articles is directed at Windows Administrators who may find a need to build a custom CmdLet to solve some problem they are facing or to simplify some repetitive task that is not easily solved via functions or PS scripts.

I am not addressing these articles at full time PowerShell CmdLet developers.  Developers are usually very much at home with the NET classes and the Windows APIs.  Much of what I will post here assumes a limited knowledge of these. Administrators generally have good knowledge about the physical and structural components of their systems but may have only a basic knowledge of the underlying details of the systems software.  I will endeavor to keep  the technical programming issues out of the discussion except wher they are needed for clarity.  In many instances I may only provide a link to external resources that can be used to furthur understand the issue.


How should our Get_Computer CmdLet behave?

All design should start with a requirement or list of requirements.  The first step of the design process, in my view, is to “vet” this requirements list.

My requirements list is an attempt to provide a single object that summarizes most of the common functions and information sets that an administrator needs for managing computers in a domain.  Minimally these would be service, process, accounts, sessions, software, join/rejoin, reboot, shutdown, etc.

Initially I would like my CmdLet to do the following:

1.      Take input from a file.

2.      Take input from a collection of objects

3.      Take input from a string

4.      Take input from a list of strings

5.      Take input from the pipeline

6.      Output a custom object of type “Computer”

7.      Provide direct access to instances of the “most used” WMI classes.

8.      Provide access Active Directory computer information.

9.      Provide frequently used “action” methods as direct methods on the Computer object

10.  Ease in adding new functionality in future releases.


This looks like a small list.  I have already done some building and testing with many of its elements and have found that it is not small at all.  When trying to pull all of this functionality into a CmdLet many questions bubble to the surface.  I will try and tackle most of these questions in print here.

The biggest issue I face with drawing all of this together is what the Computer object exposes in the way of properties and methods.  I have tried to approach this by modeling how the CmdLet would be used from the command line and from the pipeline.  In order to understand how the CmdLet should behave I have had to consider the differences between behavior from the command line and behavior in the pipeline.

Behavior of a CmdLet from the Command Line

From the command line a CmdLet returns an object or a collection of objects. THe information returned to the command line is generally formatted to display what is considered most useful.  The exact nature of the default display of the objects properties can be externally controlled or may be modified by switches defined by the CmdLet.  Simple command line use of a CmdLet is generally set up for direct viewing of output by the user.

The properties and switches for my CmdLet should allow the user to get usable information by typing only a minimal command line.

What happens when the user types “Get_Computer” with no arguments?

What should the input type of the default argument be? In this case it should probably be a simple string with the computer name; but is it that easy?  What is a computer name anyway?  Is it the NetBIOS name, the DNS name, the IP address…?

We will need to provide mechanisms for addressing all of these variations without the user having to know an exact definition for a computer name.

What switches will the CmdLet process specifically from the command line and will they have a different behavior in the pipeline? 

Assume we have a switch, “ping”, which cause the computer to be pinged when the computer object is created.  What will this ping do from the command line and from the pipeline? This is the kind of question that needs to be answered in order to correctly support command line usage of a CmdLet.

Behavior of a CmdLet in the pipeline

When the Get_Computer CmdLet is run as part of a pipeline it’s behavior needs to be very well defined. The behavior should be adjustable through the use of custom switches to tailor the input and output objects to their use in the pipeline.  In order to do this we need to consider the relationships between the types of input objects allowed from the pipeline and the possible uses of the output objects downstream in the pipeline.

Behavior in the pipeline will also require careful attention to how exceptions and anomalous conditions are handled by the CmdLet.  We need to assume that the CmdLet will be used arbitrarily in any pipeline that it doesn’t reject and that the CmdLet does not lie to downstream pipeline members or misinterpret upstream objects.  Failure to handle these issues could lead an administrator into deleting or removing information that they didn’t intend to alter.

Let’s begin the design!

In the next installment I will discuss some of the initial properties and the bootstrap behavior of "Get-CmdLet".  Getting the first few components defined will move us towards a test rig for “proof-of-concept” testing which we will use for the remainder of the design process.

It may seem that this is all a bit complicated but it’s not.  The wordiness is driven by a need to show to some level of detail the issues that have to be considered.  In practice much of this is done quickly.  Many of these issues may not come up at all if the CmdLet is a simple command line only utility like a game or a lookup.  For non-utility CmdLets much of the above applies selectively based on whether the CmdLet is used for altering the pipeline or just filtering it.  The Get-Computer CmdLet, at this time, is only going to produce objects that can be manipulated by the pipeline for basic purposes like reporting and restarting computers and services.  This will reduce the number of issues that we will initially have to deal with.  In the future I may extend the CmdLet design to be more radical. 

No comments:

Post a Comment