It seems the default behaviour of Powershell is to propagate -WhatIf and -Confirm to all functions called from within a function that was called with -WhatIf or -Confirm.
But what if (pun intended) I don't want that ? What if all I want is to protect a bit of code that performs an actual change on my system and not the rest of my code ?
Here is an example:
function Set-Something {
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
[OutputType([string])]
Param (
[Parameter(Mandatory = $false, ValueFromPipeline = $true)]
[string] $ComputerName = $env:COMPUTERNAME
)
Process {
New-Item -Path "beforeChange" -Type File | Out-Null
Remove-Item -Path "beforeChange" -ErrorAction SilentlyContinue
# Here is the bit of code that needs to be "protected"
if ($PSCmdlet.ShouldProcess($ComputerName, "Do something")) {
# Actually do something on $ComputerName
New-Item -Path "inChange" -Type File | Out-Null
Remove-Item -Path "inChange" -ErrorAction SilentlyContinue
}
New-Item -Path "afterChange" -Type File | Out-Null
Remove-Item -Path "afterChange" -ErrorAction SilentlyContinue
}
}
"-"*50 + "Set-Something -WhatIf" + "-"*50
Set-Something -WhatIf
"-"*50 + "Set-Something -Confirm" + "-"*50
Set-Something -Confirm
The Set-Something function only performs system-altering stuff in the protected bit. It also uses temporary files, but New-Item and Remove-Item shouldn't trigger WhatIf or Confirm. Unfortunately that's not how it works:
--------------------------------------------------Set-Something -WhatIf--------------------------------------------------
What if: Performing the operation "Create File" on target "Destination: C:\Users\GO7\beforeChange".
What if: Performing the operation "Do something" on target "GEOCED".
What if: Performing the operation "Create File" on target "Destination: C:\Users\GO7\afterChange".
I could add -WhatIf:$false -Confirm:$false to each and every function that supports ShouldProcess but that would be tedious.
So What I've done so far is :
- save the $WhatIfPreference and $ConfirmPreference at the top of my function
- reset those preferences to their default value
- restore the saved preferences before the protected block
- reset the preferences again inside the block and outside the block
Like so:
function Set-Something2 {
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
[OutputType([string])]
Param (
[Parameter(Mandatory = $false, ValueFromPipeline = $true)]
[string] $ComputerName = $env:COMPUTERNAME
)
Begin {
# Save the ShouldProcess parameters for later use
$WhatIfPref = $WhatIfPreference
$ConfirmPref = $ConfirmPreference
# Reset ShouldProcess parameters to their default value
$WhatIfPreference = 0
$ConfirmPreference = 'High'
}
Process {
New-Item -Path "beforeChange" -Type File | Out-Null
Remove-Item -Path "beforeChange" -ErrorAction SilentlyContinue
# We restore the previously saved ShouldProcess parameters just before the "protected" bit of code
$WhatIfPreference = $WhatIfPref
$ConfirmPreference = $ConfirmPref
# Here is the bit of code that needs to be "protected"
if ($PSCmdlet.ShouldProcess($ComputerName, "Do something")) {
# Inside the protected bit, we reset ShouldProcess parameters to their default value
$WhatIfPreference = 0
$ConfirmPreference = 'High'
# Actually do something on $ComputerName
New-Item -Path "inChange" -Type File | Out-Null
Remove-Item -Path "inChange" -ErrorAction SilentlyContinue
}
# After the protected bit, we reset ShouldProcess parameters to their default value
$WhatIfPreference = 0
$ConfirmPreference = 'High'
New-Item -Path "afterChange" -Type File | Out-Null
Remove-Item -Path "afterChange" -ErrorAction SilentlyContinue
}
}
"-"*50 + "Set-Something2 -WhatIf" + "-"*50
Set-Something2 -WhatIf
"-"*50 + "Set-Something2 -Confirm" + "-"*50
Set-Something2 -Confirm
The result is as I want it:
--------------------------------------------------Set-Something2 -WhatIf--------------------------------------------------
What if: Performing the operation "Do something" on target "GEOCED".
--------------------------------------------------Set-Something2 -Confirm--------------------------------------------------
Confirm
Are you sure you want to perform this action?
Performing the operation "Do something" on target "GEOCED".
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"):
But is that how I'm supposed to do it ? I don't find it very elegant...