PowershellVMWare

Match Windows Disks to VMWare VMDK Files

By January 27, 2021February 3rd, 2021No Comments

When expanding the disk size of a VMWare virtual machine or deleting a disk, sometimes it is hard to understand which VMware virtual disk matches the specific Windows VM disk. If there are few disks and they differ by their size, it is easy to find the disk you need. But what to do if several VMDK (or RDM) disks of the same size or several virtual SCSI controllers have been created for a VM? How to avoid errors and select the disk a Windows administrator asks you to expand (or shrink)?

In this post I’ll show you how to match Windows disks and virtual disks (VMDK) on a VMWare VM server.

How to Get SCSI Device Number in Windows and VMWare?

Open the Disk Management console (diskmgmt.msc) in Windows (in our example, it is Windows Server 2016). The SCSI controller name and SCSI device number are not displayed in the list of disks. To get the SCSI device number, right-click a disk and select Properties. As you can see, the information about the device port for VMWare Virtual disk SCSI Disk Device is shown in the Location field of the General tab.

  • Location 160 = SCSI Bus Controller 0
  • Target ID 1 = device SCSI ID is 1

Join the data you see and get the SCSI disk address: SCSI(0:1).

windows get scsi disk number - bus number, target id

Then open the virtual machine properties in your VMWare vSphere Client. Find the disk that has the same Virtual Device Node number as the ID you have got. In our example, it is SCSI(0:1) Hard Disk 2.

Get VMWare Virtual Disk SCSI ID

If multiple virtual disks with different SCSI controllers are configured on a virtual machine (you can add up to 4 SCSI controllers with 16 disks each to a virtual machine), it is quite challenging to find a SCSI device number manually. Also please note that SCSI controller numbers in Windows and VMWare may differ.

How to Match Windows Disk with VMDK by UUID/Serial Number Using PowerShell?

Another way to map VMWare virtual disks to disks inside a guest VM is to compare their unique disk IDs. In VMWare this attribute is called UUID (Unique ID), and in Windows – a Serial Number. Let’s see how to get UUID and SerialNumber of a virtual disk using PowerShell. By default, all VMWare VMs have the disk EnableUUID=TRUE parameter enabled. It means that a guest OS must see virtual disk IDs.

To get the information about disks in Windows, you can use the Storage module cmdlets or WMI queries. Since we still have some VMs running Windows Server 2008 R2 that do not have the Storage module, we will use WMI.

To get a SCSI controller number, a SCSI device number on it, a serial number of a virtual disk (SerialNumber/UUID), a disk size and disk number in Windows, run this PowerShell command:

$DiskInfo = foreach ($disk in Get-WmiObject Win32_DiskDrive) {
    [pscustomobject]@{
    "DeviceID"=$disk.DeviceID;
    "Caption"=$disk.Caption;
    "Capacity (GB)"=[math]::Round($disk.size / 1GB,0);  
    "SerialNumber" =$disk.SerialNumber
    "SCSIControllerNum"=$disk.scsiport;
    "SCSIDeviceNum"=$disk.scsitargetid;   
    }
}
$DiskInfo|ft

In our example, Windows has detected three disks:

  • PHYSICALDRIVE0: SCSI Port 0, SCSI Target 0, Serial 6000c2939b157427dadbace321ed4973
  • PHYSICALDRIVE1: SCSI Port 0, SCSI Target 1, Serial 6000c2950ee961954909938642bb03b4
  • PHYSICALDRIVE1: SCSI Port 4, SCSI Target 10, Serial 6000c2995fc3c4928d6650596bb02cef
get SCSI Controller and Device Number using Windows Powershell

Then let’s try to get SCSI controller numbers and UUIDs of the disks specified in the settings of the VMWare virtual machine. To view the VM settings, use the PowerCLI console.

Import-Module VMware.VimAutomation.Core -ErrorAction SilentlyContinue
connect-viserver my-esxi-server-01 

$vmName="my-server-01"
$vmHardDisks = Get-VM -Name $vmName | Get-HardDisk 
$vmDatacenterView = Get-VM -Name $vmName | Get-Datacenter | Get-View 
$virtualDiskManager = Get-View -Id VirtualDiskManager-virtualDiskManager 

$vmresults = @()  
foreach ($vmHardDisk in $vmHardDisks)  
{  
  $string = $vmHardDisk.Filename
  $vmHardDiskUuid = ($vmHardDisk.ExtensionData.Backing.Uuid | ForEach-Object {$_.replace(' ','').replace('-','')})
  $vmresult = "" | Select-Object vmHardDiskDatastore,vmHardDiskVmdk,vmHardDiskName,vmHardDiskSize,vmHardDiskUuid
  $vmresult.vmHardDiskDatastore = $vmHardDisk.filename.split(']')[0].split('[')[1]  
  $vmresult.vmHardDiskVmdk = $vmHardDisk.filename.split(']')[1].trim()  
  $vmresult.vmHardDiskName = $vmHardDisk.Name
  $vmresult.vmHardDiskSize = $vmHardDisk.CapacityGB
  $vmresult.vmHardDiskUuid = $vmHardDiskUuid    
  $vmresults += $vmresult  
}
$vmresults | ft 

This script will connect to the vCenter (or ESXi) server and get the list of disks for the specified VM.  The result must contain the DataStore name, VMDK file path, disk number, disk size and UUID.

vmware powercli - get vmdk uuid

Then you can manually match the disks you see in the guest Windows OS with VMWare virtual disks by their UUIDs.

If you have administrator permissions in the guest OS of the VM, you can match Windows disks and VMWare VMDK files using a more convenient PowerShell script. The script connects the guest Windows OS over the network, collects the information about its local disks and matches them with VMWare VMDKs.

Here is the full code of the PowerShell script:

Import-Module VMware.VimAutomation.Core -ErrorAction SilentlyContinue
connect-viserver my-esxi-server-01
$vmName = "my-server-01"
$WinHostName = "my-server-01.contoso.com"
#Get the list of disks of a VMWare virtual machine
$vmDisks = Get-VM -Name $vmName | Get-HardDisk 
$vmDatacenterView = Get-VM -Name $vmName | Get-Datacenter | Get-View 
$virtualDiskManager = Get-View -Id VirtualDiskManager-virtualDiskManager 
# Enter the administrator credentials to access the guest Windows
$cred = if ($cred){$cred}else{Get-Credential}  
# Getting the list of Windows disks and partitions using WMI
$winDisk  = Get-WmiObject -Class Win32_DiskDrive -ComputerName $WinHostName -Credential $cred
$diskToDriveVolume = Get-WmiObject Win32_DiskDrive -ComputerName $WinHostName -Credential $cred| % {
  $disk = $_
  $partitions = "ASSOCIATORS OF " +
                "{Win32_DiskDrive.DeviceID='$($disk.DeviceID)'} " +
                "WHERE AssocClass = Win32_DiskDriveToDiskPartition"
  Get-WmiObject -Query $partitions -ComputerName $WinHostName -Credential $cred| % {
    $partition = $_
    $drives = "ASSOCIATORS OF " +
              "{Win32_DiskPartition.DeviceID='$($partition.DeviceID)'} " +
              "WHERE AssocClass = Win32_LogicalDiskToPartition"
    Get-WmiObject -Query $drives  -ComputerName $WinHostName -Credential $cred| % {
      New-Object -Type PSCustomObject -Property @{
        Disk        = $disk.DeviceID
        DriveLetter = $_.DeviceID
        VolumeName  = $_.VolumeName
       }
    }
  }
}
 #Getting a disk serial number 
foreach ($disk in $winDisk)  
{  
  $disk | Add-Member -MemberType NoteProperty -Name AltSerialNumber -Value $null 
  $diskSerialNumber = $disk.SerialNumber  
  if ($disk.Model -notmatch 'VMware Virtual disk SCSI Disk Device')  
  {  
    if ($diskSerialNumber -match '^\S{12}$'){$diskSerialNumber = ($diskSerialNumber | foreach {[byte[]]$bytes = $_.ToCharArray(); $bytes | foreach {$_.ToString('x2')} }  ) -join ''}  
    $disk.AltSerialNumber = $diskSerialNumber 
  }  
}  
#Searching all VM disks and matching them with Windows disks by their SerialNumber / UUID
$diskMaps = @()  
foreach ($vmDisk in $vmDisks)  
{  
  $vmDiskUuid = $virtualDiskManager.queryvirtualdiskuuid($vmDisk.Filename, $vmDatacenterView.MoRef) | foreach {$_.replace(' ','').replace('-','')}  
  $windowsDisk = $winDisk | where {$_.SerialNumber -eq $vmDiskUuid}  
  if (-not $windowsDisk){$windowsDisk = $winDisk | where {$_.AltSerialNumber -eq $vmDisk.ScsiCanonicalName.substring(12,24)}}  
  $curDiskMap = "" | select  vmDiskDatastore, vmDiskVmdk, vmDiskName, windowsDiskIndex,  vmDiskUuid, windowsDeviceID, drives, volumes  
  $curDiskMap.vmDiskDatastore = $vmDisk.filename.split(']')[0].split('[')[1]  
  $curDiskMap.vmDiskVmdk = $vmDisk.filename.split(']')[1].trim()  
  $curDiskMap.vmDiskName = $vmDisk.Name  
  $curDiskMap.windowsDiskIndex = if ($windowsDisk){$windowsDisk.Index}else{"FAILED TO MATCH"}  
  $curDiskMap.vmDiskUuid = $vmDiskUuid  
  $curDiskMap.windowsDeviceID = if ($windowsDisk){$windowsDisk.DeviceID}else{"FAILED TO MATCH"}  
  $driveVolumes = $diskToDriveVolume | where {$_.Disk -eq $windowsDisk.DeviceID}
  $curDiskMap.drives = $driveVolumes.DriveLetter
  $curDiskMap.volumes = $driveVolumes.VolumeName
  $diskMaps += $curDiskMap
}  
$diskMaps = $diskMaps | sort {[int]$_.vmDiskName.split(' ')[2]}  
$diskMaps | ft

The script also returns information about drive letters and volume labels in Windows.

powershell script to map Mapping guest VM drives to corresponding vmware VMDK files

Now you can easily find what Windows disk matches the given virtual vmdk disk. If virtual disks in Windows are connected via mount points, there will be no information about the assigned drive letters and volume labels in the output.

Leave a Reply