This is a small PowerShell script I wrote to retrieve the key value pairs that Hyper-V exposes to Linux VMs. To view the most up to date version of this script, go to this GitHub link, otherwise look at the initial public code at the bottom of this blog post.
What is Hyper-V Key-Value Pair Data Exchange?
Simply put, Hyper-V Key-Value Pair Data Exchange is simply a way of transferring data between Hyper-V and a VM, both to and from. In Windows, this is exposed as registry keys in HKLM:\Software\Microsoft\Virtual Machine
.
I am definitely not the most informed on this, if you would like to learn more, please see the following links:
- Eric Siron on Altaro (bonus: he wrote a C++ program to read AND write to the kvp_pools in Linux!)
- Microsoft's Documenation
How my script works
This script doesn't really do anything fancy. In linux, these values are exposed via a file. My script primarily targets the content in /var/lib/hyperv/.kvp_pool_3
. In fact, if you wanted to access them without a script, you could just by using cat
in bash or Get-Content
in PowerShell.
$ cat /var/lib/hyperv/.kvp_pool_3
HostNameHyperV.ad.example.comHostingSystemEditionId8HostingSystemNestedLevel0HostingSystemOsMajor10HostingSystemOsMinor0HostingSystemProcessorArchitecture9HostingSystemProcessorIdleStateMax0HostingSystemProcessorThrottleMax100HostingSystemProcessorThrottleMin5HostingSystemSpMajor0HostingSystemSpMinor0PhysicalHostNameHYPERVPhysicalHostNameFullyQualifiedHyperV.ad.example.comVirtualMachineDynamicMemoryBalancingEnabled0VirtualMachineId5A535AFF-1708-4F6B-9B23-E506DF8CC5C5VirtualMachineNamekvp-testingtu18.04
But, that is literally just a long string with no delimiter. Or is it?
Each KVP in the file is stored with a 512 byte key and 2048 byte value. Everything after the actual content is null content basically. So, we can use that to parse the data.
Essentially, my script reads the file as a byte stream, and parses out 512 bytes for the key, 2048 for the value, and then starts on the next one. I store this in a hashtable, and then return it as PSObject (because objects are awesome).
Here is a sample output:
PS /home/serveradmin> Get-LinuxHypervKvpValues
HostingSystemOsMinor : 0
HostingSystemProcessorThrottleMax : 100
HostingSystemProcessorIdleStateMax : 0
PhysicalHostName : HYPERV
HostingSystemEditionId : 8
HostingSystemOsMajor : 10
HostingSystemNestedLevel : 0
HostingSystemSpMajor : 0
HostName : HyperV.ad.example.com
VirtualMachineDynamicMemoryBalancingEnabled : 0
HostingSystemSpMinor : 0
VirtualMachineName : kvp-testingtu18.04
HostingSystemProcessorThrottleMin : 5
HostingSystemProcessorArchitecture : 9
PhysicalHostNameFullyQualified : HyperV.ad.example.com
VirtualMachineId : 5A535AFF-1708-4F6B-9B23-E506DF8CC5C5
In addition, I added the ability to send a PSSession to the script, so I could get info about remote VMs without making sure to copy this function over.
The Script
Function Get-LinuxHypervKvpValues {
<#
.SYNOPSIS
Retrive Hyper-V KVP Data Exchange values from a Linux based Hyper-V virtual machine.
.DESCRIPTION
Hyper-V provides key value pairs to VMs to send VM/Host info in a safe way. This function retrieves those key value pairs and returns them as a PowerShell object.
.PARAMETER Path
Location of the kvp_pool you would like to access. They are usually named .kvp_pool_x, where x is an integer between 0 and 4.
.PARAMETER Session
Optional parameter for a PSSession to remotely retrieve the KVP values.
.EXAMPLE
Get-LinuxHypervKvpValues -Session (New-PSSession -Hostname 192.168.1.1 -Username serveradmin)
.NOTES
Cody Ernesti
github.com/soarinferret
.LINK
https://github.com/Soarinferret/PowerShell
https://blog.kanto.cloud/retrieving-linux-hyper-v-kvps-in-powershell
#>
Param(
[ValidateScript({Test-Path $_ -PathType 'Leaf'})]
[String]$Path = "/var/lib/hyperv/.kvp_pool_3",
[Parameter(Mandatory=$false)]
[ValidateScript({$_.State -eq "Opened"})]
[PsSession]$Session
)
function get-kvp ($KvpPath){
$KEY_LENGTH = 512
$VALUE_LENGTH = 2048
$KVP_POOL = Get-Content $KvpPath -AsByteStream
$properties = @{}
for($y = 0; $y -lt $KVP_POOL.Length; $y = $y + $KEY_LENGTH + $VALUE_LENGTH){
$properties.add(
# Key
$([System.Text.Encoding]::UTF8.GetString($KVP_POOL[$y..$($y+$KEY_LENGTH -1)]) -replace "`0", ""),
# Value
$([System.Text.Encoding]::UTF8.GetString($KVP_POOL[$($y+$KEY_LENGTH)..$($y+$VALUE_LENGTH -1)]) -replace "`0", "")
)
}
return New-Object PSObject -Property $properties
}
if($Session -and $Session.State -eq "Opened"){
Invoke-Command -Session $Session -ScriptBlock ${function:get-kvp} -ArgumentList $Path
}else{
get-kvp $Path
}
}