Here are a set of 3 scripts for:
NXOS
EOS
IOS (and IOS-XE)
The scripts look for configuration related to source interfaces. Over the years, engineers have used Loopback0, dedicated management interfaces, Loopback10, or other random interfaces to source traffic from. We have an an inititiative to clean these inconsistencies up.
These scripts parse through the configs looking anything related to the ‘source-interface’ or ‘local-interface’ etc. Then it finds the associated configuration of those interfaces, such as IP Address, VRF, description.
I have also tuned them to ignore parts of the configuration that are not important to the effort , ex:
no ip redirects
no ip route-cache
no ip unreachables
no ip proxy-arp
ip mtu {number}
load-interval {number}
ip helper-address {string}
negotiation auto
standbye {string}
etc...
Cisco NXOS
/*** * @intent Source-Interface Extraction (NXOS)
* @description Extracts all source-interface configs and interface details from config text.
***/
// --- 1. Helper Functions ---
getConfigAsString(device) =
max(foreach command in device.outputs.commands
where command.commandType == CommandType.CONFIG
select command.response);
// --- 2. Regex Patterns ---
// Full line containing source-interface
reSourceInterfacePattern = re`[^\n]*source-interface[^\n]*`;
// Just the "source-interface <name>" portion of a line
reIfaceNamePattern = re`source-interface \S+`;
// An interface block: only matches lines that START with "interface"
// The \n anchor prevents matching "source-interface" or "ip ... interface" mid-line
reInterfaceBlock = re`\ninterface [^\n]+(?:\n [^\n]+)*`;
// Lines to exclude from interface block output
reExcludeNoLines = re`\n\s+no [^\n]*`;
reExcludePimLines = re`\n\s+ip pim sparse-mode[^\n]*`;
isAciNode(device) =
CommandType.CISCO_ACI_FABRIC_VRFS in
(foreach command in device.outputs.commands
select command.commandType);
// Filters an interface block string: strips leading \ninterface prefix,
// then removes "no ..." and "ip pim sparse-mode" lines.
filterBlock(block) =
join("\n",
foreach r in regexMatches(replace(block, "\ninterface ", "interface "), re`[^\n]+`)
let line = r.string
where length(regexMatches(line, re`^\s+no \S`)) == 0
where length(regexMatches(line, re`\s+ip pim sparse-mode`)) == 0
select line);
// --- 3. Main Logic ---
main =
foreach device in network.devices
where !isAciNode(device)
where device.platform.os == OS.NXOS
where device.name == device.system.physicalName
let configResponse = getConfigAsString(device)
let configAsString = if isPresent(configResponse) then configResponse else ""
let ip = device.snapshotInfo.collectionIp
// Step 1: Extract all source-interface lines
let sourceIntLines = (foreach r in regexMatches(configAsString, reSourceInterfacePattern)
select r.string)
// Step 2: Extract just the interface names from those lines
// e.g. "ip tacacs vrf NM source-interface Management1"
// -> regex matches "source-interface Management1"
// -> replace "source-interface" and spaces -> "Management1"
let targetIfaceNames = distinct(
foreach srcLine in sourceIntLines
foreach m in regexMatches(srcLine, reIfaceNamePattern)
select replace(replace(m.string, "source-interface", ""), " ", ""))
// Step 3: Extract all interface blocks from the config text
let allInterfaceBlocks = (foreach r in regexMatches(configAsString, reInterfaceBlock)
select r.string)
// Step 4: Match each target name to its interface block
let ifaceInfoList =
(foreach targetName in targetIfaceNames
foreach block in allInterfaceBlocks
let headerList = (foreach r in regexMatches(block, re`\ninterface [^\n]+`)
select r.string)
where length(headerList) > 0
where replace(replace(headerList[0], "\ninterface ", ""), "\n", "") == targetName
select filterBlock(block))
select {
device: device.name,
ip: ip,
OS: device.platform.os,
model: device.platform.model,
Source_Interface: if length(sourceIntLines) == 0
then "None"
else join("\n", order(sourceIntLines)),
Info: if length(ifaceInfoList) == 0
then "None"
else join("\n\n", ifaceInfoList),
};
export eos_source_interface = main();
main()Cisco IOS/XE
/*** * @intent Source-Interface Extraction (Cisco IOS/XE)
* @description Extracts all source-interface configs and interface details from config text.
***/
// --- 1. Helper Functions ---
getConfigAsString(device) =
max(foreach command in device.outputs.commands
where command.commandType == CommandType.CONFIG
select command.response);
// --- 2. Regex Patterns ---
// Full line containing source-interface
reSourceInterfacePattern = re`[^\n]*source-interface[^\n]*`;
// Just the "source-interface <name>" portion of a line
reIfaceNamePattern = re`source-interface \S+`;
// An interface block: only matches lines that START with "interface"
// The \n anchor prevents matching "source-interface" or "ip ... interface" mid-line
reInterfaceBlock = re`\ninterface [^\n]+(?:\n [^\n]+)*`;
// --- 2b. filterBlock Helper ---
// Strips the leading \ninterface prefix, then removes unwanted lines.
filterBlock(block) =
join("\n",
foreach r in regexMatches(replace(block, "\ninterface ", "interface "), re`[^\n]+`)
let line = r.string
where length(regexMatches(line, re`\s+no ip redirects`)) == 0
where length(regexMatches(line, re`\s+no ip route-cache`)) == 0
where length(regexMatches(line, re`\s+no ip unreachables`)) == 0
where length(regexMatches(line, re`\s+no ip proxy-arp`)) == 0
where length(regexMatches(line, re`\s+ip mtu \d+`)) == 0
where length(regexMatches(line, re`\s+load-interval \d+`)) == 0
where length(regexMatches(line, re`\s+ip helper-address \S+`)) == 0
where length(regexMatches(line, re`\s+negotiation auto`)) == 0
where length(regexMatches(line, re`\s+standby \S+`)) == 0
where length(regexMatches(line, re`\s+action 3\.0 mail `)) == 0
select line);
// --- 3. Main Logic ---
main =
foreach device in network.devices
where device.platform.os == OS.IOS_XE || device.platform.os == OS.IOS
// where device.name == device.system.physicalName
let configResponse = getConfigAsString(device)
let configAsString = if isPresent(configResponse) then configResponse else ""
let ip = device.snapshotInfo.collectionIp
// Step 1: Extract all source-interface lines
let sourceIntLines = (foreach r in regexMatches(configAsString, reSourceInterfacePattern)
select r.string)
// Step 2: Extract just the interface names from those lines
// e.g. "ip tacacs vrf NM source-interface Management1"
// -> regex matches "source-interface Management1"
// -> replace "source-interface" and spaces -> "Management1"
let targetIfaceNames = distinct(
foreach srcLine in sourceIntLines
foreach m in regexMatches(srcLine, reIfaceNamePattern)
select replace(replace(m.string, "source-interface", ""), " ", ""))
// Step 3: Extract all interface blocks from the config text
let allInterfaceBlocks = (foreach r in regexMatches(configAsString, reInterfaceBlock)
select r.string)
// Step 4: Match each target name to its interface block
let ifaceInfoList =
(foreach targetName in targetIfaceNames
foreach block in allInterfaceBlocks
let headerList = (foreach r in regexMatches(block, re`\ninterface [^\n]+`)
select r.string)
where length(headerList) > 0 where replace(replace(headerList[0], "\ninterface ", ""), "\n", "") == targetName
select filterBlock(block))
select {
device: device.name,
ip: ip,
OS: device.platform.osVersion,
model: device.platform.model,
Source_Interface: if length(sourceIntLines) == 0
then "None"
else join("\n", order(sourceIntLines)),
Info: if length(ifaceInfoList) == 0
then "None"
else join("\n\n", ifaceInfoList),
tags: device.tagNames
};
export eos_source_interface = main();
main()Arista EoS
/*** * @intent Source-Interface & Local-Interface Extraction (Arista EoS)
* @description Extracts all source-interface and local-interface configs and interface details from config text.
***/
// --- 1. Helper Functions ---
getConfigAsString(device) =
max(foreach command in device.outputs.commands
where command.commandType == CommandType.CONFIG
select command.response);
// --- 2. Regex Patterns ---
reSourceInterfacePattern = re`[^\n]*source-interface[^\n]*`;
reLocalInterfacePattern = re`[^\n]*local-interface[^\n]*`;
reIfaceNamePattern = re`source-interface \S+`;
reLocalIfaceNamePattern = re`local-interface \S+`;
reInterfaceBlock = re`\ninterface [^\n]+(?:\n [^\n]+)*`;
// --- 2b. filterBlock Helper ---
// Strips the leading \ninterface prefix, then removes unwanted lines.
filterBlock(block) =
join("\n",
foreach r in regexMatches(replace(block, "\ninterface ", "interface "), re`[^\n]+`)
let line = r.string
where length(regexMatches(line, re`\s+mtu \d+`)) == 0
where length(regexMatches(line, re`\s+load-interval \d+`)) == 0
where length(regexMatches(line, re`\s+ipv6 address \S+`)) == 0
select line);
// --- 3. Main Logic ---
main =
foreach device in network.devices
where device.platform.os == OS.ARISTA_EOS
where device.name == device.system.physicalName
let configResponse = getConfigAsString(device)
let configAsString = if isPresent(configResponse) then configResponse else ""
let ip = device.snapshotInfo.collectionIp
// Step 1: Extract all source-interface and local-interface lines (excluding Vlan4094)
let sourceIntLines = (foreach r in regexMatches(configAsString, reSourceInterfacePattern)
let line = r.string
where length(regexMatches(line, re`Vlan4094`)) == 0
select line)
let localIntLines = (foreach r in regexMatches(configAsString, reLocalInterfacePattern)
let line = r.string
where length(regexMatches(line, re`Vlan4094`)) == 0
select line)
// Step 2: Combine both into a single deduplicated list
let allSourceLines = distinct(sourceIntLines + localIntLines)
// Step 3: Extract distinct interface names from both sets
let sourceIfaceNames = distinct(
foreach srcLine in sourceIntLines
foreach m in regexMatches(srcLine, reIfaceNamePattern)
select replace(replace(m.string, "source-interface", ""), " ", ""))
let localIfaceNames = distinct(
foreach srcLine in localIntLines
foreach m in regexMatches(srcLine, reLocalIfaceNamePattern)
select replace(replace(m.string, "local-interface", ""), " ", ""))
let targetIfaceNames = distinct(
foreach name in (sourceIfaceNames + localIfaceNames)
where name != "Vlan4094"
select name)
// Step 4: Extract all interface blocks from the config text
let allInterfaceBlocks = (foreach r in regexMatches(configAsString, reInterfaceBlock)
select r.string)
// Step 5: Match each target name to its interface block
let ifaceInfoList =
(foreach targetName in targetIfaceNames
foreach block in allInterfaceBlocks
let headerList = (foreach r in regexMatches(block, re`\ninterface [^\n]+`)
select r.string)
where length(headerList) > 0 where replace(replace(headerList[0], "\ninterface ", ""), "\n", "") == targetName
select filterBlock(block))
select {
device: device.name,
ip: ip,
OS: device.platform.osVersion,
model: device.platform.model,
sources: if length(allSourceLines) == 0
then "None"
else join("\n", order(allSourceLines)),
Interfaces: if length(ifaceInfoList) == 0
then "None"
else join("\n\n", ifaceInfoList),
};
export eos_source_interface = main();
main()


