Checking for failing Hard Drives using S.M.A.R.T and PowerShell

I noticed a link to an article on twitter yesterday that talked about how you can use PowerShell to query for a SMART enabled hard drive and detect a possible imminent failure. It was an interesting article and showed me something I didn’t know regarding detecting a potential failure on a drive.

What is SMART you ask? Good question, SMART stands for Self-Monitoring, Analysis and Reporting Technology and is used, as cited in this Wikipedia article:

The purpose of S.M.A.R.T. is to warn a user of impending drive failure while there is still time to take action, such as copying the data to a replacement devices

While I enjoyed the article and the PowerShell example that referenced the MSStorageDriver_FailurePredictStatus class used to determine if a drive was failing, something kept nagging at me saying that I could really put something useful together that would give you more information if a potential failure was detected rather than bringing up the device manager and looking for the drive and its data.

So with that, I went ahead and started looking at what it would take to not only detect if a failure was going to happen, but to also look for that drive and gather some more information. Understand that this only works on local drives, not SAN attached or network drives.

The first thing I did was deciding to use the Win32_DiskDrive class as that lists each local hard drive installed on my computers. Once I had that information, I then had to figure out what items from each of the WMI classes I could use to match the failed drive up with the appropriate information. At last I found my two items:

gwmi win32_diskdrive |
Select PNPDeviceID

PNPDeviceID
———–
IDE\DISKST9320320AS_____________________________DE05____\5&446824F&0&0.0.0

Get-WmiObject -namespace root\wmi -class MSStorageDriver_FailurePredictStatus | 
Select InstanceName

InstanceName
————
IDE\DiskST9320320AS_____________________________DE05____\5&446824f&0&0.0.0_0

Ok, close… But not completely equal. The problem is that the InstanceName has an extra _0 after it,thus ruining the chance to perform a –filter in the get-WmiObject to locate the appropriate drive. I cannot perform a wildcard query either because of that extra stuff at the end. Looks like this is a job for Regular Expressions!

Here is the RegEx code that I used to parse the string:

[regex]$regex = "(?<DriveName>\w+\\[A-Za-z0-9_]*)\w+"

I created a RegEx object that I can use later to grab an important piece of the drive name that I can add into a wildcard search to locate the proper drive. Some key pieces from this expression:

  • (?<DriveName>) This allows me to use a named group which makes it easier to locate the correct match. More information on this can be found here.
  • \w+ This allows me to search for anyword or character with a couple of limitations. More information on that can be found here.
  • \\ The “\” backslash is the escape character used in Regular Expressions in PowerShell. By doing this, I am escaping a “\” backslash for my query.
  • [A-Za-z0-9_] I am looking for a Upper or Lower case letter or a number within the range given plus any underscores. More information can be found here.
  • * This means to search for 1 or more of whatever you have preceding this, in my case look for more matching letters or numbers.

That was a very brief look into the RegEx values I used to pull the drive information that can then be used to perform my wmi query and find the drive.

$drive = $regex.Matches('IDE\DiskST9320320AS_____________________________DE05____\5&446824f&0&0.0.0_0') | ForEach {
$_.Groups['DriveName'].value
}
$drive

image

Now we are able to use this to query the Win32_DiskDrive class and find the matching drive.

gwmi Win32_DiskDrive| Where {$_.PNPDeviceID -like "$drive*"}

image

And now we have the problem drive.

The finished Advanced Function that I came up with is Get-FailingDrive which can be ran against either a local or remote machine/s. If a drive using SMART shows that a failure may occur, it will then retrieve more information about that drive and display the information for the user.

For the sake of showing the output, I am modifying a piece of the code to return a non-failing drive. But the code I am making available to download is configured properly.

Get-FailingDrive

image

The Reason code appears to be vendor specific based on limited research. However, there is a list of reason codes available here.

Of course, I have performed limited testing and use of this code on my laptop at home, so I welcome any and all feedback so I can make improvements as needed. Thanks!

 

Download Script

Script Repository

PoshCode

(remove ‘.doc’ extension)

Code

Function Get-FailingDrive {
<#
.SYNOPSIS
    Checks for any potentially failing drives and reports back drive information.
    
.DESCRIPTION
    Checks for any potentially failing drives and reports back drive information. This only works
    against local hard drives using SMART technology. Reason values and their meanings can be found
    here: http://en.wikipedia.org/wiki/S.M.A.R.T#Known_ATA_S.M.A.R.T._attributes
    
.PARAMETER Computer
    Remote or local computer to check for possible failed hard drive.
    
.PARAMETER Credential
    Provide alternate credential to perform query.

.NOTES
    Author: Boe Prox
    Version: 1.0
    http://learn-powershell.net

.EXAMPLE
    Get-FailingDrive
    
    WARNING: ST9320320AS ATA Device may fail!


    MediaType       : Fixed hard disk media
    InterFace       : IDE
    DriveName       : ST9320320AS ATA Device
    Reason          : 1
    SerialNumber    : 202020202020202020202020533531584e5a4d50
    FailureImminent : True
    
    Description
    -----------
    Command ran against the local computer to check for potential failed hard drive.
#>
    [cmdletbinding()]
    Param (
        [parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
        [string[]]$Computername,
        [parameter()]
        [System.Management.Automation.PSCredential]$Credential
    )
    Begin {
        $queryhash = @{}
        $BadDriveHash = @{}
    }
    Process {
        ForEach ($Computer in $Computername) {
            If ($PSBoundParameters['Computer']) {
                $queryhash['Computername'] = $Computer
                $BadDriveHash['Computername'] = $Computer
            } Else {
                $queryhash['Computername'] = $Env:Computername
                $BadDriveHash['Computername'] = $Env:Computername            
            }
            If ($PSBoundParameters['Credential']) {
                $queryhash['Credential'] = $Credential
                $BadDriveHash['Credential'] = $Credential
            }            
            Write-Verbose "Creating SplatTable"
            $queryhash['NameSpace'] = 'root\wmi'
            $queryhash['Class'] = 'MSStorageDriver_FailurePredictStatus'
            $queryhash['Filter'] = "PredictFailure='False'"
            $queryhash['ErrorAction'] = 'Stop'
            $BadDriveHash['Class'] = 'win32_diskdrive'
            $BadDriveHash['ErrorAction'] = 'Stop'
            [regex]$regex = "(?<DriveName>\w+\\[A-Za-z0-9_]*)\w+"
            Try {
                Write-Verbose "Checking for failed drives"
                Get-WmiObject @queryhash | ForEach {
                    $drive = $regex.Matches($_.InstanceName) | ForEach {$_.Groups['DriveName'].value}
                    Write-Verbose "Gathering more information about failing drive"
                    $BadDrive = gwmi @BadDriveHash | Where {$_.PNPDeviceID -like "$drive*"}
                    If ($BadDrive) {
                        Write-Warning "$($BadDriveHash['Computername']): $($BadDrive.Model) may fail!"
                        New-Object PSObject -Property @{
                            DriveName = $BadDrive.Model
                            FailureImminent  = $_.PredictFailure
                            Reason = $_.Reason
                            MediaType = $BadDrive.MediaType
                            SerialNumber = $BadDrive.SerialNumber
                            InterFace = $BadDrive.InterfaceType
                            Partitions = $BadDrive.Partitions
                            Size = $BadDrive.Size
                            Computer = $BadDriveHash['Computername']
                        }
                    }
                }
            } Catch {
                Write-Warning "$($Error[0])"
            }
        }
    }
}

About Boe Prox

Microsoft Cloud and Datacenter MVP working as a SQL DBA.
This entry was posted in powershell, scripts and tagged , , , . Bookmark the permalink.

5 Responses to Checking for failing Hard Drives using S.M.A.R.T and PowerShell

  1. Pingback: SCCM – контроль здоровья дисков | ILYA Sazonov: ITPro

  2. joebarteam73 says:

    How can I do if I want receive an email when the status of the value “PredictFailure” is true ?

    Thanks a lot for your help

  3. Chris Jenson says:

    So if we run this script and it returns nothing, that’s a good thing right? It only returns output if a SMART drive is flagged as failure predicted?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s