Coming Soon: GET:IT Endpoint Management 1-Day Conference on September 28th at 9:30 AM ET Coming Soon: GET:IT Endpoint Management 1-Day Conference on September 28th at 9:30 AM ET
Hyper-V|PowerShell

Hyper-V Host Memory Utilization with PowerShell

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)
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.

Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName $computername |
Select PSComputername,
@{Name="OS";Expression = {$_.Caption}},
@{Name = "TotalMemoryGB";Expression={$_.totalVisibleMemorySize/1MB -as [int]}},
@{Name = "FreeMemoryGB";Expression={[Math]::Round($_.FreePhysicalMemory/1MB,2)}},
@{Name = "PctMemoryFree";Expression = {[Math]::Round(($_.FreePhysicalMemory/$_.totalVisibleMemorySize) *100,2)}},
@{Name = "TotalVirtualMemoryGB";Expression={$_.totalVirtualMemorySize/1MB -as [int]}},
@{Name = "FreeVirtualMemoryGB";Expression={[Math]::Round($_.FreeVirtualMemory/1MB,2)}},
@{Name = "PctVirtualMemoryFree";Expression = {[Math]::Round(($_.FreeVirtualMemory/$_.totalVirtualMemorySize) *100,2)}}

A formatted display of memory properties (Image Credit: Jeff Hicks)
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.

Sponsored Content

Say Goodbye to Traditional PC Lifecycle Management

Traditional IT tools, including Microsoft SCCM, Ghost Solution Suite, and KACE, often require considerable custom configurations by T3 technicians (an expensive and often elusive IT resource) to enable management of a hybrid onsite + remote workforce. In many cases, even with the best resources, organizations are finding that these on-premise tools simply cannot support remote endpoints consistently and reliably due to infrastructure limitations.

​
How much memory is currently assigned?
Total assigned virtual machine memory (Image Credit: Jeff Hicks)
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)
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)
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.

[pscustomobject]@{
Computername = $Computername.toUpper()
RunningVMs = $vms.count
TotalAssignedMemoryGB = ($vms | Measure-Object -Property MemoryAssigned -sum).sum/1GB
TotalDemandMemoryGB = ($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)
TotalMaximumMemoryGB = ($vms | Measure-Object -Property MemoryMaximum -sum).sum/1GB
}

A VM memory summary (Image Credit: Jeff Hicks)
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.

$vmhost = Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName $computername
$usage = $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)}}

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)
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:
http://jdhitsolutions.com/blog/essential-powershell-resources/

  ****************************************************************
  * 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)
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)
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.

Related Topics:

BECOME A PETRI MEMBER:

Don't have a login but want to join the conversation? Sign up for a Petri Account

Register
Comments (0)

Leave a Reply