The PowerShell script (using PowerCLI) I discuss in this post maps virtual disks of a VMware vSphere host to volumes on Windows drives. This information is useful if you have to extend the storage space of Windows volumes.

Let's say I have a VMware VM with Windows Server as the guest operating system. The host has three SCSI controllers and 10 drives on each controller. Some drives have different sizes and some are the same.

At some point, the drive space runs low and you're looking at the ticket to add space to the volumes of a VM with tens of virtual disks. Of course, the admin working on this server just sees the Windows volumes. But to complete this task successfully, I need to know how the Windows volumes correspond to VM disks.

If you are looking at the VM from the VMware vSphere client, everything looks nice and simple:

SCSI controllers with assigned drives on vSphere client

SCSI controllers with assigned drives on vSphere client

But when you looking on the drives from Windows, you'll notice the problem.

Windows Disk Management is not showing SCSI controllers

Windows Disk Management is not showing SCSI controllers

As you can see in the above screenshot, SCSI controllers are not visible in Windows Disk Management. For instance, if I have three different Windows volumes that I want to expand and a bunch of other drives, some of them potentially having the same size as those three, how am I going to determine where I need to add the space? There is no explicit dependency between the VMware virtual drive number and the drive number in Windows.

That’s where PowerShell can help. The script below gets all drives from the VMware virtual machine and the corresponding Windows guest, matches them to each other, and then saves the result to the .csv file.

Add-PSSnapin "Vmware.VimAutomation.Core" 
Connect-VIServer -Server myvcenter.com
$VM = get-vm "testsql" #Replace this string with your VM name
$VMSummaries = @()
$DiskMatches = @()
$VMView = $VM | Get-View
    ForEach ($VirtualSCSIController in ($VMView.Config.Hardware.Device | Where {$_.DeviceInfo.Label -match "SCSI Controller"}))
        {
        ForEach ($VirtualDiskDevice  in ($VMView.Config.Hardware.Device | Where {$_.ControllerKey -eq $VirtualSCSIController.Key}))
            {
            $VMSummary = "" | Select VM, HostName, PowerState, DiskFile, DiskName, DiskSize, SCSIController, SCSITarget
            $VMSummary.VM = $VM.Name
            $VMSummary.HostName = $VMView.Guest.HostName
            $VMSummary.PowerState = $VM.PowerState
            $VMSummary.DiskFile = $VirtualDiskDevice.Backing.FileName
            $VMSummary.DiskName = $VirtualDiskDevice.DeviceInfo.Label
            $VMSummary.DiskSize = $VirtualDiskDevice.CapacityInKB * 1KB
            $VMSummary.SCSIController = $VirtualSCSIController.BusNumber
            $VMSummary.SCSITarget = $VirtualDiskDevice.UnitNumber
            $VMSummaries += $VMSummary
            }
        }
$Disks = Get-WmiObject -Class Win32_DiskDrive -ComputerName $VM.Name
$Diff = $Disks.SCSIPort | sort-object -Descending | Select -last 1 
foreach ($device in $VMSummaries)
   {
    $Disks | % {if((($_.SCSIPort - $Diff) -eq $device.SCSIController) -and ($_.SCSITargetID -eq $device.SCSITarget))
           {
             $DiskMatch = "" | Select VMWareDisk, VMWareDiskSize, WindowsDeviceID, WindowsDiskSize 
             $DiskMatch.VMWareDisk = $device.DiskName
             $DiskMatch.WindowsDeviceID = $_.DeviceID.Substring(4)
             $DiskMatch.VMWareDiskSize = $device.DiskSize/1gb
             $DiskMatch.WindowsDiskSize =  [decimal]::round($_.Size/1gb)
             $DiskMatches+=$DiskMatch
            }
        }
   }
$DiskMatches | export-csv -path "c:\temp\$($VM.Name)drive_matches.csv"

Let’s go line by line through the script.

Add-PSSnapin "Vmware.VimAutomation.Core"
Connect-VIServer -Server myvcenter.com

The first two lines add the VMware PowerCLI snapin to the current session and create a connection to the VMware vCenter server.

$VM = get-vm "testsql"

This one is just getting the VM object data and storing it in the $VM variable.

$VMSummaries = @()
$DiskMatches = @()

Those two lines above create hash tables to save the VMware and Windows SCSI controllers and drives’ data.

 $VMView = $VM | Get-View

There is no way to get the SCSI controller data from the VMware VM using default fields, so I’m kicking the Get-View cmdlet to retrieve the corresponding .net object information.

ForEach ($VirtualSCSIController in ($VMView.Config.Hardware.Device | Where {$_.DeviceInfo.Label -match "SCSI Controller"}) {

The loop above gets all SCSI controller data from the VM and iterates through them.

ForEach ($VirtualDiskDevice  in ($VMView.Config.Hardware.Device | Where {$_.ControllerKey -eq $VirtualSCSIController.Key})) {

Now I’m doing something similar to the previous operation with all the VM disks. I iterate through the disks to find those that are connected to the SCSI controller from the previous loop.

$VMSummary = "" | Select VM, HostName, PowerState, DiskFile, DiskName, DiskSize, SCSIController, SCSITarget
			$VMSummary.VM = $VM.Name
			$VMSummary.HostName = $VMView.Guest.HostName
			$VMSummary.PowerState = $VM.PowerState
			$VMSummary.DiskFile = $VirtualDiskDevice.Backing.FileName
			$VMSummary.DiskName = $VirtualDiskDevice.DeviceInfo.Label
			$VMSummary.DiskSize = $VirtualDiskDevice.CapacityInKB * 1KB
			$VMSummary.SCSIController = $VirtualSCSIController.BusNumber
			$VMSummary.SCSITarget = $VirtualDiskDevice.UnitNumber
			$VMSummaries += $VMSummary
			}
		}

In the above lines, I put all the information together. The first line sets up the hash table to store the information about the VM drives. The following eight lines populate this table with the information about the VM name, the VM guest name, its power state, and other drive and SCSI controller details. The last line adds the current record to the hash table $VMSummaries.

I now have all the needed information about the VMware SCSI controller and disks. Next, I have to get the same kind of data from Windows.

$Disks = Get-WmiObject -Class Win32_DiskDrive -ComputerName $VM.Name

To do that, I’m utilizing Get-WmiObject PowerShell cmdlet, reaching for the Win32_DiskDrive object, which stores the Windows physical disk’s information. I’m putting each Windows physical disk data into $Disks variable.

$Diff = $Disks.SCSIPort | sort-object -Descending | Select -last 1

Now comes the trickiest part. VMware and Windows have different starting numbers for SCSI controllers. VMware always begins with 0 and Windows just uses an arbitrary number. The Windows numbers are incremented one by one from the start number, which can’t be less than 1.

Thus, what I really need to know is the number of the first controller. To extract this number, I’m reaching to the SCSIPort property of each Windows drive object and then find the minimal value using the Sort-Object. This is the first Windows SCSI controller number, which corresponds to the VMware SCSI controller 0. I’m storing the number into the $Diff variable and compare it later with the VMware controller numbers.

ForEach ($device in $VMSummaries)
   {
    $Disks | % {if((($_.SCSIPort - $Diff) -eq $device.SCSIController) -and ($_.SCSITargetID -eq $device.SCSITarget))         {

Here, I’m looping through the $VMSummaries hash table records and then again through the contents of the $Disks variable to compare the disk and controllers data I’ve taken from VMware with the information I got from Windows.

Then I’m checking if the current Windows disk SCSIPort number corresponds to the SCSI controller number I’ve got from VMware. To do so, I’m subtracting the number of the first Windows SCSI controller stored in the $Diff variable from the number of the current controller in the loop and then comparing the result with the VMware SCSI controller number. If the numbers are the same, I’m good to go.

Once I know that this is the right SCSI controller, I need to compare the disk's SCSI target ID. Fortunately, those numbers correspond to each other in VMware and Windows, so I don’t need to do any transformations. If the controller number and the disk number are equal, I’ve found the right drive. Now is the time to save the information:

$DiskMatch = "" | Select VMWareDisk, VMWareDiskSize, WindowsDeviceID, WindowsDiskSize 
             $DiskMatch.VMWareDisk = $device.DiskName
             $DiskMatch.WindowsDeviceID = $_.DeviceID.Substring(4)
             $DiskMatch.VMWareDiskSize = $device.DiskSize/1gb
             $DiskMatch.WindowsDiskSize =  [decimal]::round($_.Size/1gb)
             $DiskMatches+=$DiskMatch
            }
        }

   }

As you can see, I’m putting information about the corresponding VMware and Windows drives into the hash table.

Subscribe to 4sysops newsletter!

$DiskMatches | export-csv -path "c:\temp\$($VM.Name)drive_matches.csv"

The very last line exports all gathered information into a .csv file. At the end, we have a nice table that maps two sets of drives together.

16 Comments
  1. IT Engineer (Rank 2) 7 years ago

    Yes, it works great.

    Thank you for writing such a great script Alex !

  2. Norm Long 7 years ago

    Great Script!  Quick question how can I add volume labels (c: etc) to the script output.

    Thanks Again!

     

    Norm

     

    • Author
      Alex Chaika (Rank 2) 7 years ago

      Well, this is not a real quick question.  The short answer there is no straightforward way to do that.

      You have to firstly get the information about relationship between physical disks and partitions and then the same for logical disks. The first one could be extracted from  Win32_DiskDriveToDiskPartition wmi object, the second one from  Win32_LogicalDiskToPartition.  

  3. Khalid 7 years ago

    Alex ,

    This is script is not working I am using on VMware 5.5 and Powercli 5.5
    Get-WmiObject : Cannot validate argument on parameter ‘ComputerName’. The argum
    ent is null or empty. Supply an argument that is not null or empty and then try
    the command again.
    At D:\Script\VMDK.ps1:23 char:60
    + $Disks = Get-WmiObject -Class Win32_DiskDrive -ComputerName <<<<  $VM.Name
        + CategoryInfo          : InvalidData: (:) [Get-WmiObject], ParameterBindi
       ngValidationException
        + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.Power
       Shell.Commands.GetWmiObjectCommand

    • Author
      Alex Chaika (Rank 2) 7 years ago

      Sorry for late response, I didn’t have much time to look into it recently.

      As you can see the error comes from the get-wmiobject cmdlet and since it is using $VM.Name as a computer name argument, I’d suggest you to look if your VM name corresponds to your windows host name. This construction 

      $Disks = GetWmiObject Class Win32_DiskDrive ComputerName $VM.Name

      assumes that this is true, but if your hostname is different, you have to use your hostname instead of VM.Name in the GetWmiObject.

  4. Alam 7 years ago

    When I ran this script getting this Error

    Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)
    At H:\nalam\disk_map.ps1:23 char:23
    + $Disks = Get-WmiObject <<<<  -Class Win32_DiskDrive -ComputerName $VM.Name
    + CategoryInfo          : InvalidOperation: (:) [Get-WmiObject], COMException
    + FullyQualifiedErrorId : GetWMICOMException,Microsoft.PowerShell.Commands.GetWmiObjectCommand

    • Author
      Alex Chaika (Rank 2) 7 years ago

      Just look on the comment above. The reason it’s failing is that it can’t reach the remote machine by name. So either your VM name doesn’t correspond guest host name or it’s unreachable by network.

  5. VP 7 years ago

    This works great except that it is listing the VMWare and Windows in numerical order and not matching up the lines.  For example:

    Hard disk 7     500           PHYSICALDRIVE6         300

    Obviously these are different disks but listed on the same line because of numerical order.

    Any ideas?

  6. Brian 6 years ago

    Alex this is exactly what I’ve been looking for, so wish it worked for me!  Just spent over an hour with VMware support still with no good solution to match windows disk to VMware disk.  The first few drives in the .csv file are accurate, but then it isn’t.  There are windows disks that are matched to VMware disks of smaller size.  For example a 100GB windows disk matched to a 61GB vmwaredisk.  Would really appreciate it if you could help get this working for us, Thanks!

  7. Curtis Redkey 6 years ago

    Thank you Alex for a great script! I have four controllers with many disks attached because these systems are huge mail servers using Exchange Database Availability Groups and many drives are the same initial size and then grow from 1024 GB. The problem I am having is only the scsi ids 0,1,2 are mapped the scsi id 3 drives are not mapped. I am very happy getting the 10/17 drives mapped quickly, it would be ideal if they could all get mapped. 🙂

    Details:
    #TYPE Selected.System.String
    VMWareDisk VMWareDiskSize WindowsDeviceID WindowsDiskSize
    Hard disk 1 60 PHYSICALDRIVE0 60
    Hard disk 2 60 PHYSICALDRIVE1 60
    Hard disk 6 1024 PHYSICALDRIVE2 1024
    Hard disk 3 1055 PHYSICALDRIVE3 1055
    Hard disk 5 1054 PHYSICALDRIVE5 1054
    Hard disk 16 1024 PHYSICALDRIVE4 1024
    Hard disk 4 1024 PHYSICALDRIVE6 1024
    Hard disk 13 800 PHYSICALDRIVE14 800
    Hard disk 14 800 PHYSICALDRIVE15 800
    Hard disk 15 1024 PHYSICALDRIVE7 1024

    Disk on fourth controller
    VMWare SCSI (3:0) Hard disk 7
    Virtual Guest Disk8 Location 193 (Bus Number 0, Target Id 0, LUN 0)

  8. Curtis Redkey 6 years ago

    I decided that getting the VMWare Disk and SCSI Bus and SCSI ID was really all I needed and I could put it together from there to match the host.

    Here is the code I used to get the details:

    Connect-VIServer -Server MYVCENTER
    Get-VM MYVMGUEST | Get-HardDisk |
    Select @{N=’VM’;E={$_.Parent.Name}},
    Name,
    @{N=’SCSIid’;E={
    $hd = $_
    $ctrl = $hd.Parent.Extensiondata.Config.Hardware.Device | where{$_.Key -eq $hd.ExtensionData.ControllerKey}
    “$($ctrl.BusNumber):$($_.ExtensionData.UnitNumber)”
    }} | export-csv -path “C:\Output\MYVMGUEST_drive_table.csv”

  9. Akshay 5 years ago

    This is great script 🙂

    Just wanted to know how can we extend the drive using this script ?

    Like i want to extend D drive from both VM and os level.

    Appreciate the help

  10. Taylor Smith 5 years ago

    Not sure how often or if this thread is monitored, but in regards to Curtis Redkey’s post above, this script will not report on any drives for a Windows 2016 Server other than its first/default SCSI controller.

    For instance, we have a server with 4 SCSI Paravirtual Adapters, with 2-3 drives on each adapter, for a total of 8 drives. Drive 0 is on the first SCSI adapter (adapter 0). So when I run the script it only returns that one drive, not the other 7.

    I have tested this across all versions of Windows going back to 2003, and 2016 consistently has this issue. This is an EXTREMELY valuable script for us. Can anyone lend a hand to make it work? Could it have to do with the way it calls the WMI Object?

    Thanks in advance to anyone who can help, much appreciated!

  11. Erik 5 years ago

    Just to add a little info.

    The SCSIPort does not increment with the actual controller-ID of VMware. It increments every time you ADD a controller.

    So if you have your first disk on (0:0) and windows says SCSIport:2 SCSITargetID:0

    If you add just one disk on (2:1) in VMware, windows will tell you SCSIPort:3 SCSITargetID:1

    It does not add 2 to the first number in VMware. It cares about the order you add them in.

  12. BKhoo 5 years ago

    Hi Alex,

    The script works perfectly.  There’s one suggestion. If the VM contains 2 SCSI controller (currently SCSI Controller 0, SCSI Controller 1), it works ok.

    But if there’s additional 2 more SCSI Controller added (SCSI Controller 2 and SCSI Controller 3), it doesn’t map the drives.

    Hope you can be able to enlighten us.

     

Leave a reply to BKhoo Click here to cancel the reply

Please enclose code in pre tags

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2023

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account