Conditionals and Loops

This is the lesson where scripts start making decisions. Conditionals let your code judge each line of a config; loops let it judge every line of every config - which is the entire point of automating a network.

TL;DR - only got 10 minutes? This is the lesson. Hit to have it read to you.
  • if/elif/else branches on a condition; the indented block under each is what runs. Indentation is the syntax - 4 spaces, no tabs.
  • Comparisons: == equality (= is assignment - the classic typo), !=, <, >=. Combine with and / or / not. Membership: "aaa new-model" in config.
  • A for loop visits every item: for line in lines: - the variable name is yours to choose, and good names (line, intf, device) keep audits readable.
  • The accumulator pattern is 80% of parsing: start with results = [], loop, if condition: results.append(item). You filtered a config in four lines.
  • enumerate(lines) hands you (index, line) pairs - that's how audit findings get line numbers.
  • Truthiness: empty lists and strings are False - if matches: reads as "if we found anything".
  • Never modify a list while looping over it - build a new one instead (the accumulator again).
  • Prove it: the graded lab at the end runs in your browser - implement the functions and make every check green.
In this lesson you will:
  • Write if/elif/else blocks that branch on network facts
  • Combine comparisons with and, or, and not
  • Loop over lists of lines, interfaces, and devices with for
  • Build filtered lists with the accumulator pattern
  • Track line numbers with enumerate() for real config audits

Conditionals: code that judges

Everything in automation reduces to questions with yes/no answers. Is this line an interface? Is this VLAN in the voice range? Does this config contain aaa new-model? In Python, a question is an expression that evaluates to a boolean - True or False:

>>> vlan = 150
>>> vlan >= 100
True
>>> vlan == 150        # == asks; = assigns
True
>>> "aaa new-model" in config
False

if acts on the answer, elif chains more questions, else catches everything left:

if vlan < 100:
    role = "users"
elif vlan < 200:
    role = "voice"
elif vlan < 300:
    role = "management"
else:
    role = "other"

The indented block under each branch is what runs - indentation isn’t style in Python, it is the syntax. Four spaces, consistently, never tabs. Combine conditions with and, or, and not:

if line.startswith("interface") and "Te" in line:
    print("found an uplink stanza")

One more idea that makes code read like English - truthiness. Empty things ("", [], 0) count as False; non-empty things count as True:

if matches:                  # "if we found anything"
    print(f"{len(matches)} findings")
else:
    print("clean")

Loops: code that scales

A for loop runs a block once per item in a list. That’s the whole mechanism - and it’s the difference between checking one switch and checking the district:

devices = ["acc-sw01", "acc-sw02", "core-sw01"]
for device in devices:
    print(f"auditing {device}...")

You choose the loop variable’s name. Choose well - for line in lines, for intf in interfaces, for device in devices read like sentences; for x in y reads like a puzzle.

The accumulator pattern - most of parsing is this

Start with an empty list, loop, keep what matches:

interfaces = []
for line in config_lines:
    if line.startswith("interface "):
        interfaces.append(line)

Four lines, and you’ve filtered a 400-line config down to its interface stanzas. Lesson 5 adds regex for fancier matching and Lesson 6 wraps this in reusable functions, but the skeleton never changes: empty list → loop → condition → append.

The accumulator pattern, as a flowchart
Rendering diagram…
View diagram source - it's just text (Mermaid). Diagrams-as-code is how modern network docs work; the flagship course has a free module on it.
flowchart TD
  A["interfaces = []"] --> B{"more lines<br/>in the config?"}
  B -- "yes" --> C{"line starts with<br/>'interface '?"}
  C -- "yes" --> D["interfaces.append(line)"]
  C -- "no" --> B
  D --> B
  B -- "no" --> E["return interfaces"]

enumerate(): findings need line numbers

An audit finding without a line number is a scavenger hunt. enumerate(lines) yields (index, item) pairs:

for i, line in enumerate(config_lines):
    if "switchport mode trunk" in line:
        print(f"line {i + 1}: {line.strip()}")

(The i + 1 is because humans count config lines from 1, not 0.)

break and continue - loop steering

break exits the loop entirely (you found what you came for); continue skips to the next item (this one doesn’t apply):

for line in config_lines:
    if line.startswith("!"):
        continue          # comment separator - skip it
    if line == "end":
        break             # nothing meaningful after this
    process(line)
Making decisions about every line
Try it yourself (Python runs in your browser)
Output appears here. First run downloads the Python runtime (~10 MB), so give it a few seconds.

Prove it (graded lab)

Write each function in the editor, then Run grader - it runs the lesson’s real checks right here in your browser, nothing to install. Green means the skill is yours, and these are the building blocks the Lesson 10 capstone (netaudit) is assembled from.

Five functions - number five is the first real audit:

  1. classify_vlan(vlan_id) - map a VLAN ID to its role by range
  2. find_interfaces(config_lines) - all lines that open an interface stanza
  3. count_matching(lines, text) - how many lines contain text
  4. access_ports(interfaces) - everything that isn’t a TenGig uplink
  5. missing_descriptions(config_lines) - challenge: interface names whose next line is not a description (a real compliance check - combine enumerate, indexing, and .startswith())
Graded lab - 5 checks, run right here
Edit the functions above, then Run grader. First run downloads the Python runtime (~10 MB), so give it a few seconds.
Reveal a reference solution
"""Lesson 3 reference solutions. Read after an honest attempt."""


def classify_vlan(vlan_id):
    if 1 <= vlan_id <= 99:
        return "users"
    elif 100 <= vlan_id <= 199:
        return "voice"
    elif 200 <= vlan_id <= 299:
        return "management"
    else:
        return "other"


def find_interfaces(config_lines):
    found = []
    for line in config_lines:
        if line.startswith("interface "):
            found.append(line)
    return found


def count_matching(lines, text):
    count = 0
    for line in lines:
        if text in line:
            count = count + 1
    return count


def access_ports(interfaces):
    ports = []
    for name in interfaces:
        if not name.startswith("Te"):
            ports.append(name)
    return ports


def missing_descriptions(config_lines):
    findings = []
    for i, line in enumerate(config_lines):
        if line.startswith("interface "):
            name = line.split()[1]
            has_next = i + 1 < len(config_lines)
            if not has_next or not config_lines[i + 1].startswith(" description"):
                findings.append(name)
    return findings

Read it, close it, then re-type your own version from memory. Wrong turns are where the learning is.

Check your understanding

Your script dies with SyntaxError pointing at: if hostname = "core-sw01": - what is wrong?

1 / 3

Summary

Conditionals turn facts into decisions: if/elif/else branch on comparisons (==, not =), joined by and/or/not, with truthiness letting if matches: read as plain English. Loops apply those decisions at scale: for line in lines visits everything, the accumulator pattern (empty list → loop → condition → append) filters configs in four lines, enumerate() attaches the line numbers an audit needs, and break/continue steer. You also learned the silent-bug rule: never modify a list mid-loop - build a new one. Next lesson: dictionaries and sets, where device inventories stop being parallel lists and start being real data structures.