Monitoring Cisco Meraki Dashboard with NetCrunch
Discover how to extend Cisco Meraki monitoring with NetCrunch for detailed visibility into SSIDs, client usage, and rogue access points. Learn what wireless metrics you can track and how centralized reporting enables proactive network management. This guide explains how integrated monitoring enables you to anticipate and resolve network issues more quickly
Cisco Meraki provides effective built-in alerts through its Dashboard for immediate network issues. However, detailed monitoring of wireless infrastructure, client usage, and rogue access points often requires additional integration with advanced monitoring tools. This guide demonstrates how to extend Cisco Meraki monitoring capabilities by integrating with NetCrunch using a PowerShell script and JavaScript parser.
Prerequisites
To proceed, you'll need:
- Cisco Meraki Dashboard API key (generate your API key)
- NetCrunch installed and configured
- Basic PowerShell knowledge
PowerShell Script for Data Collection
Create a script file on your NetCrunch Server machine— for example, at C:\Scripts\MerakiDashboardScript.ps1
- and populate it with the following content, making sure to include your API key:
$apiKey = "<your api key here>"
$baseUrl = "https://api.meraki.com/api/v1"
$outputPath = "C:\Scripts\meraki.json"
$headers = @{
"X-Cisco-Meraki-API-Key" = $apiKey
"Content-Type" = "application/json"
}
function Convert-IsoUtc {
param([long]$unix)
if ($unix -gt 1000000000) {
return ([DateTimeOffset]::FromUnixTimeSeconds($unix).UtcDateTime.ToString("yyyy-MM-ddTHH:mm:ssZ"))
} else {
return $null
}
}
$report = @{
Organization = @{}
Network = @{}
Devices = @()
Wireless = @()
Clients = @()
RogueSSIDs = @()
OtherSSIDs = @()
}
try {
$orgs = Invoke-RestMethod -Uri "$baseUrl/organizations" -Method Get -Headers $headers
$org = $orgs[0]
$report.Organization = $org
Write-Host "`n== Meraki Organizations ==" -ForegroundColor Cyan
Write-Host "ID: $($org.id) Name: $($org.name)" -ForegroundColor Green
} catch {
Write-Host "Error fetching organizations: $($_.Exception.Message)" -ForegroundColor Red
return
}
try {
$networks = Invoke-RestMethod -Uri "$baseUrl/organizations/$($org.id)/networks" -Method Get -Headers $headers
$network = $networks[0]
$report.Network = $network
Write-Host "`n== Networks in Organization ==" -ForegroundColor Cyan
Write-Host "ID: $($network.id) Name: $($network.name)" -ForegroundColor Green
} catch {
Write-Host "Error fetching networks: $($_.Exception.Message)" -ForegroundColor Red
return
}
try {
$devices = Invoke-RestMethod -Uri "$baseUrl/networks/$($network.id)/devices" -Method Get -Headers $headers
$report.Devices = $devices
Write-Host "`n== Devices in Network ==" -ForegroundColor Cyan
$devices | ForEach-Object {
Write-Host "Name: $($_.name) | Model: $($_.model) | Serial: $($_.serial)" -ForegroundColor Yellow
}
$apDevice = $devices | Where-Object { $_.model -like "MR*" } | Select-Object -First 1
$serial = $apDevice.serial
Write-Host "`nUsing device: $($apDevice.name) ($serial)" -ForegroundColor Magenta
} catch {
Write-Host "Error fetching devices: $($_.Exception.Message)" -ForegroundColor Red
return
}
try {
$apStatus = Invoke-RestMethod -Uri "$baseUrl/devices/$serial/wireless/status" -Method Get -Headers $headers
$report.Wireless = $apStatus.basicServiceSets
Write-Host "`n== AP Wireless Status ==" -ForegroundColor Cyan
$apStatus.basicServiceSets | ForEach-Object {
Write-Host "SSID: $($_.ssidName) | Band: $($_.band) | Channel: $($_.channel) | Power: $($_.power) dBm" -ForegroundColor Blue
}
} catch {
Write-Host "Error fetching AP wireless status: $($_.Exception.Message)" -ForegroundColor Red
}
try {
$clients = Invoke-RestMethod -Uri "$baseUrl/networks/$($network.id)/clients" -Method Get -Headers $headers
$report.Clients = $clients
Write-Host "`n== Clients ==" -ForegroundColor Cyan
$clients | ForEach-Object {
Write-Host "$($_.description) - $($_.mac) - Usage: $($_.usage.total) Bytes" -ForegroundColor DarkGreen
}
} catch {
Write-Host "Error fetching clients: $($_.Exception.Message)" -ForegroundColor Red
}
try {
$airMarshal = Invoke-RestMethod -Uri "$baseUrl/networks/$($network.id)/wireless/airMarshal" -Method Get -Headers $headers
if ($airMarshal) {
$rogue = @()
$other = @()
foreach ($entry in $airMarshal) {
$record = [PSCustomObject]@{
SSID = $entry.ssid
BSSID = $entry.bssid
Signal = $entry.signalStrength
LastSeen = Convert-IsoUtc $entry.lastSeen
SeenOnLAN = $entry.seenOnLan
}
if ($entry.seenOnLan -eq $true) {
$rogue += $record
} else {
$other += $record
}
}
$report.RogueSSIDs = $rogue
$report.OtherSSIDs = $other
Write-Host "`n== Air Marshal: Rogue SSIDs ==" -ForegroundColor Red
$rogue | ForEach-Object {
Write-Host "SSID: $($_.SSID) | MAC: $($_.BSSID) | Signal: $($_.Signal) dBm | Seen: $($_.LastSeen)" -ForegroundColor DarkRed
}
Write-Host "`n== Air Marshal: Other SSIDs ==" -ForegroundColor Cyan
$other | ForEach-Object {
Write-Host "SSID: $($_.SSID) | MAC: $($_.BSSID) | Signal: $($_.Signal) dBm | Seen: $($_.LastSeen)" -ForegroundColor DarkCyan
}
}
} catch {
Write-Host "Error fetching Air Marshal data: $($_.Exception.Message)" -ForegroundColor Red
}
try {
$json = $report | ConvertTo-Json -Depth 5
[System.IO.File]::WriteAllText($outputPath, $json, [System.Text.UTF8Encoding]::new($false))
Write-Host "`n== Report saved to $outputPath ==" -ForegroundColor Green
} catch {
Write-Host "Error saving JSON file: $($_.Exception.Message)" -ForegroundColor Red
}
Data Parser JavaScript code
In NetCrunch’s Data Parsers, create a new parser: Settings -> Resources -> Data Parsers. With blue plus create new parser, select type JavaScript and name it. Paste code below. Remember to paste the contents of the output file c:\Scripts\meraki.json
into the Test Data field.
const doc = typeof data === 'string' ? JSON.parse(data) : data;
result.status("Organization", {
value: "OK",
data: {
name: doc.Organization?.name,
networkName: doc.Network?.name
}
});
// Define counters
result.counters([
{ path: "Devices/Count", value: doc.Devices.length },
{ path: "Wireless/TotalSSIDs", value: doc.Wireless.length },
{ path: "Clients/Count", value: doc.Clients.length },
{ path: "RogueSSIDs/Count", value: doc.RogueSSIDs.length }
]);
Add Script Sensor in NetCrunch
Important File Location Note:
Make sure that the PowerShell script (MerakiDashboardScript.ps1
) and the resulting JSON file (meraki.json
) are placed on the same machine where the NetCrunch Server is installed. This ensures that the Script Sensor can access the script and read the JSON output directly. The file names used here are examples and can be changed to suit your environment.
- Select a Node for Monitoring Cisco Meraki Dashboard
-
Add a new Script Sensor:
- in Node Settings go to section Monitoring Sensors and use plus to add sensor. Search for Script and select it
- Sensor Name: e.g.,
MerakiDashboard
- Script Type: PowerShell
- Specify the path and script name (e.g.,
c:\Scripts\MerakiDashboardScript.ps1
) - Check Read result from file, specify the path to the JSON file (e.g.,
c:\Scripts\meraki.json
) - Select the previously created data parser in Script Output Data Format
Configure Alerts
-
Add a new Alert:
- Event Trigger: New Event for Monitoring Sensor Counter -
Choose severity and description.
- Select Counter set "average value" set "more than" and enter clients number (e.g., 20). - Counter Selection:
From the Script.Clients, select Clients . You can filter instances or simply select All instances and monitor clients regardless of, for example, organizations. - Confirm your selection and create the Alert.
- Associate your preferred Alerting Script if needed.
Completion
NetCrunch now monitors detailed data from your Cisco Meraki Dashboard.
Conclusion
Integrating Cisco Meraki with NetCrunch significantly enhances network monitoring capabilities. This method provides comprehensive visibility into device health, client activities, and wireless environment, allowing proactive infrastructure management and troubleshooting.