Monitoring UniFi WiFi Controllers with PRTG
21 Nov 2017With remote UniFi WiFi sites, I like to maintain a centralized monitoring dashboard. My PRTG server connects to sites over OpenVPN management tunnels. This also has the added advantage of monitoring end-to-end internet connectivity and latency.
Paessler’s KB has a sample script that enumerates connected access points. I go a bit further and enumerate connected wireless clients, and alert on devices that don’t have an appropriate IP, as this is indicative of a user-visible outage/connectivity issue.
Follow the instructions in the KB article above if you’re unsure how to deploy this script on your PRTG server.
Data I collect:
- Number of connected access points.
- Number of connected wireless clients.
- Number of devices with valid IP addresses.
- Number of devices with invalid IP addresses.
- Invalid client hostnames (if applicable).
- UniFi controller version.
- UniFi controller update status.
param(
[string]$server = 'unifi.domain.com',
[string]$port = '8443',
[string]$site = 'default',
[string]$username = 'admin',
[string]$password = '123456',
[string]$validIPRegex = '192.168*',
[switch]$debug = $false
)
#Ignore SSL Errors
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
#Define supported Protocols
[System.Net.ServicePointManager]::SecurityProtocol = @("Tls12","Tls11","Tls","Ssl3")
# Confirm Powershell Version.
if ($PSVersionTable.PSVersion.Major -lt 3) {
Write-Output "<prtg>"
Write-Output "<error>1</error>"
Write-Output "<text>Powershell Version is $($PSVersionTable.PSVersion.Major) Requires at least 3. </text>"
Write-Output "</prtg>"
Exit
}
# Create $controller and $credential using multiple variables/parameters.
[string]$controller = "https://$($server):$($port)"
[string]$credential = "`{`"username`":`"$username`",`"password`":`"$password`"`}"
# Start debug timer
$queryMeasurement = [System.Diagnostics.Stopwatch]::StartNew()
# Perform the authentication and store the token to myWebSession
try {
$null = Invoke-Restmethod -Uri "$controller/api/login" -method post -body $credential -ContentType "application/json; charset=utf-8" -SessionVariable myWebSession
}catch{
Write-Output "<prtg>"
Write-Output "<error>1</error>"
Write-Output "<text>Authentication Failed: $($_.Exception.Message)</text>"
Write-Output "</prtg>"
Exit
}
#Query API providing token from first query.
try {
$jsonresultat = Invoke-Restmethod -Uri "$controller/api/s/$site/stat/device/" -WebSession $myWebSession
$jsonresultat2 = Invoke-Restmethod -Uri "$controller/api/s/$site/stat/sta/" -WebSession $myWebSession
$jsonresultat3 = Invoke-Restmethod -Uri "$controller/api/s/$site/stat/sysinfo/" -WebSession $myWebSession
}catch{
Write-Output "<prtg>"
Write-Output "<error>1</error>"
Write-Output "<text>API Query Failed: $($_.Exception.Message)</text>"
Write-Output "</prtg>"
Exit
}
# Stop debug timer
$queryMeasurement.Stop()
# Iterate jsonresultat and count the number of APs.
# $_.state -eq "1" = Connected
# $_.type -like "uap" = Access Point ?
$apCount = 0
Foreach ($entry in ($jsonresultat.data | where-object { $_.state -eq "1" -and $_.type -like "uap"})){
$apCount ++
}
$apUpgradeable = 0
Foreach ($entry in ($jsonresultat.data | where-object { $_.state -eq "1" -and $_.type -like "uap" -and $_.upgradable -eq "true"})){
$apUpgradeable ++
}
$userCount = 0
Foreach ($entry in ($jsonresultat.data | where-object { $_.type -like "uap"})){
$userCount += $entry.'num_sta'
}
$guestCount = 0
Foreach ($entry in ($jsonresultat.data | where-object { $_.type -like "uap"})){
$guestCount += $entry.'guest-num_sta'
}
# Now get data about connected clients
$invalidClientIPs = 0
Foreach ($entry in ($jsonresultat2.data | where-object { $_.ip -notlike $validIPRegex})){
$invalidClientIPs ++
}
$validClientIPs = 0
Foreach ($entry in ($jsonresultat2.data | where-object { $_.ip -like $validIPRegex})){
$validClientIPs ++
}
$invalidClientHostnames = ''
Foreach ($entry in ($jsonresultat2.data | where-object { $_.ip -notlike $validIPRegex})){
if ($entry.hostname -ne $null){
$invalidClientHostnames += $entry.'hostname' + ', '
}
else {
$invalidClientHostnames += $entry.'mac' + ', '
}
}
# Now get controller information
$controllerVersion = [version]$jsonresultat3.data.'version'
$controllerUpdateAvailable = $jsonresultat3.data.'update_available'
$controllerUpdateDownloaded = $jsonresultat3.data.'update_downloaded'
#Write Results
Write-Host "<prtg>"
# APs
Write-Host "<result>"
Write-Host "<channel>Access Points Connected</channel>"
Write-Host "<value>$($apCount)</value>"
Write-Host "</result>"
Write-Host "<result>"
Write-Host "<channel>Access Points Upgradeable</channel>"
Write-Host "<value>$($apUpgradeable)</value>"
Write-Host "</result>"
Write-Host "<result>"
Write-Host "<channel>Clients (Total)</channel>"
Write-Host "<value>$($userCount)</value>"
Write-Host "</result>"
Write-Host "<result>"
Write-Host "<channel>Guests</channel>"
Write-Host "<value>$($guestCount)</value>"
Write-Host "</result>"
# Clients
Write-Host "<result>"
Write-Host "<channel>Devices with Invalid Client IPs</channel>"
Write-Host "<LimitMode>1</LimitMode>"
Write-Host "<LimitMaxWarning>3</LimitMaxWarning>"
Write-Host "<LimitMaxError>4</LimitMaxError>"
Write-Host "<value>$($invalidClientIPs)</value>"
Write-Host "<LimitWarningMsg>$($invalidClientHostnames)</LimitWarningMsg>"
Write-Host "<LimitErrorMsg>$($invalidClientHostnames)</LimitErrorMsg>"
Write-Host "</result>"
Write-Host "<result>"
Write-Host "<channel>Devices with Valid Client IPs</channel>"
Write-Host "<value>$($validClientIPs)</value>"
Write-Host "</result>"
# Controller
Write-Host "<result>"
Write-Host "<channel>UniFi Controller Version</channel>"
Write-Host "<value>$($controllerVersion.Major)$($controllerVersion.Minor)$($controllerVersion.Build)</value>"
Write-Host "</result>"
Write-Host "<result>"
Write-Host "<channel>UniFi Controller Update Available</channel>"
Write-Host "<value>$($controllerUpdateAvailable)</value>"
Write-Host "</result>"
Write-Host "<result>"
Write-Host "<channel>UniFi Controller Update Downloaded</channel>"
Write-Host "<value>$($controllerUpdateDownloaded)</value>"
Write-Host "</result>"
Write-Host "<result>"
Write-Host "<channel>Response Time</channel>"
Write-Host "<value>$($queryMeasurement.ElapsedMilliseconds)</value>"
Write-Host "<CustomUnit>msecs</CustomUnit>"
Write-Host "</result>"
Write-Host "<Text>Controller: $($controllerVersion.ToString()). Invalid hostnames: $($invalidClientHostnames)</Text>"
Write-Host "</prtg>"
# Write JSON file to disk when -debug is set. For troubleshooting only.
if ($debug){
[string]$logPath = ((Get-ItemProperty -Path "hklm:SOFTWARE\Wow6432Node\Paessler\PRTG Network Monitor\Server\Core" -Name "Datapath").DataPath) + "Logs (Sensors)\"
$timeStamp = (Get-Date -format yyyy-dd-MM-hh-mm-ss)
$json = $jsonresultat | ConvertTo-Json
$json | Out-File $logPath"unifi_sensor$($timeStamp)_log.json"
}