Finding Related WMI Classes (and Drive Letters)

Recently, I was asked how to find the drive letter when all you knew was a partition or disk index. Hmmm.

Accessing Related WMI Classes

After some scratching the head, the solution was simple. WMI internally maintains relationships between related classes. There are two WMI classes of interest here. Win32_DiskPartition represents partitions, and Win32_LogicalDisk represents logical drives with drive letters. So all you need to do is join them together to get a drive letter from a partition # and vice versa.

Unfortunately, WMI relationships are not directly exposed in PowerShell. Fortunately, you can join WMI classes using the GetRelated() method burried in the psbase property of each WMI object.

Get-DriveLetterByIndex

I created a function called Get-DriveLetterByIndex which accepts a disk # and/or a partition # and returns the drive letter for the drive hosted. I decided to make this function extra versatile so when you do not submit either drive # or partition #, the function returns all partitions and drive letters hosted there. To understand how this was done, grab yourself a coffee and have a look:

function Get-DriveLetterByIndex {
[CmdletBinding()]
param(
[Int]
$Disk = $null,
[Int]
$Partition = $null
)

# create a WMI filter based on supplied parameters
$filter = @()

# when -disk is provided, add it to the filter array:
if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('disk')) {
$filter += "DiskIndex=$disk"
}

# when -partition is provided, add it to the filter array:
if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('Partition')) {
$filter += "Index=$partition"
}

# set the "object field separator" $ofs to ' and ' so that all filters
# in $filter are joined by " and "
$private:ofs = ' and '

# apply the filter to Get-WMIObject:

Get-WMIObject Win32_DiskPartition -filter "$filter" |
# process each partition retrieved:
ForEach-Object {
# prepare an empty hash table to store results in:
$info = @{}

# populate the hash table with the information you want to return
$info.Disk = $_.DiskIndex
$info.Partition = $_.Index

# get the drive letter by finding the related Win32_LogicalDisk
# instance:
$info.DriveLetter = $_.psbase.GetRelated('Win32_LogicalDisk') |
Select-Object -ExpandProperty DeviceID

# finally, convert the hash table to an object and return it
New-Object PSObject -Property $info
}
}

# Getting all drives:
"All Drives:"
Get-DriveLetterByIndex

# Getting drives on partition #2:
"Partition #2:"
Get-DriveLetterByIndex -Partition 2

# Getting drives on disk #1:
"Disk #1"
Get-DriveLetterByIndex -Disk 1

# Getting drive on disk #0 and partition #1:
"Disk #0, Partition #1:"
Get-DriveLetterByIndex -Disk 0 -Partition 1

You can download the function and sample code here:  http://powershell.com/cs/media/p/7950.aspx

Dynamic WMI Filters

Both parameters (-disk and -partition) are optional, so you can omit both and examine all partitions. If you do specify one or both, the function needs to create the WMI filter from what you submit. This is done by checking whether the user has specified parameters. $PSCmdlet.MyInvocation.BoundParameters.ContainsKey() can check whether a parameter was used by the caller. If so, the function adds the appropriate filter to an array stored in $filter.

Since $filter can have null, one or two elements depending on which parameters if any the user specified, it is a little challenge to convert the $filter array to a valid WMI filter string. If there are two filter conditions, they need to be joined by a logical "and". Fortunately, PowerShell can do that for me. All I need to do is set $ofs, the "object field separator", to whatever I want PowerShell to insert inbetween array elements. That's why I set $ofs to " and " and mark it as private so it won't spill over into other contexts.

Now, WMI gets the partitions the user decided to examine, gets the related Win32_LogicalDisk instances and stores all information in a hash table. The hast table gets converted to an object and is returned by the function.

At the end of the listing, there are some sample calls illustrating how flexible the new function really is. You can easily list all partitions or select a specific disk and/or partition.

What's Next?

Needless to say that the two WMI classes used in this example aren't the only ones related to each other. Many WMI classes maintain useful relationships. To find out which relationships exist for a given WMI object, check this out. It lists all the related classes for process objects:

PS> (Get-WmiObject Win32_Process -Filter "handle=$pid").psbase.GetRelated() |
Group-Object __CLASS -NoElement

Count Name
----- ----
1 Win32_LogonSession
1 Win32_NamedJobObject
1 Win32_ComputerSystem
205 CIM_DataFile

And the next line will dump all WMI objects related to the current PowerShell process - a wealth of data:

(Get-WmiObject Win32_Process -Filter "handle=$pid").psbase.GetRelated()

Tobias

Microsoft MVP PowerShell Germany

P.S.
If you live in Germany or other parts of Europe and your company would like to set up a truly great PowerShell training, just contact me! I regularly train mid- to large-size companies. Trainings are always a blast with tons of real-world-examples and solutions. Here's how to get in touch with me: tobias.weltner@scriptinternals.de


Posted Oct 21 2010, 10:30 PM by Tobias
Concentrated Tech NSoftware Dell Compellent Sponsored by Idera and Concentrated Tech and NSoftware and Dell Compellent
Copyright 2011 PowerShell.com. All rights reserved.