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