Obtain VM hostname from Hypervisor via WMI/Hyper-V Data Exchange Service (KVP)

Before you continue please consider clicking on one of the horrible ads. I know they are a pain but they help me pay for the hosting of this site. It owes me a lot of money. Sob story over.

If you look after a hosted environment you may find yourself in a situation where you have little to no access to the VM's you are hosting.

I was asked to ensure that the VM's DNS hostname and the VM name in Hyper-V were the same. This was challenging because:

  • The hosts I'm working with do not share a network with the VM's.
  • I do not have credentials for the hosted VM's.

This meant RDNS and PowerShell Direct where both options I did not have. This left me with Hyper-V Data Exchange Service (KVP) and WMI.

The first thing I did was pull in all the data from the namespace relating to Hyper-V using the following command:

$WMIVM = Get-WmiObject -Namespace root\virtualization\v2 -Class Msvm_KvpExchangeComponent

This returns data on all the VM's on the host. I don't know what format this bulk of this data is in. If someone knows a better way to handle it please let me know in the comments . The way i did it wasn't the best I suspect.

Things to note:

  • '$WMIVM' is an array of all VM's, so we need a loop.
  • We can use a combination of "ConvertFrom-StringData" and "Split" to get to just the hostname.
  • We need the hostname and the VMid as this data does not contain the VMname.

The bellow looks after all that and puts the relevant data in an array of hashtable s called '$VMDNShostnameData' to make it easier to handle later.

$VMDNShostnameData = @() 

Foreach ($WMIVM in $WMIVMs)
    {
    $VMDNSAuditData = @{}

    $VMDNSname = ($WMIVM.GuestIntrinsicExchangeItems | ConvertFrom-StringData)[1].Values
    $VMDNSname = (($VMDNSname -split ">")[5] -split "<")[0]
    
    $VMDNSAuditData.add("VMid",$WMIVM.SystemName)
    $VMDNSAuditData.add("DNSName",$VMDNSname)

    $VMDNShostnameData = $VMDNShostnameData + $VMDNSAuditData
    }

To make this code more useful I decided to create as a couple of functions. The first returns the DNS hostnames and VMid's for all the VMs on the host as a hashtable

Function Get-AllVMDNSName
{

$WMIVMs = Get-WmiObject -Namespace root\virtualization\v2 -Class Msvm_KvpExchangeComponent #Gets all VM Data from namespace

$VMDNShostnameData = @() #Define empty array

Foreach ($WMIVM in $WMIVMs)
    {
    $VMDNSAuditData = @{} #Define Hashtable

    $VMDNSname = ($WMIVM.GuestIntrinsicExchangeItems | ConvertFrom-StringData)[1].Values #Parse hostname
    $VMDNSname = (($VMDNSname -split ">")[5] -split "<")[0] #More Parsing
    
    $VMDNSAuditData.add("VMid",$WMIVM.SystemName) #building hashtable
    $VMDNSAuditData.add("DNSName",$VMDNSname) #building hashtable

    $VMDNShostnameData = $VMDNShostnameData + $VMDNSAuditData # Add HT to array
    }
Return $VMDNShostnameData #returns hashtable of results
}

The second calls the first but then compares the data to a VMname you specify

Function Check-VMNamingConsitency
{
param 
($VMName)

$VMDNSNames = Get-AllVMDNSName #Gets WMI Data on all VM's
$VMObject = get-vm -Name $VMname #Gets the VM named in call
$FoundVM = $VMDNSNames | where {$_.VMid -eq $VMObject.VMId} #find VMIDS

Write-Host("VMid :",$VMObject.vmid)
Write-host("VMName :",$VMObject.VMName)
Write-Host("DNS Name:",$FoundVM.DNSName)
}

And this function is called as bellow:

Check-VMNamingConsitency -VMname Server1

The results are ass follows:

VMid : 60a0bcb5-07fc-47d8-b9f5-7b7d2a9e792d
VMName : Server1
DNS Name: Server1.Grumpy.net