3 Awesome Uses for the Get-VMHostStatus PowerShell Function

Save to My DOJO

3 Awesome Uses for the Get-VMHostStatus PowerShell Function

In a recent article, I showed you how I built a PowerShell function to display information about the status of a Hyper-V host. The function alone I hope is useful. But what good would it be if that is all it does?  Actually, let me rephrase that – what else can I do with that function? The whole point of PowerShell is that it provides a large set of building blocks that you can assemble to achieve the desired result.

A simple PowerShell pipelined expression

It can be as simple as the illustrated command above. For more complex tasks and especially those that you want to repeat, you will take the same commands you would run at a prompt and put them into a PowerShell script. That’s what I want to show you today. Using the Get-VMHostStatus function, here are 3 different ways you might leverage it. I won’t go into great detail on the code as I’ve added internal comments. And as with all my articles, pay as much attention to the techniques and concepts as much as the end result. You may not have a need for any of these scripts as they exist, but you might pick up an idea that you can use in building your own Hyper-V PowerShell tools.

Creating an HTML Report

My first use is to take the output from Get-VMHostStatus and create an HTML report. Even though the function is creating an object with a lot of information, I can pick and choose what I want. I can even gather additional information. The script is essentially a wrapper for Get-VMHostStatus. It runs the command and creates a series of HTML fragments which is then assembled into a final file. The script even embeds a CSS style sheet.

#requires -version 5.1

[cmdletbinding()]
Param(
    [Parameter(Position = 0 , Mandatory, HelpMessage = "Enter the name of a Hyper-V Host")]
    [string]$ComputerName,
    [Paramater(ParameterSetName = "Computername")]
    [PSCredential]$Credential,
   
    [ValidatePattern("w+.(html|htm)$")]
    [string]$Path = ".HyperVHostStatus.htm"
)

#region get VM Host Status

#dot source the required script with the Get-VMHostStatus function
. C:scriptsGet-VMHostStatus.ps1

#remove the Path from boundparameters since it isn't part of the parameters
#for Get-VMHostStatus and I want to eventually splat $PSBoundParameters
if ($PSBoundParameters.ContainsKey("Path")) {
    $PSBoundParameters.Remove("Path") | Out-Null
}

#get the VMHost data passing bound parameters
Try {
    $data = Get-VMHostStatus @PSBoundParameters -ErrorAction Stop
}
Catch {
    Throw $_
}

#endregion

#region define the CSS style in the head section

#get sample CSS from https://github.com/jdhitsolutions/SampleCSS
$head = @"
<Title>Hyper-V Host Status</Title>
<style>
@charset "UTF-8";

body {
    background-color: rgb(233, 223, 223);
    font-family: Monospace;
    font-size: 12pt;
}

td,th {
    border: 0px solid black;
    border-collapse: collapse;
    white-space: pre;
}

th {
    color: white;
    background-color: black;
}

table,tr,td,th {
    padding: 3px;
    margin: 0px;
    white-space: pre;
}

tr:nth-child(odd) {
    background-color: lightgray
}

table {
    margin-left: 25px;
    width: 50%;
}

h2 {
    font-family: Tahoma;
}

.footer {
    color: green;
    margin-left: 25px;
    font-family: Tahoma
    font-size: 7pt;
    font-style: italic;
}
</style>
"@

#endregion

#region get some additional data

$s = New-PSSession @PSBoundParameters

$procdata = Invoke-command -ScriptBlock {Get-Ciminstance Win32_Computersystem -Property 'NumberOfLogicalProcessors', 'NumberOfProcessors'} -session $s
$hostdetail = Invoke-Command -ScriptBlock {Get-Ciminstance Win32_OperatingSystem -property "Caption", "Version"} -session $s
$detail = "{0} version {1}" -f $hostdetail.caption, $hostdetail.version
Remove-PSSession $s

#endregion

#region define the pieces of the HTML report as fragments

$fragments = @()
$fragments += "<H1>Hyper-V Host Status</H1>"
$fragments += "<H2 title = '$detail'>$($data.computername)<H2>"

$fragments += "<H3>Memory</H3>"
$fragments += $data | Select-object -Property *memory*, TotalPctDemand | ConvertTo-Html -Fragment -as List
$fragments += "<H3>Processor</H3>"

$fragments += $data | Select-Object -Property @{Name = "ProcessorCount"; Expression = {$procdata.NumberOfProcessors}},
@{Name = "LogicalProcessorCount"; Expression = {$procdata.NumberOfLogicalProcessors}},
PctProcessorTime, Logical* | ConvertTo-Html -Fragment -as list

$fragments += "<H3>Virtual Machines</H3>"
$fragments += $data | Select-Object -Property *VMs | ConvertTo-Html -Fragment -as List

$fragments += "<H3>Virtual Machine Health</H3>"
$fragments += $data | Select-Object -Property Critical, Healthy | ConvertTo-Html -Fragment -as table

#a future version might include additional network-related values
$fragments += "<H3>Networking</H3>"
$fragments += $data | Select-Object VMSwitchBytesSec, VMSwitchPacketsSec | ConvertTo-Html -Fragment

$fragments += "<H3>Other</H3>"
$fragments += $data | Select-Object Uptime, TotalProcesses, PctFreeDisk| ConvertTo-Html -Fragment -as table

#endregion

#region create an object with footer information so it can be displayed neatly

[xml]$meta = [pscustomobject]@{
    "Report run"  = (Get-Date)
    Author        = "$env:USERDOMAIN$env:USERNAME"
    Script        = (Convert-Path $MyInvocation.InvocationName).Replace("", "/")
    ScriptVersion = '0.9.3'
    Source        = $env:COMPUTERNAME
} | ConvertTo-Html -Fragment -As List

$meta.CreateAttribute("Class") | Out-Null
$meta.table.SetAttribute("class", "footer")

#endregion

#region assemble the final HTML report

ConvertTo-Html -Head $head -body $fragments -postcontent "<br><br>$($meta.innerxml)"  |
    Out-File -FilePath $Path -Encoding utf8

Write-Host "See $(Convert-Path $path) for the finished report file." -ForegroundColor green

#endregion

The script will create a single HTML report per Hyper-V Host.

C:scriptsGet-VMHostStatusReport.ps1 -computername chi-p50 -path $env:tempchi-p50-status.html

The end result looks like this:

A Hyper-V Host Status HTML Report

One thing I want to point out is that I’ve added some metadata at the bottom of the report to indicate how and where the file originated from. This is the type of script you could set up as a PowerShell Scheduled Job. Maybe even emailing the results. Over time and as staff turns over, people may forget where the report is coming from. Adding the metadata helps you keep tabs on where the script is running.

Extending the PowerShell Prompt

Let’s say managing Hyper-V is a primary job function. If you are like me you may have a PowerShell window open constantly. I’ve been writing on my blog a number of articles that demonstrate how to turn your PowerShell prompt into a monitoring tool. I realized I could do something similar with a Hyper-V emphasis.

Function prompt {

    #define a hashtable of symbols to use
    $charHash = @{
        Up          = [char]0x25b2
        Down        = [char]0x25bc
        Delta       = [char]0x2206
        Pointer     = [char]0x25BA
        Pointerleft = [char]0x25C4
        TopLeft     = [char]0x250c
        TopRight    = [char]0x2510
        Border      = [char]0x2500
        BottomLeft  = [char]0x2514
        BottomRight = [char]0x2518
        Ohm         = [char]0x2126
        Mu          = [char]0x3bc
        disk        = [char]0x058d
        bps         = [char]0x20bf
    }

    Try {
        #verify there is a global hashtable variable
        Get-Variable -Name rsHash -Scope global -ErrorAction Stop | Out-Null
    }
    Catch {
        #create the runspace and synchronized hashtable
        $global:rsHash = [hashtable]::Synchronized(@{Computername = $env:computername; results = ""; date = (Get-Date); computers = @()})
        $newRunspace = [runspacefactory]::CreateRunspace()
        #set apartment state if available
        if ($newRunspace.ApartmentState) {
            $newRunspace.ApartmentState = "STA"
        }
        $newRunspace.ThreadOptions = "ReuseThread"
        $newRunspace.Open()
        $newRunspace.SessionStateProxy.SetVariable("rsHash", $rsHash)

        $pscmd = [PowerShell]::Create().AddScript( {

                #define scriptblock to run in the background
                $sb = {
                    Param($sessions)
                    #turn off Write-Progress to speed things up a bit
                    $ProgressPreference = "silentlycontinue"
                    #dot source the script file
                    . C:scriptsGet-VMHostStatus.ps1
                    #run the function using the PSSessions
                    Get-VMHostStatus $sessions
                }

                #define the Hyper-V Hosts to query as a comma separated list
                $Computers= $env:computername
                #or pull names in from a text file
                # $computers = Get-Content c:scriptshvhosts.txt

                do {
                    #reset results
                    #make sure there are sessions for all computers in the list
                    $computers | Where-Object {(get-pssession).where( {$_.state -eq 'opened'}).computername -notcontains $_} |
                        ForEach-Object {
                        New-PSSession -ComputerName $_
                    }
                    Get-PSSession | Where-Object {$_.state -eq 'broken'} | foreach-object {
                        Remove-PSSession $_
                        #attempt to recreate it
                        New-PSsession -ComputerName $_.computername
                    }
                    $results = Invoke-Command -ScriptBlock $sb -ArgumentList @(, $(Get-PSSession))

                    $global:rsHash.results = $results
                    $global:rsHash.date = Get-Date
                    $global:rshash.computers = $computers
                    #set a sleep interval between tests
                    Start-Sleep -Seconds 10
                } While ($True)
            }) # script
        $pscmd.runspace = $newrunspace
        [void]$psCmd.BeginInvoke()
    } #catch

    if (-Not $global:rsHash.results) {
        #define a working message that has the string: Working
        $workingmsg = " Working....please wait"
        $working = $True
    }
    else {
        $data = $global:rsHash.results
        $working = $False
        #get the length of the longest host name for padding purposes
        $pad = ($data.computername | Sort-object {$_.length} -Descending | Select-Object -first 1).length
    }

    #Take a guess at how wide to make the border
    $wide = 68

    #display the results in the console
    Write-Host "`n " -NoNewline
    Write-Host "Hyper-V Host Status $(Get-Date)" -BackgroundColor Gray -ForegroundColor Black
    Write-Host $charHash.TopLeft -NoNewline
    Write-Host $($charHash.Border.tostring() * $wide) -NoNewline
    Write-Host $charHash.TopRight

    if ($working) {
        Write-Host $workingmsg -ForegroundColor Yellow
    }
    else {

        foreach ($name in $($global:rsHash.computers)) {
            $hvhost = $data | where {$_.computername -eq $name}
            if (-not $Hvhost) {
                #create an empty placeholder
                $hvhost = [psobject]@{
                    Uptime = New-timespan
                    RunningVMs = 0
                    OffVMs = 0
                    PausedVMs = 0
                    SavedVMs = 0
                    PctMemoryFree = 0
                    TotalPctDemand = 0
                    PctFreeDisk = 0
                }
            }
            $hostinfo =" $($charHash.Pointer) {0}" -f $name.Padright($pad," ")
            Write-Host $hostInfo -NoNewline

            if ($hvhost.Uptime.totalseconds -gt 0) {
                write-Host " $($charHash.up)" -ForegroundColor Green -NoNewline
            }
            else {
                Write-Host " $($charHash.Down)" -ForegroundColor Red -NoNewline
            }

            write-Host (" {0:ddd.hh:mm:ss}" -f $hvhost.Uptime) -NoNewline

            Write-Host " $($charhash.up) $($hvhost.runningVMs)" -ForegroundColor Green -NoNewline
            Write-Host " $($charhash.down) $($hvhost.offVMs)" -ForegroundColor red -NoNewline
            Write-Host " $($charhash.pointer)$($charHash.pointerleft) $($hvhost.pausedVMs)" -ForegroundColor magenta -NoNewline
            Write-Host " $($charHash.pointerleft)$($charhash.pointer) $($hvhost.savedVMs)" -ForegroundColor yellow -NoNewline
            #define some threshholds which will be reflected in color
            if ($hvhost.PctMemoryFree -le 20) {
                $fg = "red"
            }
            elseif ($hvhost.PctMemoryFree -le 60) {
                $fg = "yellow"
            }
            else {
                $fg = "green"
            }

            Write-Host " $($charHash.mu)$(($hvhost.pctMemoryFree.tostring()).Padleft(5,' '))%" -NoNewline -ForegroundColor $fg
            Write-Host " $($charhash.delta)$(($hvhost.TotalPctDemand.tostring()).Padleft(5,' '))" -NoNewline
            if ($hvhost.PctFreeDisk -le 20) {
                $fg = "red"
            }
            elseif ($hvhost.PctFreeDisk -le 40) {
                $fg = "yellow"
            }
            else {
                $fg = "green"
            }
            #format pctFreeDisk
            [string]$p =[math]::round($hvhost.pctFreeDisk,2)
            Write-Host (" $($charHash.disk){0}" -f $p.padleft(5,' ')) -ForegroundColor $fg
        }
    }

    # Add the bottom of the border
    Write-Host $charHash.BottomLeft -NoNewline
    Write-Host $($charHash.Border.tostring() * $wide) -NoNewline
    Write-Host $charHash.BottomRight

    "PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) "

} #close function

You will need to specify the location and filename of the script file with the Get-HVHostStatus prompt and you might need to define $computers with a comma-separated list of server names. The function defaults to the localhost. I would keep this list to a handful of server names.

To use this, save the script to a file and dot source it in your PowerShell session.

. C:scriptshvprompt.ps1

Or dot source it in your PowerShell profile script. When the prompt initially loads you’ll see a “Working” message. In the background the prompt is spinning up a separate runspace, running Get-VMHostStatus and passing the results via a synchronized hash table. Yeah, this is a bit advanced so if you feel lost that is to be expected. But once you make the necessary changes for your environment you should see something like this:

A PowerShell Prompt with Hyper-V Host Status Details

If the host is offline, you’ll see a red downward pointing indicator and 0 in all the values. Reading from left to right this is what the prompt is showing:

  • Computername
  • Up/Down indicator
  • Uptime
  • Number of running virtual machines
  • Number of stopped virtual machines
  • Number of paused virtual machines
  • Number of saved virtual machines
  • The percent free memory (color coded)
  • The total percent memory demand
  • The percent free disk space for the default storage drive  (color coded)

This isn’t necessarily real-time data. In the background, the information is getting updated every 10 seconds. You may want to increase that value. Every time you press Enter you are getting the last obtained values.

Creating a Graphical Monitor

The last solution is a bit more complicated. I wanted to take advantage of my Convertto-WPFGrid function which I’ve written about before. I wanted to use it as a standalone graphical reporting tool that you could kick off from PowerShell yet wouldn’t block your PowerShell prompt. This was more complicated than I expected and ended up building a set of functions that start the WPF (Windows Presentation Foundation) code in a background runspace. The WPF display can be managed through the synchronized hashtable. But I didn’t want to force you to know how to modify the hashtable so I wrote a few helper functions. Here’s the complete script file.

# requires -version 5.1

<#

MonitorHVHost.ps1

This is NOT a module so you will need to dot source the script file into
your PowerShell session. You can then run Start-HVHostMonitor to kick off
a WPF GUI. Use Set-HVHostMonitor to adjust it and Stop-HVHostMonitor to
clean up.

Allow a little time for changes to be updated.

The properties are from Get-VMHostStatus

Computername                    : CHI-P50
Uptime                          : 00:46:26.8179350
PctProcessorTime                : 3.84799996455453
TotalMemoryGB                   : 64
PctMemoryFree                   : 77.16
TotalVMs                        : 13
RunningVMs                      : 4
OffVMs                          : 9
SavedVMs                        : 0
PausedVMs                       : 0
OtherVMs                        : 0
Critical                        : 0
Healthy                         : 13
TotalAssignedMemoryGB           : 10.939453125
TotalDemandMemoryGB             : 5.6279296875
TotalPctDemand                  : 8.81
PctFreeDisk                     : 27.3125310680432
VMSwitchBytesSec                : 170332.7095065
VMSwitchPacketsSec              : 263.120591389896
LogicalProcPctGuestRuntime      : 4.11198442672914
LogicalProcPctHypervisorRuntime : 0.28358272876852
TotalProcesses                  : 88

#>

Function Start-HVHostMonitor {
    [cmdletbinding()]
    Param(
        [Parameter(Position = 0, HelpMessage = "The names of the Hyper-V Hosts to monitor")]
        [ValidateNotNullorEmpty()]
        [alias("cn", "host")]
        #Hyper-V hosts to monitor
        [string[]]$Computername = $env:COMPUTERNAME,
        #properties to display from Get-VMHostStatus
        [Parameter(HelpMessage = "The properties from Get-VMHostStatus")]
        [ValidateNotNullorEmpty()]
        [string[]]$Properties = @("Computername", "Uptime", "*VMs", "Pct*"),
        [Parameter(HelpMessage = "The form height")]
        [int]$Height = 160,
        [Parameter(HelpMessage = "The form width")]
        [int]$Width = 970,
        #the windows title
        [Parameter(HelpMessage = "The form title")]
        [string]$Title = "Hyper-V Host Status",
        [Parameter(HelpMessage = "The refresh interval in seconds")]
        [int]$Timeout = 30
    )

    #define a synchronized hashtable that can be used to "communicate" with the background runspace
    $global:rsHash = [hashtable]::Synchronized(@{

            #Hyper-V hosts to monitor
            computername = @($Computername)

            #properties to display
            properties   = @($Properties)

            height       = $height
            width        = $width
            title        = $Title
            timeout      = $Timeout
            CenterScreen = $False

            #set to false to stop the display
            Run          = $True
            #these properties are for troubleshooting and development
            Data         = @()
            Updated      = (Get-Date)
            Timer        = ""
            JobID        = 0
            Job          = ""
        })

    $newRunspace = [RunspaceFactory]::CreateRunspace()
    $newRunspace.ApartmentState = "STA"
    $newRunspace.ThreadOptions = "ReuseThread"
    $newRunspace.Open()
    $newRunspace.SessionStateProxy.SetVariable("rsHash", $global:rsHash)

    $psCmd = [PowerShell]::Create().AddScript( {

            # It may not be necessary to add these types but it doesn't hurt to include them
            Add-Type -AssemblyName PresentationFramework
            Add-Type -assemblyName PresentationCore
            Add-Type -AssemblyName WindowsBase

            # !!! You will need to update the path to your copy of Get-VMHostStatus.ps1 !!!#
            # !!! The scriptblock should have a period then a space then the path to the ps1 file !!!#
            $script:Init = { . "C:scriptsGet-VMHostStatus.ps1"}
            Function _startjob {
                #this is a private internal function used to start a data gathering job in the background
                Start-Job -ScriptBlock {
                    Param($hosts)
                    #ignore errors for offline or bad hosts
                    Get-VMHostStatus -Computername $hosts -ErrorAction SilentlyContinue
                } -InitializationScript $script:Init -ArgumentList @(, @($global:rsHash.computername))
                $global:rsHash.jobid = $script:j.id
            }

            #get initial data
            $script:j = _startjob
            Do {

                #region WPF code
                # define a timer to automatically dismiss the form. The timer uses a 5 second interval tick
                if ($global:rsHash.Timeout -gt 0) {
                    Write-Verbose "Creating a timer"
                    $timer = new-object System.Windows.Threading.DispatcherTimer
                    $terminate = (Get-Date).AddSeconds($global:rsHash.timeout)
                    Write-verbose "Form will close at $terminate"
                    $timer.Interval = [TimeSpan]"0:0:5.00"

                    $timer.add_tick( {
                            if ((Get-Date) -ge $terminate) {

                                if ($global:rsHash.run) {
                                    $timer.stop()

                                    $data = $script:j | Wait-Job -ov w | Receive-Job | Select-Object $global:rsHash.Properties -OutVariable hash
                                    $global:rshash.job = $w
                                    $global:rsHash.data = $hash
                                    $global:rsHash.updated = (Get-Date)
                                    $datagrid.Clear()
                                    $DataGrid.ItemsSource = $hash
                                    $form.title = "$($global:rsHash.title) [Last Updated $(Get-Date)]"
                                    $form.Height = $global:rsHash.Height
                                    $form.Width = $global:rsHash.Width


                                    $terminate = (Get-Date).AddSeconds($global:rsHash.timeout)
                                    #kick off the next job
                                    $script:j = _startjob
                                    $global:rsHash.timer = (Get-Date)
                                    $timer.Start()
                                    $form.UpdateLayout()
                                }
                                else {
                                    $form.close()
                                }
                            }
                        })
                }

                $form = New-Object System.Windows.Window
                #define what it looks like
                $form.Title = "$($global:rsHash.title) [Last Updated $(Get-Date)]"
                $form.Height = $global:rsHash.Height
                $form.Width = $global:rsHash.Width

                if ($global:rsHash.CenterScreen) {
                    Write-Verbose "Form will be center screen"
                    $form.WindowStartupLocation = [System.Windows.WindowStartupLocation]::CenterScreen
                }
                #define a handler when the form is loaded. The scriptblock uses variables defined later
                #in the script
                $form.add_Loaded( {
                        foreach ($col in $datagrid.Columns) {
                            #because of the way I am loading data into the grid
                            #it appears I need to set the sorting on each column
                            $col.CanUserSort = $True
                            $col.SortMemberPath = $col.Header
                        }
                        $datagrid.Items.Refresh()
                        $form.focus
                    })
                #Create a stack panel to hold the datagrid
                $stack = New-object System.Windows.Controls.StackPanel

                #create a datagrid
                $datagrid = New-Object System.Windows.Controls.DataGrid

                $datagrid.VerticalAlignment = "Bottom"
                #adjust the size of the grid based on the form dimensions
                $datagrid.Height = $form.Height - 50
                $datagrid.Width = $form.Width - 50
                $datagrid.CanUserSortColumns = $True
                $datagrid.CanUserResizeColumns = $True
                $datagrid.CanUserReorderColumns = $True
                $datagrid.AutoGenerateColumns = $True
                #enable alternating color rows
                $datagrid.AlternatingRowBackground = "gainsboro"

                $stack.AddChild($datagrid)
                $form.AddChild($stack)

                #endregion
                #show the form

                $data = $script:j | Wait-Job -ov w | Receive-Job | Select-Object $global:rsHash.Properties -OutVariable hash
                $global:rshash.job = $w
                $global:rsHash.data = $hash
                $global:rsHash.updated = (Get-Date)
                $DataGrid.ItemsSource = $hash
                #kick off the next job
                $script:j = _startjob

                If ($global:rsHash.Timeout -gt 0) {
                    Write-Verbose "Starting timer"
                    $timer.IsEnabled = $True
                    $Timer.Start()
                }

                Write-Verbose "Displaying form"
                $form.Title = "$($global:rsHash.title) [Last Updated $(Get-Date)]"
                $form.ShowDialog() | Out-Null

            } while ($global:rsHash.Run)

        })

    $psCmd.Runspace = $newRunspace
    $psCmd.BeginInvoke() | Out-Null

    $msg = @"
Please wait a moment for the results to be displayed.
To terminate run this command at your prompt"

  `$rsHash.run = `$False

or run:

  Stop-HVHostMonitor

Using runspace id $($newRunspace.Id)
"@

    Write-host $msg -ForegroundColor cyan
    #set a global variable for the runspace so it can later be cleaned up
    $global:hvmonrun = $newrunspace.id
} #close Start function

Function Stop-HVHostMonitor {
    [cmdletbinding(SupportsShouldProcess)]
    Param()

    if ($pscmdlet.ShouldProcess("Hyper-VHost Monitor")) {
        $global:rsHash.run = $False
        Remove-Variable -Name rshash -Scope global
    }

    $run = Get-Runspace -id $global:hvmonrun
    if ($pscmdlet.ShouldProcess("Runspace id $($run.id)", "Clean up")) {
        $run.Close()
        $run.Dispose()
        Remove-Variable -Name hvmonrun -Scope global
    }
} #close Stop function

Function Set-HVHostMonitor {
    [cmdletbinding(SupportsShouldProcess)]
    Param(
        [Parameter(Position = 0, HelpMessage = "The names of the Hyper-V Hosts to monitor")]
        #Hyper-V hosts to monitor
        [ValidateNotNullOrEmpty()]
        [alias("cn", "host")]
        [string[]]$Computername,
        #properties to display
        [Parameter(HelpMessage = "Update the properties from Get-VMHostStatus")]
        [ValidateNotNullorEmpty()]
        [string[]]$Properties,
        [Parameter(HelpMessage = "Update the form height")]
        [int]$Height,
        [Parameter(HelpMessage = "Update the form width")]
        [int]$Width,
        #the windows title
        [Parameter(HelpMessage = "Update yhe form title")]
        [string]$Title,
        [Parameter(HelpMessage = "Update the refresh interval in seconds")]
        [int]$Timeout
    )

    #remove common parameters from bound parameters
    "Whatif", "Verbose", "ErrorAction" | foreach-object {
        if ($PSBoundParameters.ContainsKey($_)) {
            [void]$PSBoundParameters.Remove($_)
        }
    }

    $PSBoundParameters.GetEnumerator() | Foreach-Object {
        if ($pscmdlet.ShouldProcess($_.key, "Update monitor value")) {
            $global:rsHash[$_.key] = $_.value
        }
    }
} #close Set function

This is currently written as a .PS1 file. It would not take much to turn this into a module. If you have the experience, you might want to take that extra step. Once you dot source the script (or import it as a module) you will have these commands:

  • Start-HVHostMonitor
  • Set-HVHostMonitor
  • Stop-HVHostMonitor

You should be able to get help on any of them to see the syntax. To begin monitoring you can use the default parameter values. Or run it like this.

Start-HVHostMonitor -Computername CHI-P50,Bovine320,Think51 -Title "VM Hosts" -Timeout 60

This will start the process and after a brief moment, display a graphical table with selected properties from Get-VMHostStatus. In this example, the display will be updated every minute.

Continuous Display of Hyper-V Host Status in a WPF Grid

You can use Set-HVHostMonitor to adjust display properties on the fly. Although you might not see the results until the next refresh.

As long as the PowerShell session where you started it from is running, the form will be displayed. To terminate it, use the Stop-HVHostMonitor.

Your Turn

And there you have it: 3 different ways to use a single PowerShell command. Behind the scenes I’m executing my Get-VMHostStatus function against one or more Hyper-V hosts. From there I can use the data in whatever way meets my business needs. If you are still getting started with PowerShell, don’t try to create projects as complicated as these. Start simple. With time and experience you’ll soon be creating PowerShell Hyper-V management tools that you’ll wonder how you ever managed without. When you do, I hope you’ll share. I definitely would like to know what you are up to!

Need Help

I’ve mentioned this before, but if you need help with PowerShell or writing a PowerShell script, I encourage you to use the free forums at PowerShell.org.

Threat Monitor
Share this post

Not a DOJO Member yet?

Join thousands of other IT pros and receive a weekly roundup email with the latest content & updates!

2 thoughts on "3 Awesome Uses for the Get-VMHostStatus PowerShell Function"

Leave a comment or ask a question

Your email address will not be published. Required fields are marked *

Your email address will not be published. Required fields are marked *

Notify me of follow-up replies via email

Yes, I would like to receive new blog posts by email

What is the color of grass?

Please note: If you’re not already a member on the Dojo Forums you will create a new account and receive an activation email.