Menus?! But...why?

As I dove deeper into PowerShell years ago I started to think about ways to help those utilizing my scripts. I wrote a script that became a module that the whole team would use.

In this module there were a ton of functions. Some functions were for creating new hires, others for terminations, and even more for general AD user account and Exchange management. Once there were more than 6 different functions I created a basic menu system for the Helpdesk to use when using PowerShell with my module.

Where to start

The first thing you'll need to do is present the user with options. Write-Host is my favorite way to do this. You'll want to define the variable $foregroundColor to see the full effect of the following code. I like to keep that set as a global variable so it can be changed later if desired. This is of course, very important, as who doesn't want to sometimes tweak the color output of your scripts? 

I also keep a version number in the variable $ncVer.

Building the menu:

Write-Host `n"Ninja Center v" $ncVer -ForeGroundColor $foregroundcolor
Write-Host `n"Type 'q' or hit enter to drop to shell"`n
Write-Host -NoNewLine "<" -foregroundcolor $foregroundColor
Write-Host -NoNewLine "Active Directory"
Write-Host -NoNewLine ">" -foregroundcolor $foregroundColor
Write-Host -NoNewLine "["
Write-Host -NoNewLine "A" -foregroundcolor $foregroundColor
Write-Host -NoNewLine "]"

Write-Host -NoNewLine `t`n "A1 - " -foregroundcolor $foregroundcolor
Write-host -NoNewLine "Look up a user"
Write-Host -NoNewLine `t`n "A2 - " -foregroundcolor $foregroundcolor
Write-host -NoNewLine "Enter PowerShell session on DC"
Write-Host -NoNewLine `t`n "A3 - " -foregroundcolor $foregroundcolor
Write-host -NoNewLine "Run DCDiag on a DC (or all DCs)"`n`n

Write-Host -NoNewLine "<" -foregroundcolor $foregroundColor
Write-Host -NoNewLine "Exchange"
Write-Host -NoNewLine ">" -foregroundcolor $foregroundColor
Write-Host -NoNewLine "["
Write-Host -NoNewLine "E" -foregroundcolor $foregroundColor
Write-Host -NoNewLine "]"

Write-Host -NoNewLine `t`n "E1 - " -foregroundcolor $foregroundcolor
Write-host -NoNewLine "Forward a mailbox"
Write-Host -NoNewLine `t`n "E2 - " -foregroundcolor $foregroundcolor
Write-host -NoNewLine "Clear a mailbox forward"
Write-Host -NoNewLine `t`n "E3 - " -foregroundcolor $foregroundcolor
Write-host -NoNewLine "See if an IP address is being relayed"`n`n

Write-Host -NoNewLine "<" -foregroundcolor $foregroundColor
Write-Host -NoNewLine "Storage"
Write-Host -NoNewLine ">" -foregroundcolor $foregroundColor
Write-Host -NoNewLine "["
Write-Host -NoNewLine "S" -foregroundcolor $foregroundColor
Write-Host -NoNewLine "]"

Write-Host -NoNewLine `t`n "S1 - " -foregroundcolor $foregroundcolor
Write-host -NoNewLine "Connect to a NetApp controller"`n`n

When starting out I would use Write-Host to display information as I wrote scripts. This was nice, and was useful for troubleshooting. Then as my knowledge expanded I began to realize how else it worked. My new favorite, especially when building menus, is -NoNewLine. 

What -NoNewLine allows you to do is alternate different colors on the same line. Again, extremely uhhh... important. The output does look nice, though!

There are also some escaped string options I like to use. They are `t (horizontal tab), and -n(new line). These are used to keep the menu looking nice and not too cluttered.

OK so I built something that shows a bunch of stuff... what now?

Now we'll need to get some input from the user and take action! To do this we'll use Read-Host combined with a Switch to deal with the input.

$sel = Read-Host "Which option?"

Switch ($sel) {
    "A1" {Get-ADinfo;Load-NinjaCenter}
    "A2" {Enter-DCSession}
    "A3" {
        $DCs      = Read-Host "DC (specify name or put 'all' for all)?"
        $test     = Read-Host "Enter 'error' or 'full' for test feedback"         
       
        
        $global:dcDiagResults = Get-DCDiagInfo -DC $DCs -Type $test -Verbose
        
        Write-Host `n"Results stored in the variable: dcDiagResults"`n
        Write-Host -NoNewLine "Type "
        Write-Host -NoNewLine "Load-NinjaCenter " -foregroundcolor $foregroundcolor
        Write-Host -NoNewLine "to load the menu again."`n
    }
    
    "E1" {Forward-Email}
    "E2" {Clear-Forward}
    "E3" {Check-EXRelayIP}
    
    "S1" {
        Connect-NetAppController
    
        Write-Host -NoNewLine "Type "
        Write-Host -NoNewLine "Load-NinjaCenter " -foregroundcolor $foregroundcolor
        Write-Host -NoNewLine "to load the menu again."`n
}

    {($_ -like "*q*") -or ($_ -eq "")} {
        
        Write-Host `n"No input or 'q' seen... dropping to shell" -foregroundColor $foregroundColor
        Write-Host "Type Load-NinjaCenter to load them menu again"`n
        
        
    }          
        
}

Alright, now let's take some action!

As you can see above, the switch handles the user input. There are some more advanced ways to parse the input, which I will go over in a later post. For now, you can see a few things.

One is that you can stage other functions based on the input. For example, with the switch option "A3", (which is the menu option for running DC Diag against DCs), you can see that we provide the user with some more prompts to prep some variables to pass to the Get-DCDiagInfo function.

I actually wrote about my Get-DCDiagInfo (function, but also works as a standalone script), and you can read about it here.

This allows you to wrap your existing functions in different ways and present them to folks that will use them however you'd like to. For a Helpdesk environment, or those not super deep into PowerShell, this is a nifty way for them to encapsulate and utilize what you've written. 

Let's put it all together.

Here is the full code for the menu function:

function Load-NinjaCenter {  
[cmdletbinding()]
param()

    ##########################################
    #            Ninja Center Menu           #
    ##########################################
    Write-Host `n"Ninja Center v" $ncVer -ForeGroundColor $foregroundcolor
    Write-Host `n"Type 'q' or hit enter to drop to shell"`n
    Write-Host -NoNewLine "<" -foregroundcolor $foregroundColor
    Write-Host -NoNewLine "Active Directory"
    Write-Host -NoNewLine ">" -foregroundcolor $foregroundColor
    Write-Host -NoNewLine "["
    Write-Host -NoNewLine "A" -foregroundcolor $foregroundColor
    Write-Host -NoNewLine "]"

    Write-Host -NoNewLine `t`n "A1 - " -foregroundcolor $foregroundcolor
    Write-host -NoNewLine "Look up a user"
    Write-Host -NoNewLine `t`n "A2 - " -foregroundcolor $foregroundcolor
    Write-host -NoNewLine "Enter PowerShell session on DC"
    Write-Host -NoNewLine `t`n "A3 - " -foregroundcolor $foregroundcolor
    Write-host -NoNewLine "Run DCDiag on a DC (or all DCs)"`n`n

    Write-Host -NoNewLine "<" -foregroundcolor $foregroundColor
    Write-Host -NoNewLine "Exchange"
    Write-Host -NoNewLine ">" -foregroundcolor $foregroundColor
    Write-Host -NoNewLine "["
    Write-Host -NoNewLine "E" -foregroundcolor $foregroundColor
    Write-Host -NoNewLine "]"

    Write-Host -NoNewLine `t`n "E1 - " -foregroundcolor $foregroundcolor
    Write-host -NoNewLine "Forward a mailbox"
    Write-Host -NoNewLine `t`n "E2 - " -foregroundcolor $foregroundcolor
    Write-host -NoNewLine "Clear a mailbox forward"
    Write-Host -NoNewLine `t`n "E3 - " -foregroundcolor $foregroundcolor
    Write-host -NoNewLine "See if an IP address is being relayed"`n`n

    Write-Host -NoNewLine "<" -foregroundcolor $foregroundColor
    Write-Host -NoNewLine "Storage"
    Write-Host -NoNewLine ">" -foregroundcolor $foregroundColor
    Write-Host -NoNewLine "["
    Write-Host -NoNewLine "S" -foregroundcolor $foregroundColor
    Write-Host -NoNewLine "]"

    Write-Host -NoNewLine `t`n "S1 - " -foregroundcolor $foregroundcolor
    Write-host -NoNewLine "Connect to a NetApp controller"`n`n

    $sel = Read-Host "Which option?"

    Switch ($sel) {
        "A1" {Get-ADinfo;Load-NinjaCenter}
        "A2" {Enter-DCSession}
        "A3" {
            $DCs      = Read-Host "DC (specify name or put 'all' for all)?"
            $test     = Read-Host "Enter 'error' or 'full' for test feedback"         
        
            
            $global:dcDiagResults = Get-DCDiagInfo -DC $DCs -Type $test -Verbose
            
            Write-Host `n"Results stored in the variable: dcDiagResults"`n
            Write-Host -NoNewLine "Type "
            Write-Host -NoNewLine "Load-NinjaCenter " -foregroundcolor $foregroundcolor
            Write-Host -NoNewLine "to load the menu again."`n
        }
        
        "E1" {Forward-Email}
        "E2" {Clear-Forward}
        "E3" {Check-EXRelayIP}
        
        "S1" {
            Connect-NetAppController
        
            Write-Host -NoNewLine "Type "
            Write-Host -NoNewLine "Load-NinjaCenter " -foregroundcolor $foregroundcolor
            Write-Host -NoNewLine "to load the menu again."`n
    }

        {($_ -like "*q*") -or ($_ -eq "")} {
            
            Write-Host `n"No input or 'q' seen... dropping to shell" -foregroundColor $foregroundColor
            Write-Host "Type Load-NinjaCenter to load them menu again"`n
            
            
        }          
            
    }
    ##########################################
    #        End Ninja Center Menu           #
    ##########################################

}

What you'd do to run it is put it in a module you created and then call the function when the module is imported at the end of your script. Once exited, it could be called by its function name at any time to bring the menu back up.

Some things I've noticed

When I started sharing my scripts this way I noticed that people will find ways to break your code. This is a great way to learn the weak points of your scripts and become even better at writing them. I hope you've found this useful, and as always I appreciate any feedback!