Manually auditing DNS configuration across a large network is slow, tedious, and error-prone. Engineers are often forced to comb through thousands of lines of device configuration to confirm that every router, firewall, and switch is pointing to the correct name servers. By the time the audit is complete, parts of the network may already be out of compliance again.
With Forward Networks’ Network Query Engine (NQE), this kind of validation can be automated and continuously enforced using a simple, declarative query that evaluates the entire network model at once.
Why this matters
DNS is a foundational service. A single misconfigured device can:
-
Break application connectivity
-
Bypass security monitoring by resolving through an untrusted resolver
-
Violate regulatory or internal policy that mandates use of controlled DNS infrastructure
What this NQE checks
This query verifies that every monitored device is configured with the approved internal DNS servers—and only those servers.
In this example, the organization has standardized on two authorized DNS IP addresses:
-
2.2.2.2
-
1.1.1.1
The NQE script inspects each device’s DNS configuration and flags any of the following conditions:
-
A device is missing one or both required DNS servers
-
A device is using an unapproved DNS server
-
A device’s DNS configuration does not exactly match the approved pair
By expressing this requirement as network intent in NQE, you move from periodic, manual audits to continuous, automated compliance validation. Any drift from the approved DNS standard is immediately visible, searchable, and reportable across the entire digital twin of your network.
Explanation of the NQE components (scroll down for complete script):
| Component | NQE Snippet | Explanation and Function |
| Define the Standard | expectedDns = [ipAddress("2.2.2.2"), ipAddress("1.1.1.1")]; | This line establishes the single source of truth for all DNS server configurations across the network. By defining this list once, you can ensure consistency across all compliance checks, simplifying updates if the standard DNS servers ever change. |
| Define the Search Pattern | configPatternIOS = \ip name-server {dnsservers:(ipv4Address*)}`;` | NQE uses powerful patterns to accurately extract specific data from configuration text. This pattern is custom-designed to match the Cisco IOS/IOS-XE command ip name-server and reliably captures any subsequent IPv4 addresses into a list variable named dnsservers. Similar patterns would be defined for other vendors (e.g., Junos, Palo Alto, Fortinet). |
| The Extraction Function | getdnsservers(device, pattern) = ... | This function is the engine for data retrieval. It intelligently iterates over the device's configuration text, applies the correct OS-specific pattern, and returns the list of configured DNS servers present on that specific device. This abstracts away the complexity of handling different vendor syntaxes. |
| Iterate and Filter | foreach device in network.devices where device.os in ["IOS", "FORTINET", "NXOS"] ... | The main body of the query loops through every device known to the network. A where clause is applied here to filter the scope down to only the relevant operating systems, preventing false positives on devices that may not use this configuration style. |
| Execute and Compare | let servers = getdnsservers(device, pattern) - unexpectedDns = servers - expectedDns - missingExpectedDns = expectedDns - servers | This is the core logic that defines the compliance check. The configured servers are extracted. The set difference operators (-) are then used to perform a sophisticated comparison: • unexpectedDns: Identifies any server IP in the device's configuration that is not in the approved expectedDns list (a security violation). • missingExpectedDns: Identifies any required server IP that is missing from the device's configuration (a service integrity violation). |
| Report the Results | `select { violation: length(unexpectedDns) != 0 |
What’s the violation field?
In Forward Networks, the violation field is a boolean trigger within NQE that transforms a query from a data search into a state-aware health check. Using the the violation field in NQE displays results to the Verify dashboard, where they are tracked over time for trend analysis rather than buried in static reports. This "violation" status is the primary hook for proactive alerting, enabling the platform to provide notifications to Slack, Teams, or ServiceNow via API and Webhooks the moment a policy breach is detected in a snapshot.
/**
* @intent Enter your intent here (one line, 50 characters max)
* @description Checks for DNS servers for standard IOS DNS configuration
*/
expectedDns = [ipAddress("2.2.2.2"), ipAddress("1.1.1.1")];
/*The following pattern is intended to matches the following config line:
ip name-server 1.1.1.1
ip name-server 2.2.2.2 */
configPatternIOS = ```
ip name-server {dnsservers:(ipv4Address*)}
```;
/*The following pattern is intended to match the following config lines:
ip name-server 1.1.1.1 2.2.2.2
*/
configPatternNxosXe =
```
ip name-server {(!ipv4Address string)*} {dnsservers:(ipv4Address*)}
```;
/* The following pattern is inteded to match the folllowing config lines:
config system dns
set primary 53.188.5.254
set secondary 53.186.5.254 */
configPatternFortinet =
```
config system dns
set {string} {dnsservers:(ipv4Address*)}
```;
//Get DNS Server Function
getdnsservers(device, pattern) =
foreach match in blockMatches(device.files.config, pattern)
foreach dnsserver in match.data.dnsservers
select dnsserver;
/************************* Main Function********************/
foreach device in network.devices
let platform = device.platform
//Filters OS types with DNS Configured
where platform.os == OS.IOS || platform.os == OS.IOS_XR ||
platform.os == OS.IOS_XE ||
platform.os == OS.FORTINET ||
platform.os == OS.NXOS
//Selects pattern based on OS
let pattern = if platform.os == OS.IOS
then configPatternIOS
else if platform.os == OS.FORTINET
then configPatternFortinet
else configPatternNxosXe
//Gets DNS servers in the configuration
let servers = getdnsservers(device, pattern)
//Get the incorrectly configured DNS Servers
let unexpectedDns = servers - expectedDns
//Get DNS servers that should be configured
let missingExpectedDns = expectedDns - servers
select {
violation: length(unexpectedDns) != 0 || length(missingExpectedDns) != 0,
device: device.name,
Servercount: length(servers),
"Missing Expected Dns": missingExpectedDns,
"Unexpected DNS": unexpectedDns,
"DNS Servers": (foreach x in servers
select x),
OS: platform.os,
Version: platform.osVersion
}
Sample output:
| violation | device | Servercount | Missing Expected Dns | Unexpected DNS | OS |
| FAIL | Device 1 | 2 | 1.1.1.1, 2.2.2.2 | 3.3.3.3, 4.4.4.4 | IOS_XE |
| FAIL | Device 2 | 2 | 1.1.1.1, 2.2.2.2 | 3.3.3.3, 4.4.4.4 | IOS |
| FAIL | Device 3 | 2 | 1.1.1.1, 2.2.2.2 | 3.3.3.3, 4.4.4.4 | IOS |
| FAIL | Device 4 | 2 | 1.1.1.1, 2.2.2.2 | 3.3.3.3, 4.4.4.4 | IOS |
| FAIL | Device 5 | 2 | 1.1.1.1, 2.2.2.2 | 3.3.3.3, 4.4.4.4 | IOS |
| FAIL | Device 6 | 2 | 1.1.1.1, 2.2.2.2 | 3.3.3.3, 4.4.4.4 | IOS |
| FAIL | Device 7 | 2 | 1.1.1.1, 2.2.2.2 | 3.3.3.3, 4.4.4.4 | IOS |
| FAIL | Device 8 | 2 | 1.1.1.1, 2.2.2.2 | 3.3.3.3, 4.4.4.4 | IOS |
| FAIL | Device 9 | 2 | 1.1.1.1, 2.2.2.2 | 3.3.3.3, 4.4.4.4 | IOS_XE |
| FAIL | Device 10 | 2 | 1.1.1.1, 2.2.2.2 | 3.3.3.3, 4.4.4.4 | IOS |


