Save to My DOJO
Wouldn’t it be nice to periodically get an automatic performance review of your Hyper-V VMs? Well, this blog post shows you how to do exactly that.
Hyper-V Performance Counters & Past Material
Over the last few weeks, I’ve been working with Hyper-V performance counters and PowerShell, developing new reporting tools. I thought I’d write about Hyper-V Performance counters here until I realized I already have.
https://www.altaro.com/hyper-v/performance-counters-hyper-v-and-powershell-part-1/
https://www.altaro.com/hyper-v/hyper-v-performance-counters-and-powershell-part-2/
Even though I wrote these articles several years ago, nothing has really changed. If you aren’t familiar with Hyper-V performance counters I encourage you to take a few minutes and read these. Otherwise, some of the material in this article might not make sense.
Get-CimInstance
Normally, using Get-Counter is a better approach, especially if you want to watch performance over a given timespan. But sometimes you just want a quick point in time snapshot. Or you may have network challenges. As far as I can tell Get-Counter uses legacy networking protocols, i.e. RPC and DCOM. This does not make them very firewall friendly. You could use PowerShell Remoting and Invoke-Command to run Get-Counter on the remote server. Or you can use Get-CimInstance which is what I want to cover in this article.
When you run Get-Counter, you are actually querying performance counter classes in WMI. This means you can get the same information using Get-CimInstance, or Get-WmiObject. But because we want to leverage WSMan and PowerShell Remoting, we’ll stick with the former.
Building a Hyper-V Performance Report
First, we need to identify the counter classes. I’ll focus on the classes that have “cooked” or formatted data.
$computer = $env:computername Get-CimClass -ClassName *perfformatted*hyper* -ComputerName $computer | Select-Object -ExpandProperty Cimclassname
I’m setting a variable for the computername so that you can easily re-use the code. I’m demonstrating this on a Windows 10 desktop running Hyper-V but you can just as easily point $Computer to a Hyper-V host.
It is pretty easy to leverage the PowerShell pipeline and create a report for all Hyper-V performance counters.
"$($computer.toupper()) Hyper-V Performance Report" | Out-file c:workperf.txt Get-CimClass -ClassName *perfformatted*hyper* -ComputerName $computer | foreach-object { Write-Host "Querying $($_.cimclassname)" -foreground green $_.CimClassName | Out-file c:workperf.txt -append Get-CimInstance -ClassName $_.CimClassName -ComputerName $computer | out-file c:workperf.txt -Append }
The text file will list each performance counter class followed by all instances of that class. If you run this code, you’ll see there are a number of properties that won’t have any values. It might help to filter those out. Here’s a snippet of code that is a variation on the text file. This code creates an HTML report, skipping properties that likely will have no value.
Get-CimClass -ClassName *perfformatted*hyper* -ComputerName $computer | foreach-object -begin {$frags=@("<H1>Hyper-V Performance Report</H1>")} -process { write-Host "Processing $($_.CimClassName)" -ForegroundColor Cyan $split = $_.CimClassName -split "_" #count how many properties if ($_.CimClassProperties.where({$_.name -notmatch "caption|description|frequency|timestamp"}).count -gt 7) { $as = "list" } else { $as = "table" } $frags+= "<h2>$($split[-1])</h2>" $frags+= Get-CimInstance -ClassName $_.CimClassName -ComputerName $computer | Select-Object * -ExcludeProperty CimClass,Cim*Properties,PS*,Timestamp*,Frequency*,Caption,Description | ConvertTo-Html -Fragment -as $as } -end { ConvertTo-HTML -CssUri C:scriptssample2.css -Body $frags -Title "Perf Report $computer" | Out-File c:workperf.html }
This code creates an HTML report using fragments. I also am dynamically deciding to create a table or a list based on the number of properties.
Thus far I’ve been creating reports for all performance counters and all instances. But you might only be interested in a single virtual machine. This is a situation where you can take advantage of WMI filtering.
In looking at the output from all classes, I can see that the Name property on these classes can include the virtual machine name as part of the value. So I will go through every class and filter only for instances that contain the name of VM I want to monitor.
$VMName = "srv2" #group each entry by its classname $g = get-cimclass -ClassName *perfformatted*hyper* -ComputerName $computer | foreach-object { write-host "Querying $($_.cimclassname)" -foreground green Get-Ciminstance -ClassName $_.CimClassName -filter "Name Like '%$VMName%'" -ComputerName $computer | Select-object * -ExcludeProperty Cim*Properties,Timestamp*,Frequency*,Caption,Description -ov s | group-object -Property {$_.cimclass.CimClassname } } #create the array of html fragments $frags=@("<H1>Hyper-V Performance Report: $($VMName.ToUpper())</H1>") $frags+="<H2 Hyper-V Host: $($computer.toUpper())" foreach ($item in $g) { Write-Host "Creating fragment for $($item.name)" -ForegroundColor Cyan $frags+="<h2>$($item.name)</h2>" $frags+= $item.group | Select-object * -ExcludeProperty PS*Computername,CIMClass | ConvertTo-Html -Fragment -as List } $frags+="<h5><i>Report run $(Get-Date)</i></h5>" ConvertTo-HTML -CssUri C:scriptssample2.css -Body $frags -Title "Perf Report $($vmname.toupper())" | Out-File "c:work$($vmname.tolower()).html"
This example also adds a footer to the report showing when it was created.
It doesn’t take much more effort to create a report for each virtual machine. I turned my code into the beginning of a usable PowerShell function, designed to take pipeline input.
Function New-HTMLPerfReport { [cmdletbinding()] Param( [Parameter(Position = 0, Mandatory, ValueFromPipelinebyPropertyName,ValueFromPipeline)] [ValidateNotNullorEmpty()] [string]$VMName, [Parameter(Position = 0, Mandatory, ValueFromPipelinebyPropertyName)] [ValidateNotNullorEmpty()] [string]$Computername, [string]$DestinationPath = ".", [switch]$Passthru ) Begin {} Process { #this function lacks error handling Write-Host "Analyzing VM $($vmname.toupper()) on $($computername.toUpper())" -foreground magenta $file = Join-Path -Path $DestinationPath -ChildPath "$($vmname.tolower())-perf.html" #group each entry by its classname $g = get-cimclass -ClassName *perfformatted*hyper* -ComputerName $computername | foreach-object { write-verbose "Querying $($_.cimclassname)" Get-Ciminstance -ClassName $_.CimClassName -filter "Name Like '%$VMName%'" -ComputerName $computername | Select-object * -ExcludeProperty Cim*Properties,Timestamp*,Frequency*,Caption,Description -ov s | Group-Object -Property {$_.cimclass.CimClassname } } #create the array of html fragments $frags=@("<H1>Hyper-V Performance Report: $($VMName.ToUpper())</H1>") $frags+="<H2 Hyper-V Host: $($computer.toUpper())" foreach ($item in $g) { Write-Verbose "Creating fragment for $($item.name)" $frags+="<h2>$($item.name)</h2>" $frags+= $item.group | Select-object * -ExcludeProperty PS*Computername,CIMClass | ConvertTo-Html -Fragment -as List } $frags+="<h5><i>Report run $(Get-Date)</i></h5>" Write-Verbose "Creating HTML file $file" ConvertTo-HTML -CssUri C:scriptssample2.css -Body $frags -Title "Perf Report $($vmname.toupper())" | Out-File -FilePath $file if ($Passthru) { Get-Item -Path $file } } End {} }
With this function, I can query as many virtual machines and create a performance report for each.
Get-VM -computername <your VMHost> | where state -eq running | New-HTMLPerfReport -Passthru -DestinationPath c:reports
You can take this idea a step further and run this as a PowerShell scheduled job, perhaps saving the report files to an internal team web server.
I have at least one other intriguing PowerShell technique for working with Hyper-V performance counters, but I think I’ve given you enough to work with for today so I’ll save it until next time.
Wrap-Up
Did you find this useful? Have you done something similar and used a different method? Let us know in the comments section below!
Thanks for Reading!
Not a DOJO Member yet?
Join thousands of other IT pros and receive a weekly roundup email with the latest content & updates!
3 thoughts on "How to Create Automated Hyper-V Performance Reports"
Wow, what a great article – so much stuff in a short read…
Where would I find C:Scriptssample2.css?
Thanks for this – very interesting
Thank you! I posted the css file in GitHub at https://gist.github.com/jdhitsolutions/4f90f75182732519f408b3607cb991c6