Sometimes you want to collect data with PRTG when there is no native sensor and no SNMP interface available. In most cases you can find a way to collect this data with PowerShell.

In this case we had a server with nVME Disks that were not visible through the iBMC, so we needed to monitor their health status on a different way.

Collecting Data

We can collect health information of disk on the host with the Get-PhysicalDisk command on PowerShell.

List all information of all disks with PowerShell

This contains a lot of information but way more than we need. We can limit the information by selecting relevant data. In the health monitoring case the following data is relevant:

  • FriendlyName
  • HealthStatus
  • OperationalStatus
  • SerialNumber

Limiting the output looks like this:

We now have two relevant values we want to see in PRTG. As PRTG channels can only display one value, we need to create two channels per disk.

Formatting Data for PRTG

PRTG can work with a JSON with the following format as input for a Sensor:

{
    "Prtg": {
        "Result": [
            {
                "Channel": "CHANNELNAME",
                "Value": "CHANNELVALUE"
            }
        ]
    }
}

The channel may also contain optional parameters like alerting definitions. But we will take care of that in another article. Also PRTG cannot handle "Healthy" or "OK" in a good way so we need to convert that to an integer value.

The JSON for our Disk should look like this:‌

{
    "Prtg": {
        "Result": [
            {
                "Channel": "Samsung SSD 970 EVO 2TB (0025_385A_81B5_1A8A.) HealthStatus",
                "Value": "1"
            },
            {
                "Channel": "Samsung SSD 970 EVO 2TB (0025_385A_81B5_1A8A.) OperationalStatus",
                "Value": "1"
            }
        ]
    }
}

First we need to create the Result Array and add all Channels to it. Remember that Get-PhysicalDisk returns an array so we need to loop through each disk (in my case only one).

# create the array where we can add the channels
$Result = @()

# loop through each PhysicalDisk
foreach($Disk in (Get-PhysicalDisk | Select-Object FriendlyName,HealthStatus,OperationalName,SerialNumber))
{
    # create channel object for healthstatus
    $channel = New-Object PSObject
    # name channel
    $channel | Add-Member NoteProperty Channel "$($Disk.FriendlyName) ($($Disk.SerialNumber)) HealthStatus"
    # convert 'Healthy' value to 1 and everything else to 0 so PRTG can handle it and add as Value to the Channel
    if($Disk.HealthStatus -eq "Healthy")
    {
    	$channel | Add-Member NoteProperty Value 1
    }
    else
    {
    	$channel | Add-Member NoteProperty Value 0
    }
    # add channel to result
    $result = $result + $channel
    
    # create channel for OperationalStatus
    $channel = New-Object PSObject
    # name channel
    $channel | Add-Member NoteProperty Channel "$($Disk.FriendlyName) ($($Disk.SerialNumber)) OperationalStatus"
    # convert 'OK' value to 1 and everything else to 0 so PRTG can handle it and add as Value to the Channel
    if($Disk.HealthStatus -eq "OK")
    {
    	$channel | Add-Member NoteProperty Value 1
    }
    else
    {
    	$channel | Add-Member NoteProperty Value 0
    }
    # add channel to result
    $result = $result + $channel
}

Now the $Result looks like this (PS and JSON):

We now need to add the $Result to a PRTG object and the PRTG object to a parent Object so we have the surrounding braces.

### See new code at the end ###
# create the array where we can add the channels
$Result = @()

# loop through each PhysicalDisk
foreach($Disk in (Get-PhysicalDisk | Select-Object FriendlyName,HealthStatus,OperationalName,SerialNumber))
{
    # create channel object for healthstatus
    $channel = New-Object PSObject
    # name channel
    $channel | Add-Member NoteProperty Channel "$($Disk.FriendlyName) ($($Disk.SerialNumber)) HealthStatus"
    # convert 'Healthy' value to 1 and everything else to 0 so PRTG can handle it and add as Value to the Channel
    if($Disk.HealthStatus -eq "Healthy")
    {
    	$channel | Add-Member NoteProperty Value 1
    }
    else
    {
    	$channel | Add-Member NoteProperty Value 0
    }
    # add channel to result
    $result = $result + $channel
    
    # create channel for OperationalStatus
    $channel = New-Object PSObject
    # name channel
    $channel | Add-Member NoteProperty Channel "$($Disk.FriendlyName) ($($Disk.SerialNumber)) OperationalStatus"
    # convert 'OK' value to 1 and everything else to 0 so PRTG can handle it and add as Value to the Channel
    if($Disk.HealthStatus -eq "OK")
    {
    	$channel | Add-Member NoteProperty Value 1
    }
    else
    {
    	$channel | Add-Member NoteProperty Value 0
    }
    # add channel to result
    $result = $result + $channel
}

### New Code HERE ###
# creating the PRTG Object and adding the Results
$PRTG = New-Object PSObject
$PRTG | Add-Member NoteProperty Result $Result

# create an object where we can add $PRTG so it is surrounded by braces
$Return = New-Object PSObject
$Return | Add-Member NoteProperty Prtg $PRTG

# Write Object as JSON so PRTG can see it
$Return | ConvertTo-Json

Now PowerShell prints this:

Somehow the channels are serialised in an unexpected way. This is caused by the ConvertTo-Json command which is converting only two levels be default. To convert all three levels we  have we must pass a -Depth parameter of at least 3.

Executing on a specified server

With this script we can monitor the physical disks of the localhost. We need to add the possibility to specify on which host we want to read the data. To do that we need to add a parameter to the script which will take the server name and execute the script in a session on the specified computer.

Param(
    [Parameter(Mandatory=$true)]
    [string] $Server,
)

$Session = New-PSSession -ComputerName $Server

Invoke-Command -Session $Session -ScriptBlock {
    # create the array where we can add the channels
    $Result = @()

    # loop through each PhysicalDisk
    foreach($Disk in (Get-PhysicalDisk | Select-Object FriendlyName,HealthStatus,OperationalName,SerialNumber))
    {
        # create channel object for healthstatus
        $channel = New-Object PSObject
        # name channel
        $channel | Add-Member NoteProperty Channel "$($Disk.FriendlyName) ($($Disk.SerialNumber)) HealthStatus"
        # convert 'Healthy' value to 1 and everything else to 0 so PRTG can handle it and add as Value to the Channel
        if($Disk.HealthStatus -eq "Healthy")
        {
    	    $channel | Add-Member NoteProperty Value 1
        }
        else
        {
    	    $channel | Add-Member NoteProperty Value 0
        }
        # add channel to result
        $result = $result + $channel
    
        # create channel for OperationalStatus
        $channel = New-Object PSObject
        # name channel
        $channel | Add-Member NoteProperty Channel "$($Disk.FriendlyName) ($($Disk.SerialNumber)) OperationalStatus"
        # convert 'OK' value to 1 and everything else to 0 so PRTG can handle it and add as Value to the Channel
        if($Disk.HealthStatus -eq "OK")
        {
    	    $channel | Add-Member NoteProperty Value 1
        }
        else
        {
    	    $channel | Add-Member NoteProperty Value 0
        }
        # add channel to result
        $result = $result + $channel
    }

    # creating the PRTG Object and adding the Results
    $PRTG = New-Object PSObject
    $PRTG | Add-Member NoteProperty Result $Result

    # create an object where we can add $PRTG so it is surrounded by braces
    $Return = New-Object PSObject
    $Return | Add-Member NoteProperty Prtg $PRTG

    # Write Object as JSON so PRTG can see it
    $Return | ConvertTo-Json -Depth 3
}

Integrate to PRTG

To make the script available in PRTG save it on your PRTG host as C:\Program Files (x86)\PRTG Network Monitor\Custom Sensors\EXEXML\Get-PhysicalDisk.ps1

To add this sensor to a device, click you need to add one of the type "EXE/Script Advanced". The Following Settings will monitor this disks on Server01.

Now you will be able to see two channels for each disk the server has.