Last active
March 20, 2023 06:04
-
-
Save scriptingstudio/bb35f52ff8aa996e795ad31a8b8373dd to your computer and use it in GitHub Desktop.
Disk S.M.A.R.T. info
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.SYNOPSIS | |
Reads SMART info from supporting SATA drives. | |
.PARAMETER CimSession | |
Specifies an existing CIM session or a computername. By default the script works on local computer. | |
.PARAMETER NoEmpty | |
Indicates to exclude drives without SMART information from output. | |
.EXAMPLE | |
Shows SMART info for the first SATA drive present | |
Get-SMARTInfo | Select-Object -First 1 -ExpandProperty SMART | Out-GridView | |
.NOTES | |
Version: 2.2 | |
Requires admin privileges to run. | |
#> | |
function Get-SMARTInfo { | |
param ( | |
[alias('ComputerName')]$CimSession, | |
[switch]$NoEmpty, | |
[switch]$Quiet | |
) | |
$params = @{ErrorAction=0} # ; OperationTimeoutSec=2 | |
if ($cimsession) { | |
$params['cimsession'] = if ($cimsession -isnot [CimSession]) { | |
New-CimSession -ComputerName $cimsession -ErrorAction 0 | |
} else {$cimsession} | |
if (-not $params['cimsession']) { | |
if (-not $Quiet) {Write-Warning "Could not connect to $cimsession"} | |
return | |
} | |
} | |
$enumparam = @{ | |
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" | |
} | |
$enumhealth = @{ | |
'0' = 'Healthy' | |
'1' = 'Warning' | |
'2' = 'Unhealthy' | |
'5' = 'Unknown' | |
} | |
$enumBT = @{ | |
'0' = 'Unknown'; '1' = 'SCSI' | |
'2' = 'ATAPI'; '3' = 'ATA' | |
'4' = '1394'; '5' = 'SSA' | |
'6' = 'Fibre Channel'; '7' = 'USB' | |
'8' = 'RAID'; '9' = 'iSCSI' | |
'10' = 'SAS'; '11' = 'SATA' | |
'12' = 'SD'; '13' = 'MMC' | |
'14' = 'Virtual'; '15' = 'File Backed Virtual' | |
'16' = 'Storage Spaces'; '17' = 'NVMe' | |
'18' = 'Microsoft Reserved' | |
} | |
$enumMT = @{ | |
'0' = 'Unspecified' | |
'3' = 'HDD' | |
'4' = 'SSD' | |
} | |
# collect disk info | |
$disks = @{} # disk collection | |
$diskindex = @{} # NOTE: reusable variable | |
Get-CimInstance -Class MSFT_PhysicalDisk -Namespace root/microsoft/windows/storage -Property DeviceID,MediaType,BusType,SerialNumber,FirmwareVersion,HealthStatus @params | | |
ForEach-Object {$diskindex[[int]$_.DeviceID] = $_} | |
# NOTE: PNPDeviceID of Win32_DiskDrive and InstanceName of MSStorageDriver are slightly different; InstanceName adds the suffix "_0" | |
Get-CimInstance -Class Win32_DiskDrive @params | | |
ForEach-Object { | |
if (-not $Quiet) {Write-Host "Drive '$($_.PNPDeviceID)' found"} | |
$currentdisk = $diskindex[[int]$_.index] | |
$disks["$($_.PNPDeviceID)_0"] = [PSCustomObject]@{ | |
ComputerName = $_.SystemName.tolower() | |
DriveId = $_.index | |
Model = $_.Model | |
DriveInfo = $_ | |
MediaType = if ($currentdisk.MediaType.gettype().name -eq 'string') {$currentdisk.MediaType} else {$enumMT[[string]$currentdisk.MediaType]} | |
BusType = if ($currentdisk.BusType.gettype().name -eq 'string') {$currentdisk.BusType} else {$enumBT[[string]$currentdisk.BusType]} | |
SerialNumber = $_.SerialNumber | |
Firmware = $currentdisk.FirmwareVersion | |
HealthStatus = if ($currentdisk.HealthStatus.gettype().name -eq 'string') {$currentdisk.BusType} else {$enumhealth[[string]$currentdisk.HealthStatus]} | |
FailureImminent = $null | |
Reason = $null | |
SMART = [System.Collections.Generic.List[object]]::new() | |
} | |
} | |
# test admin privileges in order to obtain S.M.A.R.T. info | |
if (-not $params['cimsession'] -and -not | |
([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]'Administrator')) { | |
if (-not $Quiet) { | |
Write-Warning 'Local system: to collect S.M.A.R.T. info administrator privileges are required.' | |
$disks.Values | |
} | |
return | |
} | |
# fill PredictFailure data | |
Get-CimInstance -class MSStorageDriver_FailurePredictStatus -Namespace root\wmi @params | | |
ForEach-Object { | |
if (-not $Quiet) {Write-Host "MSStorageDriver data for '$($_.InstanceName)' found"} | |
if ($disks[$_.InstanceName]) { | |
$disks[$_.InstanceName].FailureImminent = $_.PredictFailure | |
$disks[$_.InstanceName].Reason = $_.Reason | |
} | |
} | |
# collect thresholds | |
$thresholdTable = @{} | |
Get-CimInstance -Class MSStorageDriver_FailurePredictThresholds -Namespace root\wmi @params | | |
ForEach-Object { | |
$tcol = ($thresholdTable[$_.InstanceName] = @{}) | |
$bytes = $_.VendorSpecific | |
for ($i=0; $i -lt 360; $i+=12) { | |
$idnumeric = [int]$bytes[$i + 2] | |
if ($idnumeric) { | |
$tcol[$idnumeric] = $bytes[$i + 3] | |
} | |
} | |
} | |
# collect S.M.A.R.T. info | |
Get-CimInstance -Class MSStorageDriver_ATAPISmartData -Namespace root\wmi @params | | |
#Get-CimInstance -Class MSStorageDriver_FailurePredictData -Namespace root\wmi @params | | |
ForEach-Object { | |
$diskindex = $disks[$_.InstanceName] | |
if (-not $diskindex) { # just in case | |
if (-not $Quiet) {Write-Warning "No such disk '$($_.InstanceName)' is known"} | |
return | |
} | |
$tcol = $thresholdTable[$_.InstanceName] | |
$bytes = $_.VendorSpecific | |
for ($i=0; $i -lt 360; $i+=12) { | |
$idnumeric = [int]$bytes[$i + 2] | |
if (-not $idnumeric) {continue} | |
$id = if ($enumparam.ContainsKey($idnumeric)) { | |
$enumparam[$idnumeric] | |
} else { | |
"ID $idnumeric" | |
} | |
$rawvendordata = [BitConverter]::ToInt32($bytes, ($i + 7)) | |
$attr = @{} # experimental | |
$flags = $bytes[$i + 4] # least significant status byte, +3 most significant byte, but not used so ignored. | |
#$advisory = ($flags -band 0x1) -eq 0x0 # experimental | |
$failureImminent = ($flags -band 0x1) -eq 0x1 | |
#$onlineDataCollection = ($flags -band 0x2) -eq 0x2 # experimental | |
$diskindex.SMART.Add([PSCustomObject]@{ | |
Name = $id | |
Current = $bytes[$i + 5] | |
Worst = $bytes[$i + 6] | |
RawData = $rawvendordata #[convert]::ToString($rawvendordata,2) | |
IsOK = -not $failureImminent | |
Threshold = $tcol[$idnumeric] | |
ThresholdUndefined = -not $tcol.ContainsKey($idnumeric) | |
}) | |
} # for | |
} # foreach | |
if ($params['cimsession'] -is [CimSession]) { | |
$null = remove-cimsession $params['cimsession'] -ErrorAction 4 | |
} | |
if ($NoEmpty) { | |
$disks.Values | Where-Object {$_.smart.count} | |
} else {$disks.Values} | |
} # END Get-SMARTInfo |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment