DEV Community

Vibhav Chennamadhava
Vibhav Chennamadhava

Posted on AI-assisted

I Stopped Typing Switch Configs by Hand (Ansible + My CCNA Lab)

From 584 lines of hand-typed IOS to one YAML edit per change

Six devices. 584 lines of config. Zero typed by hand.

Well... the second time. 😅


Why I did this

A while back I built a full enterprise campus network for the CCNA in Cisco Packet Tracer. Core, distribution and access layers, OSPF, HSRP, EtherChannel, NAT, ACLs, DHCP snooping, Dynamic ARP Inspection, IPv6, a wireless LAN controller. The whole thing.

Every single line was typed into a CLI. By me. One device at a time.

So what's the issue? The issue is that every job posting I read says "network automation", and I had never automated anything on this lab. Saying I know automation when all I've done is type commands felt wrong.

So I rebuilt the lab as code. Here is what that looked like, what broke, and what I learned.


The idea in one sentence

Describe the network as data, and let a template write the config.

If you've ever done a mail merge, you already get it. You have a letter with blanks like Dear {{ name }} and a spreadsheet of names. Merge them and you get 100 personalized letters.

Here it's the same thing:

  • The spreadsheet = YAML files describing each device (VLANs, IPs, HSRP, OSPF)
  • The letter = Jinja2 templates that look almost exactly like Cisco config
  • The merge button = Ansible

1. Describe the network as data

Here is VLAN 10 on one of my distribution switches, written as YAML:

- name: Vlan10
  ipv4: 10.1.0.2 255.255.255.0
  ospf: true
  ospf_passive: true
  hsrp: {group: 2, vip: 10.1.0.1, priority: 105, preempt: true}
  acl_in: OfficeA_to_OfficeB
Enter fullscreen mode Exit fullscreen mode

No interface, no standby, no ip helper-address. Just facts about the interface.

Settings every device shares (DNS, NTP, syslog, SSH hardening) live in one file. Change the syslog server once and every device picks it up.


2. Let the template write the config

That YAML gets rendered into this:

interface Vlan10
 description PCs
 ip address 10.1.0.2 255.255.255.0
 ip helper-address 10.0.0.76
 standby version 2
 standby 2 ip 10.1.0.1
 standby 2 priority 105
 standby 2 preempt
 ip access-group OfficeA_to_OfficeB in
Enter fullscreen mode Exit fullscreen mode

The description comes from the VLAN name. The DHCP relay gets added because it's a distribution switch. I didn't type any of it.

It also adds lines to other parts of the config:

  • network 10.1.0.2 0.0.0.0 area 0 and passive-interface Vlan10 under OSPF
  • spanning-tree vlan 10,99 priority 0, because this switch is the HSRP active gateway for VLAN 10, so it should also be the STP root

Pro Tip: That last one is my favorite. "STP root follows HSRP" is a design rule you learn for the CCNA. Now it's enforced in code, so the two can never drift apart.


3. The plot twist: Packet Tracer says no

My plan was simple. Write the templates, then let Ansible SSH into each device and push the config.

Packet Tracer does not accept SSH connections from outside the app. At all. lol.

So Ansible can't push to my lab. Pretty big problem for an automation project!

Instead of giving up, I changed the question from "can I push it?" to "can I prove the templates are correct?"


4. Proving it: the drift check

I already had the hand-built configs from the original lab saved in the repo. So I wrote a checker that compares the configs the templates generate against those originals.

The result:

Device Role Lines rendered Status
R1 Edge router 119 ✅ MATCH
CSW1 Core 81 ✅ MATCH
DSW-A1 Distribution 125 ✅ MATCH
DSW-B1 Distribution 120 ✅ MATCH
ASW-A1 Access 74 ✅ MATCH
ASW-B1 Access 65 ✅ MATCH

6 of 6 devices, zero drift. The templates recreate the lab I built by hand.

Note: It's not a plain text diff. Cisco doesn't care what order most commands are in, but it does care about the order of ACL entries. So the checker ignores order everywhere except inside ACLs.

To make sure it wasn't just saying "looks good!" to everything, I broke a config on purpose. I changed an HSRP priority, swapped two ACL lines and deleted a VLAN. It caught all three.

It also caught a real mistake in my original build: two access switches had enable secret and username admin typed twice. IOS doesn't mind, but the generated configs are clean. Automation 1, manual typing 0.


5. Where it gets good: adding a VLAN

Let's say I need a Guest VLAN. By hand, that means:

  1. Create the VLAN
  2. Create the SVI with an IP, DHCP relay and HSRP
  3. Add the VLAN to every trunk
  4. Update STP root priorities
  5. Add the network to OSPF and make it passive

Forget step 3 on one trunk and guests can't get an IP. Forget step 5 and it's a routing mystery at 2 AM.

With the templates, it's 7 lines of YAML:

 vlans:
   - {id: 40, name: Wi-Fi}
+  - {id: 50, name: Guest}

+  - name: Vlan50
+    ipv4: 10.7.0.2 255.255.255.0
+    ospf: true
+    ospf_passive: true
+    hsrp: {group: 5, vip: 10.7.0.1}
Enter fullscreen mode Exit fullscreen mode

And those 7 lines produce 15 correct IOS changes: the VLAN, the SVI with HSRP and DHCP relay, 4 trunks, the STP priority, and 2 OSPF lines. You can't forget a step when there's only one step.


6. Safety rails (because networks are scary)

The deploy playbook is ready for real gear, and it's built to be hard to misuse:

  • Dry run by default. It shows what each device would receive. You have to type apply=true to actually push.
  • No passwords in Git. Real secrets come from an encrypted Ansible Vault file. If a placeholder password is still in the config, it refuses to push.
  • Backup first. Every device's running config is saved before anything changes.
  • One device at a time. If one fails, the rollout stops.

7. A green checkmark on every commit

GitHub Actions runs on every push:

✅ YAML lint
✅ Ansible lint (strictest "production" profile)
✅ Syntax check on every playbook
✅ Render all configs + drift check
Enter fullscreen mode Exit fullscreen mode

If someone (me) edits the YAML and breaks something, the badge turns red before it ever gets near a switch.


What I learned

  1. Automation isn't about typing faster. It means every change gets reviewed, can be repeated, and provably matches the design.
  2. Prove it before you push it. The drift check was worth more than the deploy playbook. It's the part that makes me trust everything else.
  3. Be honest about what's tested. Deploy and verify haven't touched a live device yet, and the README says so. I'd rather say that in an interview than get caught overselling.

What's next

Rebuilding a few devices in Cisco Modeling Labs (which has real IOS and real SSH) so I can run the full push → verify → backup loop for real. When that works, expect a part 2. 👀


That is it. It's not perfect, and there's a good chance a network engineer reading this has a better way to do half of it. If so, please tell me!

If you want to dig into the code, here's the repo:

GitHub: https://github.com/VibhavChennamadhava/CCNA-Enterprise-Campus-Mega-Lab

Question for the comments: have you automated a home lab? What did you use: Ansible, Nornir, Netmiko, something else? I'd love to hear what worked (and what didn't)!

Questions or comments? Love to hear from you!

Top comments (0)