Several weeks ago, I posted a PowerShell script on my blog that uses commands in the Hyper-V module to report on memory utilization per virtual machine. I rely heavily on virtualization to provide a complete domain environment since I work at home. At present, my workload is spread between laptop running 8GB of RAM and a Gigabyte Brix with 16GB of RAM. I’m in the process of planning for something new. But in the meantime, memory utilization, especially from the Hyper-V host perspective is important to me. So I thought I’d share with you some PowerShell-related suggestions that should give you a good idea of how much memory is being used and what virtual machine is using it.
I’m going to be querying my Hyper-V server from my desktop.
The first step I can take is to query WMI for the Win32_OperatingSystem. I'm using Get-CimInstance as I am trying to wean myself off of Get-WmiObject.
I've selected a few memory related properties.
Memory properties from the Win32_OperatingSystem class (Image Credit: Jeff Hicks)
The memory values are in KB. If I divide the number by 1KB that will give me the value in MB. If I divide by 1MB, I'll get the value in GB. Here's a more complete one-line command that also calculates utilization percentages.
A formatted display of memory properties (Image Credit: Jeff Hicks)
As you can see, memory is getting pretty tight. What virtual machines are eating this memory up? To figure that out, I need to get all the currently running VMs.
How much memory is currently assigned?
Total assigned virtual machine memory (Image Credit: Jeff Hicks)
But probably more important is how much memory is actually being demanded?
Total virtual machine memory demanded (Image Credit: Jeff Hicks)
There is also a maximum memory, which would be a worst case scenario. Here's a one-line approach to getting the value.
Potential maximum VM memory (Image Credit: Jeff Hicks)
If all the current running VMs needed to use their maximum memory settings, they would need 23GB. That's a problem because the server only has 16! Hopefully it won't come to that. With this, I can create a custom object to display total virtual machine memory utilization.
A VM memory summary (Image Credit: Jeff Hicks)
Notice I’m not simply writing text to the screen I am using an object. This makes it much easier if I had multiple hosts to manage, or if I wanted to export to a CSV or something.
I can take this a step further and look at each running VM. Since I can get each machine’s assigned and demand memory, I can divide those values by the host’s available memory and calculate what percentage each VM is consuming.
I saved the results to a variable, so I could do different things with it like this:
VM utilization of host memory (Image Credit: Jeff Hicks)
Looks like my SQL Server needs some attention, or I could shut a few things down to free up resources.
In any event, I've given you several commands you could run to get a handle on memory utilization on the Hyper-V host. But personally, I'd prefer everything in one place. So I wrote a function that combines everything I've covered.
#requires -version 4.0
#requires -module Hyper-V
Function Get-VMHostMemoryStatus {
<#
.Synopsis
Get a memory summary for a Hyper-V host
.Description
This command will get a summary of memory utilization for a Hyper-V host. Unless otherwise indicated, memory sizes are in GB.
The VMs property will be a nested collection of running virtual machines showing what percentage of total host physical memory each virtual machine is assigned and demanding. See examples.
.Parameter Computername
The name of a Hyper-V host. This parameter has an alias of CN.
.Example
PS C:> Get-VMHostMemoryStatus chi-hvr2 -outvariable h
Computername : CHI-HVR2
OperatingSystem : Microsoft Hyper-V Server 2012 R2
TotalMemory : 16
FreeMemory : 2.05
PctMemoryFree : 12.91
TotalVirtualMemory : 18
FreeVirtualMemory : 4.35
PctVirtualMemoryFree : 23.8
RunningVMs : 7
TotalAssignedMemory : 12.845703125
TotalDemandMemory : 8.9794921875
PctDemand : 69.9
TotalMaximumMemory : 23
VMs : {@{Name=CHI-CORE01; Status=Low; MemAssignMB=513; PctAssignTotal=3.15; MemDemandMB=492; PctDemandTotal=3.03},
@{Name=CHI-DC04; Status=OK; MemAssignMB=2049; PctAssignTotal=12.6; MemDemandMB=799; PctDemandTotal=4.91},
@{Name=CHI-FP02; Status=Warning; MemAssignMB=821; PctAssignTotal=5.05; MemDemandMB=845; PctDemandTotal=5.2},
@{Name=CHI-SCOM01; Status=OK; MemAssignMB=4096; PctAssignTotal=25.19; MemDemandMB=1720;
PctDemandTotal=10.58}...}
.Example
PS C:> $h.vms | Sort PctDemandTotal -descending | format-table
Name Status MemAssignMB PctAssignTotal MemDemandMB PctDemandTotal
---- ------ ----------- -------------- ----------- --------------
CHI-SQL01 Warning 2653 16.32 2732 16.8
CHI-Win81 Low 2050 12.61 1927 11.85
CHI-SCOM01 OK 4096 25.19 1679 10.33
CHI-FP02 Warning 777 4.78 808 4.97
CHI-DC04 OK 2049 12.6 799 4.91
CHI-Web02 OK 1024 6.3 655 4.03
CHI-CORE01 Low 513 3.15 492 3.03
The running virtual machines and how much memory each is using.
.Notes
Version 1.0
Learn more about PowerShell:
****************************************************************
* DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED *
* THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK. IF *
* YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, *
* DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING. *
****************************************************************
.Link
Get-VM
Get-VMHost
Get-CimInstance
#>
[cmdletbinding()]
Param(
[Parameter(Position=0,Mandatory,HelpMessage="Enter the name of a Hyper-V host")]
[Alias("cn")]
[ValidateNotNullorEmpty()]
[string]$Computername
)
Write-Verbose "[Starting] $($MyInvocation.Mycommand)"
Try {
Write-Verbose "[Status] Getting Operating system information from $Computername"
$vmhost = Get-CimInstance -computername $Computername -ClassName Win32_OperatingSystem -ErrorAction Stop
Write-Verbose "[Status] Getting running virtual machines"
$vms = Get-VM -Computername $Computername -ErrorAction Stop | where {$_.state -eq 'running'}
}
Catch {
Throw $_
#bail out
Return
}
Write-Verbose "[Status] Analyzing..."
$vmusage = $vms | select Name,
@{Name = "Status";Expression={$_.MemoryStatus}},
@{Name = "MemAssignMB";Expression={$_.MemoryAssigned/1MB}},
@{Name = "PctAssignTotal";Expression={[math]::Round(($_.memoryAssigned/($vmhost.TotalVisibleMemorySize*1KB))*100,2)}},
@{Name = "MemDemandMB";Expression={$_.MemoryDemand/1MB}},
@{Name = "PctDemandTotal";Expression={[math]::Round(($_.memoryDemand/($vmhost.TotalVisibleMemorySize*1KB))*100,2)}}
[pscustomobject]@{
Computername = $vmhost.CSName
OperatingSystem = $vmhost.Caption
TotalMemory = $vmhost.totalVisibleMemorySize/1MB -as [int]
FreeMemory = [Math]::Round($vmhost.FreePhysicalMemory/1MB,2)
PctMemoryFree = [Math]::Round(($vmhost.FreePhysicalMemory/$vmhost.totalVisibleMemorySize) *100,2)
TotalVirtualMemory = $vmhost.totalVirtualMemorySize/1MB -as [int]
FreeVirtualMemory = [Math]::Round($vmhost.FreeVirtualMemory/1MB,2)
PctVirtualMemoryFree = [Math]::Round(($vmhost.FreeVirtualMemory/$vmhost.totalVirtualMemorySize) *100,2)
RunningVMs = $vms.count
TotalAssignedMemory = ($vms | Measure-Object -Property MemoryAssigned -sum).sum/1GB
TotalDemandMemory = ($vms | measure-object -Property MemoryDemand -sum).sum/1GB
PctDemand = [math]::Round(($vms.memorydemand | measure-object -sum).sum/($vms.memoryassigned | measure-object -sum).sum * 100,2)
TotalMaximumMemory = ($vms | Measure-Object -Property MemoryMaximum -sum).sum/1GB
VMs = $vmusage
}
Write-Verbose "[Ending] $($MyInvocation.Mycommand)"
} #end function
Now, I can get everything I need with a single command.
Getting Hyper-V host memory status (Image Credit: Jeff Hicks)
I modified the property names, as I thought they were getting a bit long for my tastes. The memory values are in GB. Details for each running virtual machine are stored in the VMs property. Because I used the common parameter Out-Variable, I can easily get that information without having to re-run the command.
Viewing VM memory details (Image Credit: Jeff Hicks)
Because everything is an object I can slice and dice the data anyway, I need or use any other PowerShell cmdlet. I hope you'll let me know what you think. Now if you'll excuse me, I think I have some virtual machines that need some attention.