######################################## #Author: Mark Wichall #Last Updated: 25/02/2015 #Notes: Dynamic Menu Code. #Version: Version $Ver = "Version 3" #Uncoment line below for debugging. #$DebugPreference = "Inquire" #$VerbosePreference = "Continue" #Set the Powershell window configuration e.g. buffersize and windowsize $pshost = get-host $pswindow = $pshost.ui.rawui $pswindow.windowtitle = "Powershell - DynamicMenu" <# $newsize = $pswindow.buffersize $newsize.width = 120 $newsize.height = 2000 $pswindow.buffersize = $newsize $newsize = $pswindow.windowsize $newsize.width = 120 $newsize.height = 50 $pswindow.windowsize = $newsize #> $Global:token = $Global:session #Session ID for logging use of menu. $SessionID = Get-Date -Format o | foreach {$_ -replace ":", "."} <# #Run Example of this code. #> ######################################## ############# Logging - BEGIN ############# #Used to find root of folder for loading other code using powershell_root.txt Function Get-CurrentRootPath { $Path = Split-Path $script:MyInvocation.MyCommand.Path $FolderArr = $Path.Split("\") $FolderCount = ($FolderArr.Count) do { $Path = $FolderArr[0..$FolderCount] -join "\" IF (Test-Path "$Path\powershell_root.txt") { $RootPath = $Path break } ELSE { $FolderCount = $FolderCount -1 } } until ($FolderCount -lt 0) IF ($RootPath) { $Global:RootPath = $RootPath write-debug $Global:RootPath } ELSE { Write-Host "`nError - folder root not found.`n`n" -ForegroundColor Red Break; } } #Run function above $Global:RootPath = $null #Get-CurrentRootPath #Output to log of whats being run from menu Function Set-ModLog ($FunctionName,$Note) { $Date = Get-Date -format "ddMMyyyy" $Time = Get-Date -UFormat "%H:%M" $FilePath = "$Global:RootPath\Logs\DynamicMenu\" $PathTest = Test-Path $FilePath If(!$PathTest) { md "$Global:RootPath\Logs\DynamicMenu\" | out-null $PathTest2 = Test-Path $FilePath If(!$PathTest2) { Write-Host "`nFailed to create log folder`n`n" -ForegroundColor Red break } } $FilePathTxt = "$FilePath" + $date + "_" + $ModuleName + ".txt" $FileTest = Test-Path $FilePathTxt #New log file If (!$FileTest) { Try { $OutputtoLog = "SessionID,Time,Date,FunctionName,Account,Ticket,ModuleName,Username,ComputerName,Note" $OutputtoLog | Out-File -filepath $FilePathTxt } Catch { #Do nothing } } #Append to log file Try { $OutputtoLog = "$SessionID,$Time,$Date,$FunctionName,$Global:accountnum,$Global:ticketnum,$ModuleName,$env:username,$env:computername,$Note" $OutputtoLog | Out-File -filepath $FilePathTxt -append } Catch { #Do nothing } } ############# Logging - END ############# ############## Module Menu Functions - Start ############# <# .Synopsis Imports named module and indexs/caches function details in a pso. .DESCRIPTION Imports named module and creates an array of psobject. Each psobject contains name and get-help synopsis of each function in the module that start with Menu-*, MenuNew-*.or MenuDisable-* .EXAMPLE Used to import a module that has not yet been imported. $FunctionsArray = Import-ModuleFunctions -ModuleName $ModuleName -ModulePath $ModulePath .EXAMPLE Used re-import and inventory a module that has already been imported. Allow you to run this in code to re-import a module after making edits to it while code is running. $ModulePath = (Get-module | Where-Object {$_.Name -eq $ModuleName}).Path $FunctionsArray = Import-ModuleFunctions -ModuleName $ModuleName -ModulePath $ModulePath .NOTES Author: Mark Wichall #> Function Import-ModuleFunctions { Param ( #Name of module file to build menu from. [Parameter(Mandatory=$true, Position=0)] $ModuleName, #Module file path. [Parameter(Mandatory=$true, Position=1)] $ModulePath ) Write-Verbose "ModPath = $ModulePath" If ($ModulePath) { If (Test-Path $ModulePath) { Write-Host "Importing Module: $ModuleName and Caching Function Details...`n" -ForegroundColor Yellow Import-Module "$ModulePath" -Force -DisableNameChecking } Else { Write-Host "Can't find path!" -ForegroundColor Red Break } } Else { Write-Host "Path Empty!" -ForegroundColor Red Break } #This is designed so that you can select which functions to include from the module file. #List all functions in module file containing verbs specified with reg ex in $verb e.g. 'menu|menunew|menudisable' #$Verb = 'menu|menunew|menudisable'; #$ModFunctions = Get-Command -Module "$ModuleName" | where-object { $_.Verb -match "$Verb" } | select name #Replaced due to required PS version e.g. 3. Version below is less demanding. #Idea for Sort - Sort-Object -Property Noun #Verb #Noun #Name $ModFunctions = Get-Command -Module "$ModuleName" | Where-Object {$_.Name -like "Menu-*" -or $_.Name -like "MenuNew-*" -or $_.Name -like "MenuDisable-*"} | Sort-Object Name | Select-Object Name If ($ModFunctions) { $FunctionArray = @() ForEach ($ModFunction in $ModFunctions) { $FunctionPSO = New-Object PSObject $FunctionPSO | Add-Member -type NoteProperty -Name FunctionName -Value $ModFunction.Name #Pulls the help synopsis from the function. $Synopsis = Get-Help $ModFunction.Name | Select synopsis $FunctionPSO | Add-Member -type NoteProperty -Name Synopsis -Value $Synopsis.Synopsis $FunctionArray += $FunctionPSO } } Else { Write-Host "No Functions Found, check for functions within module file $ModulePath" Break } Return $FunctionArray } <# .Synopsis Creates a Menu listing functions in a named module and allows you to chose a function to run. .DESCRIPTION Uses function Import-ModuleFunctions to reimport/import a module and create an array of psobjects. This array of psobjects is used to create the menu. Menu can be customised in several ways including chaning the title, showing functions name + synopsis or just the synopsis, adding formating, etc. See Examples. .EXAMPLE Call the function with module name. Used with either this module or with another module thats already been imported. Menu lists functions names and help synopsis of function. Start-ModuleMenu -ModuleName "" .EXAMPLE Using the -SynopsisOnly switch menu will display just the help synopsis. Opposed to the default to display both function name and help synopsis. Start-ModuleMenu -ModuleName "" -SynopsisOnly .EXAMPLE Call the function with module name and path to import a module and create the menu. Cannot be used if the module your importing is this one. Start-ModuleMenu -ModuleName "" -$ModulePath "" .EXAMPLE Using the -RunFormat switch is usefull for debugging as it tells you the functions thats beeing run, and puts formating around the function as it runs and any returned result from function. Start-ModuleMenu -ModuleName "" -RunFormat .EXAMPLE Online Line help is avalible for this function. Using the following to go to it's wiki page. Get-help Start-ModuleMenu -online .NOTES Author: Mark Wichall #> #[Alias("p1")] Function Start-ModuleMenu { Param ( #Name of module file to build menu from. [Parameter(Mandatory=$true, Position=0)] [Alias("N")] [String] $ModuleName, #Module file path. Not required if module already imported. [Parameter(Mandatory=$false, Position=1)] [Alias("P")] $ModulePath, #Name of module file to build menu from. [Parameter(Mandatory=$false, Position=2)] [Alias("S")] [Switch] $SynopsisOnly, #Name of module file to build menu from. [Parameter(Mandatory=$false, Position=3)] [Alias("R")] [Switch] $RunFormat ) #Check Version of Powershell, only run on version 2 and above. $PSVer = $psversiontable.psversion.major If ($PSVer -lt 2) { Write-Host "Powershell version $PSVer installed. Powershell 2.0 reqiured. Exiting script." -forgroundcolor Red Break } Else { Write-verbose "Powershell version $PSVer installed." } If ($Global:SynopsisOnly) { $SynopsisOnly = $True } #Sets Menu Variables $MenuInputError = $false $NoExit = $true #If module path not specified it will try and import path from already loaded module. If (!$ModulePath) { $ModulePath = (Get-module | Where-Object {$_.Name -eq $ModuleName}).Path } [Array]$FunctionsArray = Import-ModuleFunctions -ModuleName $ModuleName -ModulePath $ModulePath If ($Global:MenuSortOrder -eq "Synopsis") { $FunctionsArray = $FunctionsArray | Sort-Object Synopsis } #Note = code not indented as it breaks herestring If ($Global:MenuTitle) { $MenuTitle = @" "@ $MenuTitle = $Global:MenuTitle } Else { #Default menu title if its blank. $MenuTitle = @" ************************************************ *M***M****O****D*******M***M**EEEE**N***N**U**U* *MM*MM***O*O***DD******MM*MM**E*****NN**N**U**U* *M*M*M**O***O**D*D*===*M*M*M**EEE***N*N*N**U**U* *M***M***O*O***DD******M***M**E*****N**NN**U**U* *M***M****O****D*******M***M**EEEE**N***N**UUUU* ************************************************ "@ } #Note = code not indented of breaks herestring If ($Global:SynopsisOnly -eq $true) { $SynopsisOnly = $Global:SynopsisOnly } #Note = code not indented of breaks herestring If ($Global:MenuDescription) { $MenuDescription = @" "@ $MenuDescription = $Global:MenuDescription } Else { #Default menu title if its blank. $MenuDescription = @" Mod Menu Description. - List is a dynamiacally created menu based on content of module imported. "@ } #Set UI background to black. $originalBackgroundColor = $Host.UI.RawUI.BackgroundColor $Host.UI.RawUI.BackgroundColor = "black" #Clears Screen - commented out. If($Global:ClearMenu -eq $True) { Clear-Host } #Logging use to share. Try { Import-Module "$Global:RootPath\Shared_Modules\Logging_Module.psm1" -EA SilentlyContinue Write-MasterLogFile -functionname "$ModuleName" -scriptname "DynamicMenu" -Notes "" -Account "" -Path "$Global:RootPath" } Catch { #Do nothing } #Loops Menu While ($NoExit) { If ($NoExit) { #Display Title #Convertto-ColourASCII -String $Title -BackgroundColour "Black" Write-Host "" #Display title and colour If ($Global:MenuTitleColour) { Write-Host $MenuTitle -ForegroundColor $Global:MenuTitleColour } Else { Write-Host $MenuTitle -ForegroundColor White } Write-Host "" $AuthorOrVersion = $false If ($Global:MenuVersion) { Write-Host " Version = $Global:MenuVersion" -ForegroundColor White $AuthorOrVersion = $true } If ($Global:MenuAuthor) { Write-Host " Author = $Global:MenuAuthor" -ForegroundColor White $AuthorOrVersion = $true } #Add a line of Author or Version are outputed. If ($AuthorOrVersion) { Write-Host "" } #Display Description and colour If ($Global:MenuDescriptionColour) { Write-Host $MenuDescription -ForegroundColor $Global:MenuDescriptionColour } Else { Write-Host $MenuDescription -ForegroundColor White } If ($FunctionsArray) { $i = $null Write-Host "`nSelect an Option from list below:`n" Foreach ($Function in $FunctionsArray) { $Number = $i + 1 If ($SynopsisOnly) { If ($Function.Synopsis) { If ($Function.FunctionName -like "MenuNew-*") { $NumLenght = $Number.ToString() If (($NumLenght.length) -eq 1) { Write-Host " $Number - " -NoNewline Write-Host $Function.Synopsis" (New)" -ForegroundColor Green } Else { Write-Host " $Number - " -NoNewline Write-Host $Function.Synopsis" (New)" -ForegroundColor Green } } ElseIf ($Function.FunctionName -like "MenuDisable-*") { $NumLenght = $Number.ToString() If (($NumLenght.length) -eq 1) { Write-Host " $Number - " -NoNewline Write-Host $Function.Synopsis" (Disabled)" -ForegroundColor DarkGray } Else { Write-Host " $Number - " -NoNewline Write-Host $Function.Synopsis" (Disabled)" -ForegroundColor DarkGray } } Else { $NumLenght = $Number.ToString() If (($NumLenght.length) -eq 1) { Write-Host " $Number - " -NoNewline Write-Host $Function.Synopsis -ForegroundColor yellow } Else { Write-Host " $Number - " -NoNewline Write-Host $Function.Synopsis -ForegroundColor yellow } } } Else { $NumLenght = $Number.ToString() If (($NumLenght.length) -eq 1) { Write-Host " $Number - " -NoNewline Write-Host "Missing Help Synopsis: "$Function.FunctionName -ForegroundColor DarkGray } Else { Write-Host " $Number - " -NoNewline Write-Host "Missing Help Synopsis: "$Function.FunctionName -ForegroundColor DarkGray } } } Else { If ($Function.Synopsis) { If ($Function.FunctionName -like "MenuNew-*") { $NumLenght = $Number.ToString() If (($NumLenght.length) -eq 1) { Write-Host " $Number - "$Function.FunctionName -ForegroundColor Green -NoNewline Write-Host " "$Function.Synopsis -ForegroundColor yellow } Else { Write-Host " $Number - "$Function.FunctionName -ForegroundColor Green -NoNewline Write-Host " "$Function.Synopsis -ForegroundColor yellow } } ElseIf ($Function.FunctionName -like "MenuDisable-*") { $NumLenght = $Number.ToString() If (($NumLenght.length) -eq 1) { Write-Host " $Number - "$Function.FunctionName -ForegroundColor DarkGray -NoNewline Write-Host " "$Function.Synopsis -ForegroundColor yellow } Else { Write-Host " $Number - "$Function.FunctionName -ForegroundColor DarkGray -NoNewline Write-Host " "$Function.Synopsis -ForegroundColor yellow } } Else { $NumLenght = $Number.ToString() If (($NumLenght.length) -eq 1) { Write-Host " $Number - "$Function.FunctionName -NoNewline Write-Host " "$Function.Synopsis -ForegroundColor yellow } Else { Write-Host " $Number - "$Function.FunctionName -NoNewline Write-Host " "$Function.Synopsis -ForegroundColor yellow } } } Else { If ($Function.FunctionName -like "MenuNew-*") { Write-Host " $Number - "$Function.FunctionName -ForegroundColor Green } If ($Function.FunctionName -like "MenuDisable-*") { Write-Host " $Number - "$Function.FunctionName -ForegroundColor DarkGray } Else { Write-Host " $Number - "$Function.FunctionName } } } $i++ } } Else { Write-Host "Error Importing module $ModuleName" -ForegroundColor Red } $ReadHostOption = "" Write-Host "`n [Q] = Quit Menu.`n" -nonewline -ForegroundColor Red If ($MenuInputError) { Write-host "`nInvalid input entered, enter [1-$Number ,Q]`n" -ForegroundColor Red $MenuInputError = $false } $ServersMenu = Read-Host "`nSelect a Function or Option [1-$Number ,Q]" switch ($ServersMenu) { 'Q' {$NoExit = $false;break} {$_ -match "^([1-9]|[1-9][0-9]|[1-9][0-9][0-9])$"} { #Checks if input is not greater than [int]$ServersMenuInt = $ServersMenu If ($ServersMenuInt -gt $Number) { #Invalid input. Clear-Host $MenuInputError = $true break } Write-Host " " If ($RunFormat) { Write-Host "Running selected function: "$FunctionsArray[$ServersMenu - 1].FunctionName -ForegroundColor Green Write-Host ("*" * 20) "Running - Start" ("*" * 20) "`n" -ForegroundColor Green Write-Host " " } $Result = $Null If ($FunctionsArray[$ServersMenu - 1].FunctionName -like "MenuDisable-*") { Write-Host "Function selected has been disabled, sorry." -ForegroundColor Red } Else { $FunctionName = $FunctionsArray[$ServersMenu - 1].FunctionName #Set-ModLog $FunctionName $Result = Invoke-Expression $FunctionName } Write-Host " " If ($RunFormat) { Write-Host ("*" * 21) "Running - End" ("*" * 21) -ForegroundColor Green If (!$Result) { Write-Host "Function did not return a variable." -ForegroundColor Yellow } Else { Write-Host " " Write-Host "Function returned vaiable content:" -ForegroundColor Cyan Write-Host ("*" * 21) "Result - Start" ("*" * 21) -ForegroundColor Cyan Write-Host " " Write-Host $Result Write-Host " " Write-Host ("*" * 22) "Result - End" ("*" * 22)"`n" -ForegroundColor Cyan } } Else { If ($Result) { $Result } } #Pause code Write-Host "Press any key to return to menu." -ForegroundColor Cyan $HOST.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | OUT-NULL $HOST.UI.RawUI.Flushinputbuffer() break } Default { Clear-Host $MenuInputError = $true } } } } #Close window on exit. If($Global:ExitonQuit) { Write-Host "`nExiting." -NoNewline -ForegroundColor yellow Write-Host "." -NoNewline -ForegroundColor yellow start-sleep -m 250 Write-Host ".`n" -ForegroundColor yellow start-sleep -m 250 Stop-process $pid } Else { Write-Host "`nExiting..." -ForegroundColor yellow } $Host.UI.RawUI.BackgroundColor = $originalBackgroundColor Clear-Host #Comented out returned data not required. #Return $Data } ############## Module Menu Functions - End #############