PowerShell Basics: Don't Fear Hitting Enter with -WhatIf
Published Feb 20 2019 07:38 PM 131K Views
Microsoft

Chances are you've run into this situation. You've built a script, or a one-liner, to perform a specific task, but you don't have a way to thoroughly test it without hitting Enter. That moment before hitting enter can be difficult. Knowing this need, there is a switch available with many PowerShell commands called -WhatIf.

 

With -WhatIf, PowerShell will run your command in its entirety without executing the actions of the command so no changes occur. It displays a listing of actions to be performed against the affected objects in the console window. This is great for the commands that do not display any output when executed, or if you are unsure of the overall impact of your command.

 

Important Note: -WhatIf is only available on commands that make changes AND when it has been implemented by the creator of the commands.

 

How do you know if a command supports -WhatIf? That's pretty simple. Use get-help to view the syntax of the command.


PS> get-help remove-item
NAME
    Remove-Item

SYNOPSIS
    Deletes files and folders.


SYNTAX
    Remove-Item [-Confirm] [-Credential <PSCredential>] [-Exclude <String[]>] [-Filter <String>] [-Force] [-Include
    <String[]>] -LiteralPath <String[]> [-Recurse] [-Stream <String[]>] [-UseTransaction] [-WhatIf]
    [<CommonParameters>]

    Remove-Item [-Path] <String[]> [-Confirm] [-Credential <PSCredential>] [-Exclude <String[]>] [-Filter <String>]
    [-Force] [-Include <String[]>] [-Recurse] [-Stream <String[]>] [-UseTransaction] [-WhatIf] [<CommonParameters>]

    Remove-Item [-Stream <string>] [<CommonParameters>]

 

As you can see, [-WhatIf] is listed in the syntax for the command. Also it will be available as a tab-complete option when entering the command. If you do happen to enter it with a command that doesn't support it, you'll receive the following error:


PS C:\Users\mibender> get-help -Whatif
Get-Help : A parameter cannot be found that matches parameter name 'Whatif'.
At line:1 char:10
+ get-help -Whatif
+          ~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Get-Help], ParameterBindingException
    + FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell.Commands.GetHelpCommand

 

Now let's see an example of how this works. A common scenario is needing to clean up a file share by file type. In this case, I want to find all of the video files in an MP4 format and remove them from my c:\recordings directory. Here's the process you would run through:

First, let's find all of the MP4 files with Get-ChildItem:


PS> Get-ChildItem -File *.MP4 -Recurse -LiteralPath C:\Recordings\

    Directory: C:\Recordings\car-Talks\Attitude-Control

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----         9/7/2018   3:47 PM       58188931 Attitude-Control.mp4

    Directory: C:\Recordings\car-Talks\Personal-KanBan

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        10/5/2018   1:56 PM       45875317 Personal-kanBan.mp4

    Directory: C:\Recordings\car-Talks\Redmond-Week-1\Redmond-Week-1

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----         8/3/2018   9:01 AM       19616170 Redmond-Week-1.mp4

So that shows us all of the MP4 files, but I'd like to see them in a standard tables format with just their name and directory location and not separated by directory. To do that, we use Select-Object:


PS> Get-ChildItem -File *.MP4 -Recurse -LiteralPath C:\Recordings\ | Select-Object Name,Directory

Name                         Directory
----                         ---------
Attitude-Control.mp4         C:\Recordings\car-Talks\Attitude-Control
Personal-kanBan.mp4          C:\Recordings\car-Talks\Personal-KanBan
Redmond-Week-1.mp4           C:\Recordings\car-Talks\Redmond-Week-1\Redmond-Week-1
SoundTest1.mp4               C:\Recordings\car-Talks\Soundtest

That looks better. What I did was use the pipeline and select-object to choose just the properties I wanted from the objects output from the initial command.

 

After I've reviewed the list, I think that it includes all the items I want to remove. But being paranoid, I'd really like to know that it is going to remove just those files in a specific directory, and not all the files. That's where -WhatIf comes in with the remove-item command:


PS> Get-ChildItem -File *.MP4 -Recurse -LiteralPath C:\Recordings\ | Remove-Item -WhatIf

What if: Performing the operation "Remove File" on target "C:\Recordings\car-Talks\Attitude-Control\Attitude-Control.mp4".
What if: Performing the operation "Remove File" on target "C:\Recordings\car-Talks\Personal-KanBan\Personal-kanBan.mp4".
What if: Performing the operation "Remove File" on target "C:\Recordings\car-Talks\Redmond-Week-1\Redmond-Week-1\Redmond-Week-1.mp4".
What if: Performing the operation "Remove File" on target "C:\Recordings\car-Talks\Soundtest\SoundTest1.mp4".
What if: Performing the operation "Remove File" on target "C:\Recordings\Microsoft\Content-Development-AMA\Content-Development-AMA.mp4".
What if: Performing the operation "Remove File" on target "C:\Recordings\Microsoft\MITT\HYB10\HYB10-Demos-DryRun1-0.mp4".
What if: Performing the operation "Remove File" on target "C:\Recordings\Microsoft\MITT\HYB10\HYB10-Demos-DryRun1-1.mp4".
What if: Performing the operation "Remove File" on target "C:\Recordings\Microsoft\MITT\HYB10\HYB10-Demos-DryRun1-2.mp4".
What if: Performing the operation "Remove File" on target "C:\Recordings\Microsoft\MITT\HYB10\HYB10-Demos-DryRun1-3.mp4".
What if: Performing the operation "Remove File" on target "C:\Recordings\Microsoft\MITT\HYB10\DryRuns\HYB10-Dress-Run-GSL-01.mp4".
What if: Performing the operation "Remove File" on target "C:\Recordings\Microsoft\MITT\HYB10\DryRuns\HYB10-DryRun-01.mp4".
What if: Performing the operation "Remove File" on target "C:\Recordings\Microsoft\MITT\HYB20\HYB20-Demo-SecureScore.mp4".

Based on the output, I've verified that on the MP4 files in the c:\recordings directory are being removed. Now I can go back to my command and execute it by removing the -WhatIf switch.

 

Now, let's say it's 2am in your time zone, and you need to perform multiple tasks through the PowerShell Console. If you are like me, 2am is not my 'peak productivity' hour so making mistakes, like forgetting the -WhatIf switch, could be dangerous. For those instances, you can modify the $WhatIfPreference variable in your console like this:


PS> $whatifpreference
False
PS> $whatifpreference = 'True'
PS> $whatifpreference
True
PS> Get-ChildItem -File *.MP4 -Recurse -LiteralPath C:\Recordings\ | Remove-Item

What if: Performing the operation "Remove File" on target "C:\Recordings\car-Talks\Attitude-Control\Attitude-Control.mp4".
What if: Performing the operation "Remove File" on target "C:\Recordings\car-Talks\Personal-KanBan\Personal-kanBan.mp4".
What if: Performing the operation "Remove File" on target "C:\Recordings\car-Talks\Redmond-Week-1\Redmond-Week-1\Redmond-Week-1.mp4".
What if: Performing the operation "Remove File" on target "C:\Recordings\car-Talks\Soundtest\SoundTest1.mp4".
What if: Performing the operation "Remove File" on target "C:\Recordings\Microsoft\Content-Development-AMA\Content-Development-AMA.mp4".

 

When the command to remove all the files is run and I do not include -whatif since it's 2am, it defaults to running ALL commands with -WhatIf. Now if you actually want to run the command and remove the files, you add the -WhatIf switch parameter and specify :$false like this:


PS> Get-ChildItem -File *.MP4 -LiteralPath C:\Recordings\Test-Recording\ | Remove-Item -WhatIf:$false -Verbose

VERBOSE: Performing the operation "Remove File" on target "C:\Recordings\Test-Recording\Test-Take-SA.mp4".

```powershell

PS CMD> Get-ChildItem -File *.MP4 -LiteralPath C:\Recordings\Test-Recording\
PS CMD>

One thing to remember with this and other preference variables you define in the console is they only maintain the setting in your current shell. When you close and re-open PowerShell, the preference will reset.

 

Another thing is a word of caution on using -WhatIf. Because it is functionality that is added into commands, the implementer may not implement it properly or not at all. I've never run into issues with Microsoft-built commands using -WhatIf like Microsoft Exchange, Active Directory, and many of the built-in commands I use below. However, I cannot speak for code samples you may find in the wild, or the full gamet of Micrsoft product team-produced commands. To be safe, you should test -whatif against a smaller pool of targets vs. trying to modify every Exchange mailbox

in your organization.

 

So the next time you need to perform some PowerShell tasks, add -whatif before you execute, and stop fearing the Enter key.

For more information on the commands used in this post, click on the links below:

Wondering what commands actually work with -WhatIf? Check out this blog by PowerShell MVP Richard Siddaway for more information.

For more information on PowerShell, check out the docs here

8 Comments
Iron Contributor

You are right about your warning. There is a know case where the WhatIf parameter doesn't work. Unfortunately, I discovered it a bit late...

https://windowsserver.uservoice.com/forums/304621-active-directory/suggestions/10844361-set-adaccoun...

Brass Contributor
There are several MICROSOFT cmdlets that say they support -WhatIf Then go ahead and perform the action regardless of -WhatIf anyway but -WhatIf is amazing when it's honoured
Microsoft

That's what I've been hearing. If you have specific cmdlets that you know don't work, let me know. I'm happy to see if I can get them fixed. Feel free to comment here or email me at michael.bender@microsoft.com. Thanks!

 

Iron Contributor

Just came through another one:

Set-Volume -DriveLetter C -NewFileSystemLabel SYSTEM -WhatIf

There is nothing dangerous here, but the the fact is that the WhatIf parameter is just ignored.

Microsoft

Thanks for the heads up, @Luc FULLENWARTH !

Copper Contributor

one more cmdlet which ignores -Whatif

Grant-CsTeamsMeetingPolicy -Identity $_.EmailAddress -PolicyName "Test Policy Name" -WhatIf

This just went and granted the policy to all the piped users.

Copper Contributor

I just tried -WhatIf with New-CsInboundBlockedNumberPattern when trying to test out a script and it ran the command fully.

Copper Contributor

Be warned that the preferences do not get passed to a script block in a parallel pipeline.  You must include a $whatifpreference=$using:whatifpreference to pass the whatif value.  Assume you get the default. 

 

This is also true if using INVOKE-COMMAND with a script block.  It might be very unpleasant if you are not aware of this.  Again, assume you get the defaults in a new session.  For example, running this from a new session show what looks to be okay using defaults is not okay once defaults are not used.  

 

# save the default session values the very first thing before we change them
if ($null -eq $defPref) { $defPref = $DebugPreference, $VerbosePreference, $InformationPreference, $WarningPreference, $ErrorActionPreference, $WhatIfPreference, $ProgressPreference}
if ($null -eq $nonPref) { $nonPref = 'Continue', 'Continue', 'Continue', 'Stop', 'Stop', $true, 'SilentlyContinue'}

write-host 'Using defaults locally:'
$localPref = $defPref # the defaults here match the defaults there
invoke-command -ComputerName SQLTEST18.DEV.LOCAL -ScriptBlock {
    write-host "`$localPref=$($using:localPref)"
    $remotePref = $DebugPreference, $VerbosePreference, $InformationPreference, $WarningPreference, $ErrorActionPreference, $WhatIfPreference, $ProgressPreference
    write-host "`$remotePref=$remotePref"
}

write-host 'Using non-defaults locally:'
$localPref = $nonPref # the non-defaults here do not match the defaults there
invoke-command -ComputerName SQLTEST18.DEV.LOCAL -ScriptBlock {
    write-host "`$localPref=$($using:localPref)"
    $remotePref = $DebugPreference, $VerbosePreference, $InformationPreference, $WarningPreference, $ErrorActionPreference, $WhatIfPreference, $ProgressPreference
    write-host "`$remotePref=$remotePref"
}

Output: 
Using defaults locally:
$localPref=SilentlyContinue SilentlyContinue SilentlyContinue Continue Continue False Continue
$remotePref=SilentlyContinue SilentlyContinue SilentlyContinue Continue Continue False Continue
Using non-defaults locally:
$localPref=Continue Continue Continue Stop Stop True SilentlyContinue
$remotePref=SilentlyContinue SilentlyContinue SilentlyContinue Continue Continue False Continue
Version history
Last update:
‎Feb 21 2019 10:50 AM
Updated by: