Back to Learn

Netmiko โ€” multi-vendor SSH automation

The de-facto Python library for talking to network devices over SSH. Send show commands, push config, and parse output into structured data across Cisco, Arista, Juniper and 100+ device types.

Python
SSH
Cisco IOS / NX-OS
Arista EOS
Juniper JunOS
TextFSM
ConnectHandler opens vendor-aware SSH sessions
send_command / send_config_set for show & config
use_textfsm=True returns structured data via ntc-templates

Setup, key commands & run examples

Step 0 โ€” install
Setup

Install Netmiko into a virtualenv. It pulls in Paramiko (SSH) and ntc-templates (TextFSM) for you.

Step 0 โ€” install
bash
python3 -m venv .venv
source .venv/bin/activate
pip install netmiko
connect.py โ€” single device
ConnectHandler

ConnectHandler opens an SSH session. device_type drives the vendor-specific behaviour (cisco_ios, arista_eos, juniper_junos, etc.).

connect.py โ€” single device
python
from netmiko import ConnectHandler

device = {
    "device_type": "cisco_ios",
    "host": "10.10.1.1",
    "username": "admin",
    "password": "secret",
    # "secret": "enablepass",  # for enable mode
}

with ConnectHandler(**device) as conn:
    output = conn.send_command("show ip interface brief")
    print(output)
parse.py โ€” structured output
TextFSM parsing

Pass use_textfsm=True to turn raw 'show' output into a list of dicts using ntc-templates โ€” no manual regex.

parse.py โ€” structured output
python
with ConnectHandler(**device) as conn:
    rows = conn.send_command("show ip interface brief", use_textfsm=True)

for row in rows:
    print(row["interface"], row["ip_address"], row["status"])
configure.py โ€” push config
send_config_set

send_config_set enters config mode, applies a list of commands, then exits. Use save_config() to write to startup.

configure.py โ€” push config
python
commands = [
    "interface loopback100",
    "description managed-by-netmiko",
    "ip address 10.99.0.1 255.255.255.255",
]

with ConnectHandler(**device) as conn:
    print(conn.send_config_set(commands))
    conn.save_config()  # write mem
inventory.py โ€” many devices
Scale out

Loop over an inventory to run the same task fleet-wide. For real concurrency, use Netmiko's ConnectHandler with threads or Nornir.

inventory.py โ€” many devices
python
from netmiko import ConnectHandler

hosts = ["10.10.1.1", "10.10.1.2", "10.10.1.3"]
base = {"device_type": "cisco_ios", "username": "admin", "password": "secret"}

for host in hosts:
    with ConnectHandler(host=host, **base) as conn:
        print(f"== {host} ==")
        print(conn.send_command("show version | include uptime"))

Hands-on exercises

Guided practice tasks โ€” copy the starter snippet, run it against a lab device, and finish the TODO to meet the goal.

Exercise 1
Your first connection

Goal: Connect to a lab device, run 'show version', and print only the line containing the uptime.

your version (simulated check)
โ–ธ Reveal reference solution
solution_1
python
from netmiko import ConnectHandler

device = {"device_type": "cisco_ios", "host": "10.10.1.1",
          "username": "admin", "password": "secret"}

with ConnectHandler(**device) as conn:
    out = conn.send_command("show version")
    for line in out.splitlines():
        if "uptime" in line:
            print(line.strip())
Exercise 2
Parse interfaces into data

Goal: Use use_textfsm=True to get structured rows, then print only interfaces that are 'up'.

your version (simulated check)
โ–ธ Reveal reference solution
solution_2
python
with ConnectHandler(**device) as conn:
    rows = conn.send_command("show ip interface brief", use_textfsm=True)
    for r in rows:
        if r["status"] == "up":
            print(r["interface"], r["ip_address"])
Exercise 3
Push and verify a loopback

Goal: Create Loopback150 with send_config_set, save_config(), then read it back to confirm it exists.

your version (simulated check)
โ–ธ Reveal reference solution
solution_3
python
cmds = ["interface loopback150", "ip address 10.99.0.50 255.255.255.255"]

with ConnectHandler(**device) as conn:
    conn.send_config_set(cmds)
    conn.save_config()
    print(conn.send_command("show ip interface brief | include Loopback150"))
Exercise 4
Run against many devices

Goal: Loop over three hosts and collect 'show clock' from each, handling failures gracefully.

your version (simulated check)
โ–ธ Reveal reference solution
solution_4
python
hosts = ["10.10.1.1", "10.10.1.2", "10.10.1.3"]
base = {"device_type": "cisco_ios", "username": "admin", "password": "secret"}

for host in hosts:
    try:
        with ConnectHandler(host=host, **base) as conn:
            print(host, conn.send_command("show clock"))
    except Exception as e:
        print(f"{host} failed: {e}")

Checklist โ€” pitfalls, gotchas & verification

Common pitfalls

  • โ€ขWrong device_type โ€” using cisco_ios for an Arista box garbles prompts and timing.
  • โ€ขForgetting enable mode โ€” add a 'secret' and call conn.enable() for privileged commands.
  • โ€ขNot saving config โ€” send_config_set changes running config only until save_config().

Gotchas to watch

  • โ€ขuse_textfsm=True silently returns raw text if no ntc-template matches the command.
  • โ€ขsend_command waits for the prompt; long-running commands may need read_timeout tuning.
  • โ€ขSequential loops are slow โ€” use threads or Nornir for real concurrency.

Verify it works

  • โ€ขPrint the raw output first to confirm the session and command worked.
  • โ€ขAfter a config push, re-read the relevant 'show' command to confirm the change landed.
  • โ€ขWrap multi-device runs in try/except and report per-host success or failure.

Netmiko is great for direct, imperative tasks. When you need inventory, concurrency and reusable tasks at scale, pair it with Nornir โ€” which can use Netmiko as its connection driver.