Weekend Scripter: Build Your Own PowerShell Cmdlet: Part 8 of 9

Weekend Scripter: Build Your Own PowerShell Cmdlet: Part 8 of 9

  • Comments 5
  • Likes

Summary: Microsoft Windows PowerShell MVP, Sean Kearney, continues a series of guest blogs detailing building your own cmdlet.

Microsoft Scripting Guy, Ed Wilson, is here. Guest blogger and Windows PowerShell MVP, Sean Kearney, has written a series about building cmdlets. For more about Sean, see his previous guest blog posts.

Note This is Part 8 of a nine-part series about building your own Windows PowerShell cmdlet. Read the entire series as it unfolds.

Here’s Sean…

Having a cmdlet to help make life easier is one thing. Enabling others to easily understand how to leverage it is an especially wonderful thing. Within Windows PowerShell, you can easily produce Help for your users in the scripts by using what is referred to as comment-based Help.

The same feature carries directly into your cmdlets and works in exactly the same way. Normally within Windows PowerShell when you precede something with a pound character (#) everything after that is recognized as a comment within the script. You can leverage this within the Windows PowerShell environment to easily build-in Help for your scripts and cmdlets.

Comment-based Help is produced within an advanced function or Windows PowerShell script by leveraging comments that are defined with specific keywords, and then laying them out in specific sections at the beginning or end of a script or advanced function.

My personal preference is to place the Help near the beginning of the script block—placing the “how to use this” near the top—but that’s simply a personal preference. In actual fact, the comment-based Help can sit in multiple locations with the cmdlet or script.

You can place your Help block right after the naming of the function, at the bottom of the script, or prefacing the name.

Note   If you place it before the name of the function, there can be no space separating the two.

When you write the comment-based Help, your block will begin and terminate in one of the following two fashions.

<#

  .keyword

  Descriptive Text

#>

Or:

# .keyword

# Descriptive Text

When I have to choose a style, my own personal preference is beginning <# and ending with #>. In that way, I look and know where the start and end of my comment-based Help is.

For comment-based Help, you have the following keywords to leverage. Each time you use a keyword, it must be preceded with a period (.).

  • .SYNOPSIS

  • .DESCRIPTION

  • .PARAMETER

  • .EXAMPLE

  • .INPUTS

  • .OUTPUTS

  • .NOTES

  • .LINK

  • .COMPONENT

  • .ROLE

  • .FUNCTIONALITY

  • .FORWARDHELPTARGETNAME

  • .REMOTEHELPRUNSPACE

  • .EXTERNALHELP

Comment-based Help keywords

Each of these keywords has their own special use and format. I’ll try to provide a very compressed version of their meaning.

SYNOPSIS

A very quick description of the cmdlet or script. Think in your head, “If I had to say what this cmdlet does in twelve words or less, what would they be?” Of course, the synopsis can be longer, but my personal feeling is, “The boss is asking you what this does, so be quick, brief, and to the point.”

DESCRIPTION

This is where you can get chatty. Get very descriptive if you like, and be liberal with words to describe what the cmdlet does and how it works. Use whatever text is most appropriate. Perhaps you want to provide a long story about the day you and the cmdlet went for supper in a quaint little diner in Albuquerque.

PARAMETER

For each parameter that your cmdlet or script is accepting, you can place a spot to describe what that parameter does, and what type of data it is expecting and wanting. You can place as many of these entries as you have parameters.

EXAMPLE

My greatest love in Windows PowerShell is when people who build cmdlets give me real world examples of how they work and how they can work. This is your time to show how your cmdlet will shine. You can provide as many examples as you want. Use an EXAMPLE keyword and descriptive lines for each example you provide.

Show people what the cmdlet does in a pipeline and what it does by itself. Give interactive examples. The more people have of examples of how your cmdlet can be used, the more likely it will be used. Unless, of course, your cmdlet is meant to write, “Hello, I am cool!” all over on the screen, infinitely, in random colors. Then there is a very good chance that it won’t get used—no matter how many examples you provide.

INPUTS and OUTPUTS

INPUTS and OUTPUTS are similar and related within the Help system. INPUTS are whatever types of objects your cmdlet can accept from the pipeline. OUTPUTS are whatever type of objects you are returning. If you’d like to describe this type of data, here is the place to put it

NOTES

Anything that isn’t covered in the previous keywords. It could be a trivial pursuit about your cmdlet if you feel so inclined, or maybe even information about how much time your cmdlet saved you at work. Write whatever you feel is relevant—even something as trivial as, “Whatever you do, don’t run this on the production Exchange Server—I wish I didn’t.”

LINK

Links to other topics about your cmdlet, maybe resources you used. It can be as simple as, “Here is the link to my website to send me money, complaints, or Jelly Babies.”

COMPONENT

“What does my script need to work? What dependencies does it need? Will it work with Novell?” These are questions you should answer for your users. For example, “My cmdlet requires the Quest ActiveRoles Server component to function.” The wording, of course, is your own, but this is your opportunity to state what prerequisites you need.

EXTERNALHELP

When you see EXTERNALHELP, it is not a phone call to your friend down the road. EXTERNALHELP is a link to a file outside of the module or cmdlet. If we want to place some sample Help in our cmdlet, the block could look like this, depending on what details you want to provide for the cmdlet.

function global:ADD-LOGFILE{

 

<#

            .SYNOPSIS

            This Cmdlet Creates a Blank Logfile

 

            .DESCRIPTION

            This Cmdlet Creates a Blank Logfile in any specified folder, assigned preface or file extension. It will default to the location C:\Logfile with an extension of .TXT

 

            .EXAMPLE

            ADD-LOGFILE

 

 Creates a Logfile in C:\Logfile folder

 

            .EXAMPLE

            ADD-LOGFILE C:\Here

 

            .EXAMPLE

            ADD-LOGFILE -Extension .FIL

 

            .NOTES

            Creates logfiles based upon Date and Time

 

            .LINK

            http://www.powershell.ca

 

#>

 

[CmdletBinding(

DefaultParameterSetName=”Folder”,

SupportsShouldProcess=$True,

ConfirmImpact=’High’

)]

 

PARAM(

 

[parameter(Mandatory=$True,

ValueFromPipeline=$True,

Position=0,

HelpMessage=’Folder to Store Logfiles in’)]

[STRING[]]$Folder="C:\PowerShell",

 

[parameter(ValueFromPipeline=$True,

Position=1,

HelpMessage=’TEXT to prepend all logfiles with’)]

[STRING[]]$Preface="Logfile",

 

[parameter(ValueFromPipeline=$True,

Position=1,

ValueFromPipelineByPropertyName=$True,

HelpMessage=’File Extension for Logfiles’)]

[STRING[]]$Extension=".log"

)

 

Begin {}

Process {

 

WRITE-DEBUG “`$Folder: $Folder[0]”

WRITE-DEBUG “`$Preface: $Preface[0]”

WRITE-DEBUG “`$Extension: $Extension[0]”

 

# GET the Current Date for our Logfile

 

$Today=GET-DATE

WRITE-DEBUG “`$Today: $Today”

# Extract the Date removing the “/”

 

$Date=$Today.toshortdatestring().Replace(“/”,””)

WRITE-DEBUG “`$Date: $Date”

 

# Extract the Time removing the “:”

 

$Time=$Today.tostring(“HH:mm:ss”).Replace(“:”,”“)

WRITE-DEBUG “`$Time: $Time”

 

# Build our Filename

 

$Logfilename=$Folder[0]+"\"+$Preface[0]+”-“+$Date+”-“+$Time+$Extension[0]

WRITE-DEBUG “`$Logfilename: $Logfilename”

 

# Test and ensure file does not already exist

 

IF (TEST-PATH -path $Logfilename)

 

{ WRITE-ERROR –message “Error: $Logfilename exists.” –category ‘WriteError’

 

# If file exists, return a status of Boolean $False for Unsuccessful

 

RETURN $Logfilename,$FALSE }

 

ELSE

 

{

 

# Create logfile

 

NEW-ITEM –Type File -path $Logfilename -Force | OUT-NULL

WRITE-DEBUG “$Logfilename successfully created”

 

# Return the Full path and filename if successful

 

RETURN $Logfilename,$TRUE

}

End {}

}

}

At this point, please save this as ADDLOG.PS1 and execute it to reload the cmdlet into memory. Then try the following cmdlets:

  • GET-HELP ADD-LOGFILE –examples

  • ADD-LOGFILE C:\MyFolder

  • ADD-LOGFILE –Extension .FIL

  • ADD-LOGFILE –whatif

As you can see, although this particular cmdlet has a very simple function, it is not difficult to leverage many of the features in Windows PowerShell to extend its abilities. It feels very much like a real cmdlet, and that is because it is a real cmdlet.

~Sean

Thank you, Sean. Whew! This is some good stuff. Join me tomorrow when we will see Sean bring this thing to a conclusion. It is great! It is awesome! It is downright cool!!

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • In this blog of yours, I found so many interesting themes and even if I did not yet responded to none, you to  know that I read them with great interest. Success.

  • Thank you Gabyhandyman for your kind words. That makes my day.

    Ed

  • Thanks heaps for your dedication to writing advanced functions(cmdlets). I knew nothing about them when I started with POSH but I think its great to have resources to encourage all of us to code more in this style.

    I particularly liked your comment: " It feels very much like a real cmdlet, and that is because it is a real cmdlet.". If it behaves like a cmdlet then it ought to be called one. Call it the "Turing" test if you like!

  • Why are you torturting yourself with those weird date and time manipulations?

    Date: Get-Date -format d

    Time: Get-Date -format HHmmss or (Get-Date).tostring(“HHmmss”)

    Does the job just fine.

  • @Sebastian

    Excellent point.  My background is the ITPro and sometimes my code is not as elegant as it should be.  You're correct and there are ALWAYS different (And often much better) solutions.

    Nice job :)

    @David

    My first time on Cmdlets and Powershell I knew nothing as well.  My first venture into Powershell was deleting files by date and time..... Which I learned from this very blog.

    We all start somewhere.  Enjoy the Power of Shell ! :)

    Sean