This is effectively and update from one my very old posts Hyper-V WMI: KVP Exchange aka Data Exchange (Retrieving and Modifying Parent/Host KVP’s) which still receives a lot of traffic.  The same code from back in 2008 still works today even on Windows Server 2012 – however with Windows Server 2012 we have introduced a new version of our WMI API’s (WMI v2) and for future releases the old code may stop working as we have not committed to (or not to) supporting the old version 1 API’s.  In the case of the Msvm_KvpExchangeComponent class not much has changed – literally the only difference between the version 1 sample and version 2 sample below is the ‘\v2’ in the namespace, this is not true for most other components.

 

Adding/Modifying Key Value Pairs From A Guest

Adding values from within the guest is as simple as creating a new registry value under the HKLM\Software\Microsoft\Virtual Machine\Guest section of the registry.  You do have to be an administrator in the VM in order to access this section of the registry.  Retreating the value from the host or a remote machine with permissions on the host is just a WMI query away.

Adding A New Key Value Pair In The Guest

$regPath = "HKLM:\SOFTWARE\Microsoft\Virtual Machine\Guest“
Set-ItemProperty -Path $regPath -Name "Status" -Value “Ready" -Type String

Modifying the value is as easy as just changing the value above…

Reading The Value From The Host – Using WMI Version 2 Namespace

$vm = Get-WmiObject -Namespace root\virtualization\v2 -Class `
    Msvm_ComputerSystem -Filter {ElementName = 'Vm1'}

$vm.GetRelated("Msvm_KvpExchangeComponent").GuestExchangeItems | % { `
    $GuestExchangeItemXml = ([XML]$_).SelectSingleNode(`
       
"/INSTANCE/PROPERTY[@NAME='Name']/VALUE[child::text() = 'Status']")

    if ($GuestExchangeItemXml -ne $null)
    {
        $GuestExchangeItemXml.SelectSingleNode(`
            "/INSTANCE/PROPERTY[@NAME='Data']/VALUE/child::text()").Value
   
}
}

Reading The Value From The Host – Using WMI Version 1 Namespace

$vm = Get-WmiObject -Namespace root\virtualization -Class `
    Msvm_ComputerSystem -Filter {ElementName = 'Vm1'}

$vm.GetRelated("Msvm_KvpExchangeComponent").GuestExchangeItems | % { `
    $GuestExchangeItemXml = ([XML]$_).SelectSingleNode(`
       
"/INSTANCE/PROPERTY[@NAME='Name']/VALUE[child::text() = 'Status']")

    if ($GuestExchangeItemXml -ne $null)
    {
        $GuestExchangeItemXml.SelectSingleNode(`
            "/INSTANCE/PROPERTY[@NAME='Data']/VALUE/child::text()").Value
   
}
}

Adding/Modifying Key Value Pairs From A Host

To add a key value pair from the host you must get the instance of the management service as well as the VM.  You must also create a new instance of the Msvm_KvpExchangeDataItem class – I build the path to this class using the instance of the management service such that if the script is running remotely it will connect to the right server.  Once you have the new instance of the class you just need to specify the Name, Data and Source parameters (source must be 0).  Then just call the AddKvpItems Method providing the instance of the VM and the instance of the created class.

Querying for key value pairs created from the host is very similar to ones from the guest except that you have to go though one more association to get to the Msvm_KvpExchangeComponentSettingsData class other than that it’s pretty much the same process.  Modifying and deleting values is almost the same as creating them – you just specify the same Key name as the value you wish to update/delete and call the Modify or Delete method.

Note that the example below utilizes the V2 namespace, if you are using Windows Server 2008 or R2 you can remove the \v2 and the script will work just fine.

Adding A New Key Value Pair

$VmMgmt = Get-WmiObject -Namespace root\virtualization\v2 -Class `
    Msvm_VirtualSystemManagementService

$vm = Get-WmiObject -Namespace root\virtualization\v2 -Class `
    Msvm_ComputerSystem -Filter {ElementName='VM1'}

$kvpDataItem = ([WMIClass][String]::Format("\\{0}\{1}:{2}", `
    $VmMgmt.ClassPath.Server, `
    $VmMgmt.ClassPath.NamespacePath, `
    "Msvm_KvpExchangeDataItem")).CreateInstance()

$kvpDataItem.Name = "Name"
$kvpDataItem.Data = "Data"
$kvpDataItem.Source = 0

$VmMgmt.AddKvpItems($Vm, $kvpDataItem.PSBase.GetText(1))

Querying For Key Value Pairs In The Guest

$regPath = "HKLM:\SOFTWARE\Microsoft\Virtual Machine\External"
Get-ItemProperty -Path $regPath -Name "Name"

Querying For Key Value Pairs On The Host

$VmMgmt = Get-WmiObject -Namespace root\virtualization\v2 -Class `
    Msvm_VirtualSystemManagementService

$vm = Get-WmiObject -Namespace root\virtualization\v2 -Class `
    Msvm_ComputerSystem -Filter {ElementName='VM1'}

($vm.GetRelated("Msvm_KvpExchangeComponent")[0] `
    ).GetRelated("Msvm_KvpExchangeComponentSettingData").HostExchangeItems | % { `
        $GuestExchangeItemXml = ([XML]$_).SelectSingleNode(`
            "/INSTANCE/PROPERTY[@NAME='Name']/VALUE[child::text() = 'Name2']")
       
        if ($GuestExchangeItemXml -ne $null)
        {
      $GuestExchangeItemXml.SelectSingleNode(`
            "/INSTANCE/PROPERTY[@NAME='Data']/VALUE/child::text()").Value
        }   
    }

Modifying Key Value Pairs

$VmMgmt = Get-WmiObject -Namespace root\virtualization\v2 -Class `
    Msvm_VirtualSystemManagementService

$vm = Get-WmiObject -Namespace root\virtualization\v2 -Class `
    Msvm_ComputerSystem -Filter {ElementName='VM1'}

$kvpDataItem = ([WMIClass][String]::Format("\\{0}\{1}:{2}", `
    $VmMgmt.ClassPath.Server, `
    $VmMgmt.ClassPath.NamespacePath, `
    "Msvm_KvpExchangeDataItem")).CreateInstance()

$kvpDataItem.Name = "Name"
$kvpDataItem.Data = "Data2"
$kvpDataItem.Source = 0

$VmMgmt.ModifyKvpItems($Vm, $kvpDataItem.PSBase.GetText(1))

Removing Key Value Pairs

$VmMgmt = Get-WmiObject -Namespace root\virtualization\v2 -Class `
    Msvm_VirtualSystemManagementService

$vm = Get-WmiObject -Namespace root\virtualization\v2 -Class `
    Msvm_ComputerSystem -Filter {ElementName='VM1'}

$kvpDataItem = ([WMIClass][String]::Format("\\{0}\{1}:{2}", `
    $VmMgmt.ClassPath.Server, `
    $VmMgmt.ClassPath.NamespacePath, `
    "Msvm_KvpExchangeDataItem")).CreateInstance()

$kvpDataItem.Name = "Name"
$kvpDataItem.Data = [String]::Empty
$kvpDataItem.Source = 0


$VmMgmt.RemoveKvpItems($Vm, $kvpDataItem.PSBase.GetText(1))

 

-taylorb