PowerShell Pipeline

Provide Safe Scripting by Adding WhatIf Support

Be sure something won't break with your code with this handy parameter.

Have you ever been in a position where you weren't sure that the command that you were going to run was really going to work in a way that you had hoped? Such as querying for a process and then,  in the same one liner, wanting to kill that process? If you were comfortable with your query and just went right for it without a second thought, well that's great! But if you wanted to be sure (I know I always want to be sure) then with most cmdlets that perform some sort of change action whether it is setting, creating or deleting, you want to make use of an amazing parameter that should be available called –WhatIf. Let's just assume that I wanted to stop some process that started with S. Probably not the best idea running around but luckily I know enough to use –WhatIf with Stop-Process to see what might happen.

Get-Process s*  | Stop-Process  -WhatIf 
[Click on image for larger view.]  Figure 1. Using builtin -WhatIf functionality to prevent outages.

Good thing I just didn't let it run without some training wheels. As you can see, instead of just following through and stopping all of these processes, I can see what would have happened had I not used –WhatIf. Pretty nice to see what kind of damage that would have been done.

While having this available out of the box with our cmdlets is great, we need to make sure that anything that has been written by ourselves should abide by this as well. In fact, if you are building a function that makes any sort of change, it should absolutely provide support for –WhatIf.  By not providing this type of support, we are just sending out potentially dangerous commands into the wild and potentially breaking the trust of those who would come to expect this to be available in a command based on what they have seen with what is currently available with PowerShell.

So with all of that said, how do we actually do this? The first step is to ensure that in your function that [cmdletbinding()] and Param() have been defined in the function body.

Function Invoke-PowerShellCrash  { 
[cmdletbinding()]
Param ()
...
}

Within [cmdletbinding()], we have to set the SupportsShouldProcess attribute to True to let PowerShell know that this function will support –WhatIf as a parameter. This does not mean that we have configured –WhatIf support at this point in time.

Function Invoke-PowerShellCrash  { 
[cmdletbinding(
SupportsShouldProcess = $True
)]
Param ()
...
}

Now I am assuming some backwards compatibility with V2, otherwise you can just leave out the = $True part and just specify SupportsShouldProcess instead.

Now comes the fun part. We will now begin the process of putting together the code needed to ensure that we actually do use –WhatIf when specified as a parameter. We first need to call $PSCmdlet.ShouldProcess(),  which will check to see if –WhatIf has been used and then, based on the parameters within ShouldProcess, display some sort of information to the user.

[Click on image for larger view.]  Figure 2. Possible parameters to use with ShouldProcess()

What I am most concerned with is ensuring that we specify both a target, that is the object that this will be run against, and a short caption so the user running this commands knows exactly what is happening.

If ($PSCmdlet.ShouldProcess($PID,'Crashing PowerShell')) {
[environment]::FailFast('Invoked Crash')
}

In this case, I am going to specify the process ID of the current PowerShell console that this is being run on and letting the user know that this will crash the console. Yea, it's probably not something you would ever put out in production, but it provides a nice example of protecting not only yourself, but those who are using your code by seeing what would happen.

 

Invoke-PowerShellCrash -WhatIf 
[Click on image for larger view.]  Figure 3. Using -WhatIf support in custom function

Works like a champ! Had I not done this, well, this is what you would end up seeing instead:

Invoke-PowerShellCrash -Verbose 
[Click on image for larger view.]  Figure 4. Built-in Verbose support with SupportsShouldProcess

Whoops! Did I mention that providing –WhatIf support also throws in a free Verbose output as well for this particular action? How great is that?

Another thing to mention is that adding the SupportsShouldProcess=$True means that any cmdlets that already have this functionality will automatically use it if you happen to specify –WhatIf as show in the examples below.

Function Stop-ProcessV2  {
[cmdletbinding(
SupportsShouldProcess = $True
)]
Param (
[parameter(ValueFromPipelineByPropertyName=$True)]
[Alias('Id')]
[int]$ProcessID
)
Stop-Process -Id $ProcessID
}
Get-Process -Name System | Stop-ProcessV2 -WhatIf 

[Click on image for larger view.]  Figure 5. Inherited WhatIf support on existing cmdlets.

This is important in case you have other parts of your code which maybe should run regardless of the –WhatIf preference and should be handled accordingly.

With that you now have the skill set needed to move forward and update all of your scripts/functions that have the ability to modify, remove or create new things and now ensure that these have the necessary safety net that it deserves!

About the Author

Boe Prox is a Microsoft MVP in Windows PowerShell and a Senior Windows System Administrator. He has worked in the IT field since 2003, and he supports a variety of different platforms. He is a contributing author in PowerShell Deep Dives with chapters about WSUS and TCP communication. He is a moderator on the Hey, Scripting Guy! forum, and he has been a judge for the Scripting Games. He has presented talks on the topics of WSUS and PowerShell as well as runspaces to PowerShell user groups. He is an Honorary Scripting Guy, and he has submitted a number of posts as a to Microsoft's Hey, Scripting Guy! He also has a number of open source projects available on Codeplex and GitHub. His personal blog is at http://learn-powershell.net.

comments powered by Disqus
Most   Popular