PowerShell function to get disk SCSI Lun number
May 29, 2012 21 Comments
For 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.
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
GreAT THANKS FOR THE HELP
can you please also add value of free space
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
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
Thanks Thomas for making this clear. To make the Get-DiskScsiLun function also useful for LSI-Logic controllers did I modify the function to also retrieve the SCSIBus, SCSIPort and SCSITargetId.
Pingback: Enumerate DiskLuns | digital36 Blog-sphere
Pingback: The caption property of Win32_DiskDrive to see if you have a ATA or SCSI disk | ferdushblog
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.
Nice script, was trying to do that myself, saved me time, thanks!
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.
Pingback: Match OS Drive Letter to VM Disk with PowerCLI | vNoob
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
}
}
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?
Pingback: Skript - PowerCLI VM Disk Report - my cloud-(r)evolution
Pingback: Script - PowerCLI VM Disk Report - my cloud-(r)evolution
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
}
}
}
}
Nice. Thanks for sharing your code.