Get Your First Script Running in 5 Minutes
Logging into switches manually is a rite of passage for junior engineers. But when you’re facing a spreadsheet of 50 access layer switches that all need a VLAN change at 11:00 PM on a Friday, the manual approach is a recipe for disaster. I spent years copy-pasting commands and praying I didn’t miss a line. Python fixes that.
Netmiko is the industry standard for this. Built on top of Paramiko, it is specifically designed for network hardware. It handles the annoying ‘paging’ issues (the –More– prompts), detects the enable prompt automatically, and simplifies the SSH process across Cisco, Juniper, and Arista devices.
Start by setting up a clean environment. I always use a virtual environment to keep my system tidy. Install Netmiko with these three lines:
python3 -m venv venv
source venv/bin/activate
pip install netmiko
A basic connection script is surprisingly short. Here is how you connect to a single Cisco IOS router to pull interface statuses:
from netmiko import ConnectHandler
cisco_router = {
'device_type': 'cisco_ios',
'host': '192.168.1.10',
'username': 'admin',
'password': 'your_secure_password',
'secret': 'enable_password',
}
# Open connection and run a command
with ConnectHandler(**cisco_router) as net_connect:
net_connect.enable()
output = net_connect.send_command("show ip int brief")
print(output)
Python’s with statement is your best friend here. It ensures the SSH connection closes properly even if your code crashes. This prevents you from leaving 20 ‘ghost’ SSH sessions hanging on your core switch, which can block other engineers from logging in.
Scaling to 500 Devices
Connecting to one device is just a proof of concept. The real magic happens when you loop through an entire data center. If you want to move from a traditional admin role into a DevOps-focused engineering position, this is the skill you need.
Don’t hardcode IP addresses. Instead, store your device list in a devices.yaml file or a plain text list. This makes your scripts reusable across different sites. Let’s look at how to iterate through multiple hosts to check CPU usage in seconds:
from netmiko import ConnectHandler
devices = ['10.0.0.1', '10.0.0.2', '10.0.0.3']
for host in devices:
device_info = {
'device_type': 'cisco_ios',
'host': host,
'username': 'admin',
'password': 'your_password',
}
try:
print(f"--- Connecting to {host} ---")
with ConnectHandler(**device_info) as net_connect:
output = net_connect.send_command("show version | include uptime")
print(output)
except Exception as e:
print(f"Failed to connect to {host}: {str(e)}")
Error handling is non-negotiable at scale. A single offline switch shouldn’t crash your entire script. Using a ‘try-except’ block allows the script to log the failure and move on to the next IP. It’s the difference between a successful automation run and waking up to a half-finished job because of one transient timeout.
Configuration Mode vs. Show Commands
While send_command() is for gathering data, you need send_config_set() to make changes. This method enters configuration mode for you, pushes your commands, and exits cleanly. It also handles the slight timing delays that some older supervisors need when applying new configs.
commands = [
'vlan 100',
'name VOIP_VLAN',
'interface GigabitEthernet0/1',
'description Configured by Python',
'switchport access vlan 100',
'switchport mode access'
]
with ConnectHandler(**cisco_router) as net_connect:
output = net_connect.send_config_set(commands)
print(output)
net_connect.send_command("write memory")
Managing Complex Changes and Security
As your automation grows, you will eventually need to push large config blocks—like a 200-line ACL. Netmiko handles this via send_config_from_file(). This keeps your Python logic separate from the vendor-specific syntax, making your code much easier to read.
Security is the other major hurdle. Hardcoding passwords in a script is a firing offense. Use environment variables or a vault instead. On Linux, you can pull credentials directly from your shell environment using the os module:
import os
password = os.getenv("NET_PASS")
What about multi-vendor environments? Netmiko supports over 100 platforms. If you have a mix of Cisco IOS and Juniper Junos, simply flip the device_type key to 'juniper_junos'. The API stays the same. This is a massive time-saver for heterogeneous networks.
Battle-Tested Rules for Safe Automation
Automating your network is high-stakes. You can fix 1,000 devices in a minute, but you can also break them just as fast. I’ve seen entire racks go dark because of a misplaced ‘no’ in a script. Follow these rules to stay safe:
- Run Lab Simulations: Use GNS3 or Cisco Modeling Labs (CML). If it works on a virtual IOS instance, it’s 95% likely to work on the physical gear.
- Log Every Byte: Redirect your script output to a timestamped file. If the network goes sideways, you need an exact record of what was sent and where.
- Use Descriptive Tags: When your script modifies an interface, add a description like
Configured by Python-Auto [2026-04-10]. It tells your colleagues exactly who (or what) touched the config. - Start with Read-Only: Build your confidence by writing scripts that only pull data, like ‘show inventory’. Don’t touch configuration mode until you’re 100% sure of your connection logic.
One final tip: don’t ignore the fast_cli option. If your network is stable and your hardware is modern, setting 'fast_cli': True can slash execution times by 50%. It reduces the artificial ‘wait’ timers between commands. Just use caution with legacy 2960s—they still need those extra milliseconds to process input without choking.
Automating your routine tasks isn’t just about saving time; it’s about consistency. A script won’t forget to ‘write memory’ or miss a ‘no shutdown’ command. Once you’ve mastered Netmiko, you can level up to advanced orchestration tools like Nornir or Ansible.

