VMware Cloud Community
glwnieves
Enthusiast
Enthusiast
Jump to solution

Script to Map Windows and VMware Disks as well as VMware SCSI Controller configuration

Hey Guys,

So, I'm not that great at scripting put I'm pretty resourceful in finding the appropriate scripts for things I need. I am having an issue though. So I found a script that will Pull the Disk/UUID Information from VMware and from a Windows machine and then match the VMware Disk configuration to the Windows Disk Configuration.

Here is the script I am using for that:

Spoiler
Import-Module VMware.VimAutomation.Core -ErrorAction SilentlyContinue
connect-viserver VCENTERNAME
$vmName = "VM NAME"
$WinHostName = "WINDOWS SERVER NAME"
#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

 Running this produces a result like this:

glwnieves_0-1606167813748.png

I was then asked to also include the VMware SCSI information. I found another script that does that.

 

Spoiler

$vm="VMNAME"

$vmview = Get-View -ViewType VirtualMachine -Filter @{"Name" = $vm}

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})) {

Write-Host SCSI" ("$($VirtualSCSIController.BusNumber):$($VirtualDiskDevice.UnitNumber)")" $VirtualDiskDevice.DeviceInfo.Label

}

}

 That produces the following result:

glwnieves_1-1606167913908.png

 

So I have two questions really.

1. Can someone help me combine both scripts into one?

2. Can someone also help me figure out how to have this script run against a list of machines and export it to a CSV File? The output can either be one file or separate files per VM. I'm okay with either.

I was thinking maybe the script can reference a .txt or csv file that I populate with all of the machines but I'll defer to whatever you guys recommend. The only issue I see there is since you have to define the VM Name and the DNS Name (Windows WMI Portion of the script) I'm not sure how I can do both with one txt input file.

I have about 50 machines I have to run this against and just looking for the easiest way to accomplish this. At a minimum if someone can help be combine both scripts into one that would be awesome. Thanks guys, I appreciate the help! 

Reply
0 Kudos
1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

Since all your VMs are meeting the specific requirements, you could try something like this

 

# User with permission to query the WMI on the guest
$user = 'domain\user'
$pswd = 'VMware1!'
$cred = New-Object -TypeName PSCredential -Argumentlist $user,(ConvertTo-SecureString -String $pswd -AsPlainText -Force) 

# CSV file with a column named vmName
Import-Csv -Path .\vmnames.csv -UseCulture -PipelineVariable row |
ForEach-Object -Process {
    $vm = Get-VM -Name $row.vmName

    # If the script can't determine the FQDN of the Guest OS, the VM is skipped
    $winHostName = ''
    if($vm.Guest.State -eq 'Running'){
        if($vm.Guest.HostName -ne ''){
            $WinHostName = $vm.Guest.HostName
        }
        elseif($vm.Guest.IPAddress){
            $resolve = Resolve-DnsName -Name $vm.guest.IPAddress[0]
            if($resolve){
                $WinHostName = $resolve.NameHost
            }
        }
    }
    if($winHostName){
        $vmDisks = Get-HardDisk -VM $vm | Sort-Object -Property Name
        $vmDatacenterView = Get-VM -Name $vmName | Get-Datacenter | Get-View
        $virtualDiskManager = Get-View -Id VirtualDiskManager-virtualDiskManager

        # 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
        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)}
            }
            $ctrl = $vm.ExtensionData.Config.Hardware.Device | where{$_.Key -eq $vmDisk.ExtensionData.ControllerKey}
            $curDiskMap = "" | select vmDiskDatastore, vmDiskVmdk, SCSIAddress, vmDiskName, windowsDiskIndex, vmDiskUuid, windowsDeviceID, drives, volumes
            $driveVolumes = $diskToDriveVolume | where {$_.Disk -eq $windowsDisk.DeviceID}
            New-Object -TypeName PSObject -Property ([ordered]@{
                VM = $vm.Name
                vmDiskDatastore = $vmDisk.filename.split(']')[0].split('[')[1]
                vmDiskVmdk = $vmDisk.filename.split(']')[1].trim()
                SCSIAddress = "SCSI($($vmDisk.ExtensionData.UnitNumber):$($ctrl.BusNumber))" 
                vmDiskName = $vmDisk.Name
                windowsDiskIndex = if ($windowsDisk){$windowsDisk.Index}else{"FAILED TO MATCH"}
                vmDiskUuid = $vmDiskUuid
                windowsDeviceID = if ($windowsDisk){$windowsDisk.DeviceID}else{"FAILED TO MATCH"}
                drives = $driveVolumes.DriveLetter
                volumes = $driveVolumes.VolumeName
            })
        }
    }
    else{
        Write-Host "Could not determine FQDN for VM ($vm.Name)"
    }
} | Export-Csv -Path .\report.csv -NoTypeInformation -UseCulture

 


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

View solution in original post

Reply
0 Kudos
32 Replies
LucD
Leadership
Leadership
Jump to solution

First, let me start with a word of warning that I always give when I see a question to map Guest OS disks to VM's VMDK files.

There is no fool-proof way to make that mapping.
There are so many exceptions that there is no general rule that works.
And I didn't even start about the mapping in Linux Guest OS.

On the other hand, when certain conditions are met, you might be able to pull it off.
If there is only one Guest OS partition/disk per VMDK.
If there hasn't been any removal/addition of Guest OS partitions and/or VMDK since the creation of the VM.
And a few other prerequisites.

In your case (environment) all the conditions seem to be met.
Then it would be a matter of mapping the SCSI controller to the VMDK and the VMDK to the Guest OS drive/partition.

Is my assumption correct?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
glwnieves
Enthusiast
Enthusiast
Jump to solution

Yes, your assumptions are correct. There is only one Windows partition per VMDK. Also, the number of Guest OS partitions/VMDKs have been the same since the creation of the VM.

 

These servers are used for a specific purpose and that configuration shouldn't have changed since the creation of the VM. So far in all of my tests when I manually went to match up the result it has been accurate thus far. There should be a single standard across all of them.

 

The only reason to run this script is to confirm that they all conform to the same standard. Anything that produces a different/odd result will require us to investigate it anyway. Thanks again for your help.

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Since all your VMs are meeting the specific requirements, you could try something like this

 

# User with permission to query the WMI on the guest
$user = 'domain\user'
$pswd = 'VMware1!'
$cred = New-Object -TypeName PSCredential -Argumentlist $user,(ConvertTo-SecureString -String $pswd -AsPlainText -Force) 

# CSV file with a column named vmName
Import-Csv -Path .\vmnames.csv -UseCulture -PipelineVariable row |
ForEach-Object -Process {
    $vm = Get-VM -Name $row.vmName

    # If the script can't determine the FQDN of the Guest OS, the VM is skipped
    $winHostName = ''
    if($vm.Guest.State -eq 'Running'){
        if($vm.Guest.HostName -ne ''){
            $WinHostName = $vm.Guest.HostName
        }
        elseif($vm.Guest.IPAddress){
            $resolve = Resolve-DnsName -Name $vm.guest.IPAddress[0]
            if($resolve){
                $WinHostName = $resolve.NameHost
            }
        }
    }
    if($winHostName){
        $vmDisks = Get-HardDisk -VM $vm | Sort-Object -Property Name
        $vmDatacenterView = Get-VM -Name $vmName | Get-Datacenter | Get-View
        $virtualDiskManager = Get-View -Id VirtualDiskManager-virtualDiskManager

        # 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
        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)}
            }
            $ctrl = $vm.ExtensionData.Config.Hardware.Device | where{$_.Key -eq $vmDisk.ExtensionData.ControllerKey}
            $curDiskMap = "" | select vmDiskDatastore, vmDiskVmdk, SCSIAddress, vmDiskName, windowsDiskIndex, vmDiskUuid, windowsDeviceID, drives, volumes
            $driveVolumes = $diskToDriveVolume | where {$_.Disk -eq $windowsDisk.DeviceID}
            New-Object -TypeName PSObject -Property ([ordered]@{
                VM = $vm.Name
                vmDiskDatastore = $vmDisk.filename.split(']')[0].split('[')[1]
                vmDiskVmdk = $vmDisk.filename.split(']')[1].trim()
                SCSIAddress = "SCSI($($vmDisk.ExtensionData.UnitNumber):$($ctrl.BusNumber))" 
                vmDiskName = $vmDisk.Name
                windowsDiskIndex = if ($windowsDisk){$windowsDisk.Index}else{"FAILED TO MATCH"}
                vmDiskUuid = $vmDiskUuid
                windowsDeviceID = if ($windowsDisk){$windowsDisk.DeviceID}else{"FAILED TO MATCH"}
                drives = $driveVolumes.DriveLetter
                volumes = $driveVolumes.VolumeName
            })
        }
    }
    else{
        Write-Host "Could not determine FQDN for VM ($vm.Name)"
    }
} | Export-Csv -Path .\report.csv -NoTypeInformation -UseCulture

 


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
glwnieves
Enthusiast
Enthusiast
Jump to solution

So I modified the $user and $pswd values with the appropriate credentials. I created a vmware.csv file in the same directory that this script resides. I created a column "vmName" and I put one of the servers I want to run it against in that column.

Snag_193d3536.png

 

 

I received the following errors from PowerShell:

 

 

 

Get-VM : 11/24/2020 9:49:34 AM Get-VM VM with name 'vEng' was not found using the specified filter(s). 
At line:27 char:29
+ $vmDatacenterView = Get-VM -Name $vmName | Get-Datacenter | G ...
+ ~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (:) [Get-VM], VimException
+ FullyQualifiedErrorId : Core_OutputHelper_WriteNotFoundError,VMware.VimAutomation.ViCore.Cmdlets.Commands.GetVM

Exception calling "QueryVirtualDiskUuid" with "2" argument(s): "A specified parameter was not correct: dc"
At line:65 char:13
+ $vmDiskUuid = $virtualDiskManager.queryvirtualdiskuuid($v ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : VimException

Exception calling "QueryVirtualDiskUuid" with "2" argument(s): "A specified parameter was not correct: dc"
At line:65 char:13
+ $vmDiskUuid = $virtualDiskManager.queryvirtualdiskuuid($v ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : VimException

Exception calling "QueryVirtualDiskUuid" with "2" argument(s): "A specified parameter was not correct: dc"
At line:65 char:13
+ $vmDiskUuid = $virtualDiskManager.queryvirtualdiskuuid($v ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : VimException

Exception calling "QueryVirtualDiskUuid" with "2" argument(s): "A specified parameter was not correct: dc"
At line:65 char:13
+ $vmDiskUuid = $virtualDiskManager.queryvirtualdiskuuid($v ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : VimException

Exception calling "QueryVirtualDiskUuid" with "2" argument(s): "A specified parameter was not correct: dc"
At line:65 char:13
+ $vmDiskUuid = $virtualDiskManager.queryvirtualdiskuuid($v ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : VimException

Exception calling "QueryVirtualDiskUuid" with "2" argument(s): "A specified parameter was not correct: dc"
At line:65 char:13
+ $vmDiskUuid = $virtualDiskManager.queryvirtualdiskuuid($v ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : VimException

Exception calling "QueryVirtualDiskUuid" with "2" argument(s): "A specified parameter was not correct: dc"
At line:65 char:13
+ $vmDiskUuid = $virtualDiskManager.queryvirtualdiskuuid($v ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : VimException

 

 

 

I looked and found the vEng value in the script but not sure if I'm supposed to change this value.

 

 

glwnieves_2-1606230595645.png

 

Also, I'm not sure if the errors caused this but the output mapped all of the Windows Drive letters to the same Uuid.

glwnieves_4-1606231018961.png

 

However, when I run the original script I used without the SCSI controller information it maps correctly.

 
 
 

Snag_19348dc2.png

 

What am I missing?

 

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

I forgot to remove a test line from the script.
Hence the error you got.
I updated the code


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

glwnieves
Enthusiast
Enthusiast
Jump to solution

That fixed it. Thanks so much!!!

Reply
0 Kudos
glwnieves
Enthusiast
Enthusiast
Jump to solution

@LucD So I ran it against a few machines and manually checked the results. I found that while it lists the SCSI controller addresses on the VM they are not properly matching to the VM Hard Disks. All other information is correct. 

 

Here is the export from the script I got from you:

Snag_19c47656.png

 

In the export is shows Hard Disk 2 as using SCSI (1:0) but when I manually checked in VMware Settings is shows that Hard Disk 2 is actually using SCSI (0:1)

Snag_19d33702.png

 

I even made sure that the export was showing the same VMDK disk as in settings and it is.

 

Snag_19d3904d.png

 

 

If I run the two original scripts I had where I do the Mapping and Lists the SCSI controller addresses separately it does match up to what is in VMware settings.

 

Snag_19cb2286.png

Snag_19d33702.png

Snag_19d3904d.png

Do you think I'm trying to do too many things at the same time with one script and should keep the two queries separate or do you think there's an easy fix for this? If you think I should separate them into two scripts how can I get the desired effect of having an input file and export file for each separate script?

 

 

Reply
0 Kudos
glwnieves
Enthusiast
Enthusiast
Jump to solution

@LucD so I ran the script against a few VMs and I'm noticing that the SCSI Controller Address isn't matching up.

 

Snag_19e79b19.png

For example, If you look at Hard Disk 2 the export says it's using SCSI (1:0) but it's actually using SCSI (0:1)

Snag_19e89325.png

 

I even checked the VMDK to make sure that was matching correctly on the Export and it is.

 

Snag_19e95c61.png

 

All of the other information on the export is matching up properly. I even ran the original two scripts I mentioned and it too gave me the correct results of 0:1 for Hard Disk 2.

 

Snag_19ea73cc.png

 

Is there an easy fix for this or do you think I'm trying to do too much with one script? If I need to run them as two separate scripts how can I have the same desired effect of having an input file with the list of servers and an export with the Server Name and the SCSI addresses?

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

My bad, I just noticed that I swapped the controller and the disk in the SCSI address.

It should be

SCSIAddress = "SCSI($($ctrl.BusNumber):$($vmDisk.ExtensionData.UnitNumber))" 


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
glwnieves
Enthusiast
Enthusiast
Jump to solution

@LucD Yup. That was it. Awesome. Last question and I'll stop bugging you. If I wanted to add the Hard Disk Sizes to a column on the export as well what would I need to change in the script?

I assume I have to add the values here but I'm not 100% sure what the values that need to be added and how the formatting needs to be changed.

$curDiskMap = "" | select vmDiskDatastore, vmDiskVmdk, SCSIAddress, vmDiskName, windowsDiskIndex, vmDiskUuid, windowsDeviceID, drives, volumes
            $driveVolumes = $diskToDriveVolume | where {$_.Disk -eq $windowsDisk.DeviceID}
            New-Object -TypeName PSObject -Property ([ordered]@{
                VM = $vm.Name
                vmDiskDatastore = $vmDisk.filename.split(']')[0].split('[')[1]
                vmDiskVmdk = $vmDisk.filename.split(']')[1].trim()
                SCSIAddress = "SCSI($($ctrl.BusNumber):$($vmDisk.ExtensionData.UnitNumber))" 
                vmDiskName = $vmDisk.Name
                windowsDiskIndex = if ($windowsDisk){$windowsDisk.Index}else{"FAILED TO MATCH"}
                vmDiskUuid = $vmDiskUuid
                windowsDeviceID = if ($windowsDisk){$windowsDisk.DeviceID}else{"FAILED TO MATCH"}
                drives = $driveVolumes.DriveLetter
                volumes = $driveVolumes.VolumeName

 

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

No problem, add this line

vmDiskSizeGB = $vmDisk.CapacityGB

 


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
glwnieves
Enthusiast
Enthusiast
Jump to solution

Great. Everything works perfectly now. I noticed there was another leftover from your original testing when you had the $vmName value.

if($winHostName){
$vmDisks = Get-HardDisk -VM $vm | Sort-Object -Property Name
$vmDatacenterView = Get-VM -Name $vmName | Get-Datacenter | Get-View
$virtualDiskManager = Get-View -Id VirtualDiskManager-virtualDiskManager

If I'm not mistaken $vmName should be changed to $vm as well. I was getting errors on that until I changed it to $vm. Is that correct?

if($winHostName){
$vmDisks = Get-HardDisk -VM $vm | Sort-Object -Property Name
$vmDatacenterView = Get-VM -Name $vm | Get-Datacenter | Get-View
$virtualDiskManager = Get-View -Id VirtualDiskManager-virtualDiskManager

Finally, I changed this portion from putting the static credentials inside the powershell script:

# User with permission to query the WMI on the guest
$user = 'Username'
$pswd = 'Password'
$cred = New-Object -TypeName PSCredential -Argumentlist $user,(ConvertTo-SecureString -String $pswd -AsPlainText -Force)

 

To this so it just prompts for the credentials so I can share it will my colleagues:

# User with permission to query the WMI on the guest
$cred = if ($cred){$cred}else{Get-Credential}

Thanks again for all your help!

 

 

 

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Indeed


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

LucD
Leadership
Leadership
Jump to solution

Or even better, just

$vmDatacenterView = Get-Datacenter -VM $vm | Get-View


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

glwnieves
Enthusiast
Enthusiast
Jump to solution

Here is my final version of the script in case anybody wants to reference it:

 

# Load PowerCLI Plugin and connect to VCenter Server (Put in your FQDN where it says YOURVCENTERSERVERNAME)
Import-Module VMware.VimAutomation.Core -ErrorAction SilentlyContinue
connect-viserver YOURVCENTERSERVERNAME

# User with permission to query the WMI on the guest
$cred = if ($cred){$cred}else{Get-Credential}

# CSV file with a column named vmName
Import-Csv -Path .\vmnames.csv -UseCulture -PipelineVariable row |
ForEach-Object -Process {
    $vm = Get-VM -Name $row.vmName

    # If the script can't determine the FQDN of the Guest OS, the VM is skipped
    $winHostName = ''
    if($vm.Guest.State -eq 'Running'){
        if($vm.Guest.HostName -ne ''){
            $WinHostName = $vm.Guest.HostName
        }
        elseif($vm.Guest.IPAddress){
            $resolve = Resolve-DnsName -Name $vm.guest.IPAddress[0]
            if($resolve){
                $WinHostName = $resolve.NameHost
            }
        }
    }
    if($winHostName){
        $vmDisks = Get-HardDisk -VM $vm | Sort-Object -Property Name
        $vmDatacenterView = Get-Datacenter -VM $vm | Get-View
        $virtualDiskManager = Get-View -Id VirtualDiskManager-virtualDiskManager
        
        # 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
        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)}
            }
            $ctrl = $vm.ExtensionData.Config.Hardware.Device | where{$_.Key -eq $vmDisk.ExtensionData.ControllerKey}
            $curDiskMap = "" | select vmDiskDatastore, vmDiskVmdk, SCSIAddress, vmDiskName, windowsDiskIndex, vmDiskUuid, windowsDeviceID, drives, volumes
            $driveVolumes = $diskToDriveVolume | where {$_.Disk -eq $windowsDisk.DeviceID}
            New-Object -TypeName PSObject -Property ([ordered]@{
                VM = $vm.Name
                vmDiskDatastore = $vmDisk.filename.split(']')[0].split('[')[1]
                vmDiskVmdk = $vmDisk.filename.split(']')[1].trim()
                SCSIAddress = "SCSI($($ctrl.BusNumber):$($vmDisk.ExtensionData.UnitNumber))" 
                vmDiskName = $vmDisk.Name
                vmDiskSizeGB = $vmDisk.CapacityGB
                windowsDiskIndex = if ($windowsDisk){$windowsDisk.Index}else{"FAILED TO MATCH"}
                vmDiskUuid = $vmDiskUuid
                windowsDeviceID = if ($windowsDisk){$windowsDisk.DeviceID}else{"FAILED TO MATCH"}
                drives = $driveVolumes.DriveLetter
                volumes = $driveVolumes.VolumeName
                
            })
        }
    }
    else{
        Write-Host "Could not determine FQDN for VM ($vm.Name)"
    }
} | Export-Csv -Path .\report.csv -NoTypeInformation -UseCulture

 

 

Make sure when you're running the PowerShell script that you are in the working directory first where the vmnames.csv file resides. Otherwise it'll complain it can't find the vmnames.csv file. All of the VMs you want to run this against need to go in this file. This will also be the same directory that the report.csv exports to. 

 

You can do a search and export the list of VMs from the Virtual Machines view in vCenter so you don't have to type them all out by hand if you have a lot.

 

Another issue I had was some VMs were affected by a vSphere 6.5 bug where the "disk.EnableUUID = "TRUE" value was missing from multiple vmx configuration files. I'll have to go back and fix that on those VMs.  Without that value the Uuid is not exposed to the Guest OS. Therefore, it can't match the drives up. 

 

You can reference that KB article here.

https://kb.vmware.com/s/article/52815

 

Finally, As @LucD mentioned. This is not a fool proof method and only works under the right conditions. Your mileage may vary. 

HUGE THANKS to @LucD. I wouldn't have even known where to begin (or end) without his help!

 

Reply
0 Kudos
glwnieves
Enthusiast
Enthusiast
Jump to solution

Here is my final version of the script in case anybody wants to reference it:

# Load PowerCLI Plugin and connect to VCenter Server (Put in your FQDN where it says YOURVCENTERSERVERNAME)
Import-Module VMware.VimAutomation.Core -ErrorAction SilentlyContinue
connect-viserver YOURVCENTERSERVERNAME

# User with permission to query the WMI on the guest
$cred = if ($cred){$cred}else{Get-Credential}

# CSV file with a column named vmName
Import-Csv -Path .\vmnames.csv -UseCulture -PipelineVariable row |
ForEach-Object -Process {
    $vm = Get-VM -Name $row.vmName

    # If the script can't determine the FQDN of the Guest OS, the VM is skipped
    $winHostName = ''
    if($vm.Guest.State -eq 'Running'){
        if($vm.Guest.HostName -ne ''){
            $WinHostName = $vm.Guest.HostName
        }
        elseif($vm.Guest.IPAddress){
            $resolve = Resolve-DnsName -Name $vm.guest.IPAddress[0]
            if($resolve){
                $WinHostName = $resolve.NameHost
            }
        }
    }
    if($winHostName){
        $vmDisks = Get-HardDisk -VM $vm | Sort-Object -Property Name
        $vmDatacenterView = Get-Datacenter -VM $vm | Get-View
        $virtualDiskManager = Get-View -Id VirtualDiskManager-virtualDiskManager
        
        # 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
        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)}
            }
            $ctrl = $vm.ExtensionData.Config.Hardware.Device | where{$_.Key -eq $vmDisk.ExtensionData.ControllerKey}
            $curDiskMap = "" | select vmDiskDatastore, vmDiskVmdk, SCSIAddress, vmDiskName, windowsDiskIndex, vmDiskUuid, windowsDeviceID, drives, volumes
            $driveVolumes = $diskToDriveVolume | where {$_.Disk -eq $windowsDisk.DeviceID}
            New-Object -TypeName PSObject -Property ([ordered]@{
                VM = $vm.Name
                vmDiskDatastore = $vmDisk.filename.split(']')[0].split('[')[1]
                vmDiskVmdk = $vmDisk.filename.split(']')[1].trim()
                SCSIAddress = "SCSI($($ctrl.BusNumber):$($vmDisk.ExtensionData.UnitNumber))" 
                vmDiskName = $vmDisk.Name
                vmDiskSizeGB = $vmDisk.CapacityGB
                windowsDiskIndex = if ($windowsDisk){$windowsDisk.Index}else{"FAILED TO MATCH"}
                vmDiskUuid = $vmDiskUuid
                windowsDeviceID = if ($windowsDisk){$windowsDisk.DeviceID}else{"FAILED TO MATCH"}
                drives = $driveVolumes.DriveLetter
                volumes = $driveVolumes.VolumeName
                
            })
        }
    }
    else{
        Write-Host "Could not determine FQDN for VM ($vm.Name)"
    }
} | Export-Csv -Path .\report.csv -NoTypeInformation -UseCulture

 

Make sure when you're running the PowerShell script that you are in the working directory first where the vmnames.csv file resides. Otherwise it'll complain it can't find the vmnames.csv file. All of the VMs you want to run this against need to go in this file. This will also be the same directory that the report.csv exports to. 

 

You can do a search and export the list of VMs from the Virtual Machines view in vCenter so you don't have to type them all out by hand if you have a lot.

 

Another issue I had was some VMs were affected by a vSphere 6.5 bug where the "disk.EnableUUID = "TRUE" value was missing from multiple vmx configuration files. I'll have to go back and fix that on those VMs.  Without that value the Uuid is not exposed to the Guest OS. Therefore, it can't match the drives up. 

 

You can reference that KB article here.

https://kb.vmware.com/s/article/52815

 

Finally, As @LucD mentioned. This is not a fool proof method and only works under the right conditions. Your mileage may vary. 

HUGE THANKS to @LucD. I wouldn't have even known where to begin (or end) without his help!

Reply
0 Kudos
glwnieves
Enthusiast
Enthusiast
Jump to solution

Here is my final version of the script in case anybody wants to reference it:

 

Spoiler

# Load PowerCLI Plugin and connect to VCenter Server (Put in your FQDN where it says YOURVCENTERSERVERNAME)
Import-Module VMware.VimAutomation.Core -ErrorAction SilentlyContinue
connect-viserver YOURVCENTERSERVERNAME

# User with permission to query the WMI on the guest
$cred = if ($cred){$cred}else{Get-Credential}

# CSV file with a column named vmName
Import-Csv -Path .\vmnames.csv -UseCulture -PipelineVariable row |
ForEach-Object -Process {
$vm = Get-VM -Name $row.vmName

# If the script can't determine the FQDN of the Guest OS, the VM is skipped
$winHostName = ''
if($vm.Guest.State -eq 'Running'){
if($vm.Guest.HostName -ne ''){
$WinHostName = $vm.Guest.HostName
}
elseif($vm.Guest.IPAddress){
$resolve = Resolve-DnsName -Name $vm.guest.IPAddress[0]
if($resolve){
$WinHostName = $resolve.NameHost
}
}
}
if($winHostName){
$vmDisks = Get-HardDisk -VM $vm | Sort-Object -Property Name
$vmDatacenterView = Get-Datacenter -VM $vm | Get-View
$virtualDiskManager = Get-View -Id VirtualDiskManager-virtualDiskManager

# 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
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)}
}
$ctrl = $vm.ExtensionData.Config.Hardware.Device | where{$_.Key -eq $vmDisk.ExtensionData.ControllerKey}
$curDiskMap = "" | select vmDiskDatastore, vmDiskVmdk, SCSIAddress, vmDiskName, windowsDiskIndex, vmDiskUuid, windowsDeviceID, drives, volumes
$driveVolumes = $diskToDriveVolume | where {$_.Disk -eq $windowsDisk.DeviceID}
New-Object -TypeName PSObject -Property ([ordered]@{
VM = $vm.Name
vmDiskDatastore = $vmDisk.filename.split(']')[0].split('[')[1]
vmDiskVmdk = $vmDisk.filename.split(']')[1].trim()
SCSIAddress = "SCSI($($ctrl.BusNumber):$($vmDisk.ExtensionData.UnitNumber))"
vmDiskName = $vmDisk.Name
vmDiskSizeGB = $vmDisk.CapacityGB
windowsDiskIndex = if ($windowsDisk){$windowsDisk.Index}else{"FAILED TO MATCH"}
vmDiskUuid = $vmDiskUuid
windowsDeviceID = if ($windowsDisk){$windowsDisk.DeviceID}else{"FAILED TO MATCH"}
drives = $driveVolumes.DriveLetter
volumes = $driveVolumes.VolumeName

})
}
}
else{
Write-Host "Could not determine FQDN for VM ($vm.Name)"
}
} | Export-Csv -Path .\report.csv -NoTypeInformation -UseCulture

 

 

 

Make sure when you're running the PowerShell script that you are in the working directory first where the vmnames.csv file resides. Otherwise it'll complain it can't find the vmnames.csv file. All of the VMs you want to run this against need to go in this file. This will also be the same directory that the report.csv exports to. 

 

You can do a search and export the list of VMs from the Virtual Machines view in vCenter so you don't have to type them all out by hand if you have a lot.

 

Another issue I had was some VMs were affected by a vSphere 6.5 bug where the "disk.EnableUUID = "TRUE" value was missing from multiple vmx configuration files. I'll have to go back and fix that on those VMs.  Without that value the Uuid is not exposed to the Guest OS. Therefore, it can't match the drives up. 

 

You can reference that KB article here.

https://kb.vmware.com/s/article/52815

 

Finally, As @LucD mentioned. This is not a fool proof method and only works under the right conditions. Your mileage may vary. 

HUGE THANKS to @LucD. I wouldn't have even known where to begin (or end) without his help!

Reply
0 Kudos
glwnieves
Enthusiast
Enthusiast
Jump to solution

Here is my final version of the script in case anybody wants to reference it:

Spoiler

# Load PowerCLI Plugin and connect to VCenter Server (Put in your FQDN where it says YOURVCENTERSERVERNAME)
Import-Module VMware.VimAutomation.Core -ErrorAction SilentlyContinue
connect-viserver YOURVCENTERSERVERNAME

# User with permission to query the WMI on the guest
$cred = if ($cred){$cred}else{Get-Credential}

# CSV file with a column named vmName
Import-Csv -Path .\vmnames.csv -UseCulture -PipelineVariable row |
ForEach-Object -Process {
$vm = Get-VM -Name $row.vmName

# If the script can't determine the FQDN of the Guest OS, the VM is skipped
$winHostName = ''
if($vm.Guest.State -eq 'Running'){
if($vm.Guest.HostName -ne ''){
$WinHostName = $vm.Guest.HostName
}
elseif($vm.Guest.IPAddress){
$resolve = Resolve-DnsName -Name $vm.guest.IPAddress[0]
if($resolve){
$WinHostName = $resolve.NameHost
}
}
}
if($winHostName){
$vmDisks = Get-HardDisk -VM $vm | Sort-Object -Property Name
$vmDatacenterView = Get-Datacenter -VM $vm | Get-View
$virtualDiskManager = Get-View -Id VirtualDiskManager-virtualDiskManager

# 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
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)}
}
$ctrl = $vm.ExtensionData.Config.Hardware.Device | where{$_.Key -eq $vmDisk.ExtensionData.ControllerKey}
$curDiskMap = "" | select vmDiskDatastore, vmDiskVmdk, SCSIAddress, vmDiskName, windowsDiskIndex, vmDiskUuid, windowsDeviceID, drives, volumes
$driveVolumes = $diskToDriveVolume | where {$_.Disk -eq $windowsDisk.DeviceID}
New-Object -TypeName PSObject -Property ([ordered]@{
VM = $vm.Name
vmDiskDatastore = $vmDisk.filename.split(']')[0].split('[')[1]
vmDiskVmdk = $vmDisk.filename.split(']')[1].trim()
SCSIAddress = "SCSI($($ctrl.BusNumber):$($vmDisk.ExtensionData.UnitNumber))"
vmDiskName = $vmDisk.Name
vmDiskSizeGB = $vmDisk.CapacityGB
windowsDiskIndex = if ($windowsDisk){$windowsDisk.Index}else{"FAILED TO MATCH"}
vmDiskUuid = $vmDiskUuid
windowsDeviceID = if ($windowsDisk){$windowsDisk.DeviceID}else{"FAILED TO MATCH"}
drives = $driveVolumes.DriveLetter
volumes = $driveVolumes.VolumeName

})
}
}
else{
Write-Host "Could not determine FQDN for VM ($vm.Name)"
}
} | Export-Csv -Path .\report.csv -NoTypeInformation -UseCulture

Make sure when you're running the PowerShell script that you are in the working directory first where the vmnames.csv file resides. Otherwise it'll complain it can't find the vmnames.csv file. All of the VMs you want to run this against need to go in this file. This will also be the same directory that the report.csv exports to.

 

You can do a search and export the list of VMs from the Virtual Machines view in vCenter so you don't have to type them all out by hand if you have a lot.

 

Another issue I had was some VMs were affected by a vSphere 6.5 bug where the "disk.EnableUUID = "TRUE" value was missing from multiple vmx configuration files. I'll have to go back and fix that on those VMs. Without that value the Uuid is not exposed to the Guest OS. Therefore, it can't match the drives up.

 

You can reference that KB article here.

https://kb.vmware.com/s/article/52815

 

Finally, As @LucD mentioned. This is not a fool proof method and only works under the right conditions. Your mileage may vary.

HUGE THANKS to @LucD. I wouldn't have even known where to begin (or end) without his help!

Reply
0 Kudos
k_anandhan
Contributor
Contributor
Jump to solution

Hi

When I try to run the script, getting below errors:

 

You cannot call a method on a null-valued expression.
At C:\Users\XODXCAKUppu\Desktop\vdisk.ps1:69 char:50
+ ... sk | where {$_.SerialNumber -eq $vmDisk.ScsiCanonicalName.substring(1 ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

 

Kindly assist to fix. thanks,

Reach me @ k_anandhan@hotmail.com or skanandhan@gmail.com 

Reply
0 Kudos