Monitoring Cisco Meraki
This step-by-step guide shows how to pull wireless client counts and rogue SSIDs from Cisco Meraki Dashboard into NetCrunch using a Script Sensor and Data Parser
NetCrunch already gives you broad visibility into traffic, flows, and infrastructure health. What makes it even more powerful is how easily you can extend it with external data sources. Cisco Meraki’s Dashboard provides valuable wireless insights, including client activity trends and rogue SSID detections, which you can seamlessly integrate into NetCrunch using a PowerShell script and JavaScript parser.
The result is a consolidated view of wireless and wired activity, where you can correlate client growth with uplink utilization or link rogue SSIDs to switch activity, giving you deeper, faster visibility across your entire IT environment.
What You’ll Need to Get Started
To proceed, you'll need:
- Cisco Meraki Dashboard API key (generate your API key)
- NetCrunch installed and configured
- Basic PowerShell knowledge
Step 1: Collect Meraki Data with PowerShell
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
}
Step 2: Create a Data Parser in NetCrunch
In NetCrunch’s Data Parsers, create a new parser: Settings Resources Data Parsers. With Blue Plus, create a new parser, select type JavaScript, and name it. Paste the 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 }
]);
Step 3: Add a Script Sensor to Bring Data In
Location of files:
Place both the PowerShell script (MerakiDashboardScript.ps1) and JSON output file (meraki.json) on the NetCrunch Server machine so that the Script Sensor can access them directly.
The file names shown 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 the section Monitoring Sensors and use the plus to add a sensor. Search for Script and select it
- Enter the Sensor Name: e.g.,
MerakiDashboard - Select the 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
Step 4: Turn Data Into Action with 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 the client's number (e.g., 20). - Counter Selection: from the Script.Clients, select Clients . You can filter instances or select All instances to monitor clients across all organizations.
- Confirm your selection and create the Alert.
- Associate your preferred Alerting Script if needed.
All Set! Your Meraki Data Now Lives in NetCrunch
NetCrunch now monitors detailed data from your Cisco Meraki Dashboard.
Conclusion
By integrating Cisco Meraki data into NetCrunch, you enhance an already comprehensive monitoring platform with wireless-specific insights. NetCrunch continues to provide detailed traffic, flow, and device monitoring, while Meraki offers visibility into client and rogue SSIDs.
Together, they create a unified monitoring environment that helps you detect issues earlier, resolve them more quickly, and manage your network more proactively.