Images

Why You Should Be Compacting Your Hyper-V Virtual Disks

The reasons, prerequisites, and steps necessary to reclaim unused space from your dynamically-expanding VHDXs. Includes PowerShell Tool for Compacting VHDX Files.

Read the post here: Why You Should Be Compacting Your Hyper-V Virtual Disks

Fixing Erratic Behavior on Hyper-V with Network Load Balancers

For years, I’d never heard of this problem. Then, suddenly, I’m seeing it everywhere. It’s not easy to precisely outline a symptom tree for you. Networked applications will behave oddly. Remote desktop sessions may skip or hang. Some network traffic will not pass at all. Other traffic will behave erratically. Rather than try to give you a thorough symptom tree, we’ll just describe the setup that can be addressed with the contents of this article: you’re using Hyper-V with a third-party network load balancer and experiencing network-related problems.

Acknowledgements

Before I ever encountered it, the problem was described to me by one my readers. Check out our Complete Guide to Hyper-V Networking article and look in the comments section for Jahn’s input. I had a different experience, but that conversation helped me reach a resolution much more quickly.

Problem Reproduction Instructions

The problem may appear under other conditions, but should always occur under these:

  • The network adapters that host the Hyper-V virtual switch are configured in a team
    • Load-balancing algorithm: Dynamic
    • Teaming mode: Switch Independent (likely occurs with switch-embedded teaming as well)
  • Traffic to/from affected virtual machines passes through a third-party load-balancer
    • Load balancer uses a MAC-based system for load balancing and source verification
      • Citrix Netscaler calls its feature “MAC based forwarding”
      • F5 load balancers call it “auto last hop”
    • The load balancer’s “internal” IP address is on the same subnet as the virtual machine’s
  • Sufficient traffic must be exiting the virtual machine for Hyper-V to load balance some of it to a different physical adapter

I’ll go into more detail later. This list should help you determine if you’re looking at an article that can help you.

Resolution

Fixing the problem is very easy, and can be done without downtime. I’ll show the options in preference order. I’ll explain the impacting differences later.

Option 1: Change the Load-Balancing Algorithm

Your best bet is to change the load-balancing algorithm to “Hyper-V port”. You can change it in the lbfoadmin.exe graphical interface if your management operating system is GUI-mode Windows Server. To change it with PowerShell (assuming only one team):

There will be a brief interruption of networking while the change is made. It won’t be as bad as the network problems that you’re already experiencing.

Option 2: Change the Teaming Mode

Your second option is to change your teaming mode. It’s more involved because you’ll also need to update your physical infrastructure to match. I’ve always been able to do that without downtime as long as I changed the physical switch first, but I can’t promise the same for anyone else.

Decide if you want to use Static teaming or LACP teaming. Configure your physical switch accordingly.

Change your Hyper-V host to use the same mode. If your Hyper-V system’s management operating system is Windows Server GUI, you can use lbfoadmin.exe. To change it in PowerShell (assuming only one team):

or

In this context, it makes no difference whether you pick static or LACP. If you want more information, read our article on the teaming modes.

Option 3: Disable the Feature on the Load Balancer

You could tell the load balancer to stop trying to be clever. In general, I would choose that option last.

An Investigation of the Problem

So, what’s going on? What caused all this? If you’ve got an environment that matches the one that I described, then you’ve unintentionally created the perfect conditions for a storm.

Whose fault is it? In this case, I don’t really think that it’s fair to assign fault. Everyone involved is trying to make your network traffic go faster. They sometimes do that by playing fast and loose in that gray area between Ethernet and TCP/IP. We have lots of standards that govern each individually, but not so many that apply to the ways that they can interact. The problem arises because Microsoft is playing one game while your load balancer plays another. The games have different rules, and neither side is aware that another game is afoot.

Traffic Leaving the Virtual Machine

We’ll start on the Windows guest side (also applies to Linux). Your application inside your virtual machine wants to send some data to another computer. That goes something like this:

  1. Application: “Network, send this data to computer www.altaro.com on port 443”.
  2. Network: “DNS server, get me the IP for www.altaro.com”
  3. Network: “IP layer, determine if the IP address for www.altaro.com is on the same subnet”
  4. Network: “IP layer, send this packet to the gateway”
  5. IP layer passes downward for packaging in an Ethernet frame
  6. Ethernet layer transfers the frame

The part to understand: your application and your operating system don’t really care about the Ethernet part. Whatever happens down there just happens. Especially, it doesn’t care at all about the source MAC.

lb_out_traffic

Traffic Crossing the Hyper-V Virtual Switch

Because this particular Ethernet frame is coming out of a Hyper-V virtual machine, the first thing that it encounters is the Hyper-V virtual switch. In our scenario, the Hyper-V virtual switch rests atop a team of network adapters. As you’ll recall, that team is configured to use the Dynamic load balancing algorithm in Switch Independent mode. The algorithm decides if load balancing can be applied. The teaming mode decides which pathway to use and if it needs to repackage the outbound frame.

Switch independent mode means that the physical switch doesn’t know anything about a team. It only knows about two or more Ethernet endpoints connected in standard access mode. A port in that mode can “host” any number of MAC addresses;the physical switch’s capability defines the limit. However, the same MAC address cannot appear on multiple access ports simultaneously. Allowing that would cause all sorts of problems.

lb_broken_si_traffic

So, if the team wants to load balance traffic coming out of a virtual machine, it needs to ensure that the traffic has a source MAC address that won’t cause the physical switch to panic. For traffic going out anything other than the primary adapter, it uses the MAC address of the physical adapter.

lb_good_si_traffic

So, no matter how many physical adapters the team owns, one of two things will happen for each outbound frame:

  • The team will choose to use the physical adapter that the virtual machine’s network adapter is registered on. The Ethernet frame will travel as-is. That means that its source MAC address will be exactly the same as the virtual network adapter’s (meaning, not repackaged)
  • The team will choose to use an adapter other than the one that the virtual machine’s network adapter is registered on. The Ethernet frame will be altered. The source MAC address will be replaced with the MAC address of the physical adapter

Note: The visualization does not cover all scenarios. A virtual network adapter might be affinitized to the second physical adapter. If so, its load balanced packets would travel out of the shown “pNIC1” and use that physical adapter’s MAC as a source.

Traffic Crossing the Load Balancer

So, our frame arrives at the load balancer. The load balancer has a really crummy job. It needs to make traffic go faster, not slower. And, it acts like a TCP/IP router. Routers need to unpackage inbound Ethernet frames, look at their IP information, and make decisions on how to transmit them. That requires compute power and time.

lb_router_hard

If it needs too much time to do all this, then people would prefer to live without the load balancer. That means that the load balancer’s manufacturer doesn’t sell any units, doesn’t make any money, and goes out of business. So, they come up with all sorts of tricks to make traffic faster. One way to do that is by not doing quite so much work on the Ethernet frame. This is a gross oversimplification, but you get the idea:

lb_router_easy

Essentially, the load balancer only needs to remember which MAC address sent which frame, and then it doesn’t need to worry so much about all that IP nonsense (it’s really more complicated than that, but this is close enough).

The Hyper-V/Load Balancer Collision

Now we’ve arrived at the core of the problem: Hyper-V sends traffic from virtual machines using source MAC addresses that don’t belong to those virtual machines. The MAC addresses belong to the physical NIC. When the load balancer tries to associate that traffic with the MAC address of the physical NIC, everything breaks.

Trying to be helpful (remember that), the load balancer attempts to return what it deems as “response” traffic to the MAC that initiated the conversation. The MAC, in this case, belongs directly to that second physical NIC. It wasn’t expecting the traffic that’s now coming in, so it silently discards the frame.

That happens because:

  • The Windows Server network teaming load balancing algorithms are send only; they will not perform reverse translations. There are lots of reasons for that and they are all good, so don’t get upset with Microsoft. Besides, it’s not like anyone else does things differently.
  • Because the inbound Ethernet frame is not reverse-translated, its destination MAC belongs to a physical NIC. The Hyper-V virtual switch will not send any Ethernet frame to a virtual network adapter unless it owns the destination MAC
  • In typical system-to-system communications, the “responding” system would have sent its traffic to the IP address of the virtual machine. Through the normal course of typical networking, that traffic’s destination MAC would always belong to the virtual machine. It’s only because your load balancer is trying to speed things along that the frame is being sent to the physical NIC’s MAC address. Otherwise, the source MAC of the original frame would have been little more than trivia.

Stated a bit more simply: Windows Server network teaming doesn’t know that anyone cares about its frames’ source MAC addresses and the load balancer doesn’t know that anyone is lying about their MAC addresses.

Why Hyper-V Port Mode Fixes the Problem

When you select the Hyper-V port load balancing algorithm in combination with the switch independent teaming mode, each virtual network adapter’s MAC address is registered on a single physical network adapter. That’s the same behavior that Dynamic uses. However, no load balancing is done for any given virtual network adapter; all traffic entering and exiting any given virtual adapter will always use the same physical adapter. The team achieves load balancing by placing each virtual network adapter across its physical members in a round-robin fashion.

lb_si_hp

Source MACs will always be those of their respective virtual adapters, so there’s nothing to get confused about.

I like this mode as a solution because it does a good job addressing the issue without making any other changes to your infrastructure. The drawback would be if you only had a few virtual network adapters and weren’t getting the best distribution. For a 10GbE system, I wouldn’t worry.

Why Static and LACP Fix the Problem

Static and LACP teaming involve your Windows Server system and the physical switch agreeing on a single logical pathway that consists of multiple physical pathways. All MAC addresses are registered on that logical pathway. Therefore, the Windows Server team has no need of performing any source MAC substitution regardless of the load balancing algorithm that you choose.

lb_stdlacp

Since no MAC substitution occurs here, the load balancer won’t get anything confused.

I don’t like this method as much. It means modifying your physical infrastructure. I’ve noticed that some physical switches don’t like the LACP failover process very much. I’ve encountered some that need a minute or more to notice that a physical link was down and react accordingly. With every physical switch that I’ve used or heard of, the switch independent mode fails over almost instantly.

That said, using a static or LACP team will allow you to continue using the Dynamic load balancing algorithm. All else being equal, you’ll get a more even load balancing distribution with Dynamic than you will with Hyper-V port mode.

Why You Should Let the Load Balancer Do Its Job

The third listed resolution suggests disabling the related feature on your load balancer. I don’t like that option, personally. I don’t have much experience with the Citrix product, but I know that the F5 buries their “Auto Last Hop” feature fairly deeply. Also, these two manufacturers enable the feature by default. It won’t be obvious to a maintainer that you’ve made the change.

However, your situation might dictate that disabling the load balancer’s feature causes fewer problems than changing the Hyper-V or physical switch configuration. Do what works best for you.

Using a Different Internal Router Also Addresses the Issue

In all of these scenarios, the load balancer performs routing. Actually, these types of load balancers always perform routing, because they present a single IP address for the service to the outside world and translate internally to the back-end systems.

However, nothing states that the internal source IP address of the load balancer must exist in the same subnet as the back-end virtual machines. You might do that for performance reasons; as I said above, routing incurs overhead. However, this all a known quantity and modern routers are pretty good at what they do. If any router is present between the load balancer and the back-end virtual machines, then the MAC address issue will sort itself out regardless of your load balancing and teaming mode selections.

Have You Experienced this Phenomenon?

If so, I’d love to hear from you. What system did you experience it happening? How did you resolve the situation (if you were able)? Perhaps you’ve just encountered it and arrived here to get a solution – if so let me know if this explanation was helpful or if you need any further assistance regarding your particular environment. The comment section below awaits.

Looking at the Hyper-V Event Log (January 2018 edition)

Hyper-V has changed over the last few years and so has our event log structure. With that in mind, here is an update of Ben’s original post in 2009 (“Looking at the Hyper-V Event Log”).

This post gives a short overview on the different Windows event log channels that Hyper-V uses. It can be used as a reference to better understand which event channels might be relevant for different purposes.

As a general guidance you should start with the Hyper-V-VMMS and Hyper-V-Worker event channels when analyzing a failure. For migration-related events it makes sense to look at the event logs both on the source and destination node.

Windows Event Viewer showing the Hyper-V-VMMS Admin log

Below are the current event log channels for Hyper-V. Using “Event Viewer” you can find them under “Applications and Services Logs”, “Microsoft”, “Windows”.
If you would like to collect events from these channels and consolidate them into a single file, we’ve published a HyperVLogs PowerShell module to help.

Event Channel Category Description
Hyper-V-Compute Events from the Host Compute Service (HCS) are collected here. The HCS is a low-level management API.
Hyper-V-Config This section is for anything that relates to virtual machine configuration files. If you have a missing or corrupt virtual machine configuration file – there will be entries here that tell you all about it.
Hyper-V-Guest-Drivers Look at this section if you are experiencing issues with VM integration components.
Hyper-V-High-Availability Hyper-V clustering-related events are collected in this section.
Hyper-V-Hypervisor This section is used for hypervisor specific events. You will usually only need to look here if the hypervisor fails to start – then you can get detailed information here.
Hyper-V-StorageVSP Events from the Storage Virtualization Service Provider. Typically you would look at these when you want to debug low-level storage operations for a virtual machine.
Hyper-V-VID These are events form the Virtualization Infrastructure Driver. Look here if you experience issues with memory assignment, e.g. dynamic memory, or changing static memory while the VM is running.
Hyper-V-VMMS Events from the virtual machine management service can be found here. When VMs are not starting properly, or VM migrations fail, this would be a good source to start investigating.
Hyper-V-VmSwitch These channels contain events from the virtual network switches.
Hyper-V-Worker This section contains events from the worker process that is used for the actual running of the virtual machine. You will see events related to startup and shutdown of the VM here.
Hyper-V-Shared-VHDX Events specific to virtual hard disks that can be shared between several virtual machines. If you are using shared VHDs this event channel can provide more detail in case of a failure.
Hyper-V-VMSP The VM security process (VMSP) is used to provide secured virtual devices like the virtual TPM module to the VM.
Hyper-V-VfpExt Events form the Virtual Filtering Platform (VFP) which is part of the Software Defined Networking Stack.
VHDMP Events from operations on virtual hard disk files (e.g. creation, merging) go here.

Please note: some of these only contain analytic/debug logs that need to be enabled separately and not all channels exist on Windows client. To enable the analytic/debug logs, you can use the HyperVLogs PowerShell module.

Alles Gute,

Lars

Automatically Generate Nagios Performance Checks for Hyper-V

<#

.SYNOPSIS

Creates Nagios monitoring configurations for Hyper-V virtual machines.

.DESCRIPTION

Scans one or more virtual machines and generates Nagios sensors for performance checks.

Each check is expressed as a “service”. A suitable “check-hypervmperf” command must already exist.

.PARAMETER VM

One or more virtual machines. Each input item will be scanned and included in the output file.

Accepts multiple input types:

* Microsoft.HyperV.PowerShell.VirtualMachine: This object type is output by the native Get-VM cmdlet. ComputerName is ignored with this type.

* System.String: Assumed to be the name of the target virtual machine. Used in conjunction with ComputerName.

* Microsoft.Management.Infrastructure.CimInstance: This type is output by Get-CimInstance. Must have a CreationClassName of Msvm_ComputerSystem. ComputerName is ignored with this type.

* System.Management.ManagementObject: This type is output by Get-WmiObject. Expected to be of type Msvm_ComputerSystem; must have a Name property that contains the VMId of a virtual machine on the objects __SERVER property to be effective. ComputerName is ignored with this type.

* Nothing: If not specified, will scan ComputerName for virtual machines.

.PARAMETER ComputerName

The target computer system to scan for virtual machines.

Ignored if VM is anything other than a string (including an empty string or an array of strings).

If a cluster name or IP is specified, only the current owner of the core cluster resource will be scanned.

If not specified, the local system is used.

.PARAMETER Path

The path to an output file. It will be created if it does not exist. By default, it will be overwritten. Use Append to override.

.PARAMETER Version

Choices: 2012R2 or 2016. Default: 2012R2

If specified, assumes that the virtual machine(s) are all of the indicated version.

If not specified, the host will be queried.

.PARAMETER LineEndFormat

Specifies the system codes to use to indicate an end-of-line in the output file. Default is “Linux”.

* Linux: Uses only a newline character (n)

* Windows: Uses a carriage-return/newline character combination (rn)

* Macintosh: Uses only a carriage-return character (r)

.PARAMETER ServiceTemplate

Text to include on each service’s “use” line. Defaults to “generic-service”.

.PARAMETER ServiceGroups

Text to include on each service’s “servicegroups” line. If left empty, no “servicegroups” line will be generated.

.PARAMETER ServiceContacts

Text to use as a service’s “contacts” line. If left empty, the “contacts” line is not generated.

.PARAMETER ServiceContactGroups

Text to use as a service’s “contact_groups” line. If left empty, the “contact_groups” line is not generated.

.PARAMETER Append

Appends the generated output to Path instead of overwriting.

.PARAMETER ResolveHost

Determines the IP address of the Hyper-V host or cluster and uses that as the service target instead of the host name.

.PARAMETER VMAsHost

Default behavior will assign the Hyper-V host or cluster as the service’s “host_name”. If VMAsHost is specified, uses the VM’s name instead.

.PARAMETER UpperCaseHostName

Nagios’ host_names are case sensitive. The techniques used in this script will alwoys generate lower-case names. Specify UpperCaseHostNames to force them to uppercase.

.PARAMETER SkipCPU

If specified, does not include any virtual CPU sensors.

.PARAMETER SkipDynamicMemory

If specified, does not include any dynamic memory sensors. Unnecessary for fixed memory virtual machines.

.PARAMETER SkipIDE

If specified, does not include any emulated IDE sensors. Unnecessary for Generation 2 virtual machines. Operates independently of SkipDisk.

.PARAMETER SkipDisk

If specified, does not generate any virtual hard disk sensors. Unnecessary for diskless virtual machines. Operates independently of SkipIDE.

.PARAMETER SkipNetwork

If specified, does not generate any virtual network sensors.

.PARAMETER IncludeAdvancedCounters

If specified, includes several uncommon and advanced counters in each category.

.PARAMETER IncludeLiveMigration

If specified, includes LiveMigration-related counters. Unnecessary for non-clustered virtual machines.

.PARAMETER IncludeSavesSnaps

If specified, includes counters related to Save and Snapshot/Checkpoint operations.

.PARAMETER IncludeSmartPaging

If specified, includes counters related to Smart Paging operations.

.PARAMETER IncludeNUMA

If specified, includes counters related to NUMA.

.PARAMETER IncludeNetworkDropReasons

If specified, includes counters for network drop reasons. Ignored if host is not 2016.

.PARAMETER IncludeVmWorkerProcess

If specified, includes counters for VM worker processors. Ignored if host is not 2016.

.PARAMETER IncludeVRSS

If specified, includes vRSS counters for all virtual network adapters on 2016 VMs.

.PARAMETER CreateVMHostDefinition

If specified, generates a host{} definition for each virtual machine. Ignored if VMAsHost is not also set.

The generated host definition will include the following lines unless you specify a VMHostTemplate:

max_check_attempts    1

check_command         null

notifications_enabled 0

.PARAMETER VMHostTemplate

Text to use as a host’s “use” line. Defaults to “generic-host”. Ignored if CreateVMHostDefinition and VMAsHost are not also set.

.PARAMETER UseSkeletonHost

If specified, will force the inclusion of the default items indicated in the description of CreateVMHostDefinition, even when VMHostTemplate is set.

Ignored if CreateVMHostDefinition and VMAsHost are not also set.

.PARAMETER VMHostGroups

Text to use as a host’s “hostgroups” line. If left empty, the “hostgroups” line is not generated. Ignored if CreateVMHostDefinition and VMAsHost are not also set.

.PARAMETER VMHostContacts

Text to use as a host’s “contacts” line. If left empty, the “contacts” line is not generated. Ignored if CreateVMHostDefinition and VMAsHost are not also set.

.PARAMETER VMHostContactGroups

Text to use as a host’s “contact_groups” line. If left empty, the “contact_groups” line is not generated. Ignored if CreateVMHostDefinition and VMAsHost are not also set.

.INPUTS

System.String[]

Microsoft.HyperV.PowerShell.VirtualMachine[]

Microsoft.Management.Infrastructure.CimInstance[]

System.Management.ManagementObject[]

.OUTPUTS

None

.NOTES

New-VMPerformanceDefinition.ps1

Version 1.0, December 27, 2017

Author: Eric Siron

Copyright: 2017 Altaro Software

.EXAMPLE

New-VMPerformanceDefinition.ps1 -ComputerName -Path C:Sourcesvhv01.cfg -ServiceTemplate ‘hyperv-vm-performance’

Creates a basic sensor set from all virtual machines on the host named svhv01 using the defined service template.

.EXAMPLE

Get-CimInstance -ComputerName svhv01 -ClassName msvm_computersystem -Namespace root/virtualization/v2 -Filter ‘ElementName=”dtmanage”‘ | New-VMPerformanceDefinition.ps1 -Path C:Sourcedtmanage.cfg -ServiceTemplate ‘hyperv-vm-performance’ -VMAsHost -IncludeAdvancedCounters -IncludeLiveMigration -IncludeSavesSnaps -IncludeSmartPaging -IncludeNUMA -IncludeNetworkDropReasons -IncludeVmWorkerProcess -IncludeVRSS -IncludeRemotingChecks -CreateVMHostDefinition

Retrieves the CIM definition from a host named “svhv01” for the virtual machine named “dtmanage” and creates a .cfg file with all possible counters.

#>

#requires -Version 4

[CmdletBinding()]

param(

[Parameter(Position = 1, ValueFromPipeline = $true)][System.Object[]]$VM,

[Parameter()][String]$ComputerName = $env:COMPUTERNAME,

[Parameter(Position = 2, Mandatory = $true)][String]$Path,

[Parameter()][ValidateSet(‘2012R2’, ‘2016’)][String]$Version,

[Parameter()][ValidateSet(‘Linux’, ‘Windows’, ‘Macintosh’)][String]$LineEndFormat = ‘Linux’,

[Parameter()][String]$ServiceTemplate = ‘generic-service’,

[Parameter()][String]$ServiceGroups = [System.String]::Empty,

[Parameter()][String]$ServiceContacts = [System.String]::Empty,

[Parameter()][String]$ServiceContactGroups = [System.String]::Empty,

[Parameter()][Switch]$Append,

[Parameter()][Switch]$ResolveHost,

[Parameter()][Switch]$VMAsHost,

[Parameter()][Switch]$UpperCaseHostName,

[Parameter()][Switch]$SkipCPU,

[Parameter()][Switch]$SkipDynamicMemory,

[Parameter()][Switch]$SkipIDE,

[Parameter()][Switch]$SkipDisk,

[Parameter()][Switch]$SkipNetwork,

[Parameter()][Switch]$IncludeAdvancedCounters,

[Parameter()][Switch]$IncludeLiveMigration,

[Parameter()][Switch]$IncludeSavesSnaps,

[Parameter()][Switch]$IncludeSmartPaging,

[Parameter()][Switch]$IncludeNUMA,

[Parameter()][Switch]$IncludeNetworkDropReasons,

[Parameter()][Switch]$IncludeVmWorkerProcess,

[Parameter()][Switch]$IncludeVRSS,

[Parameter()][Switch]$IncludeRemotingChecks,

[Parameter()][Switch]$CreateVMHostDefinition,

[Parameter()][String]$VMHostTemplate = ‘generic-host’,

[Parameter()][Switch]$UseSkeletonHost,

[Parameter()][String]$VMHostGroups = [System.String]::Empty,

[Parameter()][String]$VMHostContacts = [System.String]::Empty,

[Parameter()][String]$VMHostContactGroups = [System.String]::Empty

)

begin

{

$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop

$GUIDPattern = ‘[0-9a-fA-F]{8}.?[0-9a-fA-F]{4}.?[0-9a-fA-F]{4}.?[0-9a-fA-F]{4}.?[0-9a-fA-F]{12}’

$CounterList = New-Object -TypeName System.Collections.ArrayList

$VMList = New-Object -TypeName System.Collections.ArrayList

$LineEnding = [String]::Empty

if (-not (Test-Path -Path $Path))

{

$OutNull = New-Item -Path $Path -ItemType File

}

if (-not $Append)

{

Clear-Content -Path $Path

}

function New-ServiceObject

{

param(

[Parameter()][String]$HostName,

[Parameter()][String]$VMName,

[Parameter()][String]$CounterCategory,

[Parameter()][String]$Counter,

[Parameter()][bool]$Clustered

)

$ServiceItem = New-Object -TypeName psobject

Add-Member -InputObject $ServiceItem -MemberType NoteProperty -Name ‘HostName’ -Value $HostName

Add-Member -InputObject $ServiceItem -MemberType NoteProperty -Name ‘VMName’ -Value $VMName

Add-Member -InputObject $ServiceItem -MemberType NoteProperty -Name ‘CounterName’ -Value ([String]::Join(‘ ‘, $CounterCategory, ($Counter.Substring($Counter.LastIndexOf() + 1).Replace(‘%’, ‘Pct’) -replace ‘([()])’, )))

Add-Member -InputObject $ServiceItem -MemberType NoteProperty -Name ‘Counter’ -Value $Counter

Add-Member -InputObject $ServiceItem -MemberType NoteProperty -Name ‘Clustered’ -Value $Clustered

$ServiceItem

}

function New-ServiceDefinition

{

param(

[Parameter()][Object]$ServiceObject,

[Parameter()][String]$LineEnding,

[Parameter()][String]$ServiceTemplate,

[Parameter()][String]$ServiceGroups,

[Parameter()][String]$ServiceContacts,

[Parameter()][String]$ServiceContactGroups,

[Parameter()][bool]$VMAsHost

)

$ExtraCommands =

if ($ServiceObject.Clustered)

{

$ExtraCommands = ‘-l’

}

$ServiceText = New-Object System.Text.StringBuilder

$OutNull = $ServiceText.AppendFormat(‘define service{{{0}’, $LineEnding)

$OutNull = $ServiceText.AppendFormat(‘   use                 {0}{1}’, $ServiceTemplate, $LineEnding)

if ($VMAsHost)

{

$OutNull = $ServiceText.AppendFormat(‘   service_description {0}{1}’, $ServiceObject.CounterName, $LineEnding)

$TargetHost = $ServiceObject.VMName

}

else

{

$OutNull = $ServiceText.AppendFormat(‘   service_description {0} {1}{2}’, $ServiceObject.VMName, $ServiceObject.CounterName, $LineEnding)

$TargetHost = $ServiceObject.HostName

}

$OutNull = $ServiceText.AppendFormat(‘   host_name           {0}{1}’, $TargetHost, $LineEnding)

if ($ServiceGroups)

{

$OutNull = $ServiceText.AppendFormat(‘   servicegroups       {0}{1}’, $ServiceGroups, $LineEnding)

}

if ($ServiceContacts)

{

$OutNull = $ServiceText.AppendFormat(‘   contacts            {0}{1}’, $ServiceContacts, $LineEnding)

}

if ($ServiceContactGroups)

{

$OutNull = $ServiceText.AppendFormat(‘   contact_groups      {0}{1}’, $ServiceContactGroups, $LineEnding)

}

$OutNull = $ServiceText.AppendFormat(‘   check_command       check-hypervmperf!{0}!{1}!”{2}”!{3}{4}’, $ServiceObject.HostName, $ServiceObject.VMName, $ServiceObject.Counter, $ExtraCommands, $LineEnding)

$OutNull = $ServiceText.AppendFormat(‘}}{0}’, $LineEnding)

$ServiceText.ToString()

}

function New-HostDefinition

{

param(

[Parameter()][String]$ComputerName,

[Parameter()][String]$HostTemplate,

[Parameter()][String]$VMHostGroups,

[Parameter()][String]$VMHostContacts,

[Parameter()][String]$VMHostContactGroups,

[Parameter()][bool]$UseSkeletonHost,

[Parameter()][String]$LineEnding

)

$HostText = New-Object System.Text.StringBuilder

$OutNull = $HostText.AppendFormat(‘define host{{{0}’, $LineEnding)

$OutNull = $HostText.AppendFormat(‘   host_name             {0}{1}’, $ComputerName, $LineEnding)

$OutNull = $HostText.AppendFormat(‘   use                   {0}{1}’, $HostTemplate, $LineEnding)

if (($HostTemplate -eq ‘generic-host’) -or $UseSkeletonHost)

{

$OutNull = $HostText.AppendFormat(‘   max_check_attempts    1{0}’, $LineEnding)

$OutNull = $HostText.AppendFormat(‘   check_command         null{0}’, $LineEnding)

$OutNull = $HostText.AppendFormat(‘   notifications_enabled 0{0}’, $LineEnding)

}

if ($VMHostGroups)

{

$OutNull = $HostText.AppendFormat(‘   hostgroups            {0}{1}’, $VMHostGroups, $LineEnding)

}

if ($VMHostContacts)

{

$OutNull = $HostText.AppendFormat(‘   contacts              {0}{1}’, $VMHostContacts, $LineEnding)

}

if ($VMHostContactGroups)

{

$OutNull = $HostText.AppendFormat(‘   contact_groups        {0}{1}’, $VMHostContactGroups, $LineEnding)

}

$OutNull = $HostText.AppendFormat(‘}}{0}’, $LineEnding)

$HostText.ToString()

}

$AdvancedChecks = @(

‘\Hyper-V Hypervisor Partition({0}:HvPt)\Skipped Timer Ticks’, #

‘\Hyper-V Hypervisor Partition({0}:HvPt)\Device Interrupt Throttle Events’, #

‘\Hyper-V Hypervisor Partition({0}:HvPt)\Device DMA Errors’, #

‘\Hyper-V Hypervisor Partition({0}:HvPt)\Device Interrupt Errors’, #

‘\Hyper-V Hypervisor Partition({0}:HvPt)\I/O TLB Flush Cost’, #

‘\Hyper-V Hypervisor Partition({0}:HvPt)\I/O TLB Flushes/sec’, #

‘\Hyper-V Hypervisor Partition({0}:HvPt)\Device Interrupt Mappings’, #

‘\Hyper-V Hypervisor Partition({0}:HvPt)\Attached Devices’, #

‘\Hyper-V Hypervisor Partition({0}:HvPt)\1G device pages’, #

‘\Hyper-V Hypervisor Partition({0}:HvPt)\2M device pages’, #

‘\Hyper-V Hypervisor Partition({0}:HvPt)\4K device pages’, #

‘\Hyper-V Hypervisor Partition({0}:HvPt)\1G GPA pages’, #

‘\Hyper-V Hypervisor Partition({0}:HvPt)\2M GPA pages’, #

‘\Hyper-V Hypervisor Partition({0}:HvPt)\4K GPA pages’, #

‘\Hyper-V Hypervisor Partition({0}:HvPt)\Recommended Virtual TLB Size’,

‘\Hyper-V Hypervisor Partition({0}:HvPt)\Virtual TLB Flush Entires/sec’,

‘\Hyper-V Hypervisor Partition({0}:HvPt)\GPA Space Modifications/sec’,

‘\Hyper-V Hypervisor Partition({0}:HvPt)\GPA Pages’,

‘\Hyper-V Hypervisor Partition({0}:HvPt)\Deposited Pages’,

‘\Hyper-V Hypervisor Partition({0}:HvPt)\Address Spaces’,

‘\Hyper-V Hypervisor Partition({0}:HvPt)\Virtual TLB Pages’

#’\Hyper-V Hypervisor Partition({0}:HvPt)\Virtual Processors’

)

$AdvancedChecks2016 = @(

‘\Hyper-V Hypervisor Partition(dtmanage:HvPt)\Nested TLB Trimmed Pages/sec’,

‘\Hyper-V Hypervisor Partition(dtmanage:HvPt)\Nested TLB Free List Size’,

‘\Hyper-V Hypervisor Partition(dtmanage:HvPt)\Recommended Nested TLB Size’,

‘\Hyper-V Hypervisor Partition(dtmanage:HvPt)\Nested TLB Size’

)

$CPUCoreChecks = @(

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\% Guest Run Time’

)

$CPUAdvancedChecks = @(

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\% Remote Run Time’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Total Intercepts Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Total Intercepts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Total Messages/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\% Hypervisor Run Time’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\% Total Run Time’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\CPU Wait Time Per Dispatch’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Logical Processor Dispatches/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Nested Page Fault Intercepts Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Nested Page Fault Intercepts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Hardware Interrupts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Virtual Processor Hypercalls/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Virtual MMU Hypercalls/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Virtual Interrupt Hypercalls/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Synthetic Interrupt Hypercalls/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Other Hypercalls/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Long Spin Wait Hypercalls/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Logical Processor Hypercalls/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\GPA Space Hypercalls/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\APIC Self IPIs Sent/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\APIC IPIs Sent/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Virtual Interrupts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Synthetic Interrupts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Page Table Write Intercepts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\APIC TPR Accesses/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Page Table Validations/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Page Table Resets/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Page Table Reclamations/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Page Table Evictions/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Local Flushed GVA Ranges/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Global GVA Range Flushes/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Address Space Flushes/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Address Domain Flushes/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Address Space Switches/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Address Space Evictions/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Logical Processor Migrations/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Page Table Allocations/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Other Messages/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\APIC EOI Accesses/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Memory Intercept Messages/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\IO Intercept Messages/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\APIC MMIO Accesses/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Reflected Guest Page Faults/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Small Page TLB Fills/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Large Page TLB Fills/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Guest Page Table Maps/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Page Fault Intercepts Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Page Fault Intercepts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Debug Register Accesses Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Debug Register Accesses/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Emulated Instructions Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Emulated Instructions/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Pending Interrupts Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Pending Interrupts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\External Interrupts Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\External Interrupts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Other Intercepts Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Other Intercepts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\MSR Accesses Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\MSR Accesses/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\CPUID Instructions Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\CPUID Instructions/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\MWAIT Instructions Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\MWAIT Instructions/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\HLT Instructions Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\HLT Instructions/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\IO Instructions Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\IO Instructions/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Control Register Accesses Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Control Register Accesses/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Page Invalidations Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Page Invalidations/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Hypercalls Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Hypercalls/sec’

)

$CPUAdvanced2016Checks = @(

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Total Virtualization Instructions Emulation Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Total Virtualization Instructions Emulated/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Flush Physical Address List Hypercalls/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Flush Physical Address Space Hypercalls/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Nested TLB Page Table Evictions/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Nested TLB Page Table Reclamations/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\InvVpid Single Address Instruction Emulation Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\InvVpid Single Address Emulation Intercepts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\InvVpid Single Context Instruction Emulation Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\InvVpid Single Context Emulation Intercepts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\InvVpid All Context Instruction Emulation Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\InvVpid All Context Emulation Intercepts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\InvEpt Single Context Instruction Emulation Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\InvEpt Single Context Emulation Intercepts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\InvEpt All Context Instruction Emulation Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\InvEpt All Context Emulation Intercepts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Nested SLAT Hard Page Faults Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Nested SLAT Hard Page Faults/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Nested SLAT Soft Page Faults Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Nested SLAT Soft Page Faults/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Nested VM Entries Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Nested VM Entries/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\VMXON Instruction Emulation Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\VMXON Emulation Intercepts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\VMXOFF Instruction Emulation Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\VMXOFF Emulation Intercepts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\VMWRITE Instruction Emulation Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\VMWRITE Emulation Intercepts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\VMREAD Instruction Emulation Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\VMREAD Emulation Intercepts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\VMPTRST Instruction Emulation Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\VMPTRST Emulation Intercepts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\VMPTRLD Instruction Emulation Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\VMPTRLD Emulation Intercepts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\VMCLEAR Instruction Emulation Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\VMCLEAR Emulation Intercepts/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Page Fault Intercepts Forwarding Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Page Fault Intercepts Forwarded/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Debug Register Accesses Forwarding Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Debug Register Accesses Forwarded/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Emulated Instructions Forwarding Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Emulated Instructions Forwarded/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Pending Interrupts Forwarding Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Pending Interrupts Forwarded/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\External Interrupts Forwarded/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Other Intercepts Forwarding Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Other Intercepts Forwarded/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\MSR Accesses Forwarding Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\MSR Accesses Forwarded/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\CPUID Instructions Forwarding Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\CPUID Instructions Forwarded/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\MWAIT Instructions Forwarding Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\MWAIT Instructions Forwarded/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\HLT Instructions Forwarding Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\HLT Instructions Forwarded/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\IO Instructions Forwarding Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\IO Instructions Forwarded/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Control Register Accesses Forwarding Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Control Register Accesses Forwarded/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Page Invalidations Forwarding Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Page Invalidations Forwarded/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Hypercalls Forwarding Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Hypercalls Forwarded/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Local I/O TLB Flush Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Local I/O TLB Flushes/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Global I/O TLB Flush Cost’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Global I/O TLB Flushes/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Other Reflected Guest Exceptions/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\MBEC Nested Page Table Switches/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Extended Hypercall Intercept Messages/sec’,

‘\Hyper-V Hypervisor Virtual Processor({0}:Hv VP {1})\Extended Hypercalls/sec’

)

$DiskCoreChecks = @(

‘\Hyper-V Virtual Storage Device({0})\Queue Length’,

‘\Hyper-V Virtual Storage Device({0})\Normalized Throughput’,

‘\Hyper-V Virtual Storage Device({0})\Write Operations/Sec’,

‘\Hyper-V Virtual Storage Device({0})\Read Operations/Sec’,

‘\Hyper-V Virtual Storage Device({0})\Write Bytes/sec’,

‘\Hyper-V Virtual Storage Device({0})\Read Bytes/sec’

)

$DiskAdvancedChecks = @(

‘\Hyper-V Virtual Storage Device({0})\Error Count’,

‘\Hyper-V Virtual Storage Device({0})\Flush Count’,

‘\Hyper-V Virtual Storage Device({0})\Write Count’,

‘\Hyper-V Virtual Storage Device({0})\Read Count’

)

$DiskAdvanced2012R2Checks = @(

‘\Hyper-V Virtual Storage Device({0})\Quota Replenishment Rate’

)

$DiskAdvanced2016Checks = @(

‘Hyper-V Virtual Storage Device({0})\Maximum Bandwidth’,

‘Hyper-V Virtual Storage Device({0})\Byte Quota Replenishment Rate’,

‘Hyper-V Virtual Storage Device({0})\Io Quota Replenishment Rate’,

‘Hyper-V Virtual Storage Device({0})\Lower Latency’,

‘Hyper-V Virtual Storage Device({0})\Minimum IO Rate’,

‘Hyper-V Virtual Storage Device({0})\Maximum IO Rate’,

‘Hyper-V Virtual Storage Device({0})\Latency’,

‘Hyper-V Virtual Storage Device({0})\Throughput’,

‘Hyper-V Virtual Storage Device({0})\Lower Queue Length’

)

$DynamicMemoryChecks = @( # same for 2012R2 and 2016, but always works on 2016

‘\Hyper-V Dynamic Memory VM({0})\Maximum Pressure’,

‘\Hyper-V Dynamic Memory VM({0})\Minimum Pressure’,

‘\Hyper-V Dynamic Memory VM({0})\Average Pressure’,

‘\Hyper-V Dynamic Memory VM({0})\Current Pressure’

)

$DynamicMemoryDMOnlyChecks = @(

‘\Hyper-V Dynamic Memory VM({0})\Physical Memory’, # –

‘\Hyper-V Dynamic Memory VM({0})\Guest Visible Physical Memory’ # –

)

$DynamicMemoryChecks2016 = @(

‘\Hyper-V Dynamic Memory VM({0})\Memory Remove Operations’,

‘\Hyper-V Dynamic Memory VM({0})\Removed Memory’,

‘\Hyper-V Dynamic Memory VM({0})\Memory Add Operations’,

‘\Hyper-V Dynamic Memory VM({0})\Added Memory’

)

$IDEChecks = @(

‘\Hyper-V Virtual IDE Controller (Emulated)({0}:Ide Controller)\Write Bytes/sec’,

‘\Hyper-V Virtual IDE Controller (Emulated)({0}:Ide Controller)\Read Bytes/sec’,

‘\Hyper-V Virtual IDE Controller (Emulated)({0}:Ide Controller)\Written Sectors/sec’,

‘\Hyper-V Virtual IDE Controller (Emulated)({0}:Ide Controller)\Read Sectors/sec’

)

$LiveMigrationChecks = @(

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Receiver: Decompressed Bytes/sec’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Receiver: Maximum Threadpool Thread Count’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Receiver: Uncompressed Bytes Received/sec’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Receiver: Bytes Pending Write’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Receiver: Bytes Written/sec’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Memory Walker: Uncompressed Bytes Sent/sec’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Memory Walker: Uncompressed Bytes Sent’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Memory Walker: Bytes Read/sec’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Memory Walker: Maximum Threads’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\TCP Transport: Bytes Received/sec’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\TCP Transport: Bytes Pending Processing’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\TCP Transport: Posted Receive Buffer Count’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\TCP Transport: Bytes Sent/sec’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\TCP Transport: Bytes Pending Send’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\TCP Transport: Pending Send Count’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\TCP Transport: Total buffer count’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Transfer pass: CPU Cap’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Transfer pass: Dirty Page Count’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Transfer Pass: Is blackout’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Transfer Pass: Number’

)

$LiveMigrationCompressionChecks = @(

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Receiver: Bytes Pending Decompression’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Receiver: Compressed Bytes Received/sec’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Memory Walker: Bytes Sent for Compression/sec’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Memory Walker: Bytes Sent for Compression’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Compressor: Enabled Threads’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Compressor: Maximum Threads’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Compressor: Compressed Bytes Sent/sec’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Compressor: Compressed Bytes Sent’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\Compressor: Bytes to be Compressed’

)

$LiveMigrationSMBChecks = @( # same for 2012R2 and 2016

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\SMB Transport: Bytes Sent/sec’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\SMB Transport: Bytes Sent’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\SMB Transport: Pending Send Bytes’,

‘\Hyper-V VM Live Migration({0}:VM Live Migration)\SMB Transport: Pending Send Count’

)

$NetworkCoreChecks = @(

‘\Hyper-V Virtual Network Adapter({0}_Network Adapter_{1}–{2})\Bytes Sent/sec’,

‘\Hyper-V Virtual Network Adapter({0}_Network Adapter_{1}–{2})\Bytes Received/sec’

)

$NetworkAdvancedChecks = @(

‘\Hyper-V Virtual Network Adapter({0}_Network Adapter_{1}–{2})\Extensions Dropped Packets Outgoing/sec’,

‘\Hyper-V Virtual Network Adapter({0}_Network Adapter_{1}–{2})\Extensions Dropped Packets Incoming/sec’,

‘\Hyper-V Virtual Network Adapter({0}_Network Adapter_{1}–{2})\Dropped Packets Outgoing/sec’,

‘\Hyper-V Virtual Network Adapter({0}_Network Adapter_{1}–{2})\Dropped Packets Incoming/sec’,

‘\Hyper-V Virtual Network Adapter({0}_Network Adapter_{1}–{2})\IPsec offload Bytes Receive/sec’,

‘\Hyper-V Virtual Network Adapter({0}_Network Adapter_{1}–{2})\IPsec offload Bytes Sent/sec’,

‘\Hyper-V Virtual Network Adapter({0}_Network Adapter_{1}–{2})\Directed Packets Sent/sec’,

‘\Hyper-V Virtual Network Adapter({0}_Network Adapter_{1}–{2})\Directed Packets Received/sec’,

‘\Hyper-V Virtual Network Adapter({0}_Network Adapter_{1}–{2})\Broadcast Packets Sent/sec’,

‘\Hyper-V Virtual Network Adapter({0}_Network Adapter_{1}–{2})\Broadcast Packets Received/sec’,

‘\Hyper-V Virtual Network Adapter({0}_Network Adapter_{1}–{2})\Multicast Packets Sent/sec’,

‘\Hyper-V Virtual Network Adapter({0}_Network Adapter_{1}–{2})\Multicast Packets Received/sec’,

‘\Hyper-V Virtual Network Adapter({0}_Network Adapter_{1}–{2})\Packets Sent/sec’,

‘\Hyper-V Virtual Network Adapter({0}_Network Adapter_{1}–{2})\Packets Received/sec’,

‘\Hyper-V Virtual Network Adapter({0}_Network Adapter_{1}–{2})\Packets/sec’,

‘\Hyper-V Virtual Network Adapter({0}_Network Adapter_{1}–{2})\Bytes/sec’

)

$NetworkDropReasonChecks = @(

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing LowPowerPacketFilter’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming LowPowerPacketFilter’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing InvalidPDQueue’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming InvalidPDQueue’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing FilteredIsolationUntagged’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming FilteredIsolationUntagged’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing SwitchDataFlowDisabled’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming SwitchDataFlowDisabled’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing FailedPacketFilter’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming FailedPacketFilter’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing NicDisabled’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming NicDisabled’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing FailedDestinationListUpdate’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming FailedDestinationListUpdate’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing InjectedIcmp’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming InjectedIcmp’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing StormLimit’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming StormLimit’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing Wnv’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming Wnv’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing InvalidFirstNBTooSmall’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming InvalidFirstNBTooSmall’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing InvalidSourceMac’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming InvalidSourceMac’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing InvalidDestMac’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming InvalidDestMac’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing InvalidVlanFormat’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming InvalidVlanFormat’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing NativeFwdingReq’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming NativeFwdingReq’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing MTUMismatch’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming MTUMismatch’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing InvalidConfig’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming InvalidConfig’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing RequiredExtensionMissing’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming RequiredExtensionMissing’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing VirtualSubnetId’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming VirtualSubnetId’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing BridgeReserved’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming BridgeReserved’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing RouterGuard’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming RouterGuard’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing DhcpGuard’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming DhcpGuard’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing MacSpoofing’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming MacSpoofing’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing Ipsec’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming Ipsec’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing Qos’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming Qos’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing FailedPvlanSetting’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming FailedPvlanSetting’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing FailedSecurityPolicy’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming FailedSecurityPolicy’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing UnauthorizedMAC’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming UnauthorizedMAC’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing UnauthorizedVLAN’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming UnauthorizedVLAN’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing FilteredVLAN’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming FilteredVLAN’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing Filtered’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming Filtered’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing Busy’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming Busy’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing NotAccepted’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming NotAccepted’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing Disconnected’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming Disconnected’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing NotReady’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming NotReady’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing Resources’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming Resources’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing InvalidPacket’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming InvalidPacket’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing InvalidData’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming InvalidData’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Outgoing Unknown’,

‘\Hyper-V Virtual Network Adapter Drop Reasons({0}_Network Adapter_{1}–{2})\Incoming Unknown’

)

$SaveSnapChecks = @( # same for 2012R2 and 2016

‘\Hyper-V VM Save, Snapshot, and Restore({0}:Current or most recent Save-Restore-Snapshot operation)\Operation Time’,

‘\Hyper-V VM Save, Snapshot, and Restore({0}:Current or most recent Save-Restore-Snapshot operation)\Requests High Priority’,

‘\Hyper-V VM Save, Snapshot, and Restore({0}:Current or most recent Save-Restore-Snapshot operation)\Requests Processed’,

‘\Hyper-V VM Save, Snapshot, and Restore({0}:Current or most recent Save-Restore-Snapshot operation)\Requests Dispatched’,

‘\Hyper-V VM Save, Snapshot, and Restore({0}:Current or most recent Save-Restore-Snapshot operation)\Requests Active’,

‘\Hyper-V VM Save, Snapshot, and Restore({0}:Current or most recent Save-Restore-Snapshot operation)\Threads Spawned’

)

$SmartPagingChecks = @( # same for 2012R2 and 2016

‘\Hyper-V Dynamic Memory VM({0})\Smart Paging Working Set Size’

)

$VidChecks = @( # same for 2012R2 and 2016

‘\Hyper-V VM Vid Partition({0})\Remote Physical Pages’,

‘\Hyper-V VM Vid Partition({0})\Preferred NUMA Node Index’,

‘\Hyper-V VM Vid Partition({0})\Physical Pages Allocated’

)

#$VirtualDevicePipeChecks = @( # 2016 only — uncertain where the GUIDs in {2}

# ‘\Hyper-V VM Virtual Device Pipe IO({0}:{1}-{{{2}}})\Receive Message Quota Exceeded’,

# ‘\Hyper-V VM Virtual Device Pipe IO({0}:{1}-{{{2}}})\Receive QoS – Total Message Delay Time (100ns)’,

# ‘\Hyper-V VM Virtual Device Pipe IO({0}:{1}-{{{2}}})\Receive QoS – Exempt Messages/sec’,

# ‘\Hyper-V VM Virtual Device Pipe IO({0}:{1}-{{{2}}})\Receive QoS – Non-Conformant Messages/sec’,

# ‘\Hyper-V VM Virtual Device Pipe IO({0}:{1}-{{{2}}})\Receive QoS – Conformant Messages/sec’

#)

#$VMBusProviderChecks = @( # 2016 only — uncertain where the GUIDs in {2}

# ‘\Hyper-V Virtual Machine Bus Provider Pipes({0}:{1}-{{{2}}})\Bytes Written/sec’,

# ‘\Hyper-V Virtual Machine Bus Provider Pipes({0}:{1}-{{{2}}})\Bytes Read/sec’,

# ‘\Hyper-V Virtual Machine Bus Provider Pipes({0}:{1}-{{{2}}})\Writes/sec’,

# ‘\Hyper-V Virtual Machine Bus Provider Pipes({0}:{1}-{{{2}}})\Reads/sec’

#)

$VMWorkerProcessChecks = @( # 2016 only

‘\Hyper-V Worker Virtual Processor({0}:WP VP {1})\Intercepts Delayed’,

‘\Hyper-V Worker Virtual Processor({0}:WP VP {1})\Intercept Delay Time (ms)’

)

$VRSSChecks = @( # 2016 only

‘\Hyper-V Virtual Network Adapter VRSS({0}_Network Adapter_Entry _{1}_{2}–{3})\SendPacketCompletionsPerSecond’,

‘\Hyper-V Virtual Network Adapter VRSS({0}_Network Adapter_Entry _{1}_{2}–{3})\SendPacketPerSecond’,

‘\Hyper-V Virtual Network Adapter VRSS({0}_Network Adapter_Entry _{1}_{2}–{3})\ReceivePacketPerSecond’,

‘\Hyper-V Virtual Network Adapter VRSS({0}_Network Adapter_Entry _{1}_{2}–{3})\SendProcessor’,

‘\Hyper-V Virtual Network Adapter VRSS({0}_Network Adapter_Entry _{1}_{2}–{3})\ReceiveProcessor’

)

$RemotingChecks = @( # 2016 only

‘\Hyper-V VM Remoting({0}:Remoting)\Updated Pixels/sec’,

‘\Hyper-V VM Remoting({0}:Remoting)\Connected Clients’

)

}

process

{

Write-Progress -Activity ‘Processing Input’ -Status ‘Processing Input’

$RawVMList = New-Object -TypeName System.Collections.ArrayList

if ($VM)

{

$InputType = $VM[0].GetType().FullName

if ($InputType -eq ‘Microsoft.HyperV.PowerShell.VirtualMachine’)

{

$RawVMList.AddRange((Get-CimInstance -ComputerName $VM.ComputerName -Namespace ‘root/virtualization/v2’ -ClassName ‘Msvm_ComputerSystem’ -Filter (‘Name=”{0}”‘ -f $VM.Id)))

}

elseif ($InputType -eq ‘System.String’)

{

foreach ($VMItem in $VM)

{

$OutNull = $RawVMList.Add((Get-CimInstance -ComputerName $ComputerName -Namespace ‘root/virtualization/v2’ -ClassName ‘Msvm_ComputerSystem’ -Filter (‘ElementName=”{0}”‘ -f $VMItem)))

}

}

elseif ($InputType -eq ‘Microsoft.Management.Infrastructure.CimInstance’ -and $VM[0].CreationClassName -eq ‘Msvm_ComputerSystem’)

{

$RawVMList.AddRange($VM)

}

elseif ($InputType -eq ‘System.Management.ManagementObject’)

{

foreach ($VMItem in $VM)

{

$OutNull = $RawVMList.Add((Get-CimInstance -ComputerName $VMItem.__SERVER -Namespace ‘root/virtualization/v2’ -ClassName ‘Msvm_ComputerSystem’ -Filter (‘Name=”{0}”‘ -f $VMItem.Name)))

}

}

else

{

throw(‘Unable to process VM objects of type {0}’ -f $InputType)

}

}

else

{

$RawVMList.AddRange((Get-CimInstance -ComputerName $ComputerName -Namespace ‘root/virtualization/v2’ -ClassName ‘Msvm_ComputerSystem’))

}

switch ($LineEndFormat[0].ToString().ToLower())

{

‘l’ { $LineEnding = “`n” }

‘m’ { $LineEnding = “`r” }

default { $LineEnding = “`r`n” }

}

Write-Progress -Activity ‘Processing Input’ -Status ‘Loading Virtual Machines’ -Completed

if (-not $RawVMList.Count)

{

Write-Debug -Message ‘No VMs specified in this pass’

return

}

$SanitizedVMList = @($RawVMList | Where-Object -Property ‘Name’ -Match $GUIDPattern)

if ($SanitizedVMList)

{

$VMList.AddRange($SanitizedVMList)

}

else

{

Write-Debug -Message ‘A Hyper-V host was specified, but no VMs were found. Verify your permissions.’

return

}

}

end

{

if (-not $VMList.Count)

{

Write-Warning -Message ‘No VMs found’

return

}

$PercentTracker = 0

$ProcessPercentagePerVM = 100 * (1 / $VMList.Count)

Write-Progress -Activity ‘Retrieving Virtual Machine Information’ -Status ‘Loading List’ -PercentComplete $PercentTracker

foreach ($TargetVM in $VMList)

{

$PercentTracker += $ProcessPercentagePerVM

Write-Progress -Activity ‘Retrieving Virtual Machine Information’ -Status $TargetVM.ElementName -PercentComplete $PercentTracker

$HostName = $TargetVM.ComputerName

try

{

$VMSD = Get-CimAssociatedInstance -InputObject $TargetVM -ResultClassName ‘Msvm_VirtualSystemSettingData’

$VMCPUData = Get-CimAssociatedInstance -InputObject $VMSD -ResultClassName ‘Msvm_ProcessorSettingData’

$VMDisks = Get-CimAssociatedInstance -InputObject $VMSD -ResultClassName ‘Msvm_StorageAllocationSettingData’ | where ResourceSubType -eq ‘Microsoft:Hyper-V:Virtual Hard Disk’

$VMMemory = (Get-CimAssociatedInstance -InputObject $VMSD -ResultClassName ‘Msvm_MemorySettingData’)[0]

$VMIsClustered = [bool](

[bool]((Get-CimAssociatedInstance -InputObject $VMSD -ResultClassName ‘Msvm_KvpExchangeComponentSettingData’).HostOnlyItems) -and

(Get-CimAssociatedInstance -InputObject $VMSD -ResultClassName ‘Msvm_KvpExchangeComponentSettingData’).HostOnlyItems[0].Contains(‘VirtualMachineIsClustered’)

)

$HostName = (Get-CimAssociatedInstance -InputObject $TargetVM -ResultClassName ‘Msvm_VirtualSystemManagementService’).SystemName

if ([String]::IsNullOrEmpty($Version))

{

$Version = ‘2012R2’

$DetectedVersion = (Get-CimInstance -ComputerName $HostName -ClassName ‘Win32_OperatingSystem’).Version

if ($DetectedVersion -match ‘^10.’) { $Version = ‘2016’ }

}

$VMMigrationSettings = Get-CimInstance -ComputerName $HostName -Namespace root/virtualization/v2 -ClassName Msvm_VirtualSystemMigrationServiceSettingData

if ($VMIsClustered)

{

$HostName = (Get-CimInstance -ComputerName $HostName -Namespace ‘root/MSCluster’ -ClassName ‘MSCluster_Cluster’).Name

}

}

catch

{

Write-Warning -Message (‘An error occurred while retrieving data for {0}’ -f $TargetVM.ElementName)

Write-Error $_

continue

}

if ($ResolveHost)

{

try

{

$HostName = (Resolve-DnsName -Name $HostName).IPAddress

}

catch

{

Write-Warning -Message (‘Cannot resolve {0} to an IP address. Using “{0}” for the generated service target’ -f $HostName)

}

}

elseif ($UpperCaseHostName)

{

$HostName = $HostName.ToUpper()

}

$ServiceObjectParameters =

@{

‘HostName’  = $HostName;

‘VMName’    = $TargetVM.ElementName;

‘Clustered’ = $VMIsClustered;

}

$VMNetAdapters = Get-CimAssociatedInstance -InputObject $TargetVM -ResultClassName ‘Msvm_SyntheticEthernetPort’

if ($IncludeAdvancedCounters)

{

$ChecksList = $AdvancedChecks

if ($Version -eq ‘2016’) { $ChecksList += $AdvancedChecks2016 }

foreach ($AdvancedCounter in $AdvancedChecks)

{

$OutNull = $CounterList.Add((New-ServiceObject @ServiceObjectParameters -CounterCategory ‘VM’ -Counter ($AdvancedCounter -f $TargetVM.ElementName)))

}

}

if (-not $SkipCPU)

{

$ChecksList = $CPUCoreChecks

if ($IncludeAdvancedCounters)

{

$ChecksList += $CPUAdvancedChecks

if ($Version -eq ‘2016’) { $ChecksList += $CPUAdvanced2016Checks }

}

if ($Version -eq ‘2016’ -and $IncludeVmWorkerProcess) { $ChecksList += $VMWorkerProcessChecks }

for ($i = 0; $i -lt $VMCPUData.VirtualQuantity; $i++)

{

foreach ($CPUCounter in $ChecksList)

{

$OutNull = $CounterList.Add((New-ServiceObject @ServiceObjectParameters -CounterCategory ([String]::Join(‘ ‘, ‘vCPU’, $i)) -Counter ($CPUCounter -f $TargetVM.ElementName, $i)))

}

}

}

if (-not $SkipDisk)

{

foreach ($VMDisk in $VMDisks)

{

$ChecksList = $DiskCoreChecks

if ($IncludeAdvancedCounters) { $ChecksList += $DiskAdvancedChecks }

if ($IncludeAdvancedCounters -and $Version -eq ‘2012R2’) { $ChecksList += $DiskAdvanced2012R2Checks }

if ($IncludeAdvancedCounters -and $Version -eq ‘2016’) { $ChecksList += $DiskAdvanced2016Checks }

$DiskName = $VMDisk.HostResource[0]

$DiskCounterPath = $DiskName -replace ‘^\\’, ‘–?-UNC-‘

$DiskCounterPath = $DiskCounterPath -replace ‘\’, ‘-‘

$CounterCategory = [String]::Join(‘ ‘, ‘vDisk’, $DiskName.Substring($DiskName.LastIndexOf() + 1))

foreach ($DiskCounter in $DiskCoreChecks)

{

$OutNull = $CounterList.Add((New-ServiceObject @ServiceObjectParameters -CounterCategory $CounterCategory -Counter ($DiskCounter -f $DiskCounterPath)))

}

}

}

if (-not $SkipDynamicMemory)

{

$ChecksList = @()

if ($Version -eq ‘2016’ -or $VMMemory.DynamicMemoryEnabled) { $ChecksList += $DynamicMemoryChecks }

if ($VMMemory.DynamicMemoryEnabled) { $ChecksList += $DynamicMemoryDMOnlyChecks }

if ($Version -eq ‘2016’ -and $VMMemory.DynamicMemoryEnabled) { $ChecksList += $DynamicMemoryChecks2016 }

foreach ($DynamicMemoryCounter in $ChecksList)

{

$OutNull = $CounterList.Add((New-ServiceObject @ServiceObjectParameters -CounterCategory ‘vMemory’ -Counter ($DynamicMemoryCounter -f $TargetVM.ElementName)))

}

}

if ($VMSD.VirtualSystemSubType -eq ‘Microsoft:Hyper-V:SubType:1’ -and -not $SkipIDE)

{

foreach ($IDECount in $IDEChecks)

{

$OutNull = $CounterList.Add((New-ServiceObject @ServiceObjectParameters -CounterCategory ‘vIDE’ -Counter ($IDECount -f $TargetVM.ElementName)))

}

}

if ($VMIsClustered -and $IncludeLiveMigration)

{

$ChecksList = $LiveMigrationChecks

if ($VMMigrationSettings.EnableCompression) { $ChecksList += $LiveMigrationCompressionChecks }

if ($VMMigrationSettings.EnableSmbTransport) { $ChecksList += $LiveMigrationSMBChecks }

foreach ($LiveMigrationCounter in $LiveMigrationChecks)

{

$OutNull = $CounterList.Add((New-ServiceObject @ServiceObjectParameters -CounterCategory ‘Live Migration’ -Counter ($LiveMigrationCounter -f $TargetVM.ElementName)))

}

}

if ($VMNetAdapters -and -not $SkipNetwork)

{

foreach ($VMNetAdapter in $VMNetAdapters)

{

$FoundAdapterIDs = $false

if ($VMNetAdapter.DeviceId -match $GUIDPattern)

{

$DeviceID = $Matches[0].ToLower()

$FoundAdapterIDs = $true

}

if ($FoundAdapterIDs -and $VMNetAdapter.SystemName -match $GUIDPattern)

{

$SystemName = $Matches[0].ToLower()

$FoundAdapterIDs = $true

}

if ($FoundAdapterIDs)

{

$ChecksList = $NetworkCoreChecks

if ($IncludeAdvancedCounters) { $ChecksList += $NetworkAdvancedChecks }

if ($Version -eq ‘2016’ -and $IncludeNetworkDropReasons) { $ChecksList += $NetworkDropReasonChecks }

foreach ($NetworkCounter in $ChecksList)

{

$OutNull = $CounterList.Add((New-ServiceObject @ServiceObjectParameters -CounterCategory ‘vNIC’ -Counter ($NetworkCounter -f $TargetVM.ElementName, $SystemName, $DeviceID)))

}

if ($Version -eq ‘2016’ -and $IncludeVRSS)

{

foreach ($VP in @(0..15))

{

foreach ($VRSSCheck in $VRSSChecks)

{

$OutNull = $CounterList.Add((New-ServiceObject @ServiceObjectParameters -CounterCategory (‘vRSS VP {0}’ -f $VP) -Counter ($VRSSCheck -f $TargetVM.ElementName, $VP, $SystemName, $DeviceID)))

}

}

}

}

}

}

if ($IncludeSavesSnaps)

{

foreach ($SaveSnapCounter in $SaveSnapChecks)

{

$OutNull = $CounterList.Add((New-ServiceObject @ServiceObjectParameters -CounterCategory ‘Save and Snapshot’ -Counter ($SaveSnapCounter -f $TargetVM.ElementName)))

}

}

if ($IncludeSmartPaging)

{

foreach ($SmartPagingCounter in $SmartPagingChecks)

{

$OutNull = $CounterList.Add((New-ServiceObject @ServiceObjectParameters -CounterCategory ‘Smart Paging’ -Counter ($SmartPagingCounter -f $TargetVM.ElementName)))

}

}

if ($IncludeNUMA)

{

foreach ($VidCounter in $VidChecks)

{

$OutNull = $CounterList.Add((New-ServiceObject @ServiceObjectParameters -CounterCategory ‘VID’ -Counter ($VidCounter -f $TargetVM.ElementName)))

}

}

if ($Version -eq ‘2016’ -and $IncludeRemotingChecks)

{

foreach ($RemotingCheck in $RemotingChecks)

{

$OutNull = $CounterList.Add((New-ServiceObject @ServiceObjectParameters -CounterCategory ‘Remoting’ -Counter ($RemotingCheck -f $TargetVM.ElementName)))

}

}

}

Write-Progress -Activity ‘Retrieving Virtual Machine Information’ -Completed

Write-Progress -Activity ‘Writing Service Definitions’ -Status ‘Initializing’

try

{

$OutStream = [System.IO.StreamWriter]::New($Path)

if ($VMAsHost -and $CreateVMHostDefinition)

{

foreach ($TargetVM in $VMList)

{

$OutStream.Write((New-HostDefinition -ComputerName $TargetVM.ElementName -HostTemplate $VMHostTemplate -UseSkeletonHost $UseSkeletonHost.ToBool() -HostGroups $VMHostGroups -LineEnding $LineEnding -HostContacts $VMHostContacts -HostContactGroups $VMHostContactGroups))

}

}

foreach ($CounterItem in $CounterList)

{

$ServiceText = New-ServiceDefinition -ServiceObject $CounterItem -LineEnding $LineEnding -ServiceTemplate $ServiceTemplate -ServiceGroups $ServiceGroups -ServiceContacts $ServiceContacts -ServiceContactGroups $ServiceContactGroups -VMAsHost $VMAsHost.ToBool()

foreach ($ServiceTextLine in $ServiceText)

{

$OutStream.Write($ServiceTextLine)

}

}

}

catch

{

$_

}

finally

{

$OutStream.Close()

}

Write-Progress -Activity ‘Writing Service Definitions’ -Completed

}

A smaller Windows Server Core Container with better Application Compatibility

In Windows Server Insider Preview Build 17074 released on Tuesday Jan 16, 2018, there are some exciting improvements to Windows Server containers that we’d like to share with you.  We’d love for you to test out the build, especially the Windows Server Core container image, and give us feedback!

Windows Server Core Container Base Image Size Reduced to 1.58GB!

You told us that the size of the Server Core container image affects your deployment times, takes too long to pull down and takes up too much space on your laptops and servers alike.  In our first Semi-Annual Channel release, Windows Server, version 1709, we made some great progress reducing the size by 60% and your excitement was noted.  We’ve continued to actively look for additional space savings while balancing application compatibility. It’s not easy but we are committed.

There are two main directions we looked at:

1)      Architecture optimization to reduce duplicate payloads

 We are always looking for way to optimize our architecture. In Windows Server, version 1709 along with the substantial reduction in Server Core container image, we also made some substantial reductions in the Nano Server container image (dropping it below 100MB).  In doing that work we identified that some of the same architecture could be leveraged with Server Core container. In partnership with other teams in Windows, we were able to implement changes in our build process to take advantage of those improvements.  The great part about this work is that you should not notice any differences in application compatibility or experiences other than a nice reduction in size and some performance improvements.

2)      Removing unused optional components

We looked at all the various roles, features and optional components available in Server Core and broke them down into a few buckets in terms of usage:  frequently in containers, rarely in containers, those that we don’t believe are being used and those that are not supported in containers.  We leveraged several data sources to help categorize this list. First, those of you that have telemetry enabled, thank you! That anonymized data is invaluable to these exercises. Second was publicly available dockerfiles/images and of course feedback from GitHub issues and forums.  Third, the roles or features that are not even supported in containers were easy to make a call and remove. Lastly, we also removed roles and features we do not see evidence of customers using.  We could do more in this space in the future but really need your feedback (telemetry is also very much appreciated) to help guide what can be removed or separated.

So, here are the numbers on Windows Server Core container size if you are curious:

  • 1.58GB, download size, 30% reduction from Windows Server, version 1709
  • 3.61GB, on disk size, 20% reduction from Windows Server, version 1709

MSMQ now installs in a Windows Server Core container

MSMQ has been one of the top asks we heard from you, and ranks very high on Windows Server User Voice here. In this release, we were able to partner with our Kernel team and make the change which was not trivial. We are happy to announce now it installs! And passed our in-house Application Compatibility test. Woohoo!

However, there are many different use cases and ways customers have used MSMQ. So please do try it out and let us know if it indeed works for you.

A Few Other Key App Compatibility Bug Fixes:

  • We fixed the issue reported on GitHub that services running in containers do not receive shutdown notification.

https://github.com/moby/moby/issues/25982

  • We fixed this issue reported on GitHub and User Voice related to BitLocker and FDVDenyWriteAccess policy: Users were not able to run basic Docker commands like Docker Pull.

https://github.com/Microsoft/Virtualization-Documentation/issues/530

https://github.com/Microsoft/Virtualization-Documentation/issues/355

https://windowsserver.uservoice.com/forums/304624-containers/suggestions/18544312-fix-docker-load-pull-build-issue-when-bitlocker-is

  • We fixed a few issues reported on GitHub related to mounting directories between hosts and containers.

https://github.com/moby/moby/issues/30556

https://github.com/git-for-windows/git/issues/1007

We are so excited and proud of what we have done so far to listen to your voice, continuously optimize Server Core container size and performance, and fix top application compatibility issues to make your Windows Container experience better and meet your business needs better. We love hearing how you are using Windows containers, and we know there is still plenty of opportunities ahead of us to make them even faster and better. Fun journey ahead of us!

Thank you.

Weijuan

The Actual Performance Impact of Spectre/Meltdown Hyper-V Updates

The Spectre and Meltdown vulnerabilities have brought a fair amount of panic and havoc to the IT industry, and for good reason. Hardware manufacturers and operating system authors have been issuing microcode updates and patches in a hurry. As administrators, we need to concern ourselves with three things: the risks of running unpatched systems, the performance hit from patching, and quality control problems with the patches. If you’re skimming, then please pay attention to the section on ensuring that you get the update — not everyone will automatically receive the patches. Below I’ll run down what you need to know to ensure you’re protected plus a benchmark analysis of the performance impact of the recently released update patches.

What are Spectre and Meltdown?

Both Spectre and Meltdown are hardware attacks. Your operating system or hypervisor choice does not affect your vulnerability. It might affect fix availability.

Spectre is a category of assault that exploits a CPU’s “branch prediction” optimizations. A CPU looks at an instruction series that contains a decision point (if condition x then continue along path a, else jump to b) and guesses in advance whether it will follow the “else” code branch or continue along without deviation. The Wikipedia article that I linked contains further links to more details for those interested. Because the Spectre vulnerability encompasses multiple attack vectors, the predictor has more than one vulnerability. All of the ones that I’m aware of involve fooling the CPU into retrieving data from a memory location other than the code’s intended target. Spectre affects almost every computing device in existence. Fixes will affect CPU performance out of necessity.

Meltdown affects Intel and some ARM processors. AMD processors appear to be immune (AMD claims immunity; I do not know of any verified tests with contrary results). As with Spectre, Meltdown exploits target the CPU’s optimization capabilities. You can read the linked Wikipedia article for more information. For a less technical description, Luke posted a great analogy on our MSP blog. Whether you read more or not, you should assume that any running process can read the memory of any other running process. The fix for this problem also includes a performance hit. That hit is potentially worse than Spectre’s — an estimated 5 percent to as much as 30 percent. I’ll revisit that later.

What are the Risks of Running without Patches?

Properly addressing Spectre and Meltdown will require more administrative effort than most other problems. Admins in large and complex environments will need to plan deployments. With unpredictable performance impacts, some will want to wait as long as possible before doing anything. The more avoidant of us will want to just ignore it as much as possible and hope that it just sorts itself out. Eventually, we’ll all have new chips, new operating systems, and redesigned applications that won’t be susceptible to these problems. That won’t be ubiquitous for some time, though. We need more immediate solutions.

In one sense, an unpatched system will be fairly easy to assault. Attacks can be carried out via simple Javascript. Unprivileged user code can access privileged kernel memory. The processor cache is an open book for many of these vulnerabilities. It looks to me as though some attack vectors could be used to read essentially any location in memory, although some other accounts dispute that. These are hardware problems, so in the hypervisor world, they transcend the normal boundaries between virtual machines as well as the management operating system. These threats are serious.

However, they cannot be exploited remotely. Attacking code must be executed directly on the target system. That separates the Spectre and Meltdown vulnerabilities from other nefarious vectors, such as Heartbleed. Furthermore, Spectre-class attacks are a bit of a gamble from the attacker’s side. Even if they could read any location in memory, it will be mostly without context. So, something may look like a password, but a password to what? The clues might be alongside the password or they might not.

So, the risk to your systems is extremely high, but the effort-to-reward ratio for an attacker is also high. I do know of one concern: LSASS keeps the passwords for all logged-on users in memory, in clear text. You should assume that a successful Spectre or Meltdown attack might be able to access those passwords. Also, attacks can come from Javascript in compromised websites. You shouldn’t be browsing from a server at all, so that could help. But what about remote desktop sessions? Do you keep those users off of the general Internet? If not, then any of them could unwittingly risk everyone else on the same host.

I cannot outline your risk profile for you, but I would counsel you to work from the assumption that a compromise of an unpatched system is inevitable.

Should I Patch My Hyper-V Hosts, Guests, or Both?

One of my primary research goals was to determine the effects of only applying OS patches, only applying firmware updates, and doing both. I did not find full, definitive answers. However, it does appear that some kernel protections only require OS updates. Others indicated that they would require firmware updates, but they did not make it clear whether or not a firmware update alone would address the problem or if a combination of firmware and OS updates were necessary. However, there is no question that the best protection only comes from updating your hardware and software.

Also, the guests’ kernels must be made aware of the changes to the physical layer in order to fully utilize their mitigation techniques, which can only be done if both the host and the guest are updated.

Therefore, for the fullest protection, you must:

  • Patch the host
  • Perform host configuration changes
  • Update the firmware on the host
  • Patch the guests
  • Cold boot the guests

Also, be aware that “host” means any Hyper-V host. Windows Server, Hyper-V Server, or desktop Windows — patch them all.

We’ll go over the how-to in a bit. Let’s knock out some other questions first.

What Happens if a Hyper-V VM Updates Before Its Host?

Well, the host isn’t patched, so that’s a problem. Aside from that, nothing bad will happen (generally speaking — outliers always exist). But the guest will not be aware of the changed capabilities of the host if it is not cold booted after the host is updated. Until all of the above has been done, the guest will not have the fullest protection.

What If No BIOS Update is Available?

If your hardware does not have an available update, then you still need to apply all software patches. That will mitigate several kernel attacks.

If you are running Windows/Hyper-V Server 2012 R2 or earlier as your management operating system, I’m sad to say that you cannot do much else. Practice defense-in-depth: if no attacker can access your systems, then they cannot execute an attack. Remember that the patches are still applicable and will help — but don’t lose sight of the hardware-based nature of the problems. If possible, upgrade to 2016.

For 2016, you can restrict which processors and cores that the management operating system and guests use. That helps to confine attacks. The relevant features are not well-documented. I had planned on performing some research and writing an article on them, but none of that has happened yet. In the meantime, you can read Microsoft’s article on using these features to mitigate Meltdown and Spectre attacks on systems that do not have an available hardware update. It contains links to instructions on using those features.

What About Non-OS Software?

Software plays an enormous role in all of this. Application developers can take precautions to protect their cached information. Interpreters and compilers can implement their own mitigations. The January updates included patches for IE and Edge to help block Spectre and Meltdown attacks via their Javascript interpreter. Microsoft has released an update to their C++ compiler that will help prevent Spectre attacks.

Expect your software vendors to be releasing updates. Remember that the nature of the attacks means that otherwise benign software can now be an attack vector.

Quality Control Problems with Spectre and Meltdown Patches

I am aware of three problems in the deployment pipeline for the Spectre and Meltdown fixes from Microsoft.

  1. Older AMD processors (Athlon series) reacted very badly to the initial patch group that contained the Spectre and Meltdown fix. In response, Microsoft stopped offering them while working on a fix. I have a Turion x2 system with Hyper-V that was offered the patch after that, so I believe that Microsoft has now issued a new fix.
  2. Many antivirus applications caused blue screen errors after the fixes were applied. To prevent that, Microsoft implemented a technique that will ensure that no system will receive the update until it has been deliberately flagged as being ready. Pay special attention, as that affects every system!
  3. Some systems simply will not take the update. I wish that I could give you some solid guidance on what to do. I do not know of any tips or tricks particular to these patches. I had one installation problem that I was able to rectify by disabling the “Give me updates for other Microsoft products when I update Windows” option. However, that particular system seemed to have problems of its own apart from the patches, so I cannot say for certain if it was anything special with this patch. You may need to open a ticket with Microsoft support.

How to Ensure that You Receive Microsoft’s Spectre and Meltdown Patches

These patches were deployed in the January 2018 security patch bundle. My system showed it as “2018-01 Security Monthly Rollup for OS Name” with a follow-up Quality Rollup. That deployment vehicle might lead you to believe that just running Windows Update will fix everything. However, that’s not necessarily the case. In order to avoid blue screens caused by incompatible antivirus applications, the updater looks for a particular registry key. If it doesn’t exist, then the updater assumes that the local antivirus is not prepared and will decline to offer the update. Since Microsoft now issues security patches in cumulative roll-up packages, it will decline every subsequent security update as well.

If Your System Runs Antivirus

In this case, “system” means host or guest. If you run an antivirus application, it is up to the antivirus program to update the registry key once it has received an update known to be compatible with the Spectre/Meltdown fixes. If you have an antivirus package and the updater will not offer the January 2018 security pack, contact your antivirus vendor. Do not attempt to force the update. Switch antivirus vendors if you must. Your antivirus situation needs to be squared away before you can move forward.

If Your System Does Not Run Antivirus

Personally, I prefer to run antivirus on my Hyper-V host. I recommend that everyone else run antivirus on their Hyper-V hosts. I’m aware that this is a debate topic and I’m aware of the counter-arguments. I’ll save that for another day.

If you do not run antivirus on your Hyper-V host (or any Windows system), then you will never receive the January 2018 security roll-up or any security roll-up thereafter without taking manual action. Microsoft has left the task of updating the related registry key to the antivirus vendors. No antivirus, no registry update. No registry update, no security patches.

In your system’s registry, navigate to HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsCurrentVersion. If it doesn’t already exist, create a new key named “QualityCompat”. Inside that key, create a new DWORD key-value pair named “cadca5fe-87d3-4b96-b7fb-a231484277cc”. Leave it at its default value of 0x0. You do not need to reboot. The updates will be offered on the next run of Windows Update (pursuant to any applicable group policy/WSUS approvals).

For more information, you can read Microsoft’s official documentation: https://support.microsoft.com/en-us/help/4072699/january-3-2018-windows-security-updates-and-antivirus-software.

How to Apply, Configure, and Verify the Spectre and Meltdown Fixes for Hyper-V and Windows

Microsoft has already published solid walk-throughs:

Things to absolutely not miss in all of that:

  • Without the post-install registry changes, your Hyper-V hosts will not have the strongest protection
  • Depending on your hypervisor and guest configurations, one of the additional registry settings may be necessary in order to
  • Each virtual machine must be COLD-booted after the host has been updated! A simple restart of the guest OS will not be sufficient.
  • Live Migrations between patched/unpatched hosts will likely fail. Keep this in mind when you’re attempting to update clusters.

The Performance Impact of the Spectre and Meltdown Patches on a Hyper-V Host

We’ve all seen the estimations that the Meltdown patch might affect performance in the range of 5 to 30 percent. What we haven’t seen is a reliable data set indicating what happens in a real-world environment. The patches are too new for us to have any meaningful levels of historical data to show. I can tell you that my usage trends are not showing any increase, but that is anecdotal information at best. I also know of another installation (using a radically different hardware, hypervisor, and software set) that has experienced crippling slowdowns.

So, for the time being, we’re going to need to take some benchmarks. For anyone who missed the not-so-subtle point: performance bottlenecks found by benchmarks do not automatically translate to performance bottlenecks in production.

Impact on Storage

From what I’ve seen so far, these patches have the greatest detriment on storage performance. Since storage already tends to be the slowest part of our systems, it makes sense that we’d find the greatest impact there.

First up is Storage Space Direct. Ben Thomas has already provided some benchmarks that show a marked slow down. Pay close attention to the differences, as they are substantial. However, most of us would be more than happy with the “slow” number of 1.3 million IOPS. Those numbers are difficult to read in comparison with builds that would be normative in a small or medium-sized business.

I also found an article detailing the storage performance impact on an Ubuntu system using ZFS in a VMware client. This helps reinforce the point that these problems are not caused by or related to your choice of hypervisor or operating system.

I don’t have an S2D environment of my own to do any comparison. However, I will perform some storage benchmarking on a standard Hyper-V build with local storage and remote Storage Spaces (not S2D) storage.

Performance Benchmarks

I ran a number of performance checks to try to help predict the impact of these updates.

The Build Environment and Methodology

I used the build outlined in our eBook on building a Hyper-V cluster for under $2500. To save you a click, the CPUs are Intel Xeon E3-1225 (Sandy Bridge). We should expect to see some of the more “significant” impacts that Microsoft and Intel warn about.

The tests were performed on one of the compute nodes. Some of the storage tests involved the storage system.

The host runs Windows Server 2016. It has multiple guests with multiple operating systems. I chose three representatives: 1 Windows 2012 R2, 1 Windows 10, and 1 Windows Server 2016. All run GUI Windows because Passmark won’t generate a full CPU report under Core.

None of these systems have the patch yet.

I took the following steps:

  1. Collected CPU and storage benchmarks from the host, a Windows 10 guest, the WS2012R2 guest, and the WS2016 guest. Tests were run separately.
  2. Ran a CPU stress utility on some guests while testing others.
  3. Ran a storage stress utility on some guests while testing others.
  4. I applied available firmware updates to the host and the OS patch to the management OS and all guests.
  5. Repeated steps 1-3 for comparison purposes.

I used these tools:

Testing Phase 1: The Pre-Patch Raw Results

To begin, I ran benchmarks against the host and some of the guests separately. This establishes a baseline.

Pre-Patch CPU Benchmark
Host Windows 10 Guest 2012 R2 Guest 2016 Guest
CPU Mark 7404 4198 4055 4197
Integer Math 8226 4209 4077 4212
Prime Numbers 33 24 21 24
Compression 8208 4084 4028 4103
Physics 492 303 295 315
CPU Single Threaded 1905 1882 1847 1886
Floating Point Math 7371 3738 3646 3522
Extended Instructions (SSE) 283 144 138 131
Encryption 1260 632 592 631
Sorting 5165 2590 2541 2608
Pre-Patch Disk Benchmark
Host (internal) Windows 10 Guest (Remote SS) 2012 R2 Guest (internal) 2016 Guest (Remote SS)
Disk Mark 416 505 355 629
Disk Sequential Read 55 124 44 153
Disk Random Seek +RW 6 4 6 4
Disk Sequential Write 53 11 47 15

Phase 1 Analysis

The primary purpose of phase 1 was to establish a performance baseline. The numbers themselves mean: “the systems can do this when not under load”.

The host has four physical cores. Each virtual machine has two vCPU. As expected, each VM’s CPU performance comes out to about half of the host’s metrics.

Testing Phase 2: Pre-Patch Under CPU Load

Next, I needed to have some idea of how a load affects performance. This system serves no real-world purpose, so I needed to fake a load.

Hyper-V is architected so that loads in the management operating system take priority over the guests (I didn’t always know that, in case you find something that I wrote to the contrary). Therefore, I didn’t feel that simulating a load in the management operating system would provide any value. It will drown out the guests and that’s that.

To test, I ran Prime95 on four virtual machines. I set the torture test to use small FFTs because we’re interested in CPU burn, not memory:

melt-prime95

For whatever reason. that only appeared to run the CPUs up to about 25% actual usage:

melt-CPUusage

The guests all believe that they’re burning full-bore, so this might be due to some optimizations. I thought the load would be higher, but we should not be overly surprised; hypervisors should do a decent job at managing their guests’ CPU usage. No matter what, this generates a load that the host distributed across all physical cores, so it works for what we’re trying to do.

While Prime95 ran in those four VMs, I tested each of the originals’ CPU and disk performance separately:

Pre-Patch CPU Benchmark during CPU Load
Host Windows 10 Guest 2012 R2 Guest 2016 Guest
CPU Mark not tested 1553 1339 1261
Integer Math not tested 1549 1356 1335
Prime Numbers not tested 7 5 5
Compression not tested 1570 1345 1308
Physics not tested 108 96 77
CPU Single Threaded not tested 753 646 564
Floating Point Math not tested 1385 1211 1109
Extended Instructions (SSE) not tested 52 45 44
Encryption not tested 232 205 201
Sorting not tested 985 841 817
Pre-Patch Disk Benchmark during CPU Load
Host (internal) Windows 10 Guest (Remote SS) 2012 R2 Guest (internal) 2016 Guest (Remote SS)
Disk Mark 416 349 385 481
Disk Sequential Read 55 90 51 111
Disk Random Seek +RW 6 3 6 4
Disk Sequential Write> 53 2 48 16

Testing Phase 3: Pre-Patch Under Disk Load

Phase 3 is like phase 2 except that we’re simulating heavy disk I/O load. It’s a little different though because the guests are spread out between local and remote storage.

I used DiskSpd.exe with the following command line:

That’s the default from the documentation with the exception of -d600 and #0. The -d600 sets the duration. I used a five-minute timeout so that I would have enough time to start the stress tool on all of the guests and run the CPU and disk benchmarks. The #0 selects a disk that the VMs have.

Pre-Patch CPU Benchmark during I/O Load
Host Windows 10 Guest 2012 R2 Guest 2016 Guest
CPU Mark not tested 3696 3814 3747
Integer Math not tested 3934 3927 3856
Prime Numbers not tested 13 18 16
Compression not tested 3715 3759 3838
Physics not tested 259 249 267
CPU Single Threaded not tested 1760 1852 1775
Floating Point Math not tested 3415 3476 3375
Extended Instructions (SSE) not tested 129 136 131
Encryption not tested 565 577 544
Sorting not tested 2399 2430 2375
Pre-Patch Disk Benchmark during I/O Load
Host (internal) Windows 10 Guest (Remote SS) 2012 R2 Guest (internal) 2016 Guest (Remote SS)
Disk Mark not tested 76 385 94
Disk Sequential Read not tested 12 57 17
Disk Random Seek +RW not tested 2 6 3
Disk Sequential Write> not tested 5 43 5

Testing Phase 4: Post-Patch Raw Results

Now we’re simply going to repeat the earlier processes on our newly-updated systems. Phase 4 aligns with Phase 1: a straight benchmark of the host and representative guests.

Post-Patch CPU Benchmark
Host Windows 10 Guest 2012 R2 Guest 2016 Guest
CPU Mark 7263 3989 3841 3705
Integer Math 8191 4052 3966 3827
Prime Numbers 34 21 17 18
Compression 8015 3890 3919 3336
Physics 489 303 301 273
CPU Single Threaded 1860 1712 1756 1747
Floating Point Math 7205 3464 3528 3281
Extended Instructions (SSE) 249 135 105 121
Encryption 1188 612 598 560
Sorting 5021 2469 2210 2448
Post-Patch Disk Benchmark
Host (internal) Windows 10 Guest (Remote SS) 2012 R2 Guest (internal) 2016 Guest (Remote SS)
Disk Mark 348 505 269 373
Disk Sequential Read 46 124 33 88
Disk Random Seek +RW 5 4 4 3
Disk Sequential Write> 44 11 36 10

Phase 4 Analysis

We don’t see that much CPU performance slowdown, but we do see some reduction in storage speeds.

Testing Phase 5: Post-Patch Under CPU Load

This test series compares against phase 2. Several VMs

Post-Patch CPU Benchmark during CPU Load
Host Windows 10 Guest 2012 R2 Guest 2016 Guest
CPU Mark not tested 1241 1267 1518
Integer Math not tested 1203 1288 1525
Prime Numbers not tested 5 5 7
Compression not tested 1280 1253 1480
Physics not tested 91 94 116
CPU Single Threaded not tested 603 620 700
Floating Point Math not tested 1087 1118 1325
Extended Instructions (SSE) not tested 43 44 45
Encryption not tested 192 192 239
Sorting not tested 811 802 941
Post-Patch Disk Benchmark during CPU Load
Host (internal) Windows 10 Guest (Remote SS) 2012 R2 Guest (internal) 2016 Guest (Remote SS)
Disk Mark 416 349 325 366
Disk Sequential Read 55 90 41 85
Disk Random Seek +RW 6 3 6 4
Disk Sequential Write> 53 2 42 12

Testing Phase 6: Post-Patch Under Disk Load

This is the post-patch equivalent of phase 3. We’ll test performance while simulating high disk I/O.

Again, I used DiskSpd.exe on several VMs with the following command line:

Post-Patch CPU Benchmark during I/O Load
Host Windows 10 Guest 2012 R2 Guest 2016 Guest
CPU Mark not tested 3152 3104 2920
Integer Math not tested 3192 3268 3106
Prime Numbers not tested 14 14 13
Compression not tested 3163 2654 2361
Physics not tested 215 219 211
CPU Single Threaded not tested 1667 1774 1614
Floating Point Math not tested 2678 2885 2672
Extended Instructions (SSE) not tested 105 95 100
Encryption not tested 476 452 449
Sorting not tested 2015 2086 1999
Post-Patch Disk Benchmark during I/O Load
Host (internal) Windows 10 Guest (Remote SS) 2012 R2 Guest (internal) 2016 Guest (Remote SS)
Disk Mark not tested 40 360 54
Disk Sequential Read not tested 5 50 7
Disk Random Seek +RW not tested 1 5 3
Disk Sequential Write not tested 3 44 4

Consolidated Charts

These charts reorganize the above data to align each result with its system rather than its condition.

Host CPU
Pre Post
CPU Mark 7404 7263
Integer Math 8226 8191
Prime Numbers 33 34
Compression 8208 8015
Physics 492 489
CPU Single Threaded 1905 1860
Floating Point Math 7371 7205
Extended Instructions (SSE) 283 249
Encryption 1260 1188
Sorting 5165 5021
Host Disk (Internal)
Pre Post
Disk Mark 416 348
Disk Sequential Read 55 46
Disk Random Seek +RW 6 5
Disk Sequential Write 53 44
Windows 10 Guest CPU
Pre Pre+CPU Pre+I/O Post Post+CPU Post+I/O
CPU Mark 4198 1553 3696 3989 1241 3152
Integer Math 4209 1549 3934 4052 1203 3192
Prime Numbers 24 7 13 21 5 14
Compression 4084 1570 3715 3890 1280 3163
Physics 303 108 259 303 91 215
CPU Single Threaded 1882 753 1760 1712 603 1667
Floating Point Math 3738 1385 3415 3464 1087 2678
Extended Instructions (SSE) 144 52 129 135 43 105
Encryption 632 232 565 612 192 476
Sorting 2590 985 2399 2469 811 2015
Windows 10 Guest Disk (Remote Storage Spaces)
Pre Pre+CPU Pre+I/O Post Post+CPU Post+I/O
Disk Mark 505 349 76 505 349 40
Disk Sequential Read 124 90 12 124 90 5
Disk Random Seek +RW 4 3 2 4 3 1
Disk Sequential Write 11 2 5 11 2 3
Windows 2012 R2 Guest CPU
Pre Pre+CPU Pre+I/O Post Post+CPU Post+I/O
CPU Mark 4055 1339 3814 3841 1267 3104
Integer Math 4077 1356 3927 3966 1288 3268
Prime Numbers 21 5 18 17 5 14
Compression 4028 1345 3759 3919 1253 2654
Physics 295 96 249 301 94 219
CPU Single Threaded 1847 646 1852 1756 620 1774
Floating Point Math 3646 1211 3476 3528 1118 2885
Extended Instructions (SSE) 138 45 136 105 44 95
Encryption 592 205 577 598 192 452
Sorting 2541 841 2430 2210 802 2086
Windows 2012 R2 Guest Disk (Internal)
Pre Pre+CPU Pre+I/O Post Post+CPU Post+I/O
Disk Mark 355 385 385 269 325 360
Disk Sequential Read 44 51 57 33 41 50
Disk Random Seek +RW 6 6 6 4 6 5
Disk Sequential Write 47 48 43 36 42 44
Windows 2016 Guest CPU
Pre Pre+CPU Pre+I/O Post Post+CPU Post+I/O
CPU Mark 4197 1261 3747 3705 1518 2920
Integer Math 4212 1335 3856 3827 1525 3106
Prime Numbers 24 5 16 18 7 13
Compression 4103 1308 3838 3336 1480 2361
Physics 315 77 267 273 116 211
CPU Single Threaded 1886 564 1775 1747 700 1614
Floating Point Math 3522 1109 3375 3281 1325 2672
Extended Instructions (SSE) 131 44 131 121 45 100
Encryption 631 201 544 560 239 449
Sorting 2608 817 2375 2448 941 1999
Windows 2016 Guest Disk (Remote Storage Spaces)
Pre Pre+CPU Pre+I/O Post Post+CPU Post+I/O
Disk Mark 629 481 94 373 366 54
Disk Sequential Read 153 111 17 88 85 7
Disk Random Seek +RW 4 4 3 3 4 3
Disk Sequential Write 15 16 5 10 12 4

Data Analysis

The analysis has a fairly simple conclusion: some CPU functions degraded noticeably, others did not, and not much changed for I/O. The Windows 2016 guest does have a wide disparity in its disk statistics. Based on the other outcomes, I suspect an environmental difference or that I made some error when running the traces more than I believe that the raw speed degraded by 50%.

Conclusion

What we learn from these charts is that the full range of updates causes CPU performance degradation for some functions but leaves others unaffected. Normal storage I/O does not suffer as much. I imagine that the higher impacts that you see in the Storages Spaces Direct benchmarks shared by Ben Thomas are because of their extremely high speed. Since most of us don’t have storage subsystems that can challenge a normal CPU, we aren’t affected as strongly.

Overall, I think that the results show what could be reasonably inferred without benchmarks: high utilization systems will see a greater impact. As far as the exact impact on your systems, that appears to largely depend on what CPU functions they require most. If your pre-patch conditions were not demanding high performance, I suspect that you’ll suffer very little. If you have higher demand workloads, I’d recommend that you try a test environment if possible. If not, see if your software vendors have any knowledge or strategies for keeping their applications running acceptably.

Tell Me Your Experiences

I’ve given a breakdown of the benchmarking from my set-up but I’m curious to know if others have experienced a similar performance impact from the patches. Furthermore, if you still have any lingering doubts about the whole issue I’m ready to answer any questions you have about Meltdown, Spectre or anything else related to these security vulnerabilities and their respective patches. Contact me through the comments section below!

How to Resize Virtual Hard Disks in Hyper-V 2016

We get lots of cool tricks with virtualization. Among them is the ability to change our minds about almost any provisioning decision. In this article, we’re going to examine Hyper-V’s ability to resize virtual hard disks. Both Hyper-V Server (2016) and Client Hyper-V (Windows 10) have this capability.

Requirements for Hyper-V Disk Resizing

If we only think of virtual hard disks as files, then we won’t have many requirements to worry about. We can grow both VHD and VHDX files easily. We can shrink VHDX files fairly easily. Shrinking VHD requires more effort. This article primarily focuses on growth operations, so I’ll wrap up with a link to a shrink how-to article.

You can resize any of Hyper-V’s three layout types (fixed, dynamically expanding, and differencing). However, you cannot resize an AVHDX file (a differencing disk automatically created by the checkpoint function).

If a virtual hard disk belongs to a virtual machine, the rules change a bit.

  • If the virtual machine is Off, any of its disks can be resized (in accordance with the restrictions that we just mentioned)
  • If the virtual machine is Saved or has checkpoints, none of its disks can be resized
  • If the virtual machine is Running, then there are additional restrictions for resizing its virtual hard disks

Can I Resize a Hyper-V Virtual Machine’s Virtual Hard Disks Online?

A very important question: do you need to turn off a Hyper-V virtual machine to resize its virtual hard disks? The answer: sometimes.

  • If the virtual disk in question is the VHD type, then no, it cannot be resized online.
  • If the virtual disk in question belongs to the virtual IDE chain, then no, you cannot resize the virtual disk while the virtual machine is online.
  • If the virtual disk in question belongs to the virtual SCSI chain, then yes, you can resize the virtual disk while the virtual machine is online.

rv_idevscsi

Does Online VHDX Resize Work with Generation 1 Hyper-V VMs?

The generation of the virtual machine does not matter for virtual hard disk resizing. If the virtual disk is on the virtual SCSI chain, then you can resize it online.

Does Hyper-V Virtual Disk Resize Work with Linux Virtual Machines?

The guest operating system and file system do not matter. Different guest operating systems might react differently to a resize event, and the steps that you take for the guest’s file system will vary. However, the act of resizing the virtual disk does not change.

Do I Need to Connect the Virtual Disk to a Virtual Machine to Resize It?

Most guides show you how to use a virtual machine’s property sheet to resize a virtual hard disk. That might lead to the impression that you can only resize a virtual hard disk while a virtual machine owns it. Fortunately, you can easily resize a disconnected virtual disk. Both PowerShell and the GUI provide suitable methods.

How to Resize a Virtual Hard Disk with PowerShell

PowerShell is the preferred method for all virtual hard disk resize operations. It’s universal, flexible, scriptable, and, once you get the hang of it, much faster than the GUI.

The cmdlet to use is Resize-VHD. As of this writing, the documentation for that cmdlet says that it operates offline only. Ignore that. Resize-VHD works under the same restrictions outlined above.

The VHDX that I used in the sample began life at 20GB. Therefore, the above cmdlet will work as long as I did at least one of the following:

  • Left it unconnected
  • Connected it to the VM’s virtual SCSI controller
  • Turned the connected VM off

Notice the gb suffix on the SizeBytes parameter. PowerShell natively provides that feature; the cmdlet itself has nothing to do with it. PowerShell will automatically translate suffixes as necessary. Be aware that 1kb equals 1,024, not 1,000 (although both b and B both mean “byte”).

Had I used a number for SizeBytes smaller than the current size of the virtual hard disk file, I might have had some trouble. Each VHDX has a specific minimum size dictated by the contents of the file. See the discussion on shrinking at the end of this article for more information. Quickly speaking, the output of Get-VHD includes a MinimumSize field that shows how far you shrink the disk without taking additional actions.

This cmdlet only affects the virtual hard disk’s size. It does not affect the contained file system(s). That’s a separate step.

How to Resize a Disconnected Virtual Hard Disk with Hyper-V Manager

Hyper-V Manager allows you to resize a virtual hard disk whether or not a virtual machine owns it.

  1. From the main screen of Hyper-V Manager, first, select a host in the left pane. All VHD/X actions are carried out by the hypervisor’s subsystems, even if the target virtual hard disk does not belong to a specific virtual machine. Ensure that you pick a host that can reach the VHD/X. If the file resides on SMB storage, delegation may be necessary.
  2. In the far right Actions pane, click Edit Disk.
    rv_actionseditdisk
  3. The first page is information. Click Next.
  4. Browse to (or type) the location of the disk to edit.
    rv_browse
  5. The directions from this point are the same as for a connected disk, so go to the next section and pick up at step 6.

Note: Even though these directions specify disconnected virtual hard disks, they can be used on connected virtual disks. All of the rules mentioned earlier apply.

How to Resize a Virtual Machine’s Virtual Hard Disk with Hyper-V Manager

Hyper-V Manager can also resize virtual hard disks that are attached to virtual machines.

  1. If the virtual hard disk is attached to the VM’s virtual IDE controller, turn off the virtual machine. If the VM is saved, start it.
  2. Open the virtual machine’s Settings dialog.
  3. In the left pane, choose the virtual disk to resize.
  4. In the right pane, click the Edit button in the Media block.
    rv_vmsettingsedit
  5. The wizard will start by displaying the location of the virtual hard disk file, but the page will be grayed out. Otherwise, it will look just like the screenshot from step 4 of the preceding section. Click Next.
  6. Choose to Expand or Shrink (VHDX only) the virtual hard disk. If the VM is off, you will see additional options. Choose the desired operation and click Next.
    rv_exorshrink
  7. If you chose Expand, it will show you the current size and give you a New Size field to fill in. It will display the maximum possible size for this VHD/X’s file type. All values are in GB, so you can only change in GB increments (use PowerShell if that’s not acceptable).
    rv_expandIf you chose Shrink (VHDX only), it will show you the current size and give you a New Size field to fill in. It will display the minimum possible size for this file, based on the contents. All values are in GB, so you can only change in GB increments (use PowerShell if that’s not acceptable).
    rv_expand
    Enter the desired size and click Next.
  8. The wizard will show a summary screen. Review it to ensure accuracy. Click Finish when ready.

The wizard will show a progress bar. That might happen so briefly that you don’t see it, or it may take some time. The variance will depend on what you selected and the speed of your hardware. Growing fixed disks will take some time; shrinking disks usually happens almost instantaneously. Assuming that all is well, you’ll be quietly returned to the screen that you started on.

Following Up After a Virtual Hard Disk Resize Operation

When you grow a virtual hard disk, only the disk’s parameters change. Nothing happens to the file system(s) inside the VHD/X. For a growth operation, you’ll need to perform some additional action. For a Windows guest, that typically means using Disk Management to extend a partition:

rv_extend

Note: You might need to use the Rescan Disks operation on the Action menu to see the added space.

Of course, you could also create a new partition (or partitions) if you prefer.

I have not performed this operation on any Linux guests, so I can’t tell you exactly what to do. The operation will depend on the file system and the tools that you have available. You can probably determine what to do with a quick Internet search.

VHDX Shrink Operations

I didn’t talk much about shrink operations in this article. Shrinking requires you to prepare the contained file system(s) before you can do anything in Hyper-V. You might find that you can’t shrink a particular VHDX at all. Rather than muddle this article will all of the necessary information, I’m going to point you to an earlier article that I wrote on this subject. That article was written for 2012 R2, but nothing has changed since then.

What About VHD/VHDX Compact Operations?

I often see confusion between shrinking a VHD/VHDX and compacting a VHD/VHDX. These operations are unrelated. When we talk about resizing, then the proper term for reducing the size of a virtual hard disk is “shrink”. “Compact” refers to removing the zeroed blocks of a dynamically expanding VHD/VHDX so that it consumes less space on physical storage. Look for a forthcoming article on that topic.

Moving to a new home…

This is the last blog post that I am going to write as Virtual PC Guy. But do not fear, I am starting a new blog over at american-boffin.com, and all the Virtual PC Guy posts are going to remain intact.

You may wonder why I am making this change?

Well, there are several reasons.

  • It’s been a long time. I have written 1399 blog posts over 14 years – averaging one new post every other working day. When I started this blog, I had more hair and fewer children.
  • The world has changed. When I started writing as Virtual PC Guy, virtualization was a new and unknown technology. Cloud computing was not even invented yet. It is amazing to think about how far we have come!
  • The scope and impact of my work has drastically increased. When I started blogging, there were a very select group of people who cared about virtualization. Now, between cloud computing, the rise of virtualization and containerization as standard development tools – and the progress we have been making on delivering virtualization based security for all environments – more and more people are affected by my work.
  • I am a manager now. When I started on this blog I was a frontline program manager – and most of my time was spent thinking about and designing technology. I have been a program manager lead for almost a decade now – and while I do still spend a lot of time working with technology – I spend more time working with people.
  • Maintaining multiple blogs is hard. I have tried, from time to time, to start up separate blogs for different parts of my life. But maintaining a blog is a lot of work. Maintaining multiple blogs is just too much work for me.
  • Virtual PC Guy has a very distinctive style. Over the years I have toyed with the idea of switching up the style of Virtual PC Guy – but I have never been able to bring myself to do it.

For all these reasons – I have decided that the best thing to do would be to archive Virtual PC Guy (I have posts that are 10 years old and are still helping people out!) and to start a new blog.

On my new blog – I will still talk about technology – but I will also spend time talking about working with customers, working with people in a corporate environment, and about whatever hobbies I happen to be spending my time on.

I hope you come and join me on my new blog!

Cheers,
Ben