VMware Cloud Community
Ivan_Nel
Contributor
Contributor
Jump to solution

PowerCLI - Matching a Disk Number OR SCSI ID to a Windows Drive Letter

I extracted info from my VCentre VCDBs because i am more comfortable using SQL than PowerCLI.

I have a list of servers, with a Disk Number, SCSI ID and Allocated Size.
SERVER0001 - DISK NUMBER 1 - SCSI1:0 - 100GB

I also have a list of servers, with Path and Sizes from the VPX_GUEST_DISK table.
SERVER0001 - C: - 100GB

I need to bridge the gap between a Disk Number OR SCSI ID -  and a Windows Drive Letter, and cannot do it with disk size alone.

I found this script, which works for some of my servers, But the function only takes a single parameter at a time,

could someone please edit this - so that it will (a) accept a list of parameters, from CSV or Txt

and (b) Output the result to CSV.

##Script Below## - Thanks

Function Get-VMDiskMap {

  [Cmdletbinding()]

  param([Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)][string]$VM)

  begin {

  }

  process {

  if ($VM) {

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

  $VirtualDisk = "" | Select VM,SCSIController, DiskName, SCSI_Id, DiskFile,  DiskSize, WindowsDisks

  $VirtualDisk.VM = $VM

  $VirtualDisk.SCSIController = $VirtualSCSIController.DeviceInfo.Label

  $VirtualDisk.DiskName = $VirtualDiskDevice.DeviceInfo.Label

  $VirtualDisk.SCSI_Id = "$($VirtualSCSIController.BusNumber) : $($VirtualDiskDevice.UnitNumber)"

  $VirtualDisk.DiskFile = $VirtualDiskDevice.Backing.FileName

  $VirtualDisk.DiskSize = $VirtualDiskDevice.CapacityInKB * 1KB / 1GB

  $LogicalDisks = @()

  # Look up path for this disk using WMI.

  $thisVirtualDisk = get-wmiobject -class "Win32_DiskDrive" -namespace "root\CIMV2" -computername $VM | where {$_.SCSIBus -eq $VirtualSCSIController.BusNumber -and $_.SCSITargetID -eq $VirtualDiskDevice.UnitNumber}

  # Look up partition using WMI.

  $Disk2Part = Get-WmiObject Win32_DiskDriveToDiskPartition -computername $VM | Where {$_.Antecedent -eq $thisVirtualDisk.__Path}

  foreach ($thisPartition in $Disk2Part) {

  #Look up logical drives for that partition using WMI.

  $Part2Log = Get-WmiObject -Class Win32_LogicalDiskToPartition -computername $VM | Where {$_.Antecedent -eq $thisPartition.Dependent}

  foreach ($thisLogical in $Part2Log) {

  if ($thisLogical.Dependent -match "[A-Z]:") {

  $LogicalDisks += $matches[0]

  }

  }

  }

  $VirtualDisk.WindowsDisks = $LogicalDisks

  Write-Output $VirtualDisk

  }

  }

  }

  }

  end {

  }

}

0 Kudos
1 Solution

Accepted Solutions
frostyk
Enthusiast
Enthusiast
Jump to solution

See if the code below is what you are looking for.  Note this isn't written as a function as the original.  This is just a block of script that you run to import your file, and exports your csv.  At the top of the script are two path variables that you should change to be the location of a txt file with VMs listed one on each line, and a location where you want the CSV to export.  See the comments.  You should be able to get what you want just by altering these two variables ($importPath and $exportPath)  This also could be easily rewritten as a function to take in those to paths as parameters.

EDIT: my attempt to use the syntax highlighting was screwing stuff up.  Here it is raw.

#change the path below to where you have your list of VMs

$importPath = "c:\Temp\VMlist.txt"

#change this one to where you want to save the csv

$exportPath = "c:\Temp\DiskNumToPart.csv"

#####Begin many nested loops#####

$VMlist = Get-Content $importPath

#start building a table to export all the data to csv

$table = @()

#loop through each VM imported from csv

foreach ($VM in $VMlist)

{

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

  {

  $VirtualDisk = "" | Select VM,SCSIController, DiskName, SCSI_Id, DiskFile,  DiskSize, WindowsDisks

  $VirtualDisk.VM = $VM

  $VirtualDisk.SCSIController = $VirtualSCSIController.DeviceInfo.Label

  $VirtualDisk.DiskName = $VirtualDiskDevice.DeviceInfo.Label

  $VirtualDisk.SCSI_Id = "$($VirtualSCSIController.BusNumber) : $($VirtualDiskDevice.UnitNumber)"

  $VirtualDisk.DiskFile = $VirtualDiskDevice.Backing.FileName

  $VirtualDisk.DiskSize = $VirtualDiskDevice.CapacityInKB * 1KB / 1GB

 

  $LogicalDisks = @()

  # Look up path for this disk using WMI.

  $thisVirtualDisk = get-wmiobject -class "Win32_DiskDrive" -namespace "root\CIMV2" -computername $VM | where {$_.SCSIBus -eq $VirtualSCSIController.BusNumber -and $_.SCSITargetID -eq $VirtualDiskDevice.UnitNumber}

  # Look up partition using WMI.

  $Disk2Part = Get-WmiObject Win32_DiskDriveToDiskPartition -computername $VM | Where {$_.Antecedent -eq $thisVirtualDisk.__Path}

  foreach ($thisPartition in $Disk2Part)

  {

  #Look up logical drives for that partition using WMI.

  $Part2Log = Get-WmiObject -Class Win32_LogicalDiskToPartition -computername $VM | Where {$_.Antecedent -eq $thisPartition.Dependent}

  foreach ($thisLogical in $Part2Log)

  {

  if ($thisLogical.Dependent -match "[A-Z]:")

  {

  $LogicalDisks += $matches[0]

    }

    }

  }

  #we have to convert the table of drive letters to a single string to write into one cell

  #in the row we are going to build below

  $LDString = $null

  foreach ($LogicalDisk in $LogicalDisks)

  {

  [string]$LDString += $LogicalDisk

  }

  $VirtualDisk.WindowsDisks = $LDString

  #you don't need this but I left it in so you can see what its doing as it runs

  Write-Output $VirtualDisk

  #now we build a row for the table and put values in each cell as NoteProperties

  #you can change the -name value to change what the row header is called

  #you can comment one out to exclude it from the report

  #there is also a lot more data that can be mined for each disk int he get-view extention data

  $row = New-Object psobject

  $row | Add-Member -Name "VM" -Value $VirtualDisk.VM -MemberType NoteProperty -Force

  $row | Add-Member -Name "SCSIController" -Value $VirtualDisk.SCSIController -MemberType NoteProperty -Force

  $row | Add-Member -Name "DiskName" -Value $VirtualDisk.DiskName -MemberType NoteProperty -Force

  $row | Add-Member -Name "SCSI_Id" -Value $VirtualDisk.SCSI_Id -MemberType NoteProperty -Force

  $row | Add-Member -Name "DiskFile" -Value $VirtualDisk.DiskFile -MemberType NoteProperty -Force

  $row | Add-Member -Name "DiskSize" -Value $VirtualDisk.DiskSize -MemberType NoteProperty -Force

  #not sure how this is going to work as we're writing a hash table into this row that will be written into another hash table

  $row | Add-Member -Name "WindowsDisks" -Value $VirtualDisk.WindowsDisks -MemberType NoteProperty -Force

  #we're done building the row, below writes it to the table

  $table += $row    

  }

  }

}

#time to export to CSV

$table | Export-Csv $exportPath

View solution in original post

7 Replies
frostyk
Enthusiast
Enthusiast
Jump to solution

See if the code below is what you are looking for.  Note this isn't written as a function as the original.  This is just a block of script that you run to import your file, and exports your csv.  At the top of the script are two path variables that you should change to be the location of a txt file with VMs listed one on each line, and a location where you want the CSV to export.  See the comments.  You should be able to get what you want just by altering these two variables ($importPath and $exportPath)  This also could be easily rewritten as a function to take in those to paths as parameters.

EDIT: my attempt to use the syntax highlighting was screwing stuff up.  Here it is raw.

#change the path below to where you have your list of VMs

$importPath = "c:\Temp\VMlist.txt"

#change this one to where you want to save the csv

$exportPath = "c:\Temp\DiskNumToPart.csv"

#####Begin many nested loops#####

$VMlist = Get-Content $importPath

#start building a table to export all the data to csv

$table = @()

#loop through each VM imported from csv

foreach ($VM in $VMlist)

{

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

  {

  $VirtualDisk = "" | Select VM,SCSIController, DiskName, SCSI_Id, DiskFile,  DiskSize, WindowsDisks

  $VirtualDisk.VM = $VM

  $VirtualDisk.SCSIController = $VirtualSCSIController.DeviceInfo.Label

  $VirtualDisk.DiskName = $VirtualDiskDevice.DeviceInfo.Label

  $VirtualDisk.SCSI_Id = "$($VirtualSCSIController.BusNumber) : $($VirtualDiskDevice.UnitNumber)"

  $VirtualDisk.DiskFile = $VirtualDiskDevice.Backing.FileName

  $VirtualDisk.DiskSize = $VirtualDiskDevice.CapacityInKB * 1KB / 1GB

 

  $LogicalDisks = @()

  # Look up path for this disk using WMI.

  $thisVirtualDisk = get-wmiobject -class "Win32_DiskDrive" -namespace "root\CIMV2" -computername $VM | where {$_.SCSIBus -eq $VirtualSCSIController.BusNumber -and $_.SCSITargetID -eq $VirtualDiskDevice.UnitNumber}

  # Look up partition using WMI.

  $Disk2Part = Get-WmiObject Win32_DiskDriveToDiskPartition -computername $VM | Where {$_.Antecedent -eq $thisVirtualDisk.__Path}

  foreach ($thisPartition in $Disk2Part)

  {

  #Look up logical drives for that partition using WMI.

  $Part2Log = Get-WmiObject -Class Win32_LogicalDiskToPartition -computername $VM | Where {$_.Antecedent -eq $thisPartition.Dependent}

  foreach ($thisLogical in $Part2Log)

  {

  if ($thisLogical.Dependent -match "[A-Z]:")

  {

  $LogicalDisks += $matches[0]

    }

    }

  }

  #we have to convert the table of drive letters to a single string to write into one cell

  #in the row we are going to build below

  $LDString = $null

  foreach ($LogicalDisk in $LogicalDisks)

  {

  [string]$LDString += $LogicalDisk

  }

  $VirtualDisk.WindowsDisks = $LDString

  #you don't need this but I left it in so you can see what its doing as it runs

  Write-Output $VirtualDisk

  #now we build a row for the table and put values in each cell as NoteProperties

  #you can change the -name value to change what the row header is called

  #you can comment one out to exclude it from the report

  #there is also a lot more data that can be mined for each disk int he get-view extention data

  $row = New-Object psobject

  $row | Add-Member -Name "VM" -Value $VirtualDisk.VM -MemberType NoteProperty -Force

  $row | Add-Member -Name "SCSIController" -Value $VirtualDisk.SCSIController -MemberType NoteProperty -Force

  $row | Add-Member -Name "DiskName" -Value $VirtualDisk.DiskName -MemberType NoteProperty -Force

  $row | Add-Member -Name "SCSI_Id" -Value $VirtualDisk.SCSI_Id -MemberType NoteProperty -Force

  $row | Add-Member -Name "DiskFile" -Value $VirtualDisk.DiskFile -MemberType NoteProperty -Force

  $row | Add-Member -Name "DiskSize" -Value $VirtualDisk.DiskSize -MemberType NoteProperty -Force

  #not sure how this is going to work as we're writing a hash table into this row that will be written into another hash table

  $row | Add-Member -Name "WindowsDisks" -Value $VirtualDisk.WindowsDisks -MemberType NoteProperty -Force

  #we're done building the row, below writes it to the table

  $table += $row    

  }

  }

}

#time to export to CSV

$table | Export-Csv $exportPath

Ivan_Nel
Contributor
Contributor
Jump to solution

That works for about half of my Windows VMs, thank you frostyk.

Do you know of another way to get the Disk Paths?

Strangely, if you query the VPX_GUEST_DISK table in the VCDB, there is

Disk Path info for every VM, along with Disk Sizes

an Ideal output would be ServerName,  Disk Path, Disk Number, SCSI ID, Disk Size,
for Windows and Linux VMs.

0 Kudos
frostyk
Enthusiast
Enthusiast
Jump to solution

For the Windows VMs that it doesn't get info on, do you see an RPC error on the console as the script is running?  I was getting that on a few VMs I tested on and it was due to the Windows Firewall blocking WMI calls.

You can get the path names from the guest information from vmware tools provided to the powercli through a few different methods, the trouble is linking it to the disk numbers.   That's why the original author (I think) used the WMI calls to match the SCSI bus to link numbers to letters.

I'm betting you just need to make sure your Windows firewall is off or set to allow WMI.

See: Setting up a Remote WMI Connection (Windows)

0 Kudos
Ivan_Nel
Contributor
Contributor
Jump to solution

Yes,  Would enabling a firewall exception for WinRM on all machines bypass this ?

Get-CimInstance : WinRM cannot complete the operation. Verify that the specified computer name is valid, that the computer is accessible over the network, and that a

firewall exception for the WinRM service is enabled and allows access from this computer. By default, the WinRM firewall exception for public profiles limits access to   remote computers within the same local subnet

I then ran WinRM QuickConfig on one of the affected VMs,

WinRM quickconfig

Created a WinRM listener on HTTP://* to accept WS-Man requests to any IP on this
 machine.

That did not resolve it. I am still confused about how the VCDB has the windows and linux path info along with the disk sizes,

but no key to link it back to a volume. Its not ideal for me to query every server, this info must exist in VCentre if I can see it in the Guest_Disk table.

0 Kudos
frostyk
Enthusiast
Enthusiast
Jump to solution

One thing I did just notice about win RM and the firewall

"Note  The winrm quickconfig command creates a firewall exception only for the current user profile. If the firewall profile is changed for any reason, winrm quickconfig should be run to enable the firewall exception for the new profile; otherwise, the exception might not be enabled."

Possibly a GPO is effecting the firewall config on those machines?

As to why there is no key linking the guest table to what you want, with virtual machines it is important to remember that there are two levels you are dealing with.  There is the Virtual Infrastructure level that most of us work on.  Then there is the OS level configuration from within the VM.  I never even log into VMs as that is territory of another team/customer.  Any kind of information on the Guest comes from VMware tools installed on the machine and is isolated from the infrastructure level information like the disk number you see in vCenter.  That's why this information is not relational.  The only time I ever have to worry about figuring out what disk number correlates to what physical disk within the VM is when I have a request from the VM owner to increase space to a drive/partition and there are multiple drives of the same size on the VM.  In this case I normally just make an educated guess based on the order of disks within the VM since normally that order matches the disk number order outside the VM (not always).

In the end I think this script is your best way of doing what you want.  You just have to get the WinRM / Firewall config figured out.  Check out the firewall config and any GPOs being pushed to the machine that might be altering any config you manually set.  If the VMs that work are in a different OU with different GPOs than the ones that don't work, that's a dead give away that a GPO is messing with the required settings.

0 Kudos
Ivan_Nel
Contributor
Contributor
Jump to solution

You have been a great help, thank you very much.

0 Kudos
winsolo
Enthusiast
Enthusiast
Jump to solution

By far, this is the only script I could find, that collects almost everything I needed (except the "Windows Disks" part)

Snag_5bac78b.png

For some reason, it is unable to populate the "WindowsDisks" column which is one of the important mapping info I'm looking for. Although I don't have windows firewall problem in my environment as mentioned in this post and that I'm able to manually collect the information remotely when using Win32_DiskDriveToDiskPartition, Win32_DiskDrive, Win32_LogicalDiskToPartition, the script is unable to pull that part.

This is what I ran which is a slightly modified than the original one (no excel export, added the type of controller).

$VMlist = "testvm"

$table = @()

#loop through each VM imported from csv

foreach ($VM in $VMlist)

{

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

            {

            $VirtualDisk = "" | Select VM,SCSIControllerName, SCSIControllerNumber, DiskName, SCSI_Id, DiskFile,  DiskSize, WindowsDisks

            $VirtualDisk.VM = $VM

            $VirtualDisk.SCSIControllerName = $VirtualSCSIController.DeviceInfo.Summary

            $VirtualDisk.SCSIControllerNumber = $VirtualSCSIController.DeviceInfo.Label

            $VirtualDisk.DiskName = $VirtualDiskDevice.DeviceInfo.Label

            $VirtualDisk.SCSI_Id = "$($VirtualSCSIController.BusNumber) : $($VirtualDiskDevice.UnitNumber)"

            $VirtualDisk.DiskFile = $VirtualDiskDevice.Backing.FileName

            $VirtualDisk.DiskSize = $VirtualDiskDevice.CapacityInKB * 1KB / 1GB

            $LogicalDisks = @()

            # Look up path for this disk using WMI.

            $thisVirtualDisk = get-wmiobject -class "Win32_DiskDrive" -namespace "root\CIMV2" -computername $VM | where {$_.SCSIBus -eq $VirtualSCSIController.BusNumber -and $_.SCSITargetID -eq $VirtualDiskDevice.UnitNumber}

            # Look up partition using WMI.

            $Disk2Part = Get-WmiObject Win32_DiskDriveToDiskPartition -computername $VM | Where {$_.Antecedent -eq $thisVirtualDisk.__Path}

            foreach ($thisPartition in $Disk2Part)

                {

                #Look up logical drives for that partition using WMI.

                $Part2Log = Get-WmiObject -Class Win32_LogicalDiskToPartition -computername $VM | Where {$_.Antecedent -eq $thisPartition.Dependent}

                    foreach ($thisLogical in $Part2Log)

                        {

                        if ($thisLogical.Dependent -match "[A-Z]:")

                            {

                            $LogicalDisks += $matches[0]

                            }

                        }

                }

            #we have to convert the table of drive letters to a single string to write into one cell

            #in the row we are going to build below

            $LDString = $null

            foreach ($LogicalDisk in $LogicalDisks)

                {

                    [string]$LDString += $LogicalDisk

                }

            $VirtualDisk.WindowsDisks = $LDString

            #you don't need this but I left it in so you can see what its doing as it runs

            Write-Output $VirtualDisk

            #now we build a row for the table and put values in each cell as NoteProperties

            #you can change the -name value to change what the row header is called

            #you can comment one out to exclude it from the report

            #there is also a lot more data that can be mined for each disk int he get-view extention data

            $row = New-Object psobject

            $row | Add-Member -Name "VM" -Value $VirtualDisk.VM -MemberType NoteProperty -Force

            $row | Add-Member -Name "SCSIControllerName" -Value $VirtualDisk.SCSIControllerName -MemberType NoteProperty -Force

            $row | Add-Member -Name "SCSIControllerNumber" -Value $VirtualDisk.SCSIControllerNumber -MemberType NoteProperty -Force

            $row | Add-Member -Name "DiskName" -Value $VirtualDisk.DiskName -MemberType NoteProperty -Force

            $row | Add-Member -Name "SCSI_Id" -Value $VirtualDisk.SCSI_Id -MemberType NoteProperty -Force

            $row | Add-Member -Name "DiskFile" -Value $VirtualDisk.DiskFile -MemberType NoteProperty -Force

            $row | Add-Member -Name "DiskSize" -Value $VirtualDisk.DiskSize -MemberType NoteProperty -Force

            #not sure how this is going to work as we're writing a hash table into this row that will be written into another hash table

            $row | Add-Member -Name "WindowsDisks" -Value $VirtualDisk.WindowsDisks -MemberType NoteProperty -Force

            #we're done building the row, below writes it to the table

            $table += $row   

  }

  }

}

$table | FT

Is there someone who can help?

0 Kudos