Saturday, June 15, 2019

Hyper-V SCSI Controller Error

I recently upgraded storage on my Hyper-V server that hosts all of my virtual machines to SSD drives. As part of this, I got lazy for some of the VMs and copied the files manually from drive to drive by using File Explorer rather than moving the VM storage by using Hyper-V Manager. After the disk reconfiguration was done, I got this error for the VMs where I had simply copied the data.
Synthetic SCSI Controller (Instance ID GUID): Failed to Power on with Error 'General access denied error'.
Account does not have permission to open attachment 'PathToVirtualDisk'. Error: 'General access denied error'.

This is a permissions error indicating that the VM doesn't have access to it's own virtual hard disk. As part of my file copying, the VM level permissions were lost. You can see in the screenshot below that only System, Administrators, and Users have permissions. Normally, you should also see permissions for a GUID that represents the VM with Full control.

A quick and dirty fix that you can use for test environments is giving Full control to Everyone. However, for production we can do better than that. We can rebuild the permissions for each VM properly.

The basic process is:
  1. Get the GUID for the virtual machine
  2. Set permissions on the virtual hard disk file for the GUID
We can get the GUID for the virtual machine by using PowerShell:
(Get-VM VMName).VMId.Guid

Then set permissions by using icacls.exe:
icacls.exe VirtualDiskFile /Grant Guid:F

Notice that in the example that granting permissions is combination of the GUID and the permissions that we want to assign. In this case, the GUID is being assigned full control (F) permission. The result is shown in the permissions.

If you only have one or two VMs to fix, this method is pretty quick and easy. However, I had about twenty of them. So, I created a script to handle this.

$VMName = Read-Host "Enter VM Name or name pattern (Example NYC*)"  
 $VM = Get-VM $VMName  
 Foreach ($v in $VM) {  
   $Disks = Get-VMHardDiskDrive $v  
   $Permissions = $v.VMId.Guid + ":F"  
   Foreach ($d in $Disks) {  
     icacls $d.path /grant $Permissions  

This script will ask you for the name of the VM. You can enter a single VM name or a text pattern to query a list of VMs.

Then the script loops through each of the VMs, finds the disks for each VM, and sets the permissions.

If you have snapshots on the VMs, it properly sets the permissions on the .avhd file in use for the snapshot. I have not verified whether permissions on the main .vhdx file that the snapshot is based on need to be modified after a snapshot is removed. Worst case, just run the script again for the VM and it will fix it after the snapshot is removed.