Rules as data, required and forbidden lines, whole-line matching

Check compliance rules

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

Security teams write standards: central time and logging, encrypted passwords, no Telnet, no default SNMP community. Turn the standard into data and check every configuration against it.

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…

  • Keep a standard as data
  • Match whole lines, not pieces of text
  • Report missing and forbidden lines

The idea

Keep the rules per platform as two lists: required lines that must be present and forbidden lines that must not be. Compare whole lines: put the stripped lines of the configuration in a set and look each rule up. Testing with in on the whole text is not enough, because a short rule can match part of a longer line. Compliance checks usually run on the backups, so they need no login at all.

config = "line vty 0 4\n transport input telnet ssh\n"
lines = {line.strip() for line in config.splitlines()}
print("transport input telnet" in config)
print("transport input telnet" in lines)

01Configuration lines as a set

strip() removes the indentation inside sections, so " transport input ssh" and "transport input ssh" become the same line.

text = open("backups/sw2.cfg").read()
lines = {line.strip() for line in text.splitlines()}
print("transport input telnet ssh" in lines)
print("ip http server" in lines)
Output
True
True

02Pieces of text mislead

The forbidden rule transport input telnet is part of the longer line on sw2, so in on the text says yes. The set of whole lines says no.

print("transport input telnet" in text)
print("transport input telnet" in lines)
Output
True
False

03Required and forbidden

Two list comprehensions produce the report, in the order of the rules.

required = ["no ip http server", "service password-encryption"]
forbidden = ["transport input telnet ssh"]
found = [f"missing: {r}" for r in required if r not in lines]
found += [f"forbidden: {r}" for r in forbidden if r in lines]
print(found)
Output
['missing: no ip http server', 'forbidden: transport input telnet ssh']

Your turn

Check each backup in backups/ against RULES for its platform (from devices.csv). Build violations: device name → the list of "missing: <line>" for required lines that are absent, followed by "forbidden: <line>" for forbidden lines that are present, both in rule order. Compare whole lines, ignoring spaces at the ends. Skip devices that have no backup.

What the checks look for

  • Compliant devices have no violations
  • sw1: encryption, NTP and the public community
  • sw2: HTTP server, log server and Telnet
  • sw3: Huawei NTP servers and Telnet
  • Devices without a backup are skipped
Try it in the editor →

Take this with you

Rules are data; checking a rule is a set lookup on whole lines.

Go a little further

Add a third kind of rule, required_any: at least one of several lines must be present, for example transport input ssh or no transport input.

Learn Network Automation for free

20 short lessons with real code and instant checks.

See the whole course →