I don't understand why when I pass -Debug
to my script, it asks for confirmation, unless I also pass -Confirm:$False
(or set ConfirmImpact='none'
).
I cannot find any documentation that states DebugPreference
interacts with ShouldProcess
.
test.ps1
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium')]
param()
Write-Host "`n--- DEBUGGING STATE ---"
Write-Host "DebugPreference = $DebugPreference"
Write-Host "WhatIfPreference = $WhatIfPreference"
Write-Host "ConfirmPreference = $ConfirmPreference"
Write-Host "-------------------------`n"
if ($PSCmdlet.ShouldProcess("Thing", "Do something on")) {
Write-Host "DID SOMETHING"
}
Tests:
PS > test.ps1
--- DEBUGGING STATE ---
DebugPreference = SilentlyContinue
WhatIfPreference = False
ConfirmPreference = High
-------------------------
DID SOMETHING
PS > test.ps1 -Debug
--- DEBUGGING STATE ---
DebugPreference = Inquire
WhatIfPreference = False
ConfirmPreference = High
-------------------------
Confirm
Are you sure you want to perform this action?
Performing operation "Do something on" on Target "Thing".
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"):
PS > test.ps1 -Debug -WhatIf
--- DEBUGGING STATE ---
DebugPreference = Inquire
WhatIfPreference = True
ConfirmPreference = High
-------------------------
What if: Performing operation "Do something on" on Target "Thing".
PS > test.ps1 -Debug -Confirm:$False
--- DEBUGGING STATE ---
DebugPreference = Inquire
WhatIfPreference = False
ConfirmPreference = None
-------------------------
DID SOMETHING
It is indeed curious that passing -Debug
would implicitly behave as if -Confirm
had also been specified, and this coupling smells like a bug or a design flaw.
Here's a workaround that bypasses the $PSCmdlet.ShouldProcess()
call if -Debug
is passed, except if -Confirm
(implicitly the same as -Confirm:true
) is also passed or if $ConfirmPreference
was set to 'Medium'
or 'Low'
before calling the script and except if -WhatIf
is also passed.
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
param()
# ...
$debugWithoutPrompt =
$PSBoundParameters['Debug'] -and
$(
if (-not $PSBoundParameters.ContainsKey('Confirm')) { $ConfirmPreference -in 'High', 'None' }
else { -not $PSBoundParameters['Confirm'] }
) -and
-not $WhatIfPreference
if ($debugWithoutPrompt -or $PSCmdlet.ShouldProcess('Thing', 'Do something on')) {
Write-Host 'DID SOMETHING'
}
Note:
Curiously even though passing -Confirm:$false
, i.e. the negated form of the common -Confirm
parameter, in advanced functions or scripts, sets a function-local copy of the $ConfirmPreference
preference variable to 'None'
, which is effective in suppressing a confirmation prompt in combination with -Debug
, setting $ConfirmPreference = 'None'
manually, whether in the caller's scope before calling the script or inside the script, does not work (it only works without -Debug
).
Similarly, passing common -WhatIf
parameter-WhatIf
gets translated to a function-local copy of the $WhatIfPreference
preference variable with value $true
(unless negated). In order to preserve -WhatIf
functionality, the $PSCmdlet.ShouldProcess()
call must not be bypassed if -WhatIf
was passed or $WhatIfPreference = $true
was set before calling the script; both conditions can therefore be detected with a Boolean test of $WhatIfPreference
By questionable design, in Windows PowerShell (the legacy, ships-with-Windows, Windows-only edition of PowerShell whose latest and last version is 5.1), the common -Debug
parameter gets translated to a function-local copy of the $DebugPreference
preference variable with a value of 'Inquire'
, which causes every call to Write-Debug
to present an interactive prompt.
This unhelpful behavior has been fixed in PowerShell (Core) 7.
You can fix the questionable Windows PowerShell behavior by placing the following after your script's param()
block:
if ($PSBoundParameters.ContainsKey('Debug') -and
$PSBoundParameters['Debug']) {
$DebugPreference = 'Continue'
}