Parsing Milestone JSON using PowerShell

For our example, we are parsing Milestone’s configuration file using PowerShell to build a weekly report. Ideally, you would leverage a fully-featured monitor like SolarWinds or AppManager to monitor Milestone devices and alert based on a defined threshold, but for some this may not be possible. Still, if needed, you could leverage PowerShell to parse the configurattion.xml file.

Our example does the following:

  • Import MileStone configuration.xml file
  • Parse configuration file for devices (JSON format).
  • Attempts to ping the firewall based on the camera’s subnet
    • if the camera is offline, it adds the camera to a report object
    • if the camera is online – do nothing.
  • Determine if a report needs to be sent:
    • if the report object has any items within it, then we want to send out a notification with the camera information for follow up.
    • If there is nothing in the report – do nothing.

Looking back, I would utilize functions to make the script modular and convert write-host to write-debug which I could then leverage to toggle data to the host during debugging.

On a side note: While writing this post I found that MileStone has released a PSModule which allows for further integration:

# Email Report
$From = "[email protected]"
$To = "[email protected]"
$Cc = "[email protected]"
$Subject = (Get-Date).ToString("%d-MMMM-yyyy") + " - Weekly Report - Offline Cameras"
$SMTPServer = ""

# Define HTML Report Header Style
$Header = @"
body { font-size:12px; font-family:Verdana, Geneva, sans-serif;}
TABLE {border-width: 1px; border-style: solid; border-color: black; border-collapse: collapse;}
TH {border-width: 1px; padding: 3px; border-style: solid; border-color: black; background-color: #e5efff;}
TD {border-width: 1px; padding: 3px; border-style: solid; border-color: black;}
tr:nth-child(even) {background: #ededed}
tr:nth-child(odd) {background: #FFF}

# Import Milestone Config
$xmlLocation = 'C:\ProgramData\Milestone\Milestone Surveillance\configuration.xml'
$deviceObjs = Select-Xml -Path $xmlLocation -XPath "//config/Devices/node()" | Select-Object -ExpandProperty node | Where-Object { $_.Name -ne '#whitespace' }

# Declare Report
$report = @()

# Go through each device, test, and report on offline cameras
foreach ($device in $deviceObjs) {
    Write-Host "Testing $($device.DisplayName)"

    # Test points - Camera and Firewall
    $camTestResult = (Test-NetConnection $device.IPAddress -Port 80 -ErrorAction SilentlyContinue -WarningAction SilentlyContinue).TcpTestSucceeded
    $gwTestResult = (Test-NetConnection $($device.IPAddress -replace "\.\d{1,3}$", ".1") -ErrorAction SilentlyContinue -WarningAction SilentlyContinue).PingSucceeded

    # Check Status
    if ($camTestResult -eq $true) {
        Write-Host -ForegroundColor Green "  Status: Online"

    if (($camTestResult -eq $false) -and ($gwTestResult -eq $true)) {
        # Debug Status
        Write-Host -ForegroundColor Red "  Firewall Status: $gwTestResult"
        Write-Host -ForegroundColor Red "  Camera Status: $camTestResult"
        # Add to Report
        $item = New-Object PSObject
        $item | Add-Member -type NoteProperty -Name 'Camera Name' -Value $($device.DisplayName)
        $item | Add-Member -type NoteProperty -Name 'Camera IP' -Value $($device.IPAddress)
        $item | Add-Member -type NoteProperty -Name 'Online' -Value $($camTestResult)
        $report += $item
    elseif (($camTestResult -eq $false) -and ($gwTestResult -eq $false)) {
        Write-Host 'Status: Tunnel Down'

if ($report) {
    $HTMLReport = $report | Sort-Object 'Camera Name' | ConvertTo-Html -property 'Camera Name', 'Camera IP', 'Online' -Head $Header -Pre "<h2>Property Weekly Report - Offline MDF Cameras</h2><p><b>Generated:</b> $(get-date)</p>"

    Send-MailMessage -To $To -Cc $Cc -From $From -SmtpServer $SMTPServer -Subject $Subject -Body ($HTMLReport | Out-String) -BodyAsHtml