When you’re writing a PowerShell script as a tool, you’ll probably want to put in a Menu at some point, allowing for options and user input.
This works well at first, and you’ll use this little While / Switch version (a select statement in the old VBS days) over an over again in lots of different places of your script, and lot of different scripts.
Basic Menu
Function Do-Menu_01 {
cls
$x=1
Write-Host " ----------------------"
Write-Host " Basic Menu"
Write-Host " ----------------------"
Write-Host " 1. Load File"
Write-Host " 2. Do Process"
Write-Host " 3. Open New Menu"
Write-Host " Q. Exit"
$choice = 0
while ($choice -ne "Q") {
$choice = Read-Host " Enter your Location choice (1-3)"
# Take action based on user input
switch ($choice) {
1 {
Write-Host " 1. You load the file"
}
2 {
Write-Host " 2. You do the Computer Math"
$x=$x+2
$y=$x*2
}
3 {
Write-Host " 3. We open another Menu"
$choice = "Q"
Do-Menu_02
}
"q"{
$choice = "Q"
}
"Q" { Exit }
default { Write-Host "Invalid option. Please try again."; continue }
}
}
}
Building something like this:

This works great on a small scale, and you could use the same menu code several times, and even have some code buried in that Switch section, or call functions that then do bits and pieces. As this script grows, going in and tweaking it, adding to it, and troubleshooting it will eventually become a nightmare.
Worse yet, you might even try to bury one Menu inside another as a sub menu, so you can exit one and come back into the first. Trust me, this is a disaster waiting to happen.
My scripts were getting so big, I needed to build a more dynamic and stable way of doing things. So the Make-Menu Function was born, and it changed my scripts forever. You can just tell it what you want the menu to have, and what separate function it should run for each option.
This means every function that has a menu, just has this simple bit of code to build the menu at the bottom.
The Dynamic Menu Function
function Make-Menu {
# You can add more, but this allow an initial
# 6 options, a quit menu, and the option to
param (
[string]$MenuTitle = $null,
[string]$Option1 = $null,
[string]$Function1 = $null,
[string]$Option2 = $null,
[string]$Function2 = $null,
[string]$Option3 = $null,
[string]$Function3 = $null,
[string]$Option4 = $null,
[string]$Function4 = $null,
[string]$Option5 = $null,
[string]$Function5 = $null,
[string]$Option6 = $null,
[string]$Function6 = $null,
[Switch]$OptionQuit
)
# This prevents the Colour bleeds while in Powershell ISE.
# You won't need this in a normal Powershell Console.
#---------------------------------------------------------
# Start-Sleep -Seconds 1
Write-Host " --------------------------------------------"
Write-Host " " $MenuTitle -ForegroundColor Cyan
Write-Host " --------------------------------------------"
if($Option1 -ne ""){
Write-Host " 1. " $Option1
}
if($Option2 -ne ""){
Write-Host " 2. " $Option2
}
if($Option3 -ne ""){
Write-Host " 3. " $Option3
}
if($Option4 -ne ""){
Write-Host " 4. " $Option4
}
if($Option5 -ne ""){
Write-Host " 5. " $Option5
}
if($Option6 -ne ""){
Write-Host " 6. " $Option6
}
if($OptionQuit -eq $true){
Write-Host " Q. Quit" -ForegroundColor Red
}
Write-Host " "
$choice01 = 0
while ($choice01 -ne "Q") {
$choice01 = Read-Host " Enter your choice"
# Take action based on user input
switch ($choice01) {
1 {
if($Option1 -ne ""){ #--------------------------------------
cls # Note: Every choice will end the Menu
$test = & $Function1.ToString() # by setting $Choice01 to "Q".
$choice01="Q" # This means wherever you go, you
} # need to Call this Function again.
} #--------------------------------------
2 {
if($Option2 -ne ""){
cls
$test = & $Function2.ToString()
$choice01="Q"
}
}
3 {
if($Option3 -ne ""){
cls
$test = & $Function3.ToString()
$choice01="Q"
}
}
4 {
if($Option4 -ne ""){
cls
$test = & $Function4.ToString()
$choice01="Q"
}
}
5 {
if($Option5 -ne ""){
cls
$test = & $Function5.ToString()
$choice01="Q"
}
}
6 {
if($Option6 -ne ""){
cls
$test = & $Function6.ToString()
$choice01="Q"
}
}
# The final conditions are for Quitting, and it changes a
# lower case "q" into a "Q", so that you use the default quit
"q" {
$choice01="Q"
}
"Q" { Get-Quiting }
default { Write-Host "Invalid option. Please try again." -foregroundcolor red; continue }
}
}
}
Lets look at how this works, with an example function ‘Do-Welcome””
function Do-Welcome {
cls
Make-Menu -OptionQuit -MenuTitle "WELCOME Menu" `
-Option1 "Display User" -Function1 "Do-Main01" `
-Option2 "Display Day" -Function2 "Do-Main02" `
-Option3 "Other Options" -Function3 "Do-SubMenu"
}
As you can see you can just include a quit option, a menu title and then the Options you want along with the an associated Function.
The above Do-Welcome function would look like this (you can see the title function was included when I ran this)

So then lets look at three functions it will call:
#-------------------------------------------------------------------------------------------------
#### Main Option 1
#-------------------------------------------------------------------------------------------------
Function Do-Main01{
Do-Option01
Write-Host " This will reuse the Do-Welcome function"
Pause
Do-Welcome
}
#-------------------------------------------------------------------------------------------------
#### Main Option 2
#-------------------------------------------------------------------------------------------------
Function Do-Main02{
Write-Host " "
Write-Host " Today is" $global:Today -ForegroundColor Green
Write-Host " This will REBUILD the Do-Welcome Menu"
Pause
cls
Make-Menu -OptionQuit -MenuTitle "WELCOME Menu" `
-Option1 "Display User" -Function1 "Do-Main01" `
-Option2 "Display Day" -Function2 "Do-Main02" `
-Option3 "Other Options" -Function3 "Do-SubMenu"
}
#-------------------------------------------------------------------------------------------------
#### Sub Menu Option 3
#-------------------------------------------------------------------------------------------------
Function Do-SubMenu{
# This checks if you should add the Description or not
# which isn't important to the script, but just demonstrates
# what you can do, to control the GUI
if ($global:AddDescription -eq $False){
# It will reset it to always show,
# so you need to decide to hide it
$global:AddDescription = $True
}Else{
Write-Host " "
Write-Host " Some additional Section or Function" -ForegroundColor Cyan
Write-Host " -------------------------------------------------"
Write-Host " "
Write-Host " This would be where you add something special or"
Write-Host " maybe you run / call a bunch of other functions,"
Write-Host " but thats really up to you."
Write-Host " "
Write-Host " "
Write-Host " The real key is to watch where you use the " -NoNewline
Write-Host "cls" -ForegroundColor Green
Write-Host " code, so that the Menu sits under the data you "
Write-Host " have displayed, like so:"
}
Write-Host " "
Make-Menu -MenuTitle "Sub Structure" `
-Option1 "Display User" -Function1 "Do-Sub01" `
-Option2 "Display Day" -Function2 "Do-Sub02" `
-Option3 "Reload the Description" -Function3 "Do-Sub03" `
-Option4 "Main Menu " -Function4 "Do-Welcome"
}
As you can see you just call Functions as the Menu choices, and you can use reuse Functions to build repeated menus or build the Menus on the fly.
The point is, the menu aspect becomes very simple and easy to update and change.
I personally like having functions for the repeated Menus. Like we see in Do-Main01, it just calls the Do-Welcome Menu.
Traps to Lookout for
- When you call the menu function, if you are doing it like I do on multiple lines (so it’s easier to read), remember to put the ” ` ” character at the end of the line in which another line follows.
- Make sure your Options and Functions line up number wise and make sense. Don’t use Option 1,2,4 & 6.
Download the Script
Base_Menu_2.0.ps1 https://github.com/Works4Me-Info/PS_Menu