Aruba EdgeConnect SD-WAN

Last updated on 31 May, 2024

LogicMonitor’s Aruba EdgeConnect SD-WAN monitoring package leverages the appliance and orchestrator API to monitor EdgeConnect OS (ECOS) appliances. It is designed to monitor and alert on vital appliance SD-WAN health and performance metrics.

Fore more information about the Aruba EdgeConnect SD-WAN Appliance, see the REST API Monitoring Examples documentation from Aruba.


As of October 2023, LogicMonitor’s Aruba EdgeConnect SD-WAN package is known to be compatible with Appliance Version 9.0-9.3.


  • Collector version 32.400 or later
  • Appliance username and password with read-only access set on the resource. For more information, see the Appliance User Accounts Tab documentation from Aruba.

Add Resources into Monitoring

The following methods are available for on-boarding Aruba EdgeConnect SD-WAN resources into monitoring:

  • (Recommended) Advanced NetScan
    For more information on NetScans, see What is a NetScan.
  • Manually Add Resources

Using the Advanced NetScan to add Resources

  1. Navigate to Modules > Exchange, and install the Aruba EdgeConnect SD-WAN modules. For a list of modules, see LogicModules in Package.
  2. Navigate to Resources > Add > Several Devices > Advanced NetScan.
    The Add Advanced NetScan page is displayed.
  3. Enter a name that you want to associate with this NetScan. For example, “Aruba SD-WAN Devices”.
  4. Select the Collector to run the NetScan.
    For more information, see Collector Assignment in Enhanced Script Netscan.
  5. Select Enhanced Script NetScan from the Method drop-down list.
  6. From the Enhanced Script section, select Device Credentials > Use custom credentials for this scan.
  7. Add the following properties that provide the NetScan with the required Aruba EdgeConnect SD-WAN credentials and change how and where the NetScan creates and organizes resources:
aruba.orchestrator.api.keyOrchestrator API Key with Read-Only permission. For more information, see to API Key DocumentationYes
aruba.orchestrator.hostOrchestrator IP OR FQDN. For more information, see to Locating Orchestrator InformationYes 
A LogicMonitor API access id to search for duplicate resources in the NetScan prior to adding them to the portal. For more information on portal monitoring, see LogicMonitor Portal Monitoring.Yes
A LogicMonitor API key to search for duplicate resources in the NetScan prior to adding them to the portal. For more information on portal monitoring, see LogicMonitor Portal Monitoring.Yes name of the LogicMonitor Resource Group that this NetScan creates or uses if already existing. The value can be a nested child folder (for example, folder/folder/folder).This defaults to “Aruba EdgeConnect SDWAN”.This should not be assigned to a dynamic device group as those are not added by NetScans. For more information, see Device Groups OverviewNo
hostname.sourceAllows for selection of the source of the hostname that is used by the NetScan. This is useful in preventing duplicate device creation when conflicts are found for existing resources. For more information, see NetScan Troubleshooting.No
  1.  In the Filters section, use the following filter properties to omit specific Resources from discovery.
    These filters behave the same way as Active Discovery filters. The following device level properties are automatically discovered and can be filtered on. Available filter operations include: Equal, NotEqual, GreaterThan, GreaterEqual, LessThan, LessEqual, Contain, NotContain, Exist, NotExist, RegexMatch, RegexNotMatch.

Recommendation:To verify desired functionality, start with a specific filter of some devices you want to monitor, then incrementally change the filter operations or values to onboard monitored resources in stages.

Warning:If you do not configure filters, NetScan attempts to add all discovered Aruba EdgeConnect SD-WAN Devices as discrete LogicMonitor Resources.

aruba.appliance.serial.numberAppliance serial number.
aruba.appliance.siteAppliance site name.
aruba.appliance.device.nameAppliance device name.
aruba.appliance.tagsAppliance tags.
  1. Select Embed a Groovy script and embed the following script:

Warning: Do not edit the script. Edited Enhanced Script NetScans are not supported. If the LogicMonitor-provided script is edited, LogicMonitor Support can require you to overwrite your edits with the supported script if problems arise. The Enhanced Script NetScan limits LM Envision Resource creation to <= 600 per hour. To create more than 600 Resources, schedule the NetScan to recur each hour until all resources are added. 9. In the Schedule section, select Run this NetScan on a schedule. For dynamic environments, you can schedule the NetScan to run as frequently as hourly.

 * © 2007-2024 - LogicMonitor, Inc. All rights reserved.

import com.logicmonitor.common.sse.utils.GroovyScriptHelper as GSH
import com.logicmonitor.mod.Snippets
import com.santaba.agent.AgentVersion
import java.text.DecimalFormat
import groovy.json.JsonOutput
import groovy.json.JsonSlurper

// To run in debug mode, set to true
Boolean debug = false

// Set props object based on whether or not we are running inside a netscan or debug console
def props
try {
    props = hostProps
    debug = true  // set debug to true so that we can ensure we do not print sensitive properties
catch (MissingPropertyException) {
    props = netscanProps

// Required properties
def clientId = props.get("")
def clientSecret = props.get("paloalto.sase.client.key")
def tsgId = props.get("")

// Optional properties
def rootFolder = props.get("paloalto.prisma.sdwan.root.folder") ?: "Prisma SD-WAN"
def collectorId = props.get("paloalto.collector")

Boolean skipDeviceDedupe = props.get("skip.device.dedupe", "false").toBoolean()
String hostnameSource = props.get("hostname.source", "")?.toLowerCase()?.trim()

// Retrieve the collector version
Integer collectorVersion = AgentVersion.AGENT_VERSION.toInteger()

// Bail out early if we don't have the correct minimum collector version to ensure netscan runs properly
if (collectorVersion < 32400) {
    def formattedVer = new DecimalFormat("00.000").format(collectorVersion / 1000)
    throw new Exception(" Upgrade collector running netscan to 32.400 or higher to run full featured enhanced script netscan. Currently running version ${formattedVer}.")

// Bail out early if we don't have the necessary credentials
if (!clientSecret || !clientId || !tsgId) {
    throw new Exception("Must provide credentials to run this script (, paloalto.sase.client.key, and Verify necessary credentials have been provided in NetScan properties.")

def modLoader = GSH.getInstance()._getScript("Snippets", Snippets.getLoader()).withBinding(getBinding())
def lmEmit = modLoader.load("lm.emit", "1.1")
def debugSnip = modLoader.load("lm.debug", "1.0")
def lmDebug = debugSnip.debugSnippetFactory(out, debug)
def httpSnippet = modLoader.load("proto.http", "0")
def http = httpSnippet.httpSnippetFactory(props)
def cache = modLoader.load("lm.cache", "0").cacheSnippetFactory(lmDebug, "PaloAlto_PrismaSDWAN")
def prisma = modLoader.load("paloalto.prismasdwan", "0.0").prismaSnippetFactory(cache, http, lmDebug, props)

// Only initialize lmApi snippet class if customer has not opted out
def lmApi
if (!skipDeviceDedupe) {
    def lmApiSnippet = modLoader.load("lm.api", "0")
    lmApi = lmApiSnippet.lmApiSnippetFactory(props, http, lmDebug)

Map sensitiveProps = [
        "" : clientId,
        "paloalto.sase.client.key": clientSecret,

// CSV file containing headers "Site ID" and "Site Name" to customize group names created
// Save in /usr/local/logicmonitor/agent/bin directory (Linux)
//  or C:\Program Files\LogicMonitor\Agent\bin directory (Windows)
String csvFile = props.get("prisma.sdwan.sites.csv")
// Convert to map with site ID as key
Map siteInfo = csvFile ? processCSV(csvFile) : null

// Get information about devices that already exist in LM portal
List fields = ["name", "currentCollectorId", "displayName", "systemProperties"]
Map args = ["size": 1000, "fields": fields.join(","), "filter": "\"system.sysname\""]
def lmDevices
// But first determine if the portal size is within a range that allows us to get all devices at once
def pathFlag, portalInfo, timeLimitSec, timeLimitMs
if (!skipDeviceDedupe) {
    portalInfo = lmApi.apiCallInfo("Devices", args)
    timeLimitSec = props.get("lmapi.timelimit.sec", "60").toInteger()
    timeLimitMs = (timeLimitSec) ? Math.min(Math.max(timeLimitSec, 30), 120) * 1000 : 60000
    // Allow range 30-120 sec if configured; default to 60 sec

    if (portalInfo.timeEstimateMs > timeLimitMs) {
        lmDebug.LMDebugPrint("Estimate indicates LM API calls would take longer than time limit configured.  Proceeding with individual queries by display name for each device to add.")
        lmDebug.LMDebugPrint("\t${portalInfo}\n\tNOTE:  Time limit is set to ${timeLimitSec} seconds.  Adjust this limit by setting the property lmapi.timelimit.sec.  Max 120 seconds, min 30 seconds.")
        pathFlag = "ind"
    } else {
        lmDebug.LMDebugPrint("Response time indicates LM API calls will complete in a reasonable time range.  Proceeding to collect info on all devices to cross reference and prevent duplicate device creation.\n\t${portalInfo}")
        pathFlag = "all"
        lmDevices = lmApi.getPortalDevices(args)

def lmDevicesBySysname
if (pathFlag == "all" && !skipDeviceDedupe) {
    // Prisma does not provide individual IP addresses, check dedupe by device name
    lmDevicesBySysname = lmDevices?.collectEntries { device ->
        def sysName = device.systemProperties.find { it -> == "system.sysname" }.value
                ("${sysName}".toString().toLowerCase()): [
                        "name"              : "${}",
                        "displayName"       : (device.displayName),
                        "currentCollectorId": (device.currentCollectorId),
                        "sysName"           : (sysName)

// Get your data and build your list of resources
List<Map> resources = []

def now = new Date()
def dateFormat = "yyyy-MM-dd'T'HH:mm:ss.s z"
TimeZone tz = TimeZone.getDefault()
Map duplicateResources = [
        "date"     : now.format(dateFormat, tz),
        "message"  : "Duplicate display names found within LogicMonitor portal wherein hostname in LM does not match hostname in Netscan output.  Refer to documentation for how to resolve name collisions using 'hostname.source' netscan property.",
        "total"    : 0,
        "resources": []

if (!prisma.token) {
    throw new Exception("Credential check failed. Verify credentials and try running in debug mode in collector debug console.")

def permissions = prisma.getPermissions()
def siteVersion = prisma.sanitizeVersion(permissions?.resource_version_map?.sites)
        ?.split(",")?.max() ?: "4.7"
def elementVersion = prisma.sanitizeVersion(permissions?.resource_version_map?.elements)
        ?.split(",")?.max() ?: "2.6"

Map sites = prisma.getSites(siteVersion)?.collectEntries { site -> [( (] }
def elements = prisma.getElements(elementVersion)
elements?.each { details ->
    def displayName =
    def displayNameToCheck = "${displayName}".toString().toLowerCase()
    // We do not get IP for prisma
    def hostName =
    // Check for existing device in LM portal with this displayName; set to false initially and update to true when dupe found
    def deviceMatch = false
    def elementId =

    // If customer has opted out of device deduplication checks, we skip the lookups where we determine if a match exists and proceed as false
    if (!skipDeviceDedupe) {
        if (pathFlag == "ind") {
            deviceMatch = lmApi.findPortalDeviceByProperty(displayName, args)
        } else if (pathFlag == "all") {
            deviceMatch = lmDevicesBySysname?.get(displayName)

    if (deviceMatch) {
        // Log duplicates that would cause additional devices to be created; unless these entries are resolved, they will not be added to resources for netscan output
        // We have a collision if the element name is the same as the hostname from the existing lm resource
        if (displayNameToCheck != {
            def collisionInfo = [
                    (displayNameToCheck): [
                            "Netscan" : [
                                    "hostname": hostName
                            "LM"      : [
                                    "hostname"   :,
                                    "collectorId": deviceMatch.currentCollectorId
                            "Resolved": false

            // If user specified to use LM hostname on display name match, update hostname variable accordingly
            // need to figure out the logic here if a customer chooses lm do we pick the hostname as display name ?
            if (hostnameSource == "lm" || hostnameSource == "logicmonitor") {
                collisionInfo[displayNameToCheck]["Resolved"] = true
                hostName =
                displayName = deviceMatch.displayName
                collectorId = deviceMatch.currentCollectorId
                deviceMatch = false

            // If user specified to use netscan data for hostname, update the display name to make it unique
            // and flag it as no longer a match since we have resolved the collision with user's input
            else if (hostnameSource == "netscan") {
                collisionInfo[displayNameToCheck]["Resolved"] = true
                displayName = "${displayName} - ${}"
                deviceMatch = false

        // Don't worry about matches where the hostname values are the same
        // These will update via normal netscan processing and should be ignored
        else {
            deviceMatch = false

    String siteId = "${details.site_id}"
    List groupName = null
    if (siteInfo) {
        if (siteInfo[sites?.get(siteId)]) {
            groupName = ["${rootFolder}/${siteInfo[sites?.get(siteId)]}"]
    } else {
        def site = sites?.get(siteId)
        groupName = site ? ["${rootFolder}/${site}"] : ["${rootFolder}"]

    Map hostProps = ["": elementId,
                     ""    : tsgId

    sensitiveProps.each { k, v ->
        hostProps.put(k, v)

    Map resource = [
            "hostname"   : hostName.replaceAll(/[!@#$%^&*()+={}\[\]|\\\\;'"<>,\/?`~ ]/, '-'),  // String
            "displayname": displayName, // String
            "hostProps"  : hostProps, // Map<String, String>
            "groupName"  : groupName,   // List<String>

    if (collectorId) {
        resource["collectorId"] = collectorId
            duplicateResources["resources"][displayNameToCheck]["Netscan"][0]["collectorId"] = collectorId

    if (!deviceMatch) {

// Output validated data in JSON format
// If errors have been made in acceptable/required keys, lm.emit will throw and inform the user
lmEmit.resource(resources, debug)

// Report devices that already exist in LM via log file named after root folder
if (duplicateResources["resources"].size() > 0) {
    def netscanDupLog = new File("../logs/NetscanDuplicates/${rootFolder.replaceAll(" ", "_")}.json")
    new File(netscanDupLog.getParent()).mkdirs()
    duplicateResources["total"] = duplicateResources["resources"].size()
    def json = JsonOutput.prettyPrint(JsonOutput.toJson(duplicateResources))
    if (hostnameSource) {
        lmDebug.LMDebug("${duplicateResources["resources"].size()} devices found that were resolved with hostname.source=${hostnameSource} in netscan output.  See LogicMonitor/Agent/logs/NetscanDuplicates/${rootFolder.replaceAll(" ", "_")}.json for details.")
    } else {
        lmDebug.LMDebug("${duplicateResources["resources"].size()} devices found that were not reported in netscan output.  See LogicMonitor/Agent/logs/NetscanDuplicates/${rootFolder.replaceAll(" ", "_")}.json for details.")

return 0

////////////////////////////////////////////////////////////// CSV PROCESSING METHODS ///////////////////////////////////////////////////////////
 * Sanitizes filepath and instantiates File object
 * @param filename String
 * @param fileExtension String
 * @return File object using sanitized relative filepath
File newFile(String filename, String fileExtension) {
    // Ensure relative filepath is complete with extension type
    def filepath
    if (!filename?.startsWith("./")) {
        filepath = "./${filename}"
    if (!filepath?.endsWith(".${fileExtension}")) {
        filepath = "${filepath}.${fileExtension}"

    return new File(filepath)
 * Helper function to process CSV with site id and name info
 * @param String filename Filename of the CSV containing user-defined site info
 * @return Map siteInfo with site ID as the key and site name as the value
Map processCSV(String filename) {
    File cacheFile = newFile(filename, "csv")
    def rows = cacheFile.readLines()*.split(",")

    def siteInfo = [:]

    // Verify whether headers are present and expected values
    // Sanitize for casing and extra whitespaces while gathering headers
    def maybeHeaders = rows[0]*.toLowerCase()*.trim()
    if (maybeHeaders.contains("site folder name") && maybeHeaders.contains("site name")) {
        Map headerIndices = [:]
        maybeHeaders.eachWithIndex { val, i ->
            headerIndices[val] = i
        // Index values for headers to ensure we key the correct index regardless of order
        def sn = headerIndices["site name"]
        def colId = headerIndices["collector id"]
        def sf = headerIndices["site folder name"]

        // Remove headers from dataset
        data = rows[1..-1]
        // Build a map of common site names with site name as key
        data.each { entry ->
            siteInfo[entry[sn]] = [
                    "SiteFolder" : entry[sf],
                    "CollectorId": entry[colId]
    // Bail out early if we don't have the expected headers in the provided CSV, we can't properly associate Site IDs with common names without clear headers
    else {
        throw new Exception(" Required headers not provided in CSV.  Please provide \"Site Folder Name\" and \"Site Name\" (case insensitive).  Headers provided: \"${rows[0]}\"")
    return siteInfo
///////////////////////////////////////////////////////////////////// END ////////////////////////////////////////////////////////////////////// 
  1. Select Save or Save & Run.

After running the NetScan, review the history for the number of resources added, or for error messages if the NetScan does not create any resources.

Manually Adding Resources

  1. Create an Aruba EdgeConnect Organization device group.
    For more information, see Adding Device Groups.
  2. Add the devices to the Aruba Organization group.
  3. Add or verify that the following properties are set on the resources or under the Aruba EdgeConnect Organization Resource Group:
Appliance username
For more information, see the Appliance User Accounts Tab documentation from Aruba.
Appliance password
For more information, see the Appliance User Accounts Tab documentation from Aruba.
system.hostnameThe Appliance IP or FQDN

For more information on setting properties, see Resource and Instance Properties.

Importing LogicModules

From the LogicMonitor public repository, import all Aruba EdgeConnect SD-WAN LogicModules.

If these LogicModules are already present, ensure you have the most recent versions.

After the LogicModules are imported (assuming all previous setup requirements have been met), data collection will automatically commence. For more information on importing modules, see LM Exchange.

Migration from Legacy LogicModules

These modules monitor Aruba EdgeConnect SD-WAN devices as discrete LogicMonitor Resources and are not in-place upgrades to Silver Peak (Legacy) modules. For more information, see Silver Peak Monitoring (Legacy).

Recommendation: Do not use these modules in parallel with Silver Peak (Legacy) modules (or other 3rd party monitoring solutions) to monitor the same Aruba EdgeConnect Devices in the same Aruba Networks. Doing so can cause errors and data gaps across both modules and third-party monitoring solutions. To mitigate risk, follow your organization’s change control processes.


  • This package relies on collector script cache to continuously retrieve and store data from the Aruba EdgeConnect API module to minimize rate limiting constraints. For more information, see Collector Script Caching. If data gaps appear verify Aruba_EdgeConnect_SDWAN_API is running successfully and check script cache health in the LogicMonitor_Collector_ScriptCache DataSource.
  • This package requires access to each individual appliance to make API calls. If you are encountering 401 errors, verify that you are setting the appliance username and password. You do not need to add the orchestrator username and password. For more information, see the Appliance User Accounts Tab documentation from Aruba.

NetScan Troubleshooting

The NetScan for this suite can update existing devices in the portal to add relevant information retrieved from the Aruba API. It is also possible for the NetScan to create duplicate devices in the portal when a conflict exists where the display name is the same, but the value for system.hostname is different. To ensure devices are updated properly and duplicate devices are not created, this NetScan uses LogicMonitor’s API to query existing devices and report name conflicts discovered. This file can be accessed in the collector logs.

For more information on retrieving collector logs, see Collector Logging – Sending Logs to LogicMonitor.

The devices in this report are not reported to LogicMonitor as part of the NetScan output unless the NetScan has been configured with the property hostname.source. This property allows a user to resolve the name conflicts discovered by selecting the source of the hostname that is used in the NetScan output. Two possible hostname sources are determined by the following values:

  • “lm” or “logicmonitor” Devices with name conflicts use the existing system.hostname in LogicMonitor to ensure a device in the portal is updated using the NetScan. This setting does not create a new device.
  • “netscan” Devices with name conflicts maintain system.hostname determined in the NetScan script and update the display name reported to append – <system.hostname> to ensure it is unique and can be added to the portal. This option is beneficial if you do not have strict naming conventions and have multiple devices with the same display name.

Note:NetScans are not able to update the value of system.hostname. NetScans can update display name, custom properties, and group assignments.

LogicModules in Package

LogicMonitor’s package for Aruba EdgeConnect SD-WAN consists of the following LogicModules. For full coverage, ensure all of the following LogicModules are imported into your LogicMonitor platform:

Display NameTypeDescription
addCategory_ArubaEdgeConnectSDWANPropertySourceAdds system category ArubaEdgeConnectSDWANAppliance and auto properties with relevant device information.
addERI_ArubaEdgeConnectSDWANPropertySourceDiscover and add ERI’s for Aruba EdgeConnect SD-WAN resources.
Aruba_EdgeConnect_SDWAN_TopologyTopologySourceMaps Aruba EdgeConnect SD-WAN tunnel topologies.
EdgeConnect SDWAN APIDataSourceHandles all appliance level caching and API calls.
EdgeConnect SDWAN BGPDataSourceMonitors the status of an appliance’s BGP neighbors.
EdgeConnect SDWAN DiskDataSourceMonitors Aruba EdgeConnect Appliance disks at the appliance level.
EdgeConnect SDWAN HealthDataSourceMonitors the health of each SD-WAN appliance.
EdgeConnect SDWAN InterfacesDataSourceMonitors the WAN/LAN interfaces of an appliance.
EdgeConnect SDWAN PerformanceDataSourceMonitors the performance of EdgeConnect SD-WAN appliances.
EdgeConnect SDWAN TunnelsDataSourceMonitor overlay tunnels for Aruba EdgeConnect SD-WAN Appliances.
EdgeConnect Next HopsDataSourceMonitors WAN next hops for Aruba Edge Connect SD-WAN Appliances.

When setting static datapoint thresholds on the various metrics tracked by this package’s DataSources, LogicMonitor follows the technology owner’s best practice KPI recommendations.

Recommendation: If necessary, adjust these predefined thresholds to meet the unique needs of your environment. For more information on tuning datapoint thresholds, see Tuning Static Thresholds for Datapoints.

In This Article