Most engineers think…
Network automation means becoming a full-time programmer — or, the opposite belief, that a plain CCNA is still enough to get hired in 2026.
Both are wrong. You don't need to be a developer — about 20 lines of Python and the right library will push config to 500 devices. And a plain CCNA now competes with thousands of identical resumes. The combination that actually gets you the call-back is CCNA + automation. By the end of this lesson you'll have written the script that proves you have it.
⚡ Quick answer
Network automation with Python means using small scripts to configure and check many network devices at once instead of typing into each one. The three tools you'll actually use: Netmiko (push CLI over SSH — your starting point), NAPALM (clean, vendor-independent data + safe commit/rollback), and Nornir (run the same task across hundreds of devices in parallel). Start with Netmiko on day one; reach for NAPALM when you have mixed vendors; add Nornir when you outgrow a simple loop.
① Why "CCNA + automation" is the 2026 hire signal
Think of how you message 50 friends about a meet-up. You don't open 50 chats and type the same line 50 times — you make one WhatsApp broadcast. Network automation is exactly that idea, for routers and switches. You write the change once, and a script applies it to every device in seconds.
For years a CCNA alone was enough to land a network job. Not anymore. Hiring data for 2026 says the same thing everywhere: manual, type-it-in-by-hand roles are shrinking, and the demand is moving to engineers who can automate. Cisco even rebuilt the CCNA, CCNP and CCIE blueprints to include Python and automation — so the exam itself now assumes you can script.
The money follows the skill. In India a plain CCNA fresher starts around ₹4.5–7 LPA, but engineers who add automation, SD-WAN or cloud-network skills move into ₹9–16 LPA roles much faster — and a security-plus-automation profile (say PCNSE) can reach ₹20–30 LPA with a few years. The job title to watch is Network Automation Engineer / DevNet Specialist — and those postings almost all list the same stack: Python, Netmiko, NAPALM, Nornir.
Aditya at a Lucknow ISP faces this
His manager asks him to add one management VLAN to 200 branch switches before Monday. By hand, that's a full weekend of console work — and one typo on switch #147 that nobody will notice until it breaks.
The task isn't hard — it's repetitive. Humans are slow and error-prone at "do this exact thing 200 times".
This is the textbook case for automation: identical change, many devices, tight deadline.
One config block → loop over an inventory → apply everywhereA ~20-line Netmiko script reads the 200 switches from a list and pushes the VLAN to each. Weekend job becomes a 5-minute run.
The script ends by running show vlan brief on each switch and logging which ones actually show VLAN — proof from the device, not from "the script said OK".
Aditya can configure one switch perfectly, but a 200-switch VLAN rollout would take him all weekend by hand. What single skill turns that overnight job into a 5-minute one?
Pause & Predict
Before we name the tools — guess: roughly how many lines of Python do you think it takes to push one command to a switch? Type your guess.
② The three tools, decoded
Almost every job posting names the same trio: Netmiko, NAPALM, Nornir. They are not competitors you choose between forever — they're three layers you add as you grow. Let's lock the vocabulary first, then meet each one.
First, four words you'll keep hearing
Tap each card — guess the meaning before you flip it.
The secure "remote control" you already use to reach a device's CLI. Netmiko automates typing into that same SSH session — so anything you can do by hand, it can do in code.
A clean "ask a question, get structured data back" door. NAPALM getters return facts as ready-to-use Python dictionaries — no screen-scraping. So what: no fragile regex per vendor.
Your list of devices (hostnames, IPs, credentials, groups) kept in a file. Nornir reads it so your script says "run on all branch routers" instead of hardcoding 600 IPs. So what: the list lives in one place.
Run it once or ten times — same final result, no duplicates, no damage. The #1 property of safe automation. So what: a retry can't double-apply an ACL and break traffic.
Netmiko — the SSH workhorse you start with
Netmiko logs into a device over SSH and types commands for you — then captures the text that comes back. It's built on top of Paramiko so you don't touch raw SSH plumbing. Because the device replies as plain text, you "read" it with screen-scraping (or, better, TextFSM templates). Use it when: you can already configure the device by hand and just want to do it across many. For a lab or fewer than ~50 devices, plain Netmiko in a for loop is perfect.
NAPALM — one clean voice for many vendors
NAPALM sits one level higher. Instead of raw text, it gives you vendor-independent structured data: ask get_facts() on a Cisco, Arista or Juniper box and you get the same dictionary shape back — model, serial, uptime, interfaces — no regex per vendor. Its real superpower is safe config changes: load a candidate config, run compare_config() to see the exact diff (a dry-run), then commit_config() — and if it's wrong, rollback(). Use it when: you run mixed vendors, or any change where "show me the diff and let me undo it" matters.
Nornir — the parallel engine for scale
Nornir isn't a replacement — it's the runner that wraps the other two. It manages your inventory and executes a task across hundreds of devices in parallel (real Python threading), with built-in grouping and result handling. The same job that takes ~40 minutes one-device-at-a-time with a Netmiko loop finishes in under 3 minutes through Nornir. Use it when: you've outgrown the simple loop — a few hundred devices, or you want to run "this task on just the Mumbai branch routers".
▶ How a Netmiko push actually runs
The four steps every config script follows. Press Play for the healthy path, then Break it to see the most common failure.
ConnectHandler() opens an SSH session to the switch at 10.20.4.11.send_config_set() types the VLAN lines into config mode — exactly as you would by hand.save_config() writes running-config to startup so the change survives a reboot.send_command('show vlan brief') reads it back — proof from the device.Sneha at Infosys faces this
Her team runs Cisco IOS, Arista EOS and Juniper Junos. She needs one report of "uptime + serial number" from all of them — and her first Netmiko script needs a different regex for every vendor's output. It's becoming a mess.
Netmiko returns raw text, which looks different on each vendor — so she's screen-scraping three times.
This is the multi-vendor data problem NAPALM was built for.
Swap raw text → NAPALMget_facts() structured dictOne NAPALM call returns the same dictionary shape for all three vendors. No per-vendor regex — she reads facts['serial_number'] everywhere.
She spot-checks one serial against the device's own show version — the structured value matches the box.
Sneha wants one script that pulls clean, vendor-independent facts from Cisco, Arista and Juniper — without writing a different regex per vendor. Which library fits best?
Pause & Predict
A Netmiko loop over 500 devices runs them one after another. If each device takes ~5 seconds, why does Nornir finish the same job ~13× faster? Type your guess.
③ Build it — your first script
Enough theory. Here's the whole pipeline in code you can copy. The shape never changes: read an inventory → connect → do the task → verify. Picture it first, then we'll write each piece.
Step 1 — Netmiko: push a VLAN to one switch
This is the "hello world" of network automation. Real, copy-pasteable, with a lab IP from the 10.20.4.x range.
from netmiko import ConnectHandler
switch = {
"device_type": "cisco_ios",
"host": "10.20.4.11",
"username": "netadmin",
"password": "use-an-env-var-here", # see Section 4 — never hardcode
}
config = ["vlan 99", "name MGMT"]
conn = ConnectHandler(**switch)
print(conn.send_config_set(config)) # push it
conn.save_config() # write mem
print(conn.send_command("show vlan brief | include 99")) # verify
conn.disconnect()configure terminal SW-BR11(config)#vlan 99 SW-BR11(config-vlan)#name MGMT SW-BR11(config-vlan)#end SW-BR11# 99 MGMT active
That last line is the whole point — the switch itself reports VLAN 99 active. To do all 200 switches, you wrap these lines in a for loop over your inventory. That's it.
1 from netmiko import ConnectHandler 2 3 switch = { 4 "device_type": "cisco_ios", 5 "host": "10.20.4.11", 6 "username": "netadmin", 7 "password": os.environ["SW_PASS"], # 🔒 from env, not hardcoded 8 } 9 conn = ConnectHandler(**switch) 10 print(conn.send_config_set(["vlan 99", "name MGMT"]))
SW-BR11(config)#vlan 99 ✓ VLAN 99 pushed in 1.8s
Step 2 — NAPALM: see the diff before you commit
This is the habit that keeps you employed. NAPALM loads the change as a candidate, shows you the exact diff, and only commits when you're sure. If it's wrong, you discard — nothing touched the device.
import napalm
driver = napalm.get_network_driver("ios")
dev = driver(hostname="172.16.10.2", username="netadmin",
password="use-an-env-var-here")
dev.open()
dev.load_merge_candidate(filename="add_ntp.cfg")
print(dev.compare_config()) # DRY RUN — show me the diff first
# only now decide:
dev.commit_config() # apply ... or dev.discard_config() to abort
dev.close()+ntp server 172.16.0.10 +ntp server 172.16.0.11
You saw the two lines that would be added before anything changed. That preview is your safety net — and it's why NAPALM is the grown-up choice for production changes.
▶ NAPALM's safe-change loop — never commit blind
The discipline that separates a junior from a senior. Press Play, then Break it to see what happens when you skip the dry-run.
load_merge_candidate() stages the change — the device is still untouched.compare_config() prints the exact diff. You read it like a code review.commit_config() applies it. Wrong → discard_config().rollback() is your undo button.$ python safe_change.py # --- compare_config() dry-run --- + ntp server 172.16.0.10 + ntp server 172.16.0.11 # diff looks right → commit_config() committed. $ ssh netadmin@172.16.10.2 'show run | inc ntp' ntp server 172.16.0.10 ✓ confirmed on the device ntp server 172.16.0.11 ✓
Step 3 — Nornir: same task, 500 devices, in parallel
When the loop gets slow, Nornir takes over. You define the inventory once, then run a task across every matching host concurrently.
from nornir import InitNornir
from nornir_netmiko.tasks import netmiko_send_config
nr = InitNornir(config_file="config.yaml") # reads inventory.yaml
branch = nr.filter(site="mumbai") # just Mumbai branch
result = branch.run(
task=netmiko_send_config,
config_commands=["vlan 99", "name MGMT"],
)
print(f"Done on {len(result)} devices")Done on 480 devices SW-BR-MUM-001 ... ok SW-BR-MUM-002 ... ok ... (all in parallel — total 2m 41s, not 38m)
Priya at TCS faces this
Her Nornir job needs to run only on the Mumbai branch routers, but it ran on every device in the inventory — including the Delhi data-centre cores. Scary, but nothing broke because she'd dry-run it.
Her filter() matched a key that didn't exist, so it silently selected all hosts.
Print the selected hosts before running the task — always confirm the target set.
filter →print(nr.inventory.hosts.keys()) → THEN runCorrect the group key (site="mumbai") and add a count check that aborts if the target list is unexpectedly large.
Re-run with the fix — the host list now prints 480 Mumbai devices, not 2,000.
A serial Netmiko for-loop over 480 branch routers runs for ~38 minutes, but the change window is only 15. The config itself is correct. What's the cleanest fix?
Pause & Predict
In the NAPALM example, what's the one line you must never skip before commit_config()? Type your answer.
compare_config() — the dry-run diff. Reading the diff before committing is the difference between "I added two NTP lines" and "I accidentally removed a routing statement". Always look before you commit.④ Don't break prod — the 5 mistakes
Automation is a force multiplier — for good and bad. A typo you'd make on one switch by hand now hits 2,000 in seconds. In October 2025, an automated process inside AWS hit a timeout edge case and cascaded into a ~15-hour disruption. Scripts that ran fine in a small lab can behave very differently in production. Here are the five disciplines that keep your automation from becoming the outage.
1. Be idempotent. Running your script twice should leave the device in the same state as running it once. If a retry can add a duplicate ACL line or apply a change twice, you don't have automation — you have a loaded gun. Check "is it already there?" before you add.
2. Handle errors and timeouts. One unreachable device should not freeze the whole run. Wrap each device in try/except, set sensible Netmiko timeouts, and log the failures instead of crashing. The AWS outage above was, at its core, an unhandled timeout that cascaded.
3. Always dry-run. Use NAPALM's compare_config() (or a "show me what would change" mode) before every commit. Reading the diff is a 10-second habit that prevents 10-hour incidents.
4. Mind the blast radius — test in a lab first. Prove every change in EVE-NG, GNS3 or Cisco CML before prod, then roll out to a small canary group, then the fleet. Never all 2,000 at once on the first run.
5. Never hardcode secrets. No passwords in the .py file. Read them from an environment variable (os.environ["SW_PASS"]) or a vault. Hardcoded credentials are the security finding that gets your script — and you — flagged in audit.
Treat every automation run like a deploy: lab → dry-run diff → small canary → fleet. The engineers who never cause outages aren't more careful by accident — they bake these four gates into the script itself.
The symptom: a change passes on your one test switch, then wipes a live VLAN off 300 production switches. The cause is almost always a negation command (no vlan 99) or a non-idempotent step applied at scale without a dry-run. Lab success ≠ prod safety.
Your script printing "success" only means the command was sent. Real verification is reading state back from the box — show vlan brief, a NAPALM getter, a ping. If you can't prove it from the device, you haven't verified it.
Karthik at a Mumbai bank faces this
His cleanup script ran no vlan 99 to remove an old test VLAN. It worked perfectly in the lab. In production it wiped VLAN 99 off 300 switches that were carrying live ATM traffic. Phones started ringing.
A destructive negation command, applied at scale, with no dry-run and no canary group. The lab switch didn't carry real traffic, so the blast radius was invisible there.
Two missing gates: a diff before commit, and a staged rollout.
compare_config() diff → canary 5 switches → watch → then fleetRebuild the change with NAPALM (so rollback() exists), preview the diff, run on 5 non-critical switches first, confirm traffic is fine, then roll forward.
After each stage, show vlan brief and a synthetic ping across VLAN 99 confirm the path is healthy before widening the rollout.
Karthik's no vlan 99 worked in the lab but wiped a live VLAN off 300 production switches. Which discipline would have caught this before the damage?
🤖 Ask the AI Tutor
Tap any question — instant, scoped to this lesson. No login, no waiting.
Pre-curated from Python docs + community Q&A, scoped to this lesson. For a live prod issue, paste your export into chat.techclick.in.
📝 Wrap-up assessment — six more
You've answered 4 inline. Six left. 70% (7 of 10) marks the lesson complete on your profile. Tap Submit all answers at the end.
🧠 In your own words
Type one line: Why do we run compare_config() before commit_config()? Then compare to the expert version.
compare_config() is a dry-run — it shows the exact diff of what would change, without changing anything. Reading that diff first is how you catch a destructive line (like a stray no vlan 99) before it hits 300 live switches. Commit blind and the device teaches you; commit after a diff and the preview teaches you.🗣 Teach a friend
Best way to lock it in — explain it in one line to a teammate. Tap to generate a paste-ready summary.
📖 Glossary
- Network automation
- Using scripts to configure, check and monitor many network devices at once, instead of typing into each one by hand.
- Netmiko
- A Python library (built on Paramiko) that automates SSH/CLI sessions to network devices — your day-one tool for pushing config.
- NAPALM
- A Python library that returns vendor-independent structured data and offers safe config changes with compare, commit and rollback.
- Nornir
- A pure-Python framework that manages your inventory and runs tasks across hundreds of devices in parallel, calling Netmiko/NAPALM underneath.
- Paramiko
- The low-level Python SSH library that Netmiko wraps, so you don't deal with raw SSH plumbing.
- SSH
- The secure remote-access protocol you already use to reach a device's CLI; Netmiko automates typing into that session.
- Getter
- A NAPALM method like
get_facts()that returns device data as a ready-to-use Python dictionary — no regex needed. - Idempotent
- A change that gives the same result whether you run it once or ten times — no duplicates, no damage on retry.
- Inventory
- Your list of devices (hosts, IPs, credentials, groups) kept in a file, so a script can say "run on all branch routers".
- Dry-run / diff
- A preview (NAPALM
compare_config()) that shows exactly what would change, before anything actually changes. - Commit / rollback
- Apply a staged change (commit) or undo it (rollback) — NAPALM's safety net for production edits.
- Blast radius
- How many devices a change can affect at once. Automation makes it huge — staged rollouts keep it small.
- Canary group
- A small first group of devices you change and watch before rolling out to the whole fleet.
- Screen-scraping / TextFSM
- Pulling values out of raw CLI text. TextFSM / NTC-templates turn that text into clean fields so you skip fragile regex.
📚 Sources
- ipSpace.net — Ivan Pepelnjak, "Paramiko, Netmiko, NAPALM or Nornir?". blog.ipspace.net
- APNIC Blog — "Automation tools: Paramiko, Netmiko, NAPALM, Ansible, Nornir or…?". blog.apnic.net
- Full Stack Networker — "Network Automation with Python Libraries Netmiko, NAPALM and Nornir." fullstacknetworker.com
- cloudmylab — "Network Automation Challenges: 5 Mistakes Engineers Make (and How to Avoid Them)." blog.cloudmylab.com
- PyNet Labs — "What is Python for Network Engineers? Its Importance in 2026." pynetlabs.com
- Networkers Home — "Network Automation Career — Skills, Certifications & 2026 Roadmap." networkershome.com
- TechTarget — "Advanced skills drive networking job market in 2026." techtarget.com/searchnetworking
What's next?
You can now push and verify config across a fleet with Python. Next, point those same instincts at a live cloud-managed API — the Meraki Dashboard — where the device speaks JSON over HTTPS instead of CLI over SSH. Same automation mindset, different door.