vSphere USB Passthru and Autoconnect Devices and PowerCLI

usbWait!  I thought I had UPS plugged into my host and setup as passthru to my VM already!  Why can't I see it now?  What happened?  Who moved that external drive I had connected to my Veeam console to seed an offsite backup?  Ever find yourself asking yourself any of these questions…I certainly have!  Due to circumstances out of my control I have a few hosts that tend to be "out in the wild".  Available and insecure, readily accessible to the hundreds of people walking by it each day.  At time to time either someone trips over a cord, some deliberately unplugs something, or equipment needs to be moved and gets plugged in to different ports upon reconnection.

Not as many options in the GUI

As is with most other products, using the GUI to configure something sometimes doesn't give you all the options that you need.  Essentially when configuring USB Passthru to a VM from within either the vSphere Client or the vSphere Web Client you the device needs to be plugged in and it gets assigned to the VM based on the host USB port that it is connected to.  Again in most cases this is fine but in my situation I needed to be able to have this device connected to the VM no matter what port it was connected to.  Turned out after reading some documentation around the vSphere API as well as having a great discussion with Luc Dekens on the VMTN forums there is indeed a way to do exactly what I needed to do.

Community to the rescue!

So, in the API reference for the VirtualUSBUSBBackingInfo object it states the following

To identify the USB device, you specify an autoconnect pattern for the deviceName. The virtual machine can connect to the USB device if the ESX server can find a USB device described by the autoconnect pattern. The autoconnect pattern consists of name:value pairs. You can use any combination of the following fields.

  • path – USB connection path on the host
  • pid – idProduct field in the USB device descriptor
  • vid – idVendor field in the USB device descriptor
  • hostId – unique ID for the host
  • speed – device speed (low, full, or high)

Perfect, this is exactly what I was looking for.  Basically I can have the USB device autoconnect to the VM by initially connecting by using ANY combination of the above parameters as the deviceName.  First off, path is out of the question, it's what is going to changed when the device is plugged into a different port.  So I decided to use pid, vid, and hostId.  Therefore, if a device with the specified product id and vendor id is plugged into a host with the specified hostId it will automatically pass this through to the VM in which I assign it to!   Awesome!  One problem, I still don't have a clue what the pid, vid, and hostId are, nor do I know the PowerCLI syntax to add the device.

PIDS and VIDS and More…

So how do you find out the pid and vid of the device you want to add?  Well, there's a KB for that…kinda, KB1648 mentions how to do it.  Basically go to in the Windows registry HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\ and search around and you'll find it πŸ™‚  In my case i was using an APC SmartUPS 1500 which had a pid and vid of 0003 and 051D respectively.

The final piece of information we need is the hostId.  By hostId the documentation really means the actual hardware id that is stored within the BIOS of your host.  Accessing ExtensionData.Hardware.Systeminfo.Uuid will actually retrieve this for you.  As you will see in the script below there is certainty some formatting issues that need to be resolved with it, but it most certainly works πŸ™‚

Putting it all together

Now that we have all the information we need it's time to fire up PowerCLI to get this task scripted (I needed to do it 40 times πŸ™‚ ).  I'm not going to lie here, I had no idea how to do this with PowerCLI but by using Onyx I got a great start.

As you can see in the script below a few things happen.  First you need to specify some credentials to and your vCenter location as well as the pid, vid, hostname of the host that will be connected to the device as well as the VM name in which you want to pass the device through.  Lines 17 and 18 get some information in regards to the target VM and lines 21 and 22 get that hardware uuid from the host.  Lines 24 through 36 is the reformatting of the uuid that I described earlier.  You can see the format in the API reference.  The rest of the script does the actually setup of the USB device, this is the code, minus a few changes here and there, that Onyx spit out for me.

The Script

  1. Add-PSSnapin VMware.VIMAutomation.Core  
  2.    
  3. $vcserver = "vcenterserver"  
  4. $vcusername = "username"  
  5. $vcpassword = "password"  
  6.    
  7. $myhost = "Host that USB is attached to"  
  8. $myvm = "VM which to pass USB through to"  
  9.    
  10. $ppid = "PID of USB Device"  
  11. $vvid = "VID of USB Device"  
  12.    
  13. Connect-VIserver $vcserver -user $vcusername -pass $vcpassword  
  14.    
  15.    
  16. #get id of VM  
  17. $vm = get-vm $myvm  
  18. $vmid = $vm.ID  
  19.    
  20. #get host uuid from BIOS  
  21. $vmhost = get-vmhost $myhost  
  22. $vmhostId =  $vmhost.ExtensionData.Hardware.SystemInfo.Uuid  
  23.    
  24. #reformat vmhostID to the proper format for autoconnect string  
  25. $vmhostd = $vmhostid.Replace("-","")  
  26. $section1 = $vmhostid.substring(0,16)  
  27. $section2 = $vmhostid.substring(16)  
  28. $newsec1 = (&{for ($i = 0;$i -lt $section1.length;$i += 2)  
  29.    {  
  30.      $section1.substring($i,2)  
  31.    }}) -join '\ '  
  32. $newsec2 = (&{for ($i = 0;$i -lt $section2.length;$i += 2)  
  33.    {  
  34.      $section2.substring($i,2)  
  35.    }}) -join '\ '  
  36. $hostId = "$newsec1-$newsec2"  
  37.    
  38.    
  39. #create usb device and add it to the VM.  
  40. $spec = New-Object VMware.Vim.VirtualMachineConfigSpec  
  41. $spec.deviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec[] (1)  
  42. $spec.deviceChange[0] = New-Object VMware.Vim.VirtualDeviceConfigSpec  
  43. $spec.deviceChange[0].operation = "add"  
  44. $spec.deviceChange[0].device = New-Object VMware.Vim.VirtualUSB  
  45. $spec.deviceChange[0].device.key = -100  
  46. $spec.deviceChange[0].device.backing = New-Object VMware.Vim.VirtualUSBUSBBackingInfo  
  47. $spec.deviceChange[0].device.backing.deviceName = "pid:$ppid vid:$vvid hostId:$hostId"  
  48. $spec.deviceChange[0].device.connectable = New-Object VMware.Vim.VirtualDeviceConnectInfo  
  49. $spec.deviceChange[0].device.connectable.startConnected = $true  
  50. $spec.deviceChange[0].device.connectable.allowGuestControl = $false  
  51. $spec.deviceChange[0].device.connectable.connected = $true  
  52. $spec.deviceChange[0].device.connected = $false  
  53.    
  54. $_this = Get-View -Id "$vmid"  
  55. $_this.ReconfigVM_Task($spec)  
  56.    

So there you have it!  You can unplug and plug this USB device in to your hearts delight.  ESXi should pick up the device no matter what port it is plugged into and pass it on to your VM every time!  Certainly this isn't something that you will do everyday, but for those that have hosts sitting out in the open, may be a handy configuration to have set up in their environment.  As always, I'm not the greatest scripter in the world, so any comments, suggestions, improvements, concerns, thoughts are most definitely appreciated.

  • Stuart

    Thanks, this is just what I need for daily backup drives. Only problem is I have never used the cli for esxi, any hints would be welcome as to where to start