difflib.unified_diff, noise filtering & changed lines

Detect configuration drift

Lesson 14 of 20 · 15 min · Module 4: Backups, drift and compliance

Someone changed two switches by hand last night and told nobody. Comparing today's configuration with the last backup shows exactly what changed.

Start this lesson →

Free. No installation: you write and run the code in your browser, and it is checked automatically.

By the end, you’ll be able to…

  • Compare two configurations with difflib
  • Ignore the lines that change on their own
  • Keep only the added and removed lines

The idea

difflib.unified_diff(old, new, lineterm="") compares two lists of lines. Lines that start with - were removed, lines that start with + were added, and the ---, +++ and @@ lines only describe the comparison. Some lines change on their own, such as the timestamps in comments and the configuration size. Remove them before comparing: lines that start with !, Building configuration, Current configuration, and empty lines.

import difflib

old = ["hostname sw1", "ntp server 10.0.0.250"]
new = ["hostname sw1", "ntp server 10.0.0.251"]
for line in difflib.unified_diff(old, new, lineterm=""):
    print(line)

01A unified diff

Context lines start with a space; - and + mark what was removed and added. The first three lines describe the comparison itself.

import difflib

old = ["interface Gi0/7", " switchport access vlan 10"]
new = ["interface Gi0/7", " switchport access vlan 99"]
for line in difflib.unified_diff(old, new, fromfile="backup", tofile="now",
                                 lineterm=""):
    print(line)
Output
--- backup
+++ now
@@ -1,2 +1,2 @@
 interface Gi0/7
- switchport access vlan 10
+ switchport access vlan 99

02Noise changes on every run

The size and the timestamps differ as soon as anything changes, even a description. Removing them leaves only real configuration.

text = open("backups/sw1.cfg").read()
print("\n".join(text.splitlines()[:6]))
Output
Building configuration...

Current configuration : 2183 bytes
!
! Last configuration change at 02:00:00 UTC Fri Oct 16 2026 by netadmin
! NVRAM config last updated at 02:00:00 UTC Fri Oct 16 2026 by netadmin

03startswith takes a tuple

One call checks several prefixes. That keeps the filter short and readable.

NOISE = ("!", "Building configuration", "Current configuration")
print("! NVRAM config last updated".startswith(NOISE))
print("hostname sw1".startswith(NOISE))
Output
True
False

Your turn

Compare each backup in backups/ with the device's running configuration now (core1, sw1 and sw2 are Cisco, sw3 is Huawei). Build drift: a dictionary mapping each device name to the list of changed lines, the ones starting with + or -, without the --- and +++ header lines and after removing the noisy lines. A device without changes maps to an empty list.

What the checks look for

  • core1 shows the new static route
  • sw2 shows the changed port
  • Unchanged devices have no drift
  • Timestamps and sizes are ignored
  • clean() removes the noisy lines
Try it in the editor →

Take this with you

Diff against the last backup, ignore the lines that change by themselves, and look at what is left.

Go a little further

Write each non-empty diff to drift/<name>.diff, so it can be attached to a ticket.

Learn Network Automation for free

20 short lessons with real code and instant checks.

See the whole course →