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])"
            }
        }
    }
}
This entry was posted in powershell, scripts and tagged , , , . Bookmark the permalink.

6 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?

      • pilot says:

        can you add script wherein result will be sent via email? appreciate your help.

        • Jay says:

          I put this in my powershell script so that it generates an email when it runs. You can have emails sent to multiple addresses, just use a semi-colon as a separator.

          $Machinename = ($env:COMPUTERNAME)
          $SMTPServer = “FQDN SMTP server”
          $msg = new-object Net.Mail.MailMessage
          $SMTP = New-Object Net.Mail.SmtpClient($SMTPServer)
          $msg.From = “DO-NOT ReplyEmail@whatever.com
          $msg.To.Add(“John.Doe@whatever.com; Jane.Doe@whatever.com“)
          $msg.Subject = “Pending Hard Disk Failure for $MachineName”
          $msg.Priority = “High”
          $msg.Body = “This email is to inform that there is a pending hard disk failure on machine…$MachineName.”
          $msg.Attachments.Add($Attach)
          $SMTP.Send($msg)

Leave a comment