Home > Mobile >  Obtain the DNS name of a VM from the Hyper-V host its running on using PowerShell
Obtain the DNS name of a VM from the Hyper-V host its running on using PowerShell

Time:12-01

Using PowerShell, can I obtain the DNS hostname of a virtual machine, if I only have access to the Hyper-V host running the VM?

I know I can get the IP of the VM and perform a reverse DNS lookup but I do not have access to the network/DNS servers that service this VM.

I also do not have credentials for the VMs

I feel like this information is accessible via integration services but have failed to find anything useful.

CodePudding user response:

I found searching for this term leads to many, many "get hyperv host name from within VM" answers, and very few for what you're actually trying to do.

You should be able to use the following code on the HyperV host to retrieve the VM dns hostname.

$code = @'
using System;
using System.IO;
using System.Xml.XPath;
using System.Management;


// exchangeDataItem xml document sample instance
//
//<INSTANCE CLASSNAME="Msvm_KvpExchangeDataItem">
//      <PROPERTY NAME="Caption" PROPAGATED="true" TYPE="string"></PROPERTY>
//      <PROPERTY NAME="Data" TYPE="string">
//         <VALUE>AUTOBVT-4OVYXAB</VALUE>
//      </PROPERTY>
//      <PROPERTY NAME="Description" PROPAGATED="true" TYPE="string"></PROPERTY>
//      <PROPERTY NAME="ElementName" PROPAGATED="true" TYPE="string"></PROPERTY>
//      <PROPERTY NAME="Name" TYPE="string">
//         <VALUE>FullyQualifiedDomainName</VALUE>
//      </PROPERTY>
//      <PROPERTY NAME="Source" TYPE="uint16">
//          <VALUE>2</VALUE>
//      </PROPERTY>
//</INSTANCE>        


namespace HyperV
{
    public class VirtualMachineQuery
    {
        static bool VMRunning(ManagementObject vm)
        {
            const int Enabled = 2;

            bool running = false;

            foreach (UInt16 operationStatus in (UInt16[])vm["OperationalStatus"])
            {
                if (operationStatus == Enabled)
                {
                    running = true;
                    break;
                }
            }

            return running;
        }

        public static string GetVirtualSystemDNS(string vmName)
        {
            ManagementScope scope = new ManagementScope(@"root\virtualization\v2", null);

            string value = null;

            string query = String.Format("select * from Msvm_ComputerSystem where ElementName = '{0}'", vmName);

            ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, new ObjectQuery(query));

            ManagementObjectCollection vms = searcher.Get();

            foreach (ManagementObject vm in vms)
            {

                if (VMRunning(vm))
                {

                    ManagementObjectCollection kvpExchangeComponents = vm.GetRelated("Msvm_KvpExchangeComponent");
                    if (kvpExchangeComponents.Count != 1)
                    {
                        throw new Exception(String.Format("{0} instance of Msvm_KvpExchangeComponent was found", kvpExchangeComponents.Count));
                    }

                    foreach (ManagementObject kvpExchangeComponent in kvpExchangeComponents)
                    {
                        foreach (string exchangeDataItem in (string[])kvpExchangeComponent["GuestIntrinsicExchangeItems"])
                        {
                            XPathDocument xpathDoc = new XPathDocument(new StringReader(exchangeDataItem));
                            XPathNavigator navigator = xpathDoc.CreateNavigator();
                            navigator = navigator.SelectSingleNode("/INSTANCE/PROPERTY[@NAME='Name']/VALUE[child::text() = 'FullyQualifiedDomainName']");
                            if (navigator != null)
                            {
                                navigator = navigator.SelectSingleNode("/INSTANCE/PROPERTY[@NAME='Data']/VALUE/child::text()");
                                value = navigator.Value;
                                break;
                            }
                        }
                    }
                }
            }
            return value;
        }
    }
}
'@

$referencingassemblies = "C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.XML.dll", "C:\windows\Microsoft.NET\Framework\v4.0.30319\System.Management.dll"

Add-Type -TypeDefinition $code -Language CSharp -ReferencedAssemblies $referencingassemblies

You query with a single VM name like

[HyperV.VirtualMachineQuery]::GetVirtualSystemDNS('VM-Name')

NOTE: This requires elevation (run as administrator) and the VM to be on.

CodePudding user response:

The mechanism I needed to solve this was "Hyper-V Data Exchange Service (KVP)" which you can access via WMI.

I haven't used WMI much but was able t fumble my way through it. First I queried WMI to obtain a list of the VM using the namespace 'root\virtualization\v2 "

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

The data in key 'GuestIntrinsicExchangeItems' contained the DNS hostname. Once I found this I was able to work out a way to parse this data. I'm sure someone could improve on how I have done this.

$seperator = '<PROPERTY NAME="Caption" TYPE="string"></PROPERTY><PROPERTY NAME="Data" TYPE="string"><VALUE>'
$VMDNSname = ($WMIVM[2].GuestIntrinsicExchangeItems -split $seperator)[3]

The result of this still isn't quite there and needed additional parsing (I am not good at parsing)

$VMDNSname = ($VMDNSname -split "<")[0]

The whole thing looks like this. $WMIVM is an array. I have parsed element 2 in the array. Hence it is referred to as '$WMIVM[2]' in this example

$WMIVM = Get-WmiObject -Namespace root\virtualization\v2 -Class Msvm_KvpExchangeComponent
$seperator = '<PROPERTY NAME="Caption" TYPE="string"></PROPERTY><PROPERTY NAME="Data" TYPE="string"><VALUE>'
$VMDNSname = ($WMIVM[2].GuestIntrinsicExchangeItems -split $seperator)[3]
$VMDNSname = ($VMDNSname -split "<")[0]
  • Related