PowerShell with a Purpose Blog

Advanced Functions Part 1: Cmdlets From Scripts

Have you ever wished you could write a "real" cmdlet without having to crank up Visual Studio and learn Visual Basic or C#? If so, then PowerShell's "advanced functions," or "script cmdlets," are just what you're looking for.

Byt the way, this will be a short series of articles - find them all on my Windows IT Pro home page.

Cmdlets have a couple of distinguishing characteristics. First, they have parameters that can be referred to by name, can accept specific types of input, and that can bind pipeline input. Those parameters can have default values, and they can also be positional, meaning you can omit the parameter name and just provide the input value. In fact, most of what makes a cmdlet special is in the way those parameters work, and in how flexible they are.

The other characteristic is that cmdlets accept pipeline input, and produce pipeline output. A normal, non-advanced function can do that - but not in quite the same neat way that an advanced function can.

Here's a complete advanced function for your consideration:

01.function Get-ComputerDetails {
02.    [CmdletBinding()]
03.    param (
04.        [parameter(Mandatory=$true,ValueFromPipeline=$true)]
05.        [string[]]$computername
06.    )
07.    BEGIN {}
08.    PROCESS {
09.        # get the OS
10.        $os = get-wmiobject win32_operatingsystem -computername $computername
12.        # create a new object
13.        $obj = new-object psobject
15.        # add properties - note that our capitalization
16.        # of the property names will be retained
17.        $obj | add-member noteproperty ComputerName $computername
18.        $obj | add-member noteproperty BuildNumber ($os.buildnumber)
19.        $obj | add-member noteproperty SPVersion ($os.servicepackmajorversion)
21.        # get the BIOS
22.        $bios = get-wmiobject win32_bios -computername $computername
23.        $obj | add-member noteproperty BIOSSerial ($bios.serialnumber)
25.        write $obj
26.    }
27.    END {}

Let's walk through this. It all begins with the function's declaration and its name, Get-ComputerDetails. Next is a special attribute, [CmdletBinding()], that tells the shell this is an advanced function that will be declaring cmdlet-style parameters. After that are the actual parameters, and I've declared one of them in the param() block. That parameter, -computerName, is mandatory. It accepts pipeline input ByValue, and it is of the value string. The "String[]" tells me that it can accept one or more values.

Next is the BEGIN block, which is empty. Normally, whatever's in there would execute first - but since it's empty, we'll move on.

Next up is the PROCESS block. This is going to execute one time for every object piped into the cmdlet, or every object assigned to a pipeline-bound parameter - like -computerName. Notice that the code uses $computerName, not the more obscure $_ placeholder, to access the piped-in data. Also note that the output is being written to the pipeline by using Write-Output.

It all wraps up with an END block, which would normally run last after PROCESS was finished. It's empty, so we're done.

You could run this function in many ways:

Get-ComputerDetails -comp localhost
"server1","server2" | Get-ComputerDetails
Get-Content servers.txt | Get-ComputerDetails

The cmdlet - sorry, the function - has all this flexibility because it's got cmdlet-style parameters. There are a few other neat things you can do with this kind of function. For example, you can set up basic data validation on parameters, provide default values, and more - run help *advanced* in PowerShell for a list of help topics related to these advanced functions.
Hide comments


  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.