- Run Exchange Online commands using Azure Automation - Tue, Jul 25 2023
- Connect to Exchange Online with PowerShell and certificate-based authentication - Wed, Jul 19 2023
- Office Deployment Tool (ODT): Deploy Office using custom XML files - Thu, Mar 30 2023
Dell provides a wide array of WMI classes and properties you can query to get anything from the server temperature to the power supply voltage. But to read physical disk status you must still use omreport, which is a tool that outputs text to a string.
If you are familiar with PowerShell, you will know that manipulating text strings can be difficult, especially if you try to "guess" the output by statically formatting the output based on carriage returns or something similar. However, using a method I developed or picked up somewhere (not invented I'm sure), you can parse a single line of the text output by using a foreach statement.
The output of an omreport shows us they follow a very specific format, which allows us to pick out the start and end of any one disk. This means we can convert that text to an object, at which point PowerShell can work its magic.
So how do we do that?
The command to pull a report from the PERC using omreport is:
Omreport storage pdisk controller=0
(where 0 is the controller number)
Depending on how many disks you have on the controller, you may be scrolling for some time to reach the top of the output, as each disk returns 43 properties. Depending on what you are querying the controller for, many of these are redundant.
Personally, I want a quick way to report on any issues with the disks. For that, I decided the best properties to collect would be ID, Status, State, ProductID, Serial, and Capacity. This should quickly allow me to identify in what position a failed disk is physically located, what capacity it is, and the product ID/serial number if I should need to look for a replacement.
If we save the entire report in a variable ($storage), we can process each line of text looking for the first and last property we want to collect, and let that signify the end of the object and the creation of a new object.
For example,
$storage = omreport storage pdisk controller=0 foreach ($line in $storage) { # ID if(($line) -like "ID*") { $disk = New-Object System.Object $line = $line.split(":")[1..3].Replace(" ","") $line = $line[0],$line[1],$line[2] -join ":" $disk | Add-Member -MemberType NoteProperty -Name ID -Value $line $disk } }
This would tell PowerShell to process the $storage report, and for every line that contained the label "ID," create a new $disk object, perform some manipulation of the text, and then add that information to the $disk object.
As we add additional properties to collect, we can start to see the $disk object becoming quite useful.
$storage = omreport storage pdisk controller=0 foreach ($line in $storage) { # ID if(($line) -like "ID*") { $disk = New-Object System.Object $line = $line.split(":")[1..3].Replace(" ","") $line = $line[0],$line[1],$line[2] -join ":" $disk | Add-Member -MemberType NoteProperty -Name ID -Value $line } # Status if(($line) -like "Status*") { $line = $line.Split(":")[1].Replace(" ","") $disk | Add-Member -MemberType NoteProperty -Name Status -Value $line } # State if(($line) -like "State*") { $line = $line.Split(":")[1].Replace(" ","") $disk | Add-Member -MemberType NoteProperty -Name State -Value $line $disk } }
So from here I can see disk IDs with non-critical errors.
Then with a small tweak, we can add these individual $disk objects into an array and start to work with them.
$disks = @() $storage = omreport storage pdisk controller=0 foreach ($line in $storage) { # ID if(($line) -like "ID*") { $disk = New-Object System.Object $line = $line.split(":")[1..3].Replace(" ","") $line = $line[0],$line[1],$line[2] -join ":" $disk | Add-Member -MemberType NoteProperty -Name ID -Value $line } # Status if(($line) -like "Status*") { $line = $line.Split(":")[1].Replace(" ","") $disk | Add-Member -MemberType NoteProperty -Name Status -Value $line } # State if(($line) -like "State*") { $line = $line.Split(":")[1].Replace(" ","") $disk | Add-Member -MemberType NoteProperty -Name State -Value $line $disks += $disk } } $disks | where { $_.Status -ne "OK" }
How about adding a parameter to set which controllers you want to search?
param( [Parameter(Mandatory=$True,Position=0)] [array]$controllers )
If we then look at the completed script querying controller 0 and 1, we get the following:
param( [Parameter(Mandatory=$True,Position=0)] [array]$controllers ) $report = @() foreach ($controller in $controllers) { $storage = omreport storage pdisk controller=$controller foreach ($line in $storage) { # ID if(($line) -like "ID*") { $disk = New-Object System.Object $line = $line.split(":")[1..3].Replace(" ","") $line = $line[0],$line[1],$line[2] -join ":" $disk | Add-Member -MemberType NoteProperty -Name Controller -Value $controller $disk | Add-Member -MemberType NoteProperty -Name ID -Value $line } # Status if(($line) -like "Status*") { $line = $line.Split(":")[1].Replace(" ","") $disk | Add-Member -MemberType NoteProperty -Name Status -Value $line } # State if(($line) -like "State*") { $line = $line.Split(":")[1].Replace(" ","") $disk | Add-Member -MemberType NoteProperty -Name State -Value $line } # Part if(($line) -like "ProductID*") { $line = $line.Split(":")[1].Replace(" ","") $disk | Add-Member -MemberType NoteProperty -Name PartNumber -Value $line } # Serial if(($line) -like "Serial*") { $line = $line.Split(":")[1].Replace(" ","") $disk | Add-Member -MemberType NoteProperty -Name Serial -Value $line } # Capacity if(($line) -like "Capacity*") { $line = $line.Split(":")[1] $line = $line.Split("GB")[0].Replace(" ","") $disk | Add-Member -MemberType NoteProperty -Name Capacity -Value $line $report += $disk } } } $report
I wanted to output a report of disks with errors to my SolarWinds Remote Monitoring & Management (RMM) platform, but unfortunately their script engine doesn't support passing variables through the command line. So I had to use one of the earlier mentioned WMI queries to collect info across all controllers automatically.
Subscribe to 4sysops newsletter!
$controllers = get-wmiobject -namespace root\cimv2\dell dell_cmdevice | where { ($_.Name -like "PERC*") -and ( $_.Device -ne 0 ) } $controllers = get-wmiobject -namespace root\cimv2\dell dell_cmdevice | where { ($_.Name -like "PERC*") -and ( $_.Device -ne 0 ) } $report = @() foreach ($controller in $controllers) { $controllerN = 0 $storage = omreport storage pdisk controller=$controllerN foreach ($line in $storage) { # ID if(($line) -like "ID*") { $disk = New-Object System.Object $line = $line.split(":")[1..3].Replace(" ","") $line = $line[0],$line[1],$line[2] -join ":" $disk | Add-Member -MemberType NoteProperty -Name Controller -Value $controller $disk | Add-Member -MemberType NoteProperty -Name ID -Value $line } # Status if(($line) -like "Status*") { $line = $line.Split(":")[1].Replace(" ","") $disk | Add-Member -MemberType NoteProperty -Name Status -Value $line } # State if(($line) -like "State*") { $line = $line.Split(":")[1].Replace(" ","") $disk | Add-Member -MemberType NoteProperty -Name State -Value $line } # Part if(($line) -like "ProductID*") { $line = $line.Split(":")[1].Replace(" ","") $disk | Add-Member -MemberType NoteProperty -Name PartNumber -Value $line } # Serial if(($line) -like "Serial*") { $line = $line.Split(":")[1].Replace(" ","") $disk | Add-Member -MemberType NoteProperty -Name Serial -Value $line } # Capacity if(($line) -like "Capacity*") { $line = $line.Split(":")[1] $line = $line.Split("GB")[0].Replace(" ","") $disk | Add-Member -MemberType NoteProperty -Name Capacity -Value $line $report += $disk } } $controllerN++ } $errorCount = ($report | where { $_.Status -ne "OK"} | Measure-Object).Count if(($errorCount) -ge 1) { Write-Output "Disk Error" Exit 1001 } else { Write-Output "Disks OK" Exit 0 }
You can find more info on the Dell WMI queries and OpenManage here.
Thanks for this, I have been using the GUI version of OpenManage Server Administrator for awhile now but I have neglected to work through the powershell queries, I have a feeling this will come in useful on Hyper-V core installs.
How come I did not notice this earlier 🙁
Nice. Just got me out of an awkward "how the hell can I do that" moment with a new (well old, Dell R410) server presented with where I suspected some failing drives but OM was not working, iDRAC6 too old to view storage, last backup 7 months ago and not rebooted for 4 months so not wanting to reboot it and get someone to look at BIOS or flashing lights.
This has got ancient PowerShell too but got the core info showing 2 of 4 drives "non critical" but predicted failure. Ouch.
No reason why Solarwinds RMM can't use parameters as a task or daily / 24:7 check as I use both a lot, going to adjust and re-format this so it works with our servers and put it with my others below with link to your site.