Most of the time, we write solutions in PowerShell to perform a task without user input. We’re automators – we create solutions and schedule them to run on-demand or on a schedule to automatically take care of something so we don’t have to worry about it anymore. However, occasionally we might write something that requires interaction – the user running the command has to make a choice. Do I want to delete this mailbox or file? Am I ready to make this change? Instead of making these decisions for the user, we can add prompts in PowerShell in order to verify with the command runner this is the action he or she wants to take.

In this post, I’m going to demonstrate two ways to create prompts in PowerShell and how to take action on the response. I’m going to attempt to replicate the prompt that appears when I try to delete a file using the Remove-Item cmdlet, like this:

Remove-Item example prompt

The Manual Method

The first method I’m going to call the manual method of creating a prompt. In this method, I need to create each part of the prompt and display it to the user. I’m going to create my version of the Remove-Item command above to illustrate how to approach this. Here is my custom function named Remove-MyItem:

Now I went to some extreme measures to 100% match the prompt displayed by the Remove-Item command. I created strings for each part of the message and matched the text color each step of the way. I had to use Write-Host and manipulate the text color and not create new lines and using spaces around text. The result is a near identical match:

Result of manual prompt creation

What is missing is the logic to handle multiple files (Yes to All and No to All) or to re-prompt after viewing the help text on what each option means. So take the above code sample and add even more to make it work! There’s got to be a better way.

The .NET Method

Instead of writing out each part of the prompt, I can use the .NET framework to create my action options and choice prompts. The prompt is made up of the title of my prompt, a message, the options to choose from, and the default option. Let’s break down each part. First, I need to create the title of my prompt. For this, I’ll keep the same text as my manual method but just rename the variable so I can keep up with which part of the prompt it is for:

$title = "Confirm"

Next, I need the message to display to the user to prompt for their action response. Again, I’ll use the same text from my manual method but change up the variable name:

$message = "Are you sure you want to perform this action?`nPerforming the operation ""Remove File"" on target $($item.FullName)."

For creating the possible prompt answers, this is where we deviate from the manual method. We can use the .NET namespace System.Management.Automation.Host along with the class ChoiceDescription to create a prompt with the choice label and the help message. In the manual method, I saved the help text into a single string $helpResponse, but now I can associate the help message with each prompt choice. Let’s look at an example for the Yes choice and then break down each part:

$yes = New-Object System.Management.Automation.Host.ChoiceDescription ` "&Yes", "Continue with only the next step of the operation."

First, I need to save the choice description into a variable, and since this is for the Yes response, it seems appropriate to save it a variable named $yes. Next, is the New-Object command, which is used to create an instance of a .NET Framework or COM object. Following the New-Object command is specifying the full qualified name of the .NET Framework object class, so this is combining both the namespace and class name into System.Management.Automation.Host.ChoiceDescription.

Following the class name is the label text and help message text, both listed as strings in quotation marks. Finally, if you notice the ampersand in front of “Y”, this indicates the single letter the user puts in the prompt (“Press Y for Yes”).

I’ll need to continue this process for each prompt choice that I want to present to the user. In the case of recreating the Remove-Item prompt, this will be for Yes to All, No, No to All, and Suspend. There’s not need to create one for help or the need to prompt for the default response, that will come later.

$yesToAll = New-Object System.Management.Automation.Host.ChoiceDescription "Yes to &All","Continue with all the steps of the operation."

$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Skip this operation and proceed with the next operation."

$noToAll = New-Object System.Management.Automation.Host.ChoiceDescription "No to Al&l", "Skip this operation and all subsequent operations."

$suspend = New-Object System.Management.Automation.Host.ChoiceDescription "&Suspend", "Pause the current pipeline and return to the command prompt. Type ""exit"" to resume the pipeline."

To complete our prompt choices, I need to take each option created above and combine it into an array of the System.Management.Automation.Host.ChoiceDescription class:

$options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $yesToAll, $no, $noToAll, $suspend)

Finally, I can prompt the user using the automatic variable $host (this contains the same object information as the command Get-Host) along with its UI property and PromptForChoice method to display our title, message, choice options, and the default choice:

$response = $host.UI.PromptForChoice($title, $message, $options, 0)

Since I am prompting for a response, this gets saved to the variable $response. I want to comment on the 0 at the end of our line of code. This represents the default choice (such as “default is Y”) in our prompt, and 0 represents the first choice response in our $options array of choices. If I wanted No to be the default, I would specify 2 here.

Finally, now that I have prompted the user and a response given, I need to take action based on the response. I’ll continue with the same method I used in the manual method with a switch statement but I’ll use the array indexes to match up with the responses:

switch ($response) {
    0 { Remove-Item -Path $item.FullName; break } # Yes
    1 { Remove-Item -Path $item.FullName; break } # Yes to All
    2 { return; break } # No
    3 { return; break } # No to ALL
    4 { return; break } # Suspend
}

Now my code for each response isn’t quite accurate as I’m not accounting for multiple items for the Yes to All and No to All statements but I think the point still comes across. I’ve also commented at the end of each line to show which switch condition matches up with each response.

Here’s the full code of my new Remove-MyItem command using .NET Framework:

And here it is in action, notice that the font color is configured automatically as well as the default choice text:

Remove-MyItem prompt using .NET

Notice in the above screenshot that I initially responded to the prompt with a question mark to display the help text; however, nowhere in my switch statement am I looking for this response. This functionality is automatically built-in and does not need to be programmed by you. It takes the help text specified earlier in each choice description and displays it.

Please check out my repository for this on GitHub for the code used in this post plus any additional examples I come up with in the future.

Questions or comments? If so, drop me a note below or find me on Twitter or LinkedIn to discuss further.

Leave a Reply