Get-SMARTInfo.ps1

function Get-SMARTInfo
{
  <#
    .SYNOPSIS
    Reads SMART info from supporting hard drives. Requires ADMIN privileges to run!
 
     
    .EXAMPLE
    Get-SMARTInfo | Select-Object -First 1 -ExpandProperty SMART | Out-GridView
    Shows SMART info for first hard drive present
 
  #>




    $disks = @(Get-WmiObject -Class Win32_DiskDrive |
        ForEach-Object {
            [PSCustomObject]@{
                Model = $_.Model
                SMART = [Collections.ArrayList]@()
                DriveInfo = $_
            }
        })


    $friendlynames = @{
        0x00="Invalid"
        0x01="Raw read error rate"
        0x02="Throughput performance"
        0x03="Spinup time"
        0x04="Start/Stop count"
        0x05="Reallocated sector count"
        0x06="Read channel margin"
        0x07="Seek error rate"
        0x08="Seek timer performance"
        0x09="Power-on hours count"
        0x0A="Spinup retry count"
        0x0B="Calibration retry count"
        0x0C="Power cycle count"
        0x0D="Soft read error rate"
        0x16="Current helium level"
        0xAA="Available reserved space"
        0xAB="Program fail count"
        0xAC="Erase fail count"
        0xAD="Wear leveling count"
        0xAE="Unexpected power loss count"
        0xAF="Power loss protection failure"
        0xB0="Erase fail count"
        0xB1="Wear range delta"
        0xB3="Used reserved block count"
        0xB4="Unused reserved block count"
        0xB5="Program fail count total / non-4K aligned access count"
        0xB6="Erase fail count"
        0xB7="SATA downshift error count / Runtime bad block"
        0xB8="End-to-End error"
        0xB9="Head stability"
        0xBA="Induced Op-vibration detection"
        0xBB="Reported uncorrectable errors"
        0xBC="Command timeout"
        0xBD="High fly writes"
        0xBE="Airflow Temperature Celsius"
        0xBF="G-sense error rate"
        0xC0="Power-off retract count"
        0xC1="Load/Unload cycle count"
        0xC2="HDD temperature"
        0xC3="Hardware ECC recovered"
        0xC4="Reallocation count"
        0xC5="Current pending sector count"
        0xC6="Offline scan uncorrectable count"
        0xC7="UDMA CRC error rate"
        0xC8="Write error rate"
        0xC9="Soft read error rate"
        0xCA="Data Address Mark errors"
        0xCB="Run out cancel"
        0xCC="Soft ECC correction"
        0xCD="Thermal asperity rate (TAR)"
        0xCE="Flying height"
        0xCF="Spin high current"
        0xD0="Spin buzz"
        0xD1="Offline seek performance"
        0xD2="Vibration during write"
        0xD3="Vibration during write"
        0xD4="Shock during write"
        0xDC="Disk shift"
        0xDD="G-sense error rate"
        0xDE="Loaded hours"
        0xDF="Load/unload retry count"
        0xE0="Load friction"
        0xE1="Load/Unload cycle count"
        0xE2="Load-in time"
        0xE3="Torque amplification count"
        0xE4="Power-off retract count"
        0xE6="GMR head amplitude"
        0xE7="Temperature"
        0xE8="Endurance remaining / available reserved space"
        0xE9="Power-on hours / media wearout indicator"
        0xEA="Average erase count / maximum erase count"
        0xEB="Good block count / System free block count"
        0xF0="Head flying hours"
        0xF1="Total LBAs written"
        0xF2="Total LBAs read"
        0xF3="Total LBAs written expanded"
        0xF4="Total LBAs read expanded"
        0xF9="NAND writes 1GiB"
        0xFA="Read error retry rate"
        0xFB="Minimum spares remaining"
        0xFC="Newly added bad flash block"
        0xFE="Free fall protection"
    }

    Get-WmiObject -Namespace root\wmi -class MSStorageDriver_FailurePredictStatus |
    ForEach-Object { $i = 0 } { 
        $disks[$i] | Add-Member -MemberType NoteProperty -Name FailureImminent -Value $_.PredictFailure 
        $i++
    }


    $threshHoldCollection = @(Get-WmiObject -Class MSStorageDriver_FailurePredictThresholds -Namespace root/wmi |
        ForEach-Object { $i = 0 } {
    
  

            $threshHolds = @{}
            $threshData = $_
            $bytes = $threshData.VendorSpecific
            for ($i = 0; $i -lt 30; $i++)
            {
                try
                {
                    $idnumeric = [int]$bytes[$i*12 + 2]
    
                    $thresh = $bytes[$i*12 + 3];
                    $threshHolds[$idnumeric] = $thresh;
                }
                catch
                {
                    # given key does not exist in attribute collection (attribute not in the dictionary of attributes)
                }
            }
            $threshHolds
    })



    Get-WmiObject -Class MSStorageDriver_FailurePredictData -Namespace root/wmi |
    ForEach-Object { $x = 0 } {
        $smartData = $_
        $bytes = $smartData.VendorSpecific
        for ($i = 0; $i -lt 30; $i++)
        {
            try
            {                  
                $idnumeric = [int]$bytes[$i*12 + 2]
                if ($idnumeric -eq 0) { continue };
                if ($friendlynames.ContainsKey($idnumeric))
                {
                    $id = $friendlynames[$idnumeric]
                }
                else
                {
                    $id = "ID $idnumeric"
                }
                $attr = @{}
                $flags = $bytes[$i * 12 + 4]; # least significant status byte, +3 most significant byte, but not used so ignored.
                $advisory = ($flags -band 0x1) -eq 0x0;
                $failureImminent = ($flags -band 0x1) -eq 0x1;
                #bool onlineDataCollection = ($flags -band 0x2) == 0x2;

                $value = $bytes[$i*12 + 5];
                $worst = $bytes[$i*12 + 6];
                $vendordata = [BitConverter]::ToInt32($bytes, $i*12 + 7);
                $threshHold = $threshHoldCollection[$x][$idnumeric]
                $threshHoldUndefined = $threshHoldCollection[$x].ContainsKey($idnumeric) -eq $false
                if (!$threshHoldUndefined) { 
                    $threshHold = $threshHoldCollection[$x][$idnumeric]
                } else {
                    $threshHold = $null
                }
                $null = $disks[$x].SMART.Add(
                    [PSCustomObject]@{
                        Name = $id
                        Current = $value;
                        Worst = $worst;
                        Data = $vendordata;
                        IsOK = $failureImminent -eq $false;
                        Threshold = $threshHolds[$idnumeric]
                        ThresholdUndefined = $threshHoldUndefined
                })
            }
            catch
            {
                # given key does not exist in attribute collection (attribute not in the dictionary of attributes)
            }                
        }
        $x++    
    }
    $disks
}