Last Update: Apr 17, 2023 | Published: Jul 08, 2020
We can all agree that it’s essential to know what the command or script that we are about to execute will do. Knowing what command can do may require hours of reading code and documentation or testing in separate environments. But PowerShell offers a great tool to speed the development of our scripts up with WhatIf.
In this article, you will learn how to create a PowerShell function that supports the WhatIf
parameter and how you can use it to make your PowerShell functions even more solid.
This tutorial only uses local computer resources and will work on PowerShell version 5.1 and up. Preferably you should have an IDE or ISE for PowerShell of your choice installed. The code in the tutorial is not tested on older versions of PowerShell but it may work on them as well.
WhatIf in PowerShell is a part of the ShouldProcess cmdlet functions in PowerShell. ShouldProcess can also prompt for confirmation and is basically a function that decides if it should process or not process code. This is primarily done with the help of an IF
statement (as you will explore further down in this article).
Now let’s get started by creating our first simple WhatIf function.
A PowerShell function does not support using the WhatIf statement out of the box (but almost) and there’s a couple of things that differ from a plain PowerShell function – The CmdletBinding and SupportsShouldProcess. **You need to specify these at the beginning of the function for them to work as shown in the example below:
Function Test-WhatIf {
[CmdletBinding(SupportsShouldProcess)]
Param(
[String]$TestString
)
Write-Output "Test string: $TestString"
}
The function above supports the ShouldProcess functions that contain the WhatIf feature. But it does not yet use WhatIf as you notice when you run the function:
PS7> Test-WhatIf -TestString Foo -WhatIf
Test string: Foo
This is because you need to surround the code that you want to use WhatIf on with an IF
statement that contains a call to $PSCmdlet.ShouldProcess
with a description of the target and the action that it performs.
Function Test-WhatIf {
[CmdletBinding(SupportsShouldProcess)]
Param(
[String]$TestString
)
# $PSCmdlet.ShouldProcess first argument is for target, the second is for action
If($PSCmdlet.ShouldProcess("Console", "Writing TestString '$TestString'")){
Write-Output "Test string: $TestString"
}
}
The result of the addition above is clearly seen when you run the function again:
PS7> Test-WhatIf -TestString Foo -WhatIf
What if: Performing the operation "Writing TestString 'Foo'" on target "Console".
You have now created a basic PowerShell function that supports WhatIf! But how should you use it and when? That you will learn about next.
There are two uses when it comes to using ShouldProcess: Describing what it will do and testing if it can do it in a non-destructive manner (i.e no writes or no “dangerous writes”).
The describing part is automatically implemented since we supply ShouldProcess with a target and an action. But the second part can be more tricky – and while there is no must to implement it can make your scripts extremely robust.
The tests that you perform can perform everything from simple to complex tests ranging everywhere from testing write permissions to return mock data of a successful write.
Below you will see examples of a function that creates 5 files, but the latter one also performs tests if WhatIf is supplied that validates that you have permission to write to the specified directory:
Function New-TestFiles {
[CmdletBinding(SupportsShouldProcess)]
Param(
[Parameter(Mandatory,ValueFromPipeline)]
[String]$Directory,
[Int]$Amount = 5
)
Begin{}
Process {
# Create $Amount number of files
1..$Amount | ForEach-Object {
$Target = Join-Path -Path $Directory -ChildPath "$_.txt"
# ShouldProcess
if($PSCmdlet.ShouldProcess($Target, "Create File")){
Set-Content -Value "No content" -Path $Target
}
}
}
End {}
}
The output that you get by running the following against a directory where you don’t have write permission looks like this:
PS7> New-TestFiles -Directory C:\\Windows -WhatIf
What if: Performing the operation "Create File" on target "C:\\Windows\\1.txt".
What if: Performing the operation "Create File" on target "C:\\Windows\\2.txt".
What if: Performing the operation "Create File" on target "C:\\Windows\\3.txt".
What if: Performing the operation "Create File" on target "C:\\Windows\\4.txt".
What if: Performing the operation "Create File" on target "C:\\Windows\\5.txt".
It shows that it’s performing “Create File” even if you don’t have write permission to C:\Windows. This can be misleading and using ShouldProcess without any validation that it can perform the actions if WhatIf is specified can remove the point of using it in many cases.
Let’s add some tests to the previous functions by adding them to an ElseIf
statement after the If statement that contains the call to ShouldProcess:
Function New-TestFiles {
[CmdletBinding(SupportsShouldProcess)]
Param(
[Parameter(Mandatory,ValueFromPipeline)]
[String]$Directory,
[Int]$Amount = 5
)
Begin{}
Process {
# Create $Amount number of files
1..$Amount | ForEach-Object {
$Target = Join-Path -Path $Directory -ChildPath "$_.txt"
# ShouldProcess
If($PSCmdlet.ShouldProcess($Target, "Create File")){
Set-Content -Value "No content" -Path $Target
}
ElseIf($WhatIfPreference){
$TestFile = Join-Path -Path $Directory -ChildPath "testfile.tst"
Try {
[Io.File]::OpenWrite($TestFile).close()
Remove-Item -Path $TestFile -WhatIf:$False -ErrorAction SilentlyContinue
}
Catch {
Write-Warning "$Directory is not writeable!"
}
}
}
}
End {}
}
The test that was added in this version of the function will write to a test file and remove it to validate that you have the permission to write to the directory.
Always use ElseIf($WhatIfPreference){<code>}
when you want to perform tests with WhatIf since ShouldProcess also takes care of Confirms (i.e. “Do you want to run this?”). This will make sure that the tests only run when “WhatIf” is specified.
If it can’t write a file to the directory it will return a warning as you can see below:
PS7> New-TestFiles -Directory C:\\Windows -WhatIf
What if: Performing the operation "Create File" on target "C:\\Windows\\1.txt".
WARNING: C:\\Windows is not writeable!
What if: Performing the operation "Create File" on target "C:\\Windows\\2.txt".
WARNING: C:\\Windows is not writeable!
What if: Performing the operation "Create File" on target "C:\\Windows\\3.txt".
WARNING: C:\\Windows is not writeable!
What if: Performing the operation "Create File" on target "C:\\Windows\\4.txt".
WARNING: C:\\Windows is not writeable!
What if: Performing the operation "Create File" on target "C:\\Windows\\5.txt".
WARNING: C:\\Windows is not writeable!
Now that you learned how to use WhatIf in your PowerShell functions from scratch it’s time to learn some more tricks when it comes to WhatIf as promised.
While implementing the WhatIf feature into your functions is pretty straight forward there are some things that you should know about that can hopefully speed up the development of your scripts. It can also save you from a lot of headaches and even downtime.
If properly implemented by the cmdlets in your function. This means that you can “piggy-back” on other peoples WhatIf implementation which is especially useful if they contain tests.
You can see that for yourself by using the function below:
Function Test-WhatIf {
[CmdletBinding(SupportsShouldProcess)]
param(
)
Set-Content -Path "C:\\temp\\whatif\\test.txt" -Value "Test"
}
If you run it you will get the following result:
PS7> Test-WhatIf -WhatIf
What if: Performing the operation "Set Content" on target "Path: C:\\temp\\whatif\\test.txt".
ShouldProcess does not only output on WhatIf – but on verbose as well! This shortens the code needed for functions that need verbose output implemented by quite a bit.
You can use the function below to demonstrate it:
Function Test-WhatIf {
[CmdletBinding(SupportsShouldProcess)]
param()
If($PSCmdlet.ShouldProcess("Console", "Writing output")){
Write-Output "Hello"
}
}
If you run the above function with the -Verbose
switch you will get the following result:
PS7> Test-WhatIf -Verbose
VERBOSE: Performing the operation "Writing output" on target "Console".
Hello
Be aware, this was years ago an even bigger problem but has become quite rare in the last few years.
Some 3rd party functions look like they support WhatIf but it is not implemented! This is because the authors of the cmdlets either have added SupportsShouldProcess
at the top of the function or added WhatIf as a switch parameter.
This means that you may in some cases add the -WhatIf
switch to a command in the belief that it won’t perform any harmful actions – but it will if it is not implemented throughout all write operations in the function.
You have now learned how to create your own PowerShell functions that support WhatIf! It adds to the length of your code but is an important feature to have – Especially in critical environments or when you share your codes with others.