Rate

In part one of this series, we talked about Hyper-V’s Key-Value Pair (KVP) data exchange feature and got an idea for how it works. As I mentioned in that piece, one of the reasons you don’t see much about it is because it can be difficult to use.

In this post, I’m going to provide you with some PowerShell modules that are intended to alleviate most of the pain. Once you’ve implemented these modules on your systems, you’ll be able to pass data back and forth from guests to hosts. What that data is, and what happens when it is received by the target system, is for your imagination to concoct. If PowerShell scripting isn’t your strong suit, I first suggest that you become acquainted. The primer in our Hyper-V and PowerShell series can set you on your way. You should also search the Internet for scripts designed by others that can accelerate implementation of your automation solutions.

Skip to part 3: Linux

Credits

If I ever figured out how to do everything you see in this article, it would have taken me a lot longer to get there without the writings of Taylor Brown and Boe Prox.

Taylor Brown’s source is mentioned in the comments for the relevant functions, I have lifted his “ProcessWMIJob” function nearly verbatim. The changes that I made were more to make it script-friendly as opposed to being meant for an interactive session. You can see the original ProcessWMIJob function on this blog article. The original script for the Add, Modify, and Remove KVP functions from WMI in a Hyper-V host is found in this blog article. That script was later copied to this TechNet article. To some degree, it might be said that my article refines that script into something more approachable and universally usable. If you’re looking for coding samples for using KVP exchange in Linux guests, that TechNet article is where you want to go.

Boe Prox’s work is not included directly in any of my modules, but is referred to heavily in the supporting sections.

The Modules

I’ll start by presenting the modules so that those of you with PowerShell experience can copy/paste them and be on your way. Explanations for each will follow.

The Host Module

This module is intended to be used on Hyper-V hosts. It exposes the following functions:

  • Get-VMWmiObject: Given the name of a virtual machine, returns the WMI object that represents it.
  • Get-VMKvpGuestToHost: Lists one or more of the KVPs transmitted from the guest to the host.
  • Get-VMKvpGuestToHostInstrinsic: Lists one or more of the KVPs transmitted from the guest to the host that are automatically generated by the Hyper-V Data Exchange service.
  • Get-VMKvpHostToGuest: Lists one or more of the KVPs transmitted from the host to the guest.
  • Send-VMKvpHostToGuest: Creates or modifies a KVP to be sent to a guest from the host. In case you jumped here without reading part one, be aware that the KVP is case-sensitive. If you use a key name that differs from an existing key by only capitalization, a duplicate KVP will be created but the guest will only see the most recent one. This is due to a flaw in the design of KVP Data Exchange and there’s nothing I can do about it short of forcing you to use all-uppercase or all-lowercase key names.
  • Remove-VMKvpHostToGuest: Deletes a host-to-guest KVP. Remember that these cannot be deleted from within the guest. This cmdlet has the same case-sensitivity caveat note as Send-VMKvpHostToGuest. Remove-KvpHostToGuest will almost always report success, even when it didn’t find a matching KVP to remove. I considered performing some pre-flight checking, but opted against it. You may want this cmdlet to work even when the virtual machine is powered off, in which case Get-VMKvpHostToGuest will not operate. I encourage you to use Get-VMKvpHostToGuest for verification.

Each of these has detailed Get-Help text.

Installation Instructions

  1. Navigate to C:\Windows\System32\WindowsPowerShell\v1.0\Modules.
  2. Create a folder named Hyper-V-KVP-Host.
  3. Inside that folder, create two empty text files: Hyper-V-KVP-Host.psd1 and Hyper-V-KVP-Host.psm1.
  4. Paste the contents of the following code blocks into the respective files.
Hyper-V-KVP-Host.psd1

Hyper-V-KVP-Host.psm1

 

The Guest Module

This module is intended to be used on Hyper-V guests. It exposes the following functions:

  • Get-VMKVPHostToGuest: Lists one or more of the KVPs that have been transmitted to the guest from the host.
  • Get-VMKVPGuestToHost: Lists one or more of the KVPs that the guest has transmitted to the host.
  • Send-VMKVPGuestToHost: Creates or modifies the specified KVP in the appropriate registry location, causing it to be transmitted to the host.
  • Remove-VMKVPGuestToHost: Removes a guest-to-host KVP.

As with the host module, the guest module’s cmdlets have detailed help viewable in Get-Help.

Installation Instructions

  1. Navigate to C:\Windows\System32\WindowsPowerShell\v1.0\Modules.
  2. Create a folder named Hyper-V-KVP-Guest.
  3. Inside that folder, create two empty text files: Hyper-V-KVP-Guest.psd1 and Hyper-V-KVP-Guest.psm1.
  4. Paste the contents of the following code blocks into the respective files.

Hyper-V-KVP-Guest.psd1

Hyper-V-KVP-Guest.psd1

Module Discussion

These modules have been marked to only operate under PowerShell 4.0, although I admit that’s mostly because I didn’t want to add in script to check for administrator privileges. I don’t think there’s anything in them that won’t operate under PS 3.0 aside from all of the #requires -RunAsAdministrator lines, so you should be able to downgrade these modules fairly easily if necessary. I’d prefer that you bring your hosts and guests up to at least PowerShell 4.0, if they aren’t there already. To check the installed version, run this at a PowerShell prompt: $PSVersionTable. If your version is below 4.0, go to the Microsoft downloads page and search for “Windows Management Framework”. Version 5 is the latest, but as of this writing it is so badly broken that it has been removed from the downloads page. I have heard of a handful of compatibility issues for PowerShell-dependent applications when upgrading to newer PowerShell releases, so you might want to look into that for any such apps that you have.

The host module is also designed to work with version 2 of the Hyper-V WMI namespace. It will be immediately compatible with Hyper-V Server 2012 and later and all versions of Client Hyper-V. I think the module will work for 2008 R2, but you’ll need to modify the $KVPVirtualizationNamespace = 'root\virtualization\v2' line of Hyper-V-KVP-Host.psm1 to read  $KVPVirtualizationNamespace = 'root\virtualization'. I highlighted the line in the source script pasted above, it’s very near the top.

The Host Module

For the most part, I genericized Taylor Brown’s script so that it could be run on demand with any guest and KVP. I also performed a fair bit of refactoring to transition it from short and clever script to readable and maintainable script. For the programming/scripting purists, I apologize for leaving in the magic numbers for the job states. I couldn’t find a definitive explanation of the JobState enumerator that lined up with the numbers that Brown used in his ProcessWMIJob and I didn’t want to guess, so I just left them as I found them.

The functions that are exported from the module should be very simple to figure out. I think tab completion exposure of the parameters should be sufficient to understand what to do, but they are Get-Help friendly just in case. The functions that are not exported by the module might be of interest to those scripters that would like to work with WMI and VMMS, as they do the real work; the exported functions are merely wrappers.

The Guest Module

The guest module contains work that is substantially more original. Truly, working with KVP on the guest side is just a matter of manipulating registry keys. I considered leaving that to you readers to sort out, but PowerShell’s functionality for working with the registry is a special kind of awful. I trust that you’ll find this module to be much more friendly.

As with the host module, I don’t think there’s anything in it that truly requires PowerShell version 4.0 since it is almost exclusively .Net calls, but you do need to be a local administrator to write to the KVP registry sections and I didn’t want to include a checking function. You could remove the #requires and everything should be OK as long as you run the script with administrative privileges.

Exploiting the Modules

Both modules work fine as-is.

KVP Basic Demo

KVP Basic Demo

I’m guessing that you want a little more, such as automation. OK.

Simple Automation

Open up a PowerShell prompt or the ISE inside a guest that has the Hyper-V-KVP-Guest module, and paste this into it:

If you’re using the ISE, press F5 to execute it. Do not close the ISE or the PS session. Switch to the host, which must have the Hyper-V-KVP-Host module. Inside the ISE or a PS session, there, enter something like the following:

Inside the guest, your system drive should now have a “Temp” folder if it didn’t before, and inside that temp folder should be a file named “kvp-incoming.log”, and that file should be a pure text file that contains something like:

The bad news is, that only works for as long as the PowerShell session is left open. I’m assuming you’d like something a bit more permanent. OK.

Creating a Permanent Guest-Side KVP Watcher

The following has been adapted from Boe Prox’s article on permanent WMI registrations. Something that I need to make clear up front is that this will only cause an action when a registry entry changes or is created anew. If you attempt to test by repeatedly transmitting the same KVP without modifying the value, the sending host will report success while the receiving guest reports nothing.

Inside the guest, run the following inside ISE or at an interactive PowerShell prompt. It only has to be done once, but it’s something you’ll probably want to save to use on other VMs:

There will be output indicating that the WMI event instance was created, which should look something like the following:

You can verify its existence at any time (helpful if you forget whether or not you created it on a given VM):

What you have done is tell Windows to trigger an event whenever that particular registry branch changes. Now what you need to do is subscribe to that event. In .Net, that’s called an event handler. In WMI, its an event consumer.

You will get some output to the screen to verify that the consumer was created. It will look similar to the output when you created the event. You can verify the existence of the consumer at any time:

You’ll need to supply the item designated in $ScriptPath. You can snip out the action part from the previous section for testing purposes, if you’d like. That’s recreated here for your convenience:

At this point, you have an event, and you have an event consumer. The final step is to connect the consumer to the event in an act called binding.

As before, you’ll get some output telling you that the object has been created. You can look at your bindings any time:

I’m sure you’ll want to try it out. You’ll find that everything works immediately. No service restarts or system reboots necessary.

Verifying that the Guest Heard You

Remember how I said that the event only fires when the registry changes, so you can’t just send the same data repeatedly? Well, I can help out with that, too. I’ve crafted a script that I use to ensure that each send is unique without me having to do anything fancy. You can drop this right into the Hyper-V-KVP-Host module if you like, which will make it always available. Don’t forget to add “Send-VMKVPCommandToGuest” into the ExportedFunctions line of the Hyper-V-KVP-Host.psd1 file (I highlighted that line for you above)!

As-is, this will modify or create a key named “VMCommand”. The value will be the host’s timestamp for the command was issued, in ticks, followed by whatever you supplied as the -Command parameter, optionally followed by the item(s) that you supplied for the -Arguments parameter. For example, entering the following in the host:

Should produce something like the following in the guest:

Because the host’s clock is constantly changing and it would be ridiculously difficult to submit two commands so closely together that they have the same timestamp, this all but guarantees that you’ll trigger the event in the guest every time you send anything… even if it’s technically the exact same KVP with an identical value.

You might have noticed that there is a -Verify parameter. I included this because the KVP operation in the host can only tell you if the KVP was modified on the host’s virtual machine object, which is probably going to work as long as the guest exists and you have the correct permissions. It doesn’t tell you whether or not the guest operating system received the KVP. Change your KVPResponse.ps1 script to the following (you can just include it at the beginning if you don’t want to modify the existing contents):

As shown, the “else” portion is unnecessary. Without it, duplicate commands will be ignored automatically and silently. I put it in to give you the option of handling them, if you wish. What you might choose to do is place a “return” statement in the else which would allow you to perform processing without nesting inside the “if” block. However, I would simplify that as:

Whatever works for you and your scripting style.

Note 1: If you’d rather use special KVP key names besides “VMCommand” and “VMAcknowledgement”, change them on lines 42 and 43, respectively, in the Send-VMKvpCommandToGuest function. I highlighted those lines in the above source to make them easy to find. Don’t forget to change the corresponding keys in your guest’s response script.

Note 2: I normally process the registry changes using a binary .Net module, and it picks up the changes instantly. To make this work in the PowerShell module, I had to add a delay in the verify portion of the Send-VMKVPCommandToGuest function. This means that you could fairly easily inject a secondary command while it is still waiting for the delay, which would cause the earlier Verify to fail. I made the delay a full second, but feel free to lower the number and test. It’s on line 44, which is highlighted in the above source. If you have a newer, not overloaded host, your guests might respond quickly enough that you don’t need it at all. Your mileage may vary, and I recommend that you test thoroughly.

Parsing the Command

Of course, with the command KVP containing a mashed-together string, you need to be able to unmash it on the guest. Here is a skeleton to help you do that. Just place this script into the KVPResponse.ps1 after the above bit:

Now, when using the Send-VMKvpCommandToGuest function, you’ll have a $Command variable with the name of the command, and an $Arguments array that might have arguments in it.

Other Notes

To wrap up, here are a few other things that might be of interest to you.

Watching VMs on the Host for KVP Changes

I have not personally tinkered with watching a virtual machine’s KVP from the host side. The basic query to do so is:

Use this with the samples in the “Create a Permanent KVP Watcher” section, but on the host. You will likely want to modify the query to only watch for specific virtual machines, and this event will trigger any time that anything at all changes on the virtual machine, not just the KVPs, but this is where you would begin your journey.

Removing Permanent WMI Watchers

The events, consumers, and bindings are permanent and will persist through reboots and function while no one is logged on. If you want to get rid of them, start with the binding. You can start by listing all bindings:

This will show you the names of all the filters on the system, something like this:

The “Name” portion is what you want. Use it like this:

All you need to do on your system is place the name shown in the output of the previous query between the two % signs in the Get-WMIObject line above, where I currently have “KVPRegistryFilter”. Then, you can clean up like this:

After that, you can remove the event and the consumer in any order. They’re a bit easier. I’ll start with the event.

First, get the name of the event from the binding object:

With the name in hand, select it from WMI:

From the binding, get the name of the consumer as you did with the filter:

This one isn’t nice enough to show you the class that you need, but everything you need to know is in the script from earlier in this post. For some reason, WMI does not like to filter event consumers by name, so we’re back to using __PATH as we did with the binding:

And, that’s it. If you remember all the names that you used, you could combine all of these into a saved script without all of the searching and do the entire delete process in only six lines (three, if you get clever, but showing off like that is rarely rewarded).

Go Forth and Automate

At this point, you’re only restricted by what you can place into that KVPResponse.ps1 file. I look forward to seeing what people come up with.

More in this series:

Part 1: Explanation

Part 3: Linux