Powershell - Confirmation before executing command in function

Viewed 79

I put the following function in my powershell profile (path from echo $profile, mine is like D:\C_Drive\Hardlink\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1)

function test {
    [cmdletbinding(SupportsShouldProcess)]
    ls
}
Set-Alias ggg test

with [cmdletbinding(SupportsShouldProcess)] to prompt confirmation, but I get the following error when running ggg

At D:\C_Drive\Hardlink\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1:2 char:5
+     [cmdletbinding(SupportsShouldProcess)]
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unexpected attribute 'cmdletbinding'.
At D:\C_Drive\Hardlink\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1:3 char:5
+     ls
+     ~~
Unexpected token 'ls' in expression or statement.
    + CategoryInfo          : ParserError: (:) [], ParseException
    + FullyQualifiedErrorId : UnexpectedAttribute

What is the right way to confirm before executing command in function?

3 Answers

You were on the right track using SupportsShouldProcess, however it's missing ConfirmImpact with ConfirmImpact set to High. This way you define that your function is a high risk function and PowerShell always asks for confirmation. This is also determined by your $ConfirmPreference preference variable which, by default, is set to High:

High: PowerShell prompts for confirmation before running cmdlets or functions with a high risk.

If, for example, the preference variable is set to None, PowerShell would no longer ask you to confirm the action.

function test {
    [cmdletbinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    param([string] $myParam)

    if($PSCmdlet.ShouldProcess([string] $myParam)) {
        Get-ChildItem $myParam
    }
}

Then if we try to call the function:

PS /> test C:\

The following confirmation prompt would appear:

Are you sure you want to perform this action?
Performing the operation "test" on target "C:\".
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

Lastly, Cmdlet.ShouldProcess has many Overloads which offer a more detailed way of asking for confirmation. The one currently used on the test function is the simplest one.

As for the error you're seeing when trying to run your function, this happens because the function has a cmdletbinding attribute declaration making your function and Advanced Function but it's missing a param block even if the function does not have any parameters it should be there. In addition, a function that SupportsShouldProcess must call $PSCmdlet.ShouldProcess(...) to prompt for confirmation.

function test {
    [cmdletbinding()]
    param()

}
  • Bolton's helpful answer provides a pragmatic workaround for your problem, by creating a custom confirmation prompt.

  • Santiago Squarzon's helpful answer shows a proper

    $PSCmdlet.ShouldProcess(...)
    implementation for conditional prompting.

  • For an example of implementing an unconditional prompt with

    $PSCmdlet.ShouldContinue(...)
    (explanation below), see this answer.

Let me try to complement these answers by providing a systematic overview:

PowerShell has two independent mechanisms for making advanced (cmdlet-like) functions prompt for confirmation:

  • Conditional confirmation prompts, based on

    $PSCmdlet.ShouldProcess(...)
    calls - note the word Process - which integrate with the common -Confirm parameter and the related $ConfirmPreference preference variable, and implicitly also enable support for the common -WhatIf parameter and the related $WhatIfPreference preference variable.

    • This mechanism requires:

      • A [CmdletBinding(...)] attribute above the param(...) block (which is what makes a function an advanced (cmdlet-like) one), with the the ShouldProcess property set to $true and, optionally, the Confirm property set to one of 'Low', 'Medium' or 'Default' (default is 'Medium'; see the System.Management.Automation.ConfirmImpact enumeration)

      • Guarding the actions the function (or script) is designed to perform with an if statement that calls the $PSCmdlet.ShouldProcess() method and only performs the actions if $true is returned.

        • However, note that prompting is expected to occur once per input object, in the process block of advanced functions (i.e, those that accept pipeline input, as is typical). PowerShell automatically handles responses that pertain to all (subsequent) input objects in subsequent calls, ([A] Yes to All or [L] No to All), by automatically returning $true or $false in subsequent calls.

        • The default prompt response - selected by just pressing Enter - is invariably [Y] Yes, i.e. confirmation of the intent to process (the input object at hand).

    • Whether a confirmation prompt will actually be shown on invocation can be controlled as follows / depends on the following:

      • To suppress prompting, pass -Confirm:$false or, for scope-wide effect, set $ConfirmPreference = 'None'.

      • To explicitly request prompting, use -Confirm (or -Confirm:$true)

      • Otherwise, prompting will only occur if the value of $ConfirmPreference - which defaults to 'High' - is equal to or less than the command's declared confirm-impact level.

        • For instance, the standard Remove-Item cmdlet's declared confirm-impact level is Medium, so if you set $ConfirmPreference = 'Medium', Remove-Item calls (that do not use -Confirm:$false) will prompt for confirmation before deleting files.
  • Unconditional confirmation prompts, based on

    $PSCmdlet.ShouldContinue(...)
    calls - note the word Continue, which are unrelated to the common parameters / preference variables mentioned above.

    • This mechanism requires:

      • A [CmdletBinding()] attribute above the param(...) block, so as to make it an advanced function, which enables access to the automatic $PSCmdlet variable, but requires no properties of that attribute to be set.

      • Guarding the actions the function (or script) is designed to perform with an if statement that calls the $PSCmdlet.ShouldContinue() method and only performs the actions if $true is returned.

        • Unlike with the .ShouldProcess() method, a prompt is normally only shown once per function invocation, and in the two-parameter overload only shows [Y] Yes and [N] No choices.

          • However, other overloads allow you to show [A] Yes to All or [L] No to All choices too, but - unlike with .ShouldProcess - you must manually manage skip prompting / processing in calls for subsequent input objects based on these choices, which are returned via [ref] parameters.
        • The default prompt response - selected by just pressing Enter - is [Y] Yes too, but you can change it to [N] No by using the overload that has a hasSecurityImpact parameter and passing $false to it.

          • Unfortunately, as of PowerShell 7.2.x, doing so invariably also shows the [A] Yes to All or [L] No to All choices, which may be undesired - see GitHub issue #9428.
    • There is no standard mechanism for suppressing such a prompt, however, by convention, commands should implement a -Force switch to do that.

      • An example is the Set-ExecutionPolicy cmdlet in Windows PowerShell, which prompts for confirmation by default, but allows use of -Force to suppress the prompt.

You may combine these two mechanisms, though the only good reason to do so is if you want to support -WhatIf functionality in addition to unconditional prompting - see this answer for an example.

Related