PowerShell function to get disk SCSI Lun number

PowerShell logoFor our Boot From SAN servers we wanted to know the SCSI Lun number of the disks. You can get this information via WMI. But to retrieve it you need four different WMI objects. I made a PowerShell advanced function that combines these four WMI objects to relate the disk with the LUN number.

The Get-DiskScsiLun function

The Get-DiskScsiLun function uses the following WMI objects:
Win32_LogicalDisk, Win32_LogicalDiskToPartition, Win32_DiskDriveToDiskPartition and Win32_DiskDrive.

The first object contains the drive letter. The last object contains the Lun number. And the two other objects are needed to relate the drive letter to the Lun number.

function Get-DiskScsiLun {
<#
  .SYNOPSIS
    Retrieves the SCSI Lun information for a disk.

  .DESCRIPTION
    Retrieves the SCSI Lun information for a disk.

  .PARAMETER  DeviceID
    Specify the disk name for wich the Scsi Lun information should be retrieved.

  .PARAMETER  ComputerName
    Specifies the computer against which you want to run the management operation.
    The value can be a fully qualified domain name, a NetBIOS name, or an IP
    address. Use the local computer name, use localhost, or use a dot (.) to specify the local
    computer. The local computer is the default.
    When the remote computer is in a different domain from the user, you must use a fully
    qualified domain name. This parameter can also be piped to the cmdlet.

    This parameter does not rely on Windows PowerShell remoting, which uses WS-Management.
    You can use the ComputerName parameter of Get-WmiObject even if your computer is not
    configured to run WS-Management remote commands.

  .PARAMETER  Credential
    Specifies a user account that has permission to perform this action. The default is
    the current user. Type a user name, such as "User01", "Domain01\User01",
    or User@Contoso.com. Or, enter a PSCredential object, such as an object that is
    returned by the Get-Credential cmdlet. When you type a user name, you will be prompted
    for a password.

  .EXAMPLE
    PS C:\> Get-DiskScsiLun

  .EXAMPLE
    PS C:\> Get-DiskScsiLun -DeviceID C: -ComputerName Server01 -Credential Domain01\User01

  .EXAMPLE
    PS C:\> "Server01","Server02" | Get-DiskScsiLun -Credential Domain01\User01
		
  .INPUTS
    System.String,PSCredential

  .OUTPUTS
    PSObject

  .NOTES
    Author: Robert van den Nieuwendijk
    Version: 1.1
    Date: 17-1-2013

  .LINK
    https://rvdnieuwendijk.com/

#>

 [CmdletBinding()]
  param([Parameter(Mandatory = $false,
                   Position = 0)]
        [alias("Disk")]
        [string] $DeviceID = '*',
        [Parameter(Mandatory = $false,
                   Position = 1,
                   ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true)]
        [alias("CN")]
        [String[]] $ComputerName = $env:COMPUTERNAME,
        [Parameter(Mandatory=$false,
                   Position = 2)]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty
  )
  
  process {
    if ($ComputerName)
    {
      # Loop through all computers in the parameter list
      foreach ($Computer in $ComputerName) {
      try {
        if ($Computer -eq "$($env:COMPUTERNAME)" -or $Computer -eq "." -or $Computer -eq "localhost")
        {
          # Define the Get-WmiObject parameter set for the local computer
          $Parameters = @{
            Impersonation = 3
            ErrorAction = 'Stop'
          }
        }
        else
        {
          # Define the Get-WmiObject parameter set for remote computers
          $Parameters = @{
            ComputerName = $Computer
            Credential = $Credential
            ErrorAction = 'Stop'
          }
        }
		  
        # Test if the computer can be connected
        if (Test-Connection -ComputerName $Computer -Count 1 -Quiet)	 
        {		  
          # Get the  WMI objects
          $Win32_LogicalDisk = Get-WmiObject -Class Win32_LogicalDisk @Parameters |
            Where-Object {$_.DeviceID -like $DeviceID}
          $Win32_LogicalDiskToPartition = Get-WmiObject -Class Win32_LogicalDiskToPartition @Parameters 
          $Win32_DiskDriveToDiskPartition = Get-WmiObject -Class Win32_DiskDriveToDiskPartition @Parameters 
          $Win32_DiskDrive = Get-WmiObject -Class Win32_DiskDrive @Parameters 
  
          # Search the SCSI Lun Unit for the disk
          $Win32_LogicalDisk |
            ForEach-Object {
              if ($_)
              {
                $LogicalDisk = $_
                $LogicalDiskToPartition = $Win32_LogicalDiskToPartition |
                  Where-Object {$_.Dependent -eq $LogicalDisk.Path}
                if ($LogicalDiskToPartition)
                {
                  $DiskDriveToDiskPartition = $Win32_DiskDriveToDiskPartition |
                    Where-Object {$_.Dependent -eq $LogicalDiskToPartition.Antecedent}
                  if ($DiskDriveToDiskPartition)
                  {
                    $DiskDrive = $Win32_DiskDrive |
                      Where-Object {$_.__Path -eq $DiskDriveToDiskPartition.Antecedent}
                    if ($DiskDrive)
                    {
                      # Return the results
                      New-Object -TypeName PSObject -Property @{
                        Computer = $Computer
                        DeviceID = $LogicalDisk.DeviceID
                        SCSIBus = $DiskDrive.SCSIBus
                        SCSIPort = $DiskDrive.SCSIPort
                        SCSITargetId = $DiskDrive.SCSITargetId
                        SCSILogicalUnit = $DiskDrive.SCSILogicalUnit
                      }
                    }
                  }
                }
              }
            }
          }
          else
          {
            Write-Warning "Unable to connect to computer $Computer."
          }
        }
        catch {
          Write-Warning "Unable to get disk information for computer $Computer.`n$($_.Exception.Message)"
        }
      }
    }
  }
}

Listing 1. The PowerShell advanced function Get-DiskScsiLun that retrieves the SCSI Lun number for a disk.

Annotations

Line 79-95: Build the parameter sets for the Get-WmiObject function calls.

Line 100-105: Retrieve the WMI objects.

Line 107-138: Match the disk drive letter to the SCSI Lun information.

Sample usage

Output 1 will show you an example of the results you can get when you run the Get-DiskScsiLun function.

Computer  DeviceID SCSIBus SCSIPort SCSITargetId SCSILogicalUnit
--------  -------- ------- -------- ------------ ---------------
server001 C:             0        2            5               1
server001 D:             0        0            4               0
server001 E:             0        2            4               2
server001 F:             0        1            5               3
server001 G:             0        2            4               4
server001 H:             0        1            5              13
server001 I:             0        1            5               5
server001 J:             0        2            5               6
server001 K:             0        2            4               7
server001 L:             0        2            5               8
server001 M:             0        1            4               9
server001 N:             0        1            4              10
server001 O:             0        2            5              11
server001 P:             0        2            5              12

Output 1. Sample output of the Get-DiskScsiLun function.

Update 17-1-2013: I modified the Get-DiskScsiLun function to not only retrieve the SCSILogicalUnit but also SCSIBus, SCSIPort and SCSITargetId.

About Robert van den Nieuwendijk
Robert van den Nieuwendijk is a freelance senior systems engineer with over 30 years of experience in the IT industry. He focusses on VMware vCloud Suite and Microsoft Windows Server. He tries to automate as much of his work as possible using Microsoft PowerShell. Robert is the author of the books “Learning PowerCLI” and “Learning PowerCLI – Second Edition.” Robert is a frequent contributor and moderator at the VMware VMTN Communities. He has a bachelor degree in software engineering and holds the following IT certifications and accreditations: VSP 2016, VTSP 2016, VCP4-DCV, VCP5-DCV, VCP6-DCV, VCP6-CMA, VCA-Cloud, VCA-WM, VCA-NV, VMSP, VMTSP, ZCS, ZCP, ZCP-Cloud, MCSE, MCSA, MCP, MCP+I, PRINCE2 Foundation and ITIL Foundation. In 2012, 2013, 2014, 2015, 2016, 2017, 2018 and 2019 Robert received the VMware vExpert award for his contribution to the community of VMware users over the past year. In 2017 Robert also received the VMware vExpert Cloud award. PernixData made him in 2015 a member of the PernixPro.

21 Responses to PowerShell function to get disk SCSI Lun number

  1. Kelvin Wong says:

    This don’t work for mount points though 😦

    • Hi Kelvin, you are right that this doesn’t work for mount points. It is not easy find a solution for mount points. I am still searching for it.

    • Personally, I have found the following to work for me (my experience is with PowerShell v3 on Win7 and v4 on Win2012 (r1 and r2)):

      Get-WmiObject Win32_DiskDrive | sort scsibus,scsilogicalunit | ft -property scsibus,scsilogicalunit,index,size,caption -autosize

      The “ScsiLogicalUnit” is the LUN (which is unique to the bus), and the “Index” is what Windows shows. If you’re looking at disks in “Server Manager”, it corresponds to the “Number” column. If you’re looking at disks in “Failover Cluster Manager”, it corresponds to the “Disk Number” column. This information is enough for me to reliably match the drives I see in a Windows GUI back to what’s defined in a DAS box. And it doesn’t matter if the drives are online/offline, initialized, used as a mount-point, or assigned a drive letter.

      My pretty-output version of the above is this:

      Get-WmiObject Win32_DiskDrive | sort scsibus,scsilogicalunit | ft @{Label=”ScsiBus”;Expression={$_.scsibus}}, @{Label=”LUN”;Expression={$_.scsilogicalunit}}, @{Label=”Disk Number”;Expression={$_.index}}, @{Label=”Size (GB)”;Expression={[Math]::Round($_.size / 1GB, 2)}}, Caption -autosize

  2. Ben says:

    My output shows all LUN#’s as ‘0’, this comes from $Win32_DiskDrive, all the LUN #’s on a VMware LSI Logic SAS.

    PS C:\> $Win32_DiskDrive | select Name,SCSILogicalUnit | ft -AutoSize

    Name SCSILogicalUnit
    —- —————
    \\.\PHYSICALDRIVE1 0
    \\.\PHYSICALDRIVE0 0

  3. Thomas Burger says:

    That is pretty normal since LSI-Logic Controllers are incrementing the target number.
    So your second RAID-Volume shows up as Target1:Lun0
    3rd ist Target2:Lun0 etc.
    in SCSI Targets are counted starting with 0

  4. Pingback: Enumerate DiskLuns | digital36 Blog-sphere

  5. Pingback: The caption property of Win32_DiskDrive to see if you have a ATA or SCSI disk | ferdushblog

  6. Luke says:

    Hi Robert, Thanks for posting this script. I’d like to use it, how can I download it? I cant seem to copy and paste from this page into the PowerShell ISE.

  7. greg says:

    Nice script, was trying to do that myself, saved me time, thanks!

  8. Marc Cote says:

    Thank you, it’s a great start to what I need, which is a mix of your solution and Granger’s powershell command.

    Basically, I want to list all LUNs with reference to where they are mounted or used. IE. partition, mount point, Storage pools, CSVs . . . etc.

    I am looking to run this once a week, send the result to my DR documentation in a data vault.
    That way if we migrate disks or make changes and forget to mod our documentation, we have something close enough (1 week old). Or a day if we go paranoid.

    Thank you again. A great starting point.

  9. Pingback: Match OS Drive Letter to VM Disk with PowerCLI | vNoob

  10. jorge says:

    hi , great function, does this work for vsphere VMs, i notice it gets incorrect data for a disk that is on SCSI 1:3

    • Hi Jorge,

      for vSphere VMs, you better use the following PowerCLI script:

      Get-VM | Get-HardDisk -PipelineVariable HardDisk |
      ForEach-Object {
      [pscustomobject]@{
      VM = $HardDisk.Parent
      HardDisk = $HardDisk.Name
      SCSIController = $HardDisk | Get-ScsiController
      SCSIUnitNumber = $HardDisk.ExtensionData.UnitNumber
      }
      }

      • jorge says:

        thanks! that does work, one more thing, i need to map that info to drive letters, do you know any way to do so from powercli in order to avoid using wmi?

  11. Pingback: Skript - PowerCLI VM Disk Report - my cloud-(r)evolution

  12. Pingback: Script - PowerCLI VM Disk Report - my cloud-(r)evolution

  13. Jeremy Sylvis says:

    I have updated this to work with PowerShell Core and Get-CimInstance.

    It includes the diagnostic dump statements I used in order to match up the different properties, just disabled.

    [CmdletBinding()]
    param(
    [string] $DeviceID
    )
    process {
    [bool] $debug = $false

    # Get the WMI objects
    $Win32_LogicalDisk = Get-CimInstance -Class Win32_LogicalDisk | Where-Object { $_.DeviceID -like $DeviceID }
    if ($true -eq $debug) {
    Write-Verbose “Logical disks:”
    $Win32_LogicalDisk | Select-Object DeviceID | Write-Verbose
    }

    $Win32_LogicalDiskToPartition = Get-CimInstance -Class Win32_LogicalDiskToPartition
    if ($true -eq $debug) {
    Write-Verbose “LogicalDisk to Partition mappings:”
    $Win32_LogicalDiskToPartition | Select-Object Dependent, Antecedent | Write-Verbose
    }

    $Win32_DiskDriveToDiskPartition = Get-CimInstance -Class Win32_DiskDriveToDiskPartition
    if ($true -eq $debug) {
    Write-Verbose “DiskDrive to Partition mappings:”
    $Win32_DiskDriveToDiskPartition | Select-Object Dependent, Antecedent | Write-Verbose
    }

    $Win32_DiskDrive = Get-CimInstance -Class Win32_DiskDrive
    if ($true -eq $debug) {
    Write-Verbose “Disk drives:”
    $Win32_DiskDrive | Select-Object DeviceID | Write-Verbose
    }

    # Search the SCSI Lun Unit for the disk
    $Win32_LogicalDisk |
    ForEach-Object {
    if ($_) {
    $LogicalDisk = $_
    $LogicalDiskToPartition = $Win32_LogicalDiskToPartition | Where-Object { $_.Dependent.DeviceID -eq $LogicalDisk.DeviceID }
    if (-not $LogicalDiskToPartition) {
    Write-Verbose (“Could not find Dependent in LogicalDiskToPartition data matching LogicalDisk DeviceID {0}.” -f $LogicalDisk.DeviceID)
    continue
    }

    $DiskDriveToDiskPartition = $Win32_DiskDriveToDiskPartition | Where-Object { $_.Dependent.DeviceID -eq $LogicalDiskToPartition.Antecedent.DeviceID }
    if (-not $DiskDriveToDiskPartition) {
    Write-Verbose (“Could not find Dependent in DiskDriveToDataPartition matching Antecedent DeviceID {0}.” -f $LogicalDiskToPartition.Antecedent.DeviceID)
    continue
    }

    $DiskDrive = $Win32_DiskDrive | Where-Object { $_.DeviceID -eq $DiskDriveToDiskPartition.Antecedent.DeviceID }
    if (-not $DiskDrive) {
    Write-Verbose (“Could not find DiskDrive matching Antecedent DeviceID {0}.” -f $DiskDriveToDiskPartition.Antecedent.DeviceID)
    continue
    }

    # Return the results
    New-Object -TypeName PSObject -Property @{
    DeviceID = $LogicalDisk.DeviceID
    SCSIBus = $DiskDrive.SCSIBus
    SCSIPort = $DiskDrive.SCSIPort
    SCSITargetId = $DiskDrive.SCSITargetId
    SCSILogicalUnit = $DiskDrive.SCSILogicalUnit
    }
    }
    }
    }

Leave a comment