Adding custom confirmation to commands

Sat, Mar 28, 2015 2-minute read

We recently had a customer question where they were concerned that some commands might be typed accidentally and end up causing significant disruption. In general, commands that fit that classification include a confirmation message to warn you, but sometimes you just don’t agree with what the cmdlet author thought was a high-impact action. While Restart-Computer might be a day-to-day operation for some servers, it might be certain doom for others.

So let’s pretend you want to make Stop-Process prompt you whenever you run it.

In PowerShell V3, the solution is amazing with PSDefaultParameterValues. In this case, it will opt in to the ‘WhatIf’ behaviour by default, which you can override if you want:

4 [C:\]
PS > $PSDefaultParameterValues["Stop-Process:WhatIf"] = $true

5 [C:\]
PS > Stop-Process -name notepad
What if: Performing the operation "Stop-Process" on target "notepad (5936)".

6 [C:\]
PS > Stop-Process -name notepad -WhatIf:$false

(Notepad stops)  

In PowerShell V2, the solution is still pretty handy. You can use PowerShell’s proxy function APIs insert your own logic into a cmdlet. In this case, we can have it confirm:

19 [C:\]
> Set-HighImpactCommand Stop-Process

20 [C:\]
> Stop-Process \-Name Notepad

Invoke Stop-Process?
Stop-Process has high impact. Invoke?
[Y] Yes [N] No  [S] Suspend [?] Help (default is "Y"): N
Invocation of Stop-Process was discontinued.
At line:25 char:106
+ ... -Process?')) { throw 'Invocation of Stop-Process was discontinued.' }
+                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 + CategoryInfo          : OperationStopped: (Invocation of S...s discontinued.:String) [], RuntimeException
 + FullyQualifiedErrorId : Invocation of Stop-Process was discontinued.
  
21 [C:\]
> Stop-Process -Name Notepad

Invoke Stop-Process?
Stop-Process has high impact. Invoke?
[Y] Yes [N] No  [S] Suspend [?] Help (default is "Y"): Y

22 [C:\]                                                                                                                                    

And the function that does the magic?

function Set-HighImpactCommand
{
    param($CommandName)

    $MetaData = New-Object System.Management.Automation.CommandMetaData `
        (Get-Command $CommandName -CommandType Cmdlet)
    $functionContent = ([System.Management.Automation.ProxyCommand]::Create($MetaData))
    $updatedFunction = $functionContent -replace "begin`r`n{",
        "begin`r`n{`r`n if(-not `$PSCmdlet.ShouldContinue('$CommandName has high impact. Invoke?',
        'Invoke ${CommandName}?')) { throw 'Invocation of $CommandName was discontinued.' }`r`n"

    Set-Item function:\GLOBAL:$CommandName $updatedFunction
}