Advanced Functions Part 2: ShouldProcess() in Your Script Cmdlets

Advanced functions' ability to behave almost exactly like a "real" cmdlet isn't limited to parameter binding. You can also take advantage of the common -verbose parameter. Simply output your verbose output this way:

Write-Verbose "Beginning query"

And when the shell's $VerbosePreference variable isn't set to "SilentlyContinue" ("Continue" will enable the output), you'll see your verbose output.
But let's do something cooler. Try this:

Get-Process | Stop-Process -whatif
And now this:
Get-Service | Stop-Service -confirm

(Um, answer "no" when it asks). Stop-Process and Stop-Service both support the -whatif and -confirm parameters, because the cmdlets attempt to modify the system. Any cmdlet - or advanced function - that is going to change something should specify an impact level of either Low, Medium, or High; if the cmdlet is run without the -whatif or -confirm parameters, it will still ask for confirmation if the specified impact level is higher than the shell's built-in $ConfirmPreference variable (which is usually "High"). Your advanced functions can declare their impact level by modifying the CmdletBinding() attribute:

Function Win32Restart-Computer {
  [CmdletBinding(
     SupportsShouldProcess=$true,
    ConfirmImpact="High"
  )]

The SupportsShouldProcess bit tells the shell that your function supports both -confirm and -whatif. The way you actually implement that support is to write conditional code around whatever dangerous stuff your cmdlet is planning to do:

if ($pscmdlet.ShouldProcess($computername)) {
   get-wmiobject win32_operatingsystem -computername $computername |
     invoke-wmimethod -name Win32Shutdown -argumentlist $_action
}

Using the $pscmdlet.ShouldProcess() method automatically invokes the -whatif or -confirm functionality. You pass in a description of what you're planning to modify; in this example, I passed in the $computername variable to indicate the computer I plan to reboot. If the function was run with -whatif, ShouldProcess() will display the "what if" statement and return False, so that your conditional code won't actually execute. If the function was run with -confirm, your conditional code will only run if the user responded with "Yes" or "Yes to All."

Here's a complete function, along with an example of how to use it at the bottom. Also notice the cool comment-based help - after pasting this into your shell, you can run "Help Win32Restart-Computer" to see the nicely-formatted help - and the cool -whatif and -confirm parameters in the parameter list!

function Win32Restart-Computer {
 
.SYNOPSIS
Restarts one or more computers using the WMI Win32_OperatingSystem method.
.DESCRIPTION
Restarts, shuts down, logs off, or powers down one or more computers. This
relies on WMI's Win32_OperatingSystem class. Supports common parameters
-verbose, -whatif, and -confirm.
.PARAM computername
One or more computer names to operate against. Accepts pipeline input ByValue
and ByPropertyName.
.PARAM action
Can be Restart, LogOff, Shutdown, or PowerOff
.PARAM force
$True or $False to force the action; defaults to $false
#>
    [CmdletBinding(
        SupportsShouldProcess=$true,
        ConfirmImpact="High"
    )]
    param (
        [parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string[]]$computerName,
         
        [parameter(Mandatory=$true)]
        [string]
        [ValidateSet("Restart","LogOff","Shutdown","PowerOff")]
        $action,
         
        [boolean]$force = $false
         
    )
    BEGIN {
        # translate action to numeric value required by the method
        switch ($action) {
            "Restart" {
                $_action = 2
                break
            }
            "LogOff" {
                $_action = 0
                break
            }
            "Shutdown" {
                $_action = 2
                break
            }
            "PowerOff" {
                $_action = 8
                break
            }
        }
         
        # to force, add 4 to the value
        if ($force) {
            $_action += 4
        }
         
        write-verbose "Action set to $action"
    }
 
    PROCESS {
        write-verbose "Attempting to connect to $computername"
         
        # this is how we support -whatif and -confirm
        # which are enabled by the SupportsShouldProcess
        # parameter in the cmdlet bindnig
        if ($pscmdlet.ShouldProcess($computername)) {
            get-wmiobject win32_operatingsystem -computername $computername | `
                invoke-wmimethod -name Win32Shutdown -argumentlist $_action
        }
    }
        
}
 
'localhost','server1' | Win32Restart-Computer -action LogOff -whatif

Please or Register to post comments.

What's PowerShell with a Purpose Blog?

Don Jones demystifies Windows PowerShell.

Blog Archive

Sponsored Introduction Continue on to (or wait seconds) ×