PowerShell -WhatIf: the safest flag you never use
Every week someone in an IT Slack posts a screenshot of a script they ran that deleted the wrong things. Disabled the wrong accounts. Wiped the wrong OUs. The post always ends the same way: “is there a way to undo this?”
There isn’t. But there was a way to not do it in the first place.
What -WhatIf actually does
-WhatIf is a common parameter built into every cmdlet that modifies state — Remove-ADUser, Set-ADUser, Move-ADObject, Disable-ADAccount, all of them. When you pass it, the cmdlet prints what it would do and exits without touching anything.
Get-ADUser -Filter { Enabled -eq $false -and LastLogonDate -lt "2024-01-01" } |
Disable-ADAccount -WhatIf
Output:
What if: Performing the operation "Set" on target "CN=john.smith,OU=Users,DC=corp,DC=local".
What if: Performing the operation "Set" on target "CN=jane.doe,OU=Contractors,DC=corp,DC=local".
You get a full list of every object that would be touched. No changes made.
The problem: most scripts don’t support it
If you write a script with a bare Remove-ADUser $user inside a loop, -WhatIf doesn’t propagate automatically. You have to wire it up yourself with [CmdletBinding(SupportsShouldProcess)].
[CmdletBinding(SupportsShouldProcess)]
param(
[string]$OUPath,
[int]$DaysInactive = 90
)
$cutoff = (Get-Date).AddDays(-$DaysInactive)
$stale = Get-ADUser -SearchBase $OUPath -Filter { LastLogonDate -lt $cutoff -and Enabled -eq $true }
foreach ($user in $stale) {
if ($PSCmdlet.ShouldProcess($user.SamAccountName, "Disable account")) {
Disable-ADAccount -Identity $user
Write-Host "Disabled: $($user.SamAccountName)"
}
}
Now .\Disable-StaleAccounts.ps1 -OUPath "OU=Users,DC=corp,DC=local" -WhatIf works correctly. Every action is gated behind ShouldProcess. Nothing runs until you remove -WhatIf.
The pattern I use for every AD script
Three lines at the top of every script that touches AD:
[CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
param(...)
ConfirmImpact='High' means PowerShell will prompt for confirmation on every action unless you explicitly pass -Force. Combined with -WhatIf, it gives you two safety rails: preview first, then confirm at execution.
Why this matters at scale
At 10,000 users, a filter bug that’s off by one day doesn’t disable 2 accounts — it disables 800. The LastLogonDate attribute is replicated, not always current, and depends on which DC answered the query. I’ve seen clean-looking scripts hit 40% of an OU because the filter logic assumed $null meant “never logged in” when it actually meant “logged in before attribute tracking was enabled.”
-WhatIf costs 30 seconds. The post-incident recovery costs days.
The three scripts every AD shop should have -WhatIf on
- Stale account disable — any user inactive >N days
- Group membership cleanup — removing accounts from security groups in bulk
- OU moves — restructuring after an org change or acquisition
These are the three that cause the most irreversible damage when they go wrong. If your versions don’t have -WhatIf support, add it before you run them again.
Need a stale account audit or AD cleanup for your environment? Get in touch — it’s one of the fixed-price packages on the services page.