![](/style/images/good.png)
![](/style/images/bad.png)
Matching VMDKs to Windows drive letters using PowerCLI
source link: https://virtuallysober.com/2018/11/29/matching-vmdks-to-windows-drive-letters-using-powercli/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Matching VMDKs to Windows drive letters using PowerCLI
![MatchingDiskByUUIDBannerv1.png?fit=2077%2C1126&ssl=1](https://i0.wp.com/virtuallysober.com/wp-content/uploads/2018/11/MatchingDiskByUUIDBannerv1.png?fit=2077%2C1126&ssl=1)
Recently one of the largest Zerto customers (10,000 protected VMs) reached out to me asking for help. It was early Friday evening, I already had a beer open, so I thought why not? Using PowerCLI they needed to match VMDKs to Windows drive letters in order to mark them as SWAP disks in their VM onboarding script. Pretty important as this saves bandwidth by Zerto not replicating changes to page files, TempDBs and SQL backup disks. In this post I’ll show you how to do this with no network access to the guest VM.
![VMDiskLayout](https://i2.wp.com/virtuallysober.com/wp-content/uploads/2018/11/VMDiskLayout.png?resize=416%2C361&ssl=1)
A quick google search turned up many examples of matching VMDKs to drive letters, but the majority relied on Get-WmiObject. This requires network access to the Guest which they don’t have, ruling out this option.
I next found a great example from Cody Hosterman here which doesn’t need network access to the guest VM, but it does rely on the advanced VM setting Disk.EnableUUID being enabled. Sod’s law they didn’t even have this setting present, never mind enabled, and adding it would require a reboot. Not a viable option for 10,000 VMs!
![VMConfigDiskUUID](https://i0.wp.com/virtuallysober.com/wp-content/uploads/2018/11/VMConfigDiskUUID.png?resize=397%2C286&ssl=1)
My solution was to combine the Disk.EnableUUID element of Cody’s module along with his use of ConvertTo-CSV to get usable data from PowerShell inside Invoke-VMScript (genius idea!). Iterating through each VMDK I match on disk UUID if present, if not then match based on the disk size in bytes. I also record which data point the disk was matched on with UUID being the preferred. Why? Because if you have 2 disks the same size and no disk UUID the script can only tell you all the possible drive letters. But this won’t happen to you, because you followed good practice by having each a different size, didn’t you?
The end result is this:
![MatchingDiskByUUID](https://i0.wp.com/virtuallysober.com/wp-content/uploads/2018/11/MatchingDiskByUUID.png?resize=1468%2C245&ssl=1)
![MatchingDiskBySize](https://i0.wp.com/virtuallysober.com/wp-content/uploads/2018/11/MatchingDiskBySize.png?resize=1197%2C243&ssl=1)
MatchingDriveLettersToVMDKsv1.zip
Extract it to C:\MatchingDriveLettersToVMDKsv1\ and run the script required. Or you can copy and paste them from below, starting with the version which prompts for guest creds:
################################################ # Configure the variables below for the vCenter ################################################ $VMName = "SQL16-VM01" $vCenter = "192.168.1.10" $ScriptDirectory = "C:\MatchingDriveLettersToVMDKsv1" ################################################ # Running the script, nothing to change below ################################################ ####################### # Importing Guest VM credentials ####################### # Setting credential file $VMCredentialsFile = $ScriptDirectory + "\VMCredentials.xml" # Testing if file exists $VMCredentialsFileTest = Test-Path $VMCredentialsFile # IF doesn't exist, prompting and saving credentials IF ($VMCredentialsFileTest -eq $False) { $VMCredentials = Get-Credential -Message "Enter VM script run credentials" $VMCredentials | EXPORT-CLIXML $VMCredentialsFile -Force } ELSE { # Importing credentials $VMCredentials = IMPORT-CLIXML $VMCredentialsFile } ####################### # Importing vCenter credentials ####################### # Setting credential file $vCenterCredentialsFile = $ScriptDirectory + "\vCenterCredentials.xml" # Testing if file exists $vCenterCredentialsFileTest = Test-Path $vCenterCredentialsFile # IF doesn't exist, prompting and saving credentials IF ($vCenterCredentialsFileTest -eq $False) { $vCenterCredentials = Get-Credential -Message "Enter vCenter login credentials" $vCenterCredentials | EXPORT-CLIXML $vCenterCredentialsFile -Force } ELSE { # Importing credentials $vCenterCredentials = IMPORT-CLIXML $vCenterCredentialsFile } ####################### # Installing then importing PowerCLI module ####################### $PowerCLIModuleCheck = Get-Module -ListAvailable VMware.PowerCLI IF ($PowerCLIModuleCheck -eq $null) { Install-Module -Name VMware.PowerCLI –Scope CurrentUser -Confirm:$false -AllowClobber } # Importing PowerCLI Import-Module VMware.PowerCLI ####################### # Connecting to vCenter ####################### Connect-VIServer -Server $vCenter -Credential $vCenterCredentials ##################### # Getting VM guest disk info ##################### $VMGuestDiskScript = Invoke-VMScript -ScriptText { # Creating alphabet array $Alphabet=@() 65..90|ForEach{$Alphabet+=[char]$_} # Getting drive letters inside the VM where the drive letter is in the alphabet $DriveLetters = Get-Partition | Where-Object {($Alphabet -match $_.DriveLetter)} | Select -ExpandProperty DriveLetter # Reseting serials $DiskArray = @() # For each drive letter getting the serial number ForEach ($DriveLetter in $DriveLetters) { # Getting disk info $DiskInfo = Get-Partition -DriveLetter $DriveLetter | Get-Disk | Select * $DiskSize = $DiskInfo.Size $DiskUUID = $DiskInfo.SerialNumber # Formatting serial to match in vSphere, if not null IF ($DiskSerial -ne $null) { $DiskSerial = $DiskSerial.Replace("_","").Replace(".","") } # Adding to array $DiskArrayLine = New-Object PSObject $DiskArrayLine | Add-Member -MemberType NoteProperty -Name "DriveLetter" -Value "$DriveLetter" $DiskArrayLine | Add-Member -MemberType NoteProperty -Name "SizeInBytes" -Value "$DiskSize" $DiskArrayLine | Add-Member -MemberType NoteProperty -Name "UUID" -Value "$DiskUUID" $DiskArray += $DiskArrayLine } # Converting Disk Array to CSV data format $DiskArrayData = $DiskArray | ConvertTo-Csv # Returning Disk Array CSV data to main PowerShell script $DiskArrayData # End of invoke-vmscript below } -VM $VMName -ToolsWaitSecs 120 -GuestCredential $VMCredentials # Pulling the serials from the invoke-vmscript and trimming blank spaces $VMGuestDiskCSVData = $VMGuestDiskScript.ScriptOutput.Trim() # Converting from CSV format $VMGuestDiskData = $VMGuestDiskCSVData | ConvertFrom-Csv # Hostoutput of VM Guest Data " VMGuestDiskData:" $VMGuestDiskData | Format-Table -AutoSize ##################### # Building list of VMDKs for the Customer VM ##################### # Creating array $VMDKArray = @() # Getting VMDKs for the VM $VMDKs = Get-VM $VMName | Get-HardDisk # For Each VMDK building table array ForEach($VMDK in $VMDKs) { # Getting VMDK info $VMDKFile = $VMDK.Filename $VMDKName = $VMDK.Name $VMDKControllerKey = $VMDK.ExtensionData.ControllerKey $VMDKUnitNumber = $VMDK.ExtensionData.UnitNumber $VMDKDiskDiskSizeInGB = $VMDK.CapacityGB $VMDKDiskDiskSizeInBytes = $VMDK.ExtensionData.CapacityInBytes # Getting UUID $VMDKUUID = $VMDK.extensiondata.backing.uuid.replace("-","") # Using Controller key to get SCSI bus number $VMDKBus = $VMDK.Parent.Extensiondata.Config.Hardware.Device | Where {$_.Key -eq $VMDKControllerKey} $VMDKBusNumber = $VMDKBus.BusNumber # Creating SCSI ID $VMDKSCSIID = "scsi:"+ $VMDKBusNumber + ":" + $VMDKUnitNumber # Matching VMDK to drive letter based on UUID first, if no serial UUID matching on size in bytes $VMDKDiveLetter = $VMGuestDiskData | Where-Object {$_.UUID -eq $VMDKUUID} | Select -ExpandProperty DriveLetter $VMDKMatchOn = "UUID" IF ($VMDKDiveLetter -eq $null) { $VMDKDiveLetter = $VMGuestDiskData | Where-Object {$_.SizeInBytes -eq $VMDKDiskDiskSizeInBytes} | Select -ExpandProperty DriveLetter $VMDKMatchOn = "Size" } # Matching drive letter for marking SWAP disk IF ($SWAPDriveLetters -match $VMDKDiveLetter) { $VMDKSwap = "true" } ELSE { $VMDKSwap = "false" } # Creating array of VMDKs $VMDKArrayLine = New-Object PSObject $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "VM" -Value $VMName $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "DiskName" -Value $VMDKName $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "DriveLetter" -Value $VMDKDiveLetter $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "MatchedOn" -Value $VMDKMatchOn $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "DiskSizeGB" -Value $VMDKDiskDiskSizeInGB $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "SCSIBus" -Value $VMDKBusNumber $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "SCSIUnit" -Value $VMDKUnitNumber $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "SCSIID" -Value $VMDKSCSIID $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "DiskUUID" -Value $VMDKUUID $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "DiskSizeBytes" -Value $VMDKDiskDiskSizeInBytes $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "DiskFile" -Value $VMDKFile $VMDKArray += $VMDKArrayLine } ##################### # Final host output of VMDK array ##################### $VMDKArray | Format-Table -AutoSize ##################### # End of script #####################
Without guest creds:
################################################ # Configure the variables below for the vCenter ################################################ $VMName = "SQL16-VM01" $vCenter = "192.168.1.10" $ScriptDirectory = "C:\MatchingDriveLettersToVMDKsv1" ################################################ # Running the script, nothing to change below ################################################ ####################### # Importing vCenter credentials ####################### # Setting credential file $vCenterCredentialsFile = $ScriptDirectory + "\vCenterCredentials.xml" # Testing if file exists $vCenterCredentialsFileTest = Test-Path $vCenterCredentialsFile # IF doesn't exist, prompting and saving credentials IF ($vCenterCredentialsFileTest -eq $False) { $vCenterCredentials = Get-Credential -Message "Enter vCenter login credentials" $vCenterCredentials | EXPORT-CLIXML $vCenterCredentialsFile -Force } ELSE { # Importing credentials $vCenterCredentials = IMPORT-CLIXML $vCenterCredentialsFile } ####################### # Installing then importing PowerCLI module ####################### $PowerCLIModuleCheck = Get-Module -ListAvailable VMware.PowerCLI IF ($PowerCLIModuleCheck -eq $null) { Install-Module -Name VMware.PowerCLI –Scope CurrentUser -Confirm:$false -AllowClobber } # Importing PowerCLI Import-Module VMware.PowerCLI ####################### # Connecting to vCenter ####################### Connect-VIServer -Server $vCenter -Credential $vCenterCredentials ##################### # Getting VM guest disk info ##################### $VMGuestDiskScript = Invoke-VMScript -ScriptText { # Creating alphabet array $Alphabet=@() 65..90|ForEach{$Alphabet+=[char]$_} # Getting drive letters inside the VM where the drive letter is in the alphabet, can't filter on null or empty for some reason $DriveLetters = Get-Partition | Where-Object {($Alphabet -match $_.DriveLetter)} | Select -ExpandProperty DriveLetter # Reseting serials $DiskArray = @() # For each drive letter getting the serial number ForEach ($DriveLetter in $DriveLetters) { # Getting disk info $DiskInfo = Get-Partition -DriveLetter $DriveLetter | Get-Disk | Select * $DiskSize = $DiskInfo.Size $DiskUUID = $DiskInfo.SerialNumber # Formatting serial to match in vSphere, if not null IF ($DiskSerial -ne $null) { $DiskSerial = $DiskSerial.Replace("_","").Replace(".","") } # Adding to array $DiskArrayLine = New-Object PSObject $DiskArrayLine | Add-Member -MemberType NoteProperty -Name "DriveLetter" -Value "$DriveLetter" $DiskArrayLine | Add-Member -MemberType NoteProperty -Name "SizeInBytes" -Value "$DiskSize" $DiskArrayLine | Add-Member -MemberType NoteProperty -Name "UUID" -Value "$DiskUUID" $DiskArray += $DiskArrayLine } # Converting Disk Array to CSV data format $DiskArrayData = $DiskArray | ConvertTo-Csv # Returning Disk Array CSV data to main PowerShell script $DiskArrayData # End of invoke-vmscript below } -VM $VMName -ToolsWaitSecs 120 # Pulling the serials from the invoke-vmscript and trimming blank spaces $VMGuestDiskCSVData = $VMGuestDiskScript.ScriptOutput.Trim() # Converting from CSV format $VMGuestDiskData = $VMGuestDiskCSVData | ConvertFrom-Csv # Hostoutput of VM Guest Data " VMGuestDiskData:" $VMGuestDiskData | Format-Table -AutoSize ##################### # Building list of VMDKs for the Customer VM ##################### # Creating array $VMDKArray = @() # Getting VMDKs for the VM $VMDKs = Get-VM $VMName | Get-HardDisk # For Each VMDK building table array ForEach($VMDK in $VMDKs) { # Getting VMDK info $VMDKFile = $VMDK.Filename $VMDKName = $VMDK.Name $VMDKControllerKey = $VMDK.ExtensionData.ControllerKey $VMDKUnitNumber = $VMDK.ExtensionData.UnitNumber $VMDKDiskDiskSizeInGB = $VMDK.CapacityGB $VMDKDiskDiskSizeInBytes = $VMDK.ExtensionData.CapacityInBytes # Getting UUID $VMDKUUID = $VMDK.extensiondata.backing.uuid.replace("-","") # Using Controller key to get SCSI bus number $VMDKBus = $VMDK.Parent.Extensiondata.Config.Hardware.Device | Where {$_.Key -eq $VMDKControllerKey} $VMDKBusNumber = $VMDKBus.BusNumber # Creating SCSI ID $VMDKSCSIID = "scsi:"+ $VMDKBusNumber + ":" + $VMDKUnitNumber # Matching VMDK to drive letter based on UUID first, if no serial UUID matching on size in bytes $VMDKDiveLetter = $VMGuestDiskData | Where-Object {$_.UUID -eq $VMDKUUID} | Select -ExpandProperty DriveLetter $VMDKMatchOn = "UUID" IF ($VMDKDiveLetter -eq $null) { $VMDKDiveLetter = $VMGuestDiskData | Where-Object {$_.SizeInBytes -eq $VMDKDiskDiskSizeInBytes} | Select -ExpandProperty DriveLetter $VMDKMatchOn = "Size" } # Matching drive letter for marking SWAP disk IF ($SWAPDriveLetters -match $VMDKDiveLetter) { $VMDKSwap = "true" } ELSE { $VMDKSwap = "false" } # Creating array of VMDKs $VMDKArrayLine = New-Object PSObject $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "VM" -Value $VMName $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "DiskName" -Value $VMDKName $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "DriveLetter" -Value $VMDKDiveLetter $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "MatchedOn" -Value $VMDKMatchOn $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "DiskSizeGB" -Value $VMDKDiskDiskSizeInGB $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "SCSIBus" -Value $VMDKBusNumber $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "SCSIUnit" -Value $VMDKUnitNumber $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "SCSIID" -Value $VMDKSCSIID $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "DiskUUID" -Value $VMDKUUID $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "DiskSizeBytes" -Value $VMDKDiskDiskSizeInBytes $VMDKArrayLine | Add-Member -MemberType NoteProperty -Name "DiskFile" -Value $VMDKFile $VMDKArray += $VMDKArrayLine } ##################### # Final host output of VMDK array ##################### $VMDKArray | Format-Table -AutoSize ##################### # End of script #####################
Happy scripting,
Like this:
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK