Automation scales Operations, that is a phrase that I have used several times today in various conversations with colleagues and customers. I truly believe organizations can scale more efficiently and consistently when leveraging Automation and not be afraid of it or worse, attempting to avoid it at all cost!
In fact, Automation is a super power when it comes to the various reporting and auditing needs of an organization such as this recent inquiry in auditing all vSphere VMs that have been configured with a USB controller. The following PowerCLI snippet leverages the vSphere API to check whether there are any VMs that have been configured with either a USB 2.x controller (VirtualUSBController) or USB 3.x controller (VirtualUSBXHCIController) and outputs that in a simple table format, as shown in the example below.
# Retrieve all VMs and only include Name and Device data $vms = Get-View -ViewType VirtualMachine -Property Name,Config.Hardware.Device $results = @() foreach ($vm in $vms) { $haveUSB2Controller = $false $haveUSB3Controller = $false # Filter out devices that have USB 2.x and 3.x controllers & USB Devices (for mapping purposes) $devices = $vm.Config.Hardware.Device | where {$_.getType().Name -eq "VirtualUSBController" -or $_.getType().Name -eq "VirtualUSBXHCIController" -or $_.getType().Name -eq "VirtualUSB"} $usb2devices = @() $usb3devices = @() foreach ($device in $devices) { # Check whether USB controller is 2.x if($device.getType().Name -eq "VirtualUSBController") { $haveUSB2Controller = $true # Collect any connected USB devices on this controller foreach ($deviceKey in $device.device) { $usbDevice = $devices | where {$_.key -eq $deviceKey} $usbVid = [String]::Format("{0:x}", $usbDevice.Vendor) $usbPid = [String]::Format("{0:x}", $usbDevice.Product) $usb2devices += "${usbVid}:${usbPid}" } } # Check whether USB controller is 3.x if($device.getType().Name -eq "VirtualUSBXHCIController") { $haveUSB3Controller = $true # Collect any connected USB devices on this controller foreach ($deviceKey in $device.device) { $usbDevice = $devices | where {$_.key -eq $deviceKey} $usbVid = [String]::Format("{0:x}", $usbDevice.Vendor) $usbPid = [String]::Format("{0:x}", $usbDevice.Product) $usb3devices += "${usbVid}:${usbPid}" } } } # Only output VMs that have USB controllers if($haveUSB2Controller -or $haveUSB3Controller) { $tmp = [pscustomobject] @{ VM = $vm.Name USB2Controller = $haveUSB2Controller USB2Devices = $usb2devices USB3Controller = $haveUSB3Controller USB3Devices = $usb3devices } $results+=$tmp } } # Format output (can easily output to CSV/Excel) $results | ft
Here is an example screenshot listing only the VMs that have a USB controller and you can easily pipe the output to CSV or Excel for further processing rather than displaying the results in the console.
UPDATE (03/11/24) - The script above has been updated to also included all connected USB devices for either USB 2.x or 3.x controller found for a given VM. The format of the connected USB devices are vendorId:productId, which you can then use sites like DeviceHunt to get the friendly vendor/product name.
uvas says
Hi William, thanks for sharing this!
I put something together to remove the controllers. Not sure if this is the best method but ill share in case it helps someone else:
# Get the goods
$usr = $env:USERNAME + '@' + $env:USERDNSDOMAIN
$creds = Get-Credential -Message 'Credentials with vCenter Access' -UserName $usr
$vcsa = 'vcsaname.fqdn.com'
# Get VM list to modify
$ListOVms = cat c:\temp\VmList.txt
# Let it rip
foreach($comp in $ListOVms)
{
$vm = Get-VM -Name "$comp"
$usbControllers = $vm.ExtensionData.Config.Hardware.Device | Where-Object { $_.DeviceInfo.Label -like "USB*Controller*" }
$deviceSpecs = @()
foreach ($usbController in $usbControllers)
{
$usbControllerName = $usbController.DeviceInfo.Label
Write-Host "Removing USB controller: $usbControllerName"
$deviceSpec = New-Object VMware.Vim.VirtualDeviceConfigSpec
$deviceSpec.operation = "remove"
$deviceSpec.device = $usbController
$deviceSpecs += $deviceSpec
}
$spec = New-Object VMware.Vim.VirtualMachineConfigSpec
$spec.deviceChange = $deviceSpecs
$vm.ExtensionData.ReconfigVM_Task($spec)
}
William Lam says
Thanks for sharing. If you know there aren't any connected USB devices on the controller, then the code above is perfect for automating the removal. If not, you'll need to add a check before just removing the controller, especially if the USB device is in use
Fish says
Caveat being the VM's need to be in a powered off state. Thanks for sharing
Greg says
Why?
Would be great to get an overview of best practices around USB to complete the picture. I can see this as a security feature and vdi already offers that sort of ability to tune how USB is used in the virtual machine. I also think that there could be some performance related too. Thanks as always the articles are really coming out quite quickly these days!
Tim Fitzpatrick says
Is there an easy way to detect if something is attached to the USB Controller that your script finds?
William Lam says
Yup. Each controller (https://vdc-download.vmware.com/vmwb-repository/dcr-public/184bb3ba-6fa8-4574-a767-d0c96e2a38f4/ba9422ef-405c-47dd-8553-e11b619185b2/SDK/vsphere-ws/docs/ReferenceGuide/vim.vm.device.VirtualController.html) has a device array which would list the connected devices, but it only contains the device key.
I've just updated the script snippet to also output the connected devices which are in the form of vendorId:productId. See the additional notes for details