Adding WhatIf and Confirm Parameters to your Powershell

One of the handy functions built into Powershell, is the ability to preview what would happen if you run a command. This could be as simple as wanting to make sure that your Remove-Item actually deletes the write files, or that Set-ADUser changes the right attribute.
Hand in hand with -WhatIf is -Confirm, it will prompt you for high risk actions and confirm if you really want to perform the action, like deleting an AD user account.
But what about when you’re writing your own Powershell function or script? How do you make sure people that use it can check it does what they want without actually making any changes? Or how do you make sure they really want to make that impacting change?

This was something that came up recently while working with Darryl van der Peijl on some code for a script he’s creating, and he suggested share the knowledge in a blog post, so here goes!

I’m going start by making the assumption that if you’re here considering -WhatIf and -Confirm then you’ve already got a handle on the basics of creating a function.
So here is our example function that we’ll be working with, it performs 2 basic tasks, checks to see if a file already and creates or overwrites the file depending on the result.

Function New-PSFile {
    [CmdletBinding()]
    Param(
        # File to Create
        [Parameter(Mandatory=$true)]
        [string]
        $FilePath
    )
    begin{
        Write-Verbose "Checking to see if the file provided already exists"
        $FileExists = Test-Path -Path $FilePath
    }
    process{
        try {
            if ($FileExists){
                Write-Warning "$FilePath already exists and will be overwritten"
                $File = New-Item -Path $FilePath -ItemType File -Force -ErrorAction Stop
            }
            else{
                Write-Verbose "Creating $FilePath"
                $File = New-Item -Path $FilePath -ItemType File -Force -ErrorAction Stop
            }
        }
        catch{
            Throw "$($_.Exception.Message)"
        }
    }
    end{
        Write-Verbose "Returning new file details"
        Return $File
    }
}

Now this example function is already got things like verbose output, warnings and a check to see if the file we’re targeting already exists, but in it’s currently state it’ll only throw up a warning about needing to overwrite the file, and then do it anyway, and we have no way to give this a dry run first to see what it will do.

To get started, we’ll add SupportsShouldProcess to our [CmbletBinding()] arguments, as this will enable the use of ShouldProcess() blocks.

Function New-PSFile {
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param(
        # File to Create
        [Parameter(Mandatory=$true)]
        [string]
        $FilePath
    )
    begin{
        ... }
    process{
        ... }
    end{
        ... }
}

While we’ve now enabled the -WhatIf Parameter, it isn’t actually able to do anything at the moment if you tried to run it.
To actually put it into action, we need to add a ShouldProcess() block into our function where we want the WhatIf to actually run.
This block is if implemented in an If() statement around the action that you want to skip in a dry run, and is part of the $PSCmdlet variable.
For a basic implementation, we can supply just 2 arguments into this block, the Target object and the Action being performed.

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
    process{
        try {
            if ($FileExists){
                ...
            }
            else{
                Write-Verbose "Creating $FilePath"
                if($PSCmdlet.ShouldProcess($FilePath,"Create File")){
                    $File = New-Item -Path $FilePath -ItemType File -Force -ErrorAction Stop
                }
            }
        }
        catch{
            Throw "$($_.Exception.Message)"
        }
    }

Now we’ve got a way to perform a dry run of our function, but how do we prevent people from accidentally overwriting important files? Well this is where the -Confirm parameter comes in handy. If your function or script is potentially impacting, you might want to change the ConfirmImpact level to High to force a user prompt when running it.
Like with adding the SupportsShouldProcess, we add need to add ConfirmImpact to our [CmdletBinding()].

Function New-PSFile {
    [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='High')]
    Param(
        # File to Create
        [Parameter(Mandatory=$true)]
        [string]
        $FilePath
    )
    begin{
        ... }
    process{
        ... }
    end{
        ... }
}

Now things are coming together!
Let’s go one step further and make our WhatIf and Confirm strings more natural and custom for when we’re overwriting an existing file, to provide more context to the end user.
To do this, we will change our ShouldProcess() this time to have 3 arguments:

  1. The message presented to the user when they run -WhatIf
  2. The message presented to the user asking for confirmation
  3. The confirmation prompt title
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
    process{
        try {
            if ($FileExists){
                Write-Warning "$FilePath already exists and will be overwritten"
                if($PSCmdlet.ShouldProcess(
                        ("Overwritting existing file {0}" -f $FilePath),
                        ("Would you like to overwrite {0}?" -f $FilePath),
                        "Create File Prompt"
                    )
                ){
                    $File = New-Item -Path $FilePath -ItemType File -Force -ErrorAction Stop
                }
            }
            else{
                ...
            }
        }
        catch{
            Throw "$($_.Exception.Message)"
        }
    }

And so, our final product now looks something like this

Function New-PSFile {
    [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='High')]
    Param(
        # File to Create
        [Parameter(Mandatory=$true)]
        [string]
        $FilePath
    )
    begin{
        Write-Verbose "Checking to see if the file provided already exists"
        $FileExists = Test-Path -Path $FilePath
    }
    process{
        try {
            if ($FileExists){
                Write-Warning "$FilePath already exists and will be overwritten"
                if($PSCmdlet.ShouldProcess(
                        ("Overwritting existing file {0}" -f $FilePath),
                        ("Would you like to overwrite {0}?" -f $FilePath),
                        "Create File Prompt"
                    )
                ){
                    $File = New-Item -Path $FilePath -ItemType File -Force -ErrorAction Stop
                }
            }
            else{
                Write-Verbose "Creating $FilePath"
                if($PSCmdlet.ShouldProcess($FilePath,"Create File")){
                    $File = New-Item -Path $FilePath -ItemType File -Force -ErrorAction Stop
                }
            }
        }
        catch{
            Throw "$($_.Exception.Message)"
        }
    }
    end{
        Write-Verbose "Returning new file details"
        Return $File
    }
}

As always, I hope this comes in handy for someone looking to improve their knowledge, or their powershell scripts and functions!