powershell

DebugPreference, ShouldProcess and ConfirmImpact


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

Solution

  • 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: