Shaun
Shaun

Reputation: 374

Powershell - Detect if Drive Letter is Mounted on a SSD Solid State Disk

I'm writing a Hyper-V VM build script and wanted to add a feature to select a SSD where possible to prioritise smaller and most frequently used VMs. I first found the way in from the Scripting Guys here: http://blogs.technet.com/b/heyscriptingguy/archive/2013/03/17/powertip-use-powershell-to-identify-ssd.aspx but I couldn't find anything that took me all of the way so I used WBEMTEST to find the classes I needed to query to make the connection between the SSD disk and the drive letter in question.

Click Start --> Run --> type WBEMTEST --> Check that the namespace is set to root\cimv2 and click Connect --> Enum Classes --> Recursive --> OK. About 5 seconds later the list will be fully populated allowing you to click on a list item. Click on a list item and then press the letter W to jump to Win32 and find one to look at then click Add. The properties and methods available are then shown.

The function IsSSDDrive below returns $True or $False depending on whether the given drive letter (including colon - e.g. C:) is mounted on a Solid State Disk (SSD) - assuming that the manufacturer inserted the letters SSD into the device name. This assumption ultimately makes this method unreliable so it would be wise to include some code to cross check using something like:

$DiskScore = (Get-WmiObject -Class Win32_WinSAT).DiskScore
If ($DiskScore -gt 6.9) { $SystemHasSSDDrive=$True } Else { $SystemHasSSDDrive=$False }
Write-Host "System Has SSD Drive: $SystemHasSSDDrive"

...if your environment uses such drives.

Function IsSSDDrive($Drive)
# Returns $True or $False depending on whether the given drive letter (including colon - e.g. C:) is mounted on a Solid State Disk (SSD) - assuming that the manufacturer inserted the letters SSD into the device name - which ultimately makes this method unreliable so beware to cross check using something like $DiskScore = (Get-WmiObject -Class Win32_WinSAT).DiskScore # Thanks to Rens Hollanders for this! http://renshollanders.nl/2013/01/sccm-mdt-identifying-ssds-from-your-task-sequence-by-windows-performance-index/
{
    [array] $SSDDeviceIDList = $Null
    [array] $SSDDeviceIDListF = $Null
    [array] $DDListF = $Null
    [array] $SSDDriveLetters = $Null

    # Build a list of DeviceIDs of disk drives that contain the string SSD.  Example output: \\.\PHYSICALDRIVE0, \\.\PHYSICALDRIVE1 etc
    $SSDDeviceIDList = Get-WmiObject Win32_DiskDrive | where { $_.model -match 'SSD'} | Select 'DeviceID'
    #$SSDDeviceIDList

    # Fix the list by removing the \\.\ from each of the obtained \\.\PHYSICALDRIVEx values
    ForEach ($SSDDeviceID in $SSDDeviceIDList) {
        $SSDDeviceIDListF += $SSDDeviceID.DeviceID.ToString().Replace("\\.\","")
    }
    #$SSDDeviceIDListF

    # Obtain disk drive to disk partition mapping information.  Section of sample output for a single partition:
        #Antecedent       : \\MyComputerName\root\cimv2:Win32_DiskDrive.DeviceID="\\\\.\\PHYSICALDRIVE1"
        #Dependent        : \\MyComputerName\root\cimv2:Win32_DiskPartition.DeviceID="Disk #1, Partition #2"
    $DDList1 = $null ; $DDList1 = Get-WmiObject Win32_DiskDriveToDiskPartition
    # Look at each partition
    ForEach ($Disk in $DDList1) {
        # Iterate through the previously collected list of filtered (SSD in the name) hard drives
        ForEach ($SSDDeviceID in $SSDDeviceIDListF) {
            # If the partition is found with a property that matches the deviceID of the known-SSD list, then add the partition dependent information to the filtered (SSD) list of disk drives (DDListF)
            If ($Disk.Antecedent.Contains($SSDDeviceID)) { $DDListF += $Disk.Dependent.Split([Char]34)[1] }
        }
    }
    #$DDListF

    # Obtain disk drive to disk partition mapping information.  We need to do this because the drive letter attribute is not present in Win32_DiskDriveToDiskPartition and the physical disk is not present in Win32_LogicalDiskToPartition. 
    # Section of sample output for a single partition:
    #    Antecedent       : \\MyComputerName\root\cimv2:Win32_DiskPartition.DeviceID="Disk #1, Partition #2"
    #    Dependent        : \\MyComputerName\root\cimv2:Win32_LogicalDisk.DeviceID="I:"
    $LDPList = Get-WmiObject Win32_LogicalDiskToPartition
    # Look at each existing partition (on all drives)
    ForEach ($Partition in $LDPList) {
        # Iterate through the list of filtered SSD partitions
        ForEach ($DDL in $DDListF) {
            # If the disk-partition information from the SSD filtered list is found in the current partition then add the current partition's drive letter to the list of SSD drive letters
            If ($Partition.Antecedent.Contains($DDL)) { $SSDDriveLetters += $Partition.Dependent.Split([Char]34)[1] }
        }
    }
    $IsSSD = $False
    If (!($SSDDriveLetters -eq $Null)) {
        ForEach ($Item in $SSDDriveLetters) {
            If ($Drive.ToUpper() -eq $Item.ToUpper()) {
                $IsSSD = $True
                break
            }
        }
    }
    Return $IsSSD
}

$d = IsSSDDrive "D:" # Returns True if the given drive letter resides on a disk with a name containing the letters SSD.
$d

$c = IsSSDDrive "C:"
$c

Upvotes: 2

Views: 4135

Answers (3)

Umair Ahmed
Umair Ahmed

Reputation: 2423

Here is my rendition for checking whether current working directory is on an SSD:

function IsWorkingDirectorOnAnSSD {
    $DriveLetter = $pwd.Path[0]
    foreach ($Drive in Get-PhysicalDisk) { 
        if (($Drive | Get-Disk | Get-Partition).DriveLetter -Contains $DriveLetter) {
            Return $Drive.MediaType -eq 'SSD'
        }
    }
}

This can be modified with drive letter as argument to check by drive letter.

Upvotes: 0

Monarc Dev
Monarc Dev

Reputation: 26

#return letters of disks and if SSD or HDD or Unspec..:
Get-PhysicalDisk | ForEach-Object { 
    $physicalDisk = $_ 
    $physicalDisk | Get-Disk | Get-Partition |
    Where-Object DriveLetter |Select-Object DriveLetter, @{n='MediaType';e={ 
    $physicalDisk.MediaType }}
    }
# | ft -HideTableHeaders if you don't want the table's headers.

Upvotes: 1

Shaun
Shaun

Reputation: 374

Here is a twist on the theme in case this is what you needed instead. This function returns an array of SSD drive letters - if any are found

Function GetSSDDriveLetters()
# Returns an array of SSD drive letters - if any are found - assuming that the manufacturer inserted the letters SSD into the device name - which ultimately makes this method unreliable so beware to cross check using something like $DiskScore = (Get-WmiObject -Class Win32_WinSAT).DiskScore # Thanks to Rens Hollanders for this! http://renshollanders.nl/2013/01/sccm-mdt-identifying-ssds-from-your-task-sequence-by-windows-performance-index/
# http://stackoverflow.com/questions/28731401/powershell-detect-if-drive-letter-is-mounted-on-a-ssd-solid-state-disk/28731402#28731402
{
    [array] $SSDDeviceIDList = $Null
    [array] $SSDDeviceIDListF = $Null
    [array] $DDListF = $Null
    [array] $SSDDriveLetters = $Null

    # Build a list of DeviceIDs of disk drives that contain the string SSD.  Example output: \\.\PHYSICALDRIVE0, \\.\PHYSICALDRIVE1 etc
    $SSDDeviceIDList = Get-WmiObject Win32_DiskDrive | where { $_.model -match 'SSD'} | Select 'DeviceID'
    #$SSDDeviceIDList

    # Fix the list by removing the \\.\ from each of the obtained \\.\PHYSICALDRIVEx values
    ForEach ($SSDDeviceID in $SSDDeviceIDList) {
        $SSDDeviceIDListF += $SSDDeviceID.DeviceID.ToString().Replace("\\.\","")
    }
    #$SSDDeviceIDListF

    # Obtain disk drive to disk partition mapping information.  Section of sample output for a single partition:
        #Antecedent       : \\MyComputerName\root\cimv2:Win32_DiskDrive.DeviceID="\\\\.\\PHYSICALDRIVE1"
        #Dependent        : \\MyComputerName\root\cimv2:Win32_DiskPartition.DeviceID="Disk #1, Partition #2"
    $DDList1 = $null ; $DDList1 = Get-WmiObject Win32_DiskDriveToDiskPartition
    # Look at each partition
    ForEach ($Disk in $DDList1) {
        # Iterate through the previously collected list of filtered (SSD in the name) hard drives
        ForEach ($SSDDeviceID in $SSDDeviceIDListF) {
            # If the partition is found with a property that matches the deviceID of the known-SSD list, then add the partition dependent information to the filtered (SSD) list of disk drives (DDListF)
            If ($Disk.Antecedent.Contains($SSDDeviceID)) { $DDListF += $Disk.Dependent.Split([Char]34)[1] }
        }
    }
    #$DDListF

    # Obtain disk drive to disk partition mapping information.  We need to do this because the drive letter attribute is not present in Win32_DiskDriveToDiskPartition and the physical disk is not present in Win32_LogicalDiskToPartition. 
    # Section of sample output for a single partition:
    #    Antecedent       : \\MyComputerName\root\cimv2:Win32_DiskPartition.DeviceID="Disk #1, Partition #2"
    #    Dependent        : \\MyComputerName\root\cimv2:Win32_LogicalDisk.DeviceID="I:"
    $LDPList = Get-WmiObject Win32_LogicalDiskToPartition
    # Look at each existing partition (on all drives)
    ForEach ($Partition in $LDPList) {
        # Iterate through the list of filtered SSD partitions
        ForEach ($DDL in $DDListF) {
            # If the disk-partition information from the SSD filtered list is found in the current partition then add the current partition's drive letter to the list of SSD drive letters
            If ($Partition.Antecedent.Contains($DDL)) { $SSDDriveLetters += $Partition.Dependent.Split([Char]34)[1] }
        }
    }
    Return $SSDDriveLetters
}

GetSSDDriveLetters

Upvotes: 2

Related Questions