This is a script that
What’s missing, what is extra, and the captured configuration.
I’ve added additional functionality to leverage tagging, which helps identify which config block belongs to what kind of devices. I have documented as best I can, hopefully this helps other as much as it’s helping me.

/*** * @intent SNMP Validation (IOS)
* @description Validates Cisco IOS-XE SNMP configs and extracts community/host details.
* * LOGIC SUMMARY:
* 1. Checks device against 3 regional patterns (AMRS, EMEA, APAC).
* 2. Uses the 'Hybrid Approach':
* - Uses 'device.files.config' (Bag) for the blockDiff engine (Accuracy).
* - Uses 'configAsString' (String) for Regex extraction (Speed).
* 3. Outputs a clean table with missing lines and current settings.
***/
// Tagging Function to look for tagged devices in order to classify configuration appropriately
export get_region_from_tags(tags: Bag<String>)=
get_list_match_from_tags(tags, ["EMEA", "AMRS", "APAC", "LATM"]);
export get_function_from_tags(tags: Bag<String>)=
get_list_match_from_tags(tags, ["Firewall", "LB", "Proxy", "WLC", "Router", "Switch", "Controller"]);
export get_branchModels_from_tags(tags: Bag<String>)=
get_list_match_from_tags(tags, ["C2K", "C3K", "C4K", "C9K", "C4948", "ASR", "ISR", "C1K", "C8K","C9800","C9500"]);
export get_env_from_tags(tags: Bag<String>)=
get_list_match_from_tags(tags, ["DC", "CoLo", "Branch", "Cloud", "AWS","CoWork"]);
// Patterns — used for both extraction and extra-line detection
snmpCommunityPattern = `snmp-server community {string}`;
snmpServerPattern = `snmp-server host {ipv4Address} version 2c {string}`;
// Regex patterns for trap-source, location, contact (handles spaces in values)
reTrapSource = re`snmp-server trap-source\s+\S+`;
reLocation = re`snmp-server location\s+.+`;
reContact = re`snmp-server contact\s+.+`;
// --- 1. Helper Functions ---
minByFunc(blockDiffResult) = blockDiffResult.diffCount;
getConfigAsString(device) =
max(foreach command in device.outputs.commands
where command.commandType == CommandType.CONFIG
select command.response);
// --- 2. Concrete Compliance Patterns ---
IosDiffPatternDefault =
```
snmp-server community xxxxxx RO snmpv3-C
snmp-server community xxxxxx RW snmpv3-A
snmp-server community xxxxxx RO snmpv3-B
snmp-server host 1.1.1.99 version 2c xxxxxx
snmp-server host 1.1.1.98 version 2c xxxxxx
snmp-server host 1.1.1.97 version 2c xxxxxx
snmp-server host 1.1.1.96 version 2c xxxxxx
snmp-server host 1.1.1.95 version 2c xxxxxx
```;
// Config patterns based on Region
IosDiffPatternAMRS =
```
snmp-server community Jimmy1 RO EmeaSnmpRO
snmp-server community Jimmy2 RW EmeaSnmpRW
snmp-server community Jimmy3 RO EmeaSnmpRO2
snmp-server trap-source
snmp-server location
snmp-server contact
snmp-server host 1.1.1.1 version 2c Jimmy1
snmp-server host 1.1.1.2 version 2c Jimmy2
snmp-server host 1.1.1.3 version 2c Jimmy3
```;
IosDiffPatternEMEA =
```
snmp-server community Jimmy1 RO EmeaSnmpRO
snmp-server community Jimmy2 RW EmeaSnmpRW
snmp-server community Jimmy3 RO EmeaSnmpRO2
snmp-server trap-source
snmp-server location
snmp-server contact
snmp-server host 1.1.1.1 version 2c Jimmy1
snmp-server host 1.1.1.2 version 2c Jimmy2
snmp-server host 1.1.1.3 version 2c Jimmy3
```;
IosDiffPatternAPAC =
```
snmp-server community Jimmy1 RO EmeaSnmpRO
snmp-server community Jimmy2 RW EmeaSnmpRW
snmp-server community Jimmy3 RO EmeaSnmpRO2
snmp-server trap-source
snmp-server location
snmp-server contact
snmp-server host 1.1.1.1 version 2c Jimmy1
snmp-server host 1.1.1.2 version 2c Jimmy2
snmp-server host 1.1.1.3 version 2c Jimmy3
```;
IosDiffPatternApacLan =
```
snmp-server community Jimmy1 RO snmpRO
snmp-server community Jimmy2 RW snmpRW
snmp-server community Jimmy3 RO snmpRO2
snmp-server trap-source
snmp-server location
snmp-server contact
snmp-server host 1.1.1.1 version 2c Jimmy1
snmp-server host 1.1.1.2 version 2c Jimmy2
snmp-server host 1.1.1.3 version 2c Jimmy3
```;
// --- 4. Main Logic ---
main =
foreach device in network.devices
where device.platform.os == OS.IOS_XE
where device.name == device.system.physicalName
let configResponse = getConfigAsString(device)
let configAsString = if isPresent(configResponse) then configResponse else ""
let region = get_region_from_tags(device.tagNames)
let deviceFunc = get_function_from_tags(device.tagNames)
let environment = get_env_from_tags(device.tagNames)
let branchModels = get_branchModels_from_tags(device.tagNames)
let IosDiffPattern =
if region == "AMRS" then IosDiffPatternAMRS
else if region == "EMEA" then IosDiffPatternEMEA
else if region == "APAC" && deviceFunc == "Switch" &&
environment == "Branch" &&
(branchModels == "C9K" || branchModels == "C4948" ||
branchModels == "C4K" || branchModels == "C3K" || branchModels == "C2K")
then IosDiffPatternApacLan
else if region == "APAC" then IosDiffPatternAPAC
// Default to pattern 1 if no match is made.
else IosDiffPatternDefault
// Step 2.5: compute valid lines from the selected standard pattern
let patternAsConfig =
parseConfigBlocks(OS.OTHER, replace(toString(IosDiffPattern), "```", ""))
let validLines =
(foreach patternMatch in patternMatches(patternAsConfig, snmpCommunityPattern)
select patternMatch.line.text) +
(foreach patternMatch in patternMatches(patternAsConfig, snmpServerPattern)
select patternMatch.line.text)
// Step 3: blockDiff for missing lines
let blockDiffResult = blockDiff(device.files.config, IosDiffPattern)
// Collect only "snmp-server community ..." lines from the device
let configuredCommunityLines =
(foreach patternMatch in patternMatches(device.files.config, snmpCommunityPattern)
select patternMatch.line.text)
// Collect only "snmp-server host ..." lines from the device
let configuredHostLines =
(foreach patternMatch in patternMatches(device.files.config, snmpServerPattern)
select patternMatch.line.text)
// Collect trap-source, location, contact lines using regex (supports spaces in values)
let configuredTrapSourceLines =
(foreach r in regexMatches(configAsString, reTrapSource)
select r.string)
let configuredLocationLines =
(foreach r in regexMatches(configAsString, reLocation)
select r.string)
let configuredContactLines =
(foreach r in regexMatches(configAsString, reContact)
select r.string)
let configuredLines = configuredCommunityLines + configuredHostLines
let extraLines = configuredLines - validLines
// Combine all relevant SNMP lines into one deduplicated ordered list for display
let snmpConfigList = distinct(
configuredCommunityLines +
configuredHostLines +
configuredTrapSourceLines +
configuredLocationLines +
configuredContactLines
)
select {
violation: blockDiffResult.diffCount > 0 || length(extraLines) > 0,
device: device.name,
OS: device.platform.os,
model: device.platform.model,
diffCount: blockDiffResult.diffCount,
missing: blockDiffResult.blocks,
extra: extraLines,
// Combined: community, host, trap-source, location, contact (sorted alphabetically)
SNMP_Config: if length(snmpConfigList) == 0
then "None"
else join("\n", order(snmpConfigList)),
environment: get_env_from_tags(device.tagNames),
region: region,
function: deviceFunc,
manager: get_mgr_from_tags(device.tagNames)
};
export ios_snmp_compliance = main();
main()




