Building a PowerShell Console Menu Revisited, Part 1

powershell-hero-img
Today, I want to revisit a topic that pops up frequently and that’s building a menuing system in PowerShell. Typically the request is to create a script that presents a simple menu, and the operator merely selects an item from the list to execute. Going through this process might take a few articles so that I don’t overwhelm you, but let’s walk through this process.

You can most likely stop anywhere along the way and build your own meaning tool, but if you stick with it, I might have something worth waiting for. And as always, hopefully you’ll learn a little PowerShell along the way. Let’s begin.
For my demonstration, I’m not focusing on the PowerShell commands to actually perform the menu items, but rather how to display and use the menu. To that end, I’m going to define a here-string, which will represent the menu.

$menu=@"
1 Show info about a computer
2 Show info about a mailbox
3 Restart the print spooler
Q Quit
Select a task by number or Q to quit
"@

A here-string is an easy way to create a multi-line string without having to resort to concatenation. Simply type the text you want, including tabs. The only thing to watch is that the closing characters (“@) can’t have any whitespace before them. I typically make sure all of the text in a here-string is left justified. My menu variable looks like this:

A sample PowerShell menu (Image Credit: Jeff Hicks)
A sample PowerShell menu (Image Credit: Jeff Hicks)

To prompt the user to enter something, we can use Read-Host. The first part of a script might look like this:

Write-Host "My Menu" -ForegroundColor Cyan
$r = Read-Host $menu

To decide what command to execute, all we have to do is figure out what $r is equal to. You could use a series of If and ElseIf statements, but I find that a bit cumbersome. Instead, this is a great place to use a switch construct.
In the script, it might look like this:

Switch ($r) {
"1" {
    Write-Host "Getting system information" -ForegroundColor Green
    #insert your code here
}
"2" {
    Write-Host "Getting mailbox information" -ForegroundColor Green
    #insert your code here
}
"3" {
    Write-Host "Restarting the print spooler" -ForegroundColor Green
    #insert your code here
}
"Q" {
    Write-Host "Quitting" -ForegroundColor Green
}
default {
    Write-Host "I don't understand what you want to do." -ForegroundColor Yellow
 }
} #end switch

In a switch statement, you tell PowerShell to do a comparison on some expression. In this case, it is the value of $r. So if $r is equal to “1”, PowerShell will execute the code in the scriptblock. Because Read-Host writes strings to the pipeline, I’m putting the numbers in quotes. In this situation, PowerShell is not case-sensitive, so it would process both lower and upper case versions of “Q”. The default scriptblock is optional, but I elected to insert one. Putting it all together, we get an experience like this.

Running a menu based script (Image Credit: Jeff Hicks)
Running a menu based script (Image Credit: Jeff Hicks)


The only thing missing is the PowerShell code within each switch scriptblock to accomplish the task. I created a very basic function called MyMenu with the commands I talked about. Here’s a more complete function for displaying a menu.

Function Invoke-Menu {
[cmdletbinding()]
Param(
[Parameter(Position=0,Mandatory=$True,HelpMessage="Enter your menu text")]
[ValidateNotNullOrEmpty()]
[string]$Menu,
[Parameter(Position=1)]
[ValidateNotNullOrEmpty()]
[string]$Title = "My Menu",
[Alias("cls")]
[switch]$ClearScreen
)
#clear the screen if requested
if ($ClearScreen) {
 Clear-Host
}
#build the menu prompt
$menuPrompt = $title
#add a return
$menuprompt+="`n"
#add an underline
$menuprompt+="-"*$title.Length
#add another return
$menuprompt+="`n"
#add the menu
$menuPrompt+=$menu
Read-Host -Prompt $menuprompt
} #end function

I’ve added parameters to display a title, as well as clear the screen. To use it, I can run code like this in a script to keep looping and displaying the menu until the user selects Q.

Do {
    #use a Switch construct to take action depending on what menu choice
    #is selected.
    Switch (Invoke-Menu -menu $menu -title "My Help Desk Tasks" -clear) {
     "1" {Write-Host "run get info code" -ForegroundColor Yellow
         sleep -seconds 2
         }
     "2" {Write-Host "run show mailbox code" -ForegroundColor Green
          sleep -seconds 2
          }
     "3" {Write-Host "restart spooler" -ForegroundColor Magenta
         sleep -seconds 2
         }
     "Q" {Write-Host "Goodbye" -ForegroundColor Cyan
         Return
         }
     Default {Write-Warning "Invalid Choice. Try again."
              sleep -milliseconds 750}
    } #switch
} While ($True)

The Return key word terminates the pipeline and ends the loop. Here’s what it looks like without clearing the screen between activities.

Using Invoke-Menu (Image Credit: Jeff Hicks)
Using Invoke-Menu (Image Credit: Jeff Hicks)


At this point, all I need to do is build a script that loads the Invoke-Menu function and defines a Do loop. But I want to find an even easier way to work with console menus, which we’ll look at that next time.