PowerShell for Storage and File System Management (Part 9)

If you would like to read the other parts in this article series please go to:

In my previous article, I finally finished showing you how to build a PowerShell script that would assess the health of the disks in all of your network servers. That script provides some very useful information. The problem however, is that the script can produce an overwhelming volume of insignificant information. After all, you probably don’t want to read through health status reports for hundreds of disks on the off chance that the report might list one disk that is having problems. It would be a lot better for the report to simply list the one disk that is having problems.

I have already started the process of filtering out the less helpful information. In the last article, I adapted the script so that temperature data would only be displayed if a disk’s temperature is above 35 degrees. Here is the code:

Function Get-Smart

#Function to get disk SMART information

{

(Get-WmiObject -namespace root\wmi –class MSStorageDriver_FailurePredictStatus -ComputerName $Input | Select-Object PSComputerName, InstanceName, PredictFailure, Reason)

}

 

Function Get-Health

#Function to get disk health information

{

$Pdisk= Get-PhysicalDisk

ForEach ( $LDisk in $PDisk )

               

{

               

$LDisk.FriendlyName

               

$LDisk.HealthStatus

               

$LDisk | Get-StorageReliabilityCounter | Where-Object {$_.Temperature -GT 35} | Select-Object Temperature | FL

               

Write-Host ==================

               

}

}

 

#Script Body

$Servers = @(“Hyper-V-1”, “Hyper-V-2”, “Hyper-V-3”, “Hyper-V-4”, “Prod1”, “Prod2”)

               

ForEach ($Server in $Servers) {

               

$Server | Get-Smart

               

Enter-PSSession -ComputerName $Server

               

Get-Health

               

Exit-PSSession

So the first thing that we need to do is to begin setting some threshold values that define an unhealthy state. These values are probably going to be different for every organization. For the sake of demonstration, let’s assume that we wanted to know about any and all read errors or write errors. In such a case, we could write two lines of code that are very similar to the line used to check whether the disk’s temperature falls within a healthy range. That line is:

$LDisk | Get-StorageReliabilityCounter | Where-Object {$_.Temperature -GT 35} | Select-Object Temperature | FL

The line above checks to see if the disk’s temperature is above 35 degrees. If the temperature is higher than 35 degrees, then the temperature is displayed. We can do exactly the same thing for read and write errors. The lines of code used to do so would look something like this:

$LDisk | Get-StorageReliabilityCounter | Where-Object {$_.ReadErrorsTotal -GT 0} | Select-Object ReadErrorsTotal | FL

$LDisk | Get-StorageReliabilityCounter | Where-Object {$_.WriteErrorsTotal -GT 0} | Select-Object WriteErrorsTotal | FL

These two lines of code are identical to one another, except that one checks for the presence of read errors, while the other looks for write errors. These lines of code can be added to our script just below the line that checks storage temperature. The resulting script would look like this:

Function Get-Smart

#Function to get disk SMART information

{

(Get-WmiObject -namespace root\wmi –class MSStorageDriver_FailurePredictStatus -ComputerName $Input | Select-Object PSComputerName, InstanceName, PredictFailure, Reason)

}

 

Function Get-Health

#Function to get disk health information

{

$Pdisk= Get-PhysicalDisk

ForEach ( $LDisk in $PDisk )

               

{

               

$LDisk.FriendlyName

               

$LDisk.HealthStatus

               

$LDisk | Get-StorageReliabilityCounter | Where-Object {$_.Temperature -GT 35} | Select-Object Temperature | FL

$LDisk | Get-StorageReliabilityCounter | Where-Object {$_.ReadErrorsTotal -GT 0} | Select-Object ReadErrorsTotal | FL

$LDisk | Get-StorageReliabilityCounter | Where-Object {$_.WriteErrorsTotal -GT 0} | Select-Object WriteErrorsTotal | FL

               

Write-Host ==================

               

}

}

 

#Script Body

$Servers = @(“Hyper-V-1”, “Hyper-V-2”, “Hyper-V-3”, “Hyper-V-4”, “Prod1”, “Prod2”)

               

ForEach ($Server in $Servers) {

               

$Server | Get-Smart

               

Enter-PSSession -ComputerName $Server

               

Get-Health

               

Exit-PSSession

You can see the output from this script in Figure A.

Image
Figure A: 
This is the output from the script in its current form.

As you look at the screen capture above, you will probably notice two things. First, no metric data is being displayed. This is because all of my disks are currently healthy and there isn’t really anything I can do to force them into an unhealthy state. The other thing that you will notice is that I have eliminated a lot of clutter, but there is still a lot of clutter remaining.

In an effort to further reduce the clutter, I have rewritten most of the Get-Health function. Here is the new function:

Function Get-Health

#Function to get disk health information

{

$Pdisk= Get-PhysicalDisk

ForEach ( $LDisk in $PDisk )

               

{

               

$CurrentDisk = $LDisk | Get-StorageReliabilityCounter

               

If ($CurrentDisk.Temperature -GT 35 -OR $CurrentDisk.ReadErrorsTotal -GT 0 -OR $_.CurrentDisk -GT 0){

                               

$LDisk.FriendlyName

                               

$LDisk.HealthStatus

                               

$LDisk | Get-StorageReliabilityCounter | Select-Object Temperature, ReadErrorsTotal, WriteErrorsTotal | FL

                               

Write-Host ================== }

               

}

}

This function still works in basically the same way that it previously did until it reaches the line that starts with $CurrentDisk. I am using $CurrentDisk as a variable to store the reliability counter data for the disk that is presently being examined.

The next line of code contains a multi-part if statement. This line of code checks three conditions. It checks to see if the disk’s termerature is above 35. It also checks to see if the number of read errors are greater than 0, and it checks to see if the number of write errors are greater than 0. If any of these conditions are true then a block of code is processed that causes the disk’s name, its health status, temperature, and number of read and write errors to be displayed.

As previously mentioned, all of my disk’s are healthy, so no metrics are displayed beyond the SMART information, which is being processed separately. Even so, Figure B shows how much the output has been reduced.

Image
Figure B: 
The modifications to the script have greatly reduced the output volume.

So what if we wanted to test the code? Well, one way of doing so would be to change the temperature threshold value from 35 to 0. This would cause the metrics to be displayed for every disk. You can see what this looks like in Figure C.

Image
Figure C: 
This is the script’s unfiltered output.

We have really chipped away at the script’s bloated output. But what about the SMART data? Well, it’s easy enough to trim that down as well. Here is how I have modified the Get-Smart function:

Function Get-Smart

#Function to get disk SMART information

{

$Smart = Get-WmiObject -namespace root\wmi –class MSStorageDriver_FailurePredictStatus -ComputerName $Input

If ($Smart.PredictFailure -EQ $True) {$Smart | Select-Object PSComputerName, InstanceName, PredictFailure, Reason | FL}

}

As you look at the code above, you can see that I have created a variable called $Smart, and I am assigning to that variable the SMART information for the disk that is currently being analyzed. The next line looks at the PredictFailure value for the current disk. If PredictFailure is equal to $True then it means that SMART is predicting that the drive will fail. In that case, the SMART data will be displayed for the drive. Otherwise nothing will be displayed.

So with that said, we have filtered the output to get rid of anything irrelevant. Maybe too much so. If you look at Figure D, you can see that when all of the target servers are healthy, there is no indication that the script has done anything at all.

Image
Figure D: 
The script shows no data on a healthy system.

So what can we do about this? There are two things. First, I recommend momentarily changing $True to $False in the code block above. This will allow you to verify that the function is working, as shown in Figure E.

Image

Figure E: By changing $True to $False, we can test the code.

When you change $False back to $True, I recommend adding a line to the main script body within the ForEach loop that simply says $Server. This will display the name of the server that is currently being analyzed. This will provide visual indication that the script is doing something. If a problem is detected, this line of code will make it easy to figure out which server is having the problem. So with that said, here is what the script now looks like:

Function Get-Smart

#Function to get disk SMART information

{

$Smart = Get-WmiObject -namespace root\wmi –class MSStorageDriver_FailurePredictStatus -ComputerName $Input

If ($Smart.PredictFailure -EQ $True) {$Smart | Select-Object PSComputerName, InstanceName, PredictFailure, Reason | FL}

}

 

Function Get-Health

#Function to get disk health information

{

$Pdisk= Get-PhysicalDisk

ForEach ( $LDisk in $PDisk )

               

{

               

$CurrentDisk = $LDisk | Get-StorageReliabilityCounter

               

If ($CurrentDisk.Temperature -GT 35 -OR $CurrentDisk.ReadErrorsTotal -GT 0 -OR $_.CurrentDisk -GT 0){

                               

$LDisk.FriendlyName

                               

$LDisk.HealthStatus

                               

$LDisk | Get-StorageReliabilityCounter | Select-Object Temperature, ReadErrorsTotal, WriteErrorsTotal | FL

                               

Write-Host ================== }

               

}

}

 

#Script Body

$Servers = @(“Hyper-V-1”, “Hyper-V-2”, “Hyper-V-3”, “Hyper-V-4”, “Prod1”, “Prod2”)

               

ForEach ($Server in $Servers) {

               

$Server

               

$Server | Get-Smart

               

Enter-PSSession -ComputerName $Server

               

Get-Health

               

Exit-PSSession

               

}

Figure F shows what it looks like when the script is executed on a series of healthy servers.

Image
Figure F: 
All of these servers are healthy.

Conclusion

As you can see, we have gotten rid of our script’s cluttered output. In the next article in this series, I will show you how to build an alert mechanism that can be used to alert you if a problem is detected.

If you would like to read the other parts in this article series please go to:

Leave a Comment

Your email address will not be published.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Scroll to Top