Network automation: a source of truth, templates and checks before change
Lesson 3 of 4
3Network automation: a source of truth, templates and checks before change
The big pictureChanging the SNMP password on 300 switches by hand takes a week and guarantees a mistake on at least one. Automated, it takes minutes and every device matches. But automation magnifies everything, mistakes too: one wrong script breaks 300 devices at once. So you do not write a faster script, you build a more careful system: correct data, a diff, checks and review.
What the picture shows: Data generates configs, generated configs are compared with running ones, the difference is reviewed and checked, then pushed, then the network is verified to be in the wanted state.
Map of this part
The concepts in order, click any one to jump to it:
Two ways into the same idea
Think of a big restaurant. A beginner cook makes each dish from memory, so it changes daily. A professional kitchen has a written recipe for every dish (the template), an exact stock list (the source of truth), and a taste before the dish leaves (the check). Automation is the network's professional kitchen: nobody types configs from memory straight onto a device.
A source of truth (such as NetBox) holds devices, ports, VLANs and addresses, and configs are derived from it with templates (usually Jinja2). Idempotency: running the tool twice gives the same result, because it applies only the difference. Connecting: SSH/CLI through Netmiko for older gear, and NETCONF (RFC 6241, on TCP 830) and RESTCONF (RFC 8040, over HTTPS) with structured data shaped by YANG models (RFC 7950), with commit and rollback on many systems. Ansible describes the wanted state in YAML with no agent on the device. The cycle looks like CI: the change in Git, a review, static config analysis (such as Batfish), a trial in containerlab, then the push with pre and post checks (neighbours, routes, ports).
Before You Start
🌐Modern networks
You met SDN and automation in brief.
🐍Python basics
You can write a loop and a function and read a dictionary.
The Core Idea
1The source of truth: data before commands
The first question in automation is not "which tool?" but "where is the truth?". If it lives in people's heads and scattered spreadsheets, every script builds on wrong data. Put devices, their roles, ports, VLANs and addresses into one system such as NetBox, and generate configs from it. After that every change starts by editing the data, not the device.
Add VLAN 30 to sw-br1-acc2 in the data and run: the config changes by itself. That is the whole idea.
from string import Template
truth = { # what NetBox would return for one branch
"sw-br1-acc1": {"mgmt": "10.99.1.11", "vlans": {10: "STAFF", 20: "VOICE", 30: "GUEST"}, "uplinks": ["te1/1/1", "te1/1/2"]},
"sw-br1-acc2": {"mgmt": "10.99.1.12", "vlans": {10: "STAFF", 20: "VOICE"}, "uplinks": ["te1/1/1"]},
}
SNMP = Template("snmp-server group NMS v3 priv\nsnmp-server host 10.99.0.5 version 3 priv $user")
def render(name, dev):
lines = [f"hostname {name}", f"ip default-gateway {dev['mgmt'].rsplit('.', 1)[0]}.1"]
lines += [f"vlan {vid}\n name {label}" for vid, label in sorted(dev["vlans"].items())]
allowed = ",".join(str(v) for v in sorted(dev["vlans"]))
for port in dev["uplinks"]:
lines += [f"interface {port}", " switchport mode trunk", f" switchport trunk allowed vlan {allowed}"]
lines.append(SNMP.substitute(user="netops"))
return "\n".join(lines)
print(render("sw-br1-acc2", truth["sw-br1-acc2"]))2The diff before the push, and idempotency
Do not push the whole config onto the device. Compare the desired state (generated from data) with the running one (from the device), and show only the diff for a colleague to read. Run the tool a second time with no data change and it must do nothing. That is idempotency, and it is what makes a nightly run safe for catching any manual change that crept in (drift).
The diff reveals a forgotten test VLAN and an old community string. These get reviewed before any push.
import difflib
running = """hostname sw-br1-acc2
vlan 10
name STAFF
vlan 20
name VOICE
vlan 99
name TEMP-TEST
snmp-server community public RO""".splitlines()
desired = """hostname sw-br1-acc2
vlan 10
name STAFF
vlan 20
name VOICE
snmp-server group NMS v3 priv""".splitlines()
diff = list(difflib.unified_diff(running, desired, "running", "desired", lineterm="", n=0))
print("\n".join(diff) if diff else "no change: nothing to push")
drift = [l[1:] for l in diff if l.startswith("-") and not l.startswith("---")]
print("\ndrift found (someone changed it by hand):", drift)3How does the tool talk to the device?
Three ways. CLI over SSH (Netmiko): works with almost every device, but you are reading text made for humans. NETCONF on TCP 830: structured XML data shaped by a YANG model, with a change that applies fully or not at all (commit). RESTCONF: the same YANG models over HTTPS and JSON. The rule: structured data always beats parsing text with regular expressions.
| Method | Transport | Data | When |
|---|---|---|---|
| CLI + Netmiko | SSH 22 | Text for humans | Older gear and quick tasks |
| NETCONF | SSH 830 | XML per YANG | Transactional changes with rollback |
| RESTCONF | HTTPS 443 | JSON or XML per YANG | Integration with web apps |
| gNMI | gRPC | Protobuf per YANG | Streaming telemetry and config |
# Ansible: describe the wanted state, run it twice, the second run changes nothing
- name: Branch access switches
hosts: branch1_access
gather_facts: false
tasks:
- name: VLANs from the source of truth
cisco.ios.ios_vlans:
config: "{{ vlans }}"
state: merged
- name: Save only when something changed
cisco.ios.ios_config:
save_when: changed4Check before and after, and a lab before production
Every automated change goes through four gates: a review of the diff in Git by a colleague, a trial in a containerlab lab running real device images in containers, a pre-check recording state (OSPF and BGP neighbours, route counts, ports up), and a post-check comparing against it. If a neighbour or route is missing, roll back automatically. Start your journey with read-only tasks: inventory, config backups and reports.
Losing more than 5% of routes or a neighbour is enough to roll back. The rule is written before the change, not after.
before = {"ospf_neighbours": {"10.0.0.1", "10.0.0.2"}, "bgp_up": 2, "routes": 812, "ports_up": 44}
after = {"ospf_neighbours": {"10.0.0.1"}, "bgp_up": 2, "routes": 640, "ports_up": 44}
problems = []
lost = before["ospf_neighbours"] - after["ospf_neighbours"]
if lost:
problems.append(f"lost OSPF neighbour(s): {sorted(lost)}")
if after["bgp_up"] < before["bgp_up"]:
problems.append("a BGP session dropped")
if after["routes"] < before["routes"] * 0.95:
problems.append(f"routes fell {before['routes']} -> {after['routes']}")
if after["ports_up"] < before["ports_up"]:
problems.append("ports went down")
print("ROLL BACK:\n - " + "\n - ".join(problems) if problems else "change verified")Worked example
The task: management wants the NTP servers changed on 120 switches across 6 branches within a week, with no outage. Write the plan.
The answer: (1) Change the NTP servers in the source of truth. (2) The template generates the two new NTP lines, the tool shows the diff per device, and a colleague reviews it in a Git merge request. (3) Try it on two switches in containerlab. (4) Pre-check: show ntp status and show clock. (5) Push to one branch, then post-check: clock synced and stratum healthy. (6) If it holds, the remaining branches in two batches. (7) Run the tool again: it must say "no change". Document all of it in the merge request.
Myth vs Reality
| Myth | Reality |
|---|---|
| Automation will end the network engineer's job. | It ends repetitive copy and paste, and raises the value of someone who understands networks and writes code. |
| Automation means one big Python script. | Automation is a system: data, templates, review and checks. The script is a small part. |
| Automation is dangerous in production. | The danger is unreviewed manual change. Automation with checks is safer and easier to roll back. |
Where you meet it at work
🔐Security compliance
A nightly report finds any device still on an old community string.
🏗️A new branch
Every device config for the branch is generated from data in minutes.
💾Backups
Every device config backed up to Git nightly, so you see who changed what and when.
Test your understanding
What is the first step of a successful automation project?
Everything generated comes from the data; wrong data means wrong configs everywhere.
Which port does NETCONF use by default?
NETCONF runs over SSH on port 830 (RFC 6242).
What does idempotency mean in automation?
The tool applies only the difference, so with no difference it does nothing.
What describes the data structure in NETCONF and RESTCONF?
YANG is a modelling language that defines fields and types, so data can be validated.
After an automated change the router lost one OSPF neighbour. What does a good tool do?
The post-check compares with the pre-change state, and any loss is a reason to roll back.
Quick review cards
Check Your Understanding
Why show the diff rather than the full config for review?
Because the colleague sees what will actually change in a few lines, instead of getting lost in a thousand unchanged ones.
Where do you start if your company has automated nothing yet?
With read-only tasks: a device inventory, nightly config backups to Git, and a compliance report.
Why run the tool every night even with no changes?
To catch drift: any manual change that moved a device away from the wanted state.
Resources for this lesson
Grouped by type. Start with the official docs for precision, or an article or video for the explanation.
Tools2
Done with this lesson?
Sign in to save your progress.