Although PowerShell is a powerful automation scripting language, you can also build some cool console-based scripts with it. Building an interactive menu is a great example. In this post, we're going to dive into a step-by-step tutorial on you can get interactive with PowerShell and how to create a simple PowerShell menu.

A good PowerShell automation script typically requires no interactivity. After all, if a script is interactive that means a human must be in front of it to press a key. We try to stay away from that by defining all of the scenarios ahead of time and feeding parameters into our scripts at runtime with no interaction. However, there are times when we need to gather input where scenarios can't be defined ahead of time or we're building scripts for end users.

PowerShell has a few ways we can introduce interactivity into our scripts. Today, we're going to discuss two of them; using the Read-Host command and creating our own custom selection menu using the .NET System.Management.Automation.Host.ChoiceDescription object.

Read-Host

The most common and easiest way to add interactivity into your scripts is by using the built-in Read-Host command. This command pauses the script wherever it's located and expects some kind of input. By default, it will simply stop code execution with no indication of what's going on besides a flashing prompt so it's important to use the Prompt parameter which displays some text before it prompts for input.

PS C:\> Read-Host
Red
Red

When input is given, it then returns that input. Above, I typed Red as input and Red was returned. This doesn't do much good if we're not capturing that input somehow and making a decision off of it. It's best we send that output to a variable. At that point, we can then perform a series of checks against the input provided.

PS> $favColor = Read-Host -Prompt 'What is your favorite color?'
What is your favorite color?: Red
PS> $favColor
Red

Now you can see that Red wasn't simply returned to the console but was captured by the $favColor variable. At this point, we can add some logic in there to do different things based on that input.

if ($favColor -eq 'Red') {
    'My favorite, too!'
} else {
    "Eww..$favColor is not a nice color!"
}
My favorite, too!

Using .NET to Create a Menu

Read-Host is versatile and can prompt for any kind of text but it doesn't really look "official". It doesn't look like it is a part of PowerShell itself. For a more professional way of asking for input (multiple input), we can use the .NET System.Management.Automation.Host.ChoiceDescription object. This object allows us to define different options and then use a .NET method called PromptForChoice() to display those options to the user.

This method of asking for input requires defining all of the options ahead of time by creating multiple System.Management.Automation.Host.ChoiceDescription .NET objects and then passing those objects to another .NET object which displays them onto the console asking the user to choose.

The .NET method that shows the options is call $host.ui.PromptForChoice(). This method requires four arguments to run; the title for the menu of options, the message to display, an array of System.Management.Automation.Host.ChoiceDescription objects and the choice to default to if the user simply hits Enter.

Defining Menu Options

First, we'll define all of the options. Using the previous example, let's limit the number of favorite colors to Red, Blue and Yellow. To do that, we'll have to create three objects each representing a choice.

$red = New-Object System.Management.Automation.Host.ChoiceDescription '&Red', 'Favorite color: Red'
$blue = New-Object System.Management.Automation.Host.ChoiceDescription '&Blue', 'Favorite color: Blue'
$yellow = New-Object System.Management.Automation.Host.ChoiceDescription '&Yellow', 'Favorite color: Yellow'

Once we have all of the options defined, we'll group them all together into an array.

$options = [System.Management.Automation.Host.ChoiceDescription[]]($red, $blue, $yellow)

Finally, we'll display them to the user first defining the title of the prompt and the message.

$title = 'Favorite color'
$message = 'What is your favorite color?'
$result = $host.ui.PromptForChoice($title, $message, $options, 0)

When ran, you'll then see a prompt that looks like it came from PowerShell itself.

Favorite color
What is your favorite color?
[R] Red  [B] Blue  [Y] Yellow  [?] Help (default is "R"):

The value of $result isn't the text specified; it's an index number starting at 0 so, for example, if you chose Red, the result would be 0 or if Yellow is chosen, the value would be 2. You could make this distinction by using a switch statement to make a decision based on the input you provided.

switch ($result)
{
    0 { 'Your favorite color is Red' }
    1 { 'Your favorite color is Blue' }
    2 { 'Your favorite color is Yellow' }
}

Once your code is in a switch statement, you could then create a handy PowerShell function like below to bring up a menu and prompt for responses. Notice the shortcut to create the [ChoiceDescription] objects using the using keyword. Thanks to ttwinlakkes on Reddit for the tip.

using namespace System.Management.Automation.Host

function New-Menu {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Title,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Question
    )
    
    $red = [ChoiceDescription]::new('&Red', 'Favorite color: Red')
    $blue = [ChoiceDescription]::new('&Blue', 'Favorite color: Blue')
    $yellow = [ChoiceDescription]::new('&Yellow', 'Favorite color: Yellow')

    $options = [ChoiceDescription[]]($red, $blue, $yellow)

    $result = $host.ui.PromptForChoice($Title, $Question, $options, 0)

    switch ($result) {
        0 { 'Your favorite color is Red' }
        1 { 'Your favorite color is Blue' }
        2 { 'Your favorite color is Yellow' }
    }

}

Once you have the function built, you can then build menus on the fly by calling the New-Menu function as shown below. If I were you, I'd also make the options a parameter too but I'll leave that one up you.

PS> New-Menu -Title 'Colors' -Question 'What is your favorite color?'

Colors
What is your favorite color?
[R] Red  [B] Blue  [Y] Yellow  [?] Help (default is "R"): B
Your favorite color is Blue

Putting it All Together

Now that you've got all of the knowledge to create a menu, let's put that all together and create an interactive menu that stays interactive!

Create a function called Show-Menu as shown below. This menu is static.

function Show-Menu {
    param (
        [string]$Title = 'My Menu'
    )
    Clear-Host
    Write-Host "================ $Title ================"
    
    Write-Host "1: Press '1' for this option."
    Write-Host "2: Press '2' for this option."
    Write-Host "3: Press '3' for this option."
    Write-Host "Q: Press 'Q' to quit."
}

The Show-Menu function just displays the menu. Nothing more.

================ My Menu ================
1: Press '1' for this option.
2: Press '2' for this option.
3: Press '3' for this option.
Q: Press 'Q' to quit.

However, you can make this an actual interactive menu by implementing a do/until loop. Using a do/until loop allows you to take an action on input and keeps the menu up if users would like to select more than one option.

The only way to get out of the menu is to type Q. Try it out!

do
 {
    Show-Menu
    $selection = Read-Host "Please make a selection"
    switch ($selection)
    {
    '1' {
    'You chose option #1'
    } '2' {
    'You chose option #2'
    } '3' {
      'You chose option #3'
    }
    }
    pause
 }
 until ($selection -eq 'q')

Join the Jar Tippers on Patreon

It takes a lot of time to write detailed blog posts like this one. In a single-income family, this blog is one way I depend on to keep the lights on. I'd be eternally grateful if you could become a Patreon patron today!

Become a Patron!