PowerShell function for menu creation revisited

Sometimes when creating blog posts you just get excited to publish a post as soon as it’s finished, but then you realize you can do something better. So yesterday I published a post on how to use a function in PowerShell to create an interactive dynamic menu but in hindsight it could have been more “generic” – so today I’m back with a new post on how you can create that generic function by providing the query in a separate string instead as part of the function itself. This allows you to merely change the query (the input for creating the menu) without having to change anything inside the function. Now it’s a “real” function! 🙂

I’ve added two mandatory parameters to the function, the first is the query to retrieve data and the second parameter is the task that you’d like to perform on the selected object. I’m calling the parameters -ListItem and -GetItem but the actual query is set in the $MyQuery and $MyTask string and they are then passed on to the function to perform the task. So if I’d like to get a list of VMs I just change the $MyQuery to something like “(Get-VM | Sort-Object)” and if I’d like to get a list of Datastores I set $MyQuery = “(Get-Datastore | Where {$_.Type -eq ‘VMFS’} | Sort-Object -Descending FreeSpaceGB)”. Once the list is presented and you select an object you then pass on what’s going to happen with it with the $MyTask – so if I want to retrieve a specific VM I set it to “Get-VM” but it could be anything (like Remove-VM but there’s no warning so be careful!)

Get a list of VMs
Get a list of VMFS datastores and sort them based on free space

Calling the function is then done using this syntax:
Set-Menu -ListItem $MyQuery -GetItem $MyTask

$VIServer = "vcsa.mydomain.com"
$VIUsername = "administrator@vsphere.local"
$VIPassword = "MyPassword"

Connect-VIServer $VIServer -User $VIUsername -Password $VIPassword -WarningAction SilentlyContinue

$MyQuery = (Get-Datastore | Where {$_.Type -eq 'VMFS'} |Sort-Object -Descending FreeSpaceGB)
$MyTask = "Get-Datastore"

Function Set-Menu {
    Param (
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        $ListItem,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        $GetItem
    )

    Try {
    $menu = @{}
    For ($i=1;$i -le $ListItem.count; $i++)
        { Write-Host "$i. $($ListItem[$i-1].Name)"
        $menu.Add($i,($ListItem[$i-1].Name)) }
    [int]$ans = if(($ans = Read-Host "Select item or press enter to accept default value [1]") -eq '') {"1"} else {$ans}
    $Selection = $menu.Item($ans); Invoke-Expression $($GetItem +  " " + $Selection)
    }

    Catch {
    Write-Host "The selection you made is unavailable, please make a valid selection between 1 and $($i-1)..."
    }
}

cls

Set-Menu -ListItem $MyQuery -GetItem $MyTask

Disconnect-VIServer $VIServer -Confirm:$false
Set-Menu waiting for user input
Set-Menu has executed the task defined in $MyTask

The way you can use the function is up to you but I use it in William Lams vghetto-vsphere-automated-lab-deployment script