Cisco Netmiko Scripting with Examples, networkjourney.com
NetworkJourney · Free Learning ResourceBack to Network Automation Course

All 15 Netmiko examples, modernized

Work from connection basics through backups, multi-device collection and controlled configuration examples. Every example is a complete Python file with prompted credentials, connection cleanup and common error handling. Each has a specific validation step.

Use configuration examples only in an isolated lab. Review and capture the original object first. Configuration errors can leave partial changes. These scripts require confirmation and never save to startup configuration automatically. Replace documentation addresses and interface names for your lab.

What is Cisco Netmiko?

Netmiko is a Python library for device-aware SSH command execution across supported network platforms. These examples target Cisco IOS/IOS-XE; VLAN and switch-status commands need a supporting switch image. It does not replace network design knowledge or verification.

Install and run the examples

Shell
python -m venv .venv
# Windows PowerShell
.\.venv\Scripts\Activate.ps1
# macOS / Linux (use this instead of the Windows line)
source .venv/bin/activate
python -m pip install netmiko
python -c "import netmiko; print(netmiko.__version__)"

Run only the activation line for your operating system. Download an example, replace lab-specific values and execute it using the virtual environment’s Python. Credentials are requested interactively. An optional enable secret is supported by single-device examples; use appropriate AAA privileges. The multi-device example assumes show-command privileges without an enable transition.

Shell
python 01_basic_connection_script.py
Browse Examples

Choose an emulator and confirm SSH

EVE-NG, GNS3, PNETLab, Cisco Modeling Labs or an authorized physical lab may be suitable when the chosen appliance supports the required CLI and SSH connectivity. Obtain images and licenses through authorized channels. Check platform requirements, nested virtualization and the management bridge before installing. Packet-level simulation alone is not a substitute for a reachable SSH endpoint.

Choose a script

1. Basic Connection Script

READ ONLY

Show the detected CLI prompt and verify the account privilege.

Python · 01_basic_connection_script.py
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout

ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"

def device_for(host, username, password):
    return dict(device_type="cisco_ios", host=host, username=username,
                password=password, conn_timeout=10, auth_timeout=15,
                banner_timeout=20)

def read(connection, command):
    return connection.send_command(command, read_timeout=60)

def apply_lab(connection, commands):
    # Review the existing object before calling this helper.
    print("PROPOSED LAB COMMANDS:")
    print("\n".join(commands))
    if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
        print("Cancelled. No configuration sent.")
        return False
    print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
    print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
    return True

def task(connection, host):
    print("Prompt:", connection.find_prompt())
    print("Enable mode:", connection.check_enable_mode())

def main():
    host = input("Authorized lab management IP: ").strip()
    username = input("SSH username: ").strip()
    password = getpass("SSH password: ")
    secret = getpass("Enable secret (Enter if not needed): ")
    device = device_for(host, username, password)
    if secret:
        device["secret"] = secret
    try:
        with ConnectHandler(**device) as connection:
            if secret:
                connection.enable()
            task(connection, host)
    except NetmikoAuthenticationException:
        print("Authentication failed: check credentials, AAA and enable privileges.")
    except NetmikoTimeoutException:
        print("Connection failed: check address, routing, SSH port and device state.")
    except ReadTimeout:
        print("Command read timed out: inspect device prompt and command completion.")
    except (OSError, ValueError) as exc:
        print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")

if __name__ == "__main__":
    main()

Verify the result

A device prompt is printed; check the expected hostname and privilege.

2. Running a Single Command

READ ONLY

Collect interface addressing and operational state.

Python · 02_running_a_single_command.py
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout

ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"

def device_for(host, username, password):
    return dict(device_type="cisco_ios", host=host, username=username,
                password=password, conn_timeout=10, auth_timeout=15,
                banner_timeout=20)

def read(connection, command):
    return connection.send_command(command, read_timeout=60)

def apply_lab(connection, commands):
    # Review the existing object before calling this helper.
    print("PROPOSED LAB COMMANDS:")
    print("\n".join(commands))
    if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
        print("Cancelled. No configuration sent.")
        return False
    print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
    print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
    return True

def task(connection, host):
    print(read(connection, "show ip interface brief"))

def main():
    host = input("Authorized lab management IP: ").strip()
    username = input("SSH username: ").strip()
    password = getpass("SSH password: ")
    secret = getpass("Enable secret (Enter if not needed): ")
    device = device_for(host, username, password)
    if secret:
        device["secret"] = secret
    try:
        with ConnectHandler(**device) as connection:
            if secret:
                connection.enable()
            task(connection, host)
    except NetmikoAuthenticationException:
        print("Authentication failed: check credentials, AAA and enable privileges.")
    except NetmikoTimeoutException:
        print("Connection failed: check address, routing, SSH port and device state.")
    except ReadTimeout:
        print("Command read timed out: inspect device prompt and command completion.")
    except (OSError, ValueError) as exc:
        print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")

if __name__ == "__main__":
    main()

Verify the result

An interface table appears; compare IP addresses and up/down state with your lab.

3. Sending Configuration Commands

LAB CHANGE

Practice a description update on an explicitly chosen lab interface.

Python · 03_sending_configuration_commands.py
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout

ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"

def device_for(host, username, password):
    return dict(device_type="cisco_ios", host=host, username=username,
                password=password, conn_timeout=10, auth_timeout=15,
                banner_timeout=20)

def read(connection, command):
    return connection.send_command(command, read_timeout=60)

def apply_lab(connection, commands):
    # Review the existing object before calling this helper.
    print("PROPOSED LAB COMMANDS:")
    print("\n".join(commands))
    if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
        print("Cancelled. No configuration sent.")
        return False
    print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
    print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
    return True

def task(connection, host):
    interface = input("Lab interface (example GigabitEthernet1): ").strip()
    if not interface or any(c.isspace() for c in interface):
        raise ValueError("Enter one interface identifier, with no spaces")
    print("BEFORE:", read(connection, f"show running-config interface {interface}"))
    if apply_lab(connection, [f"interface {interface}", "description NJ-LAB-DESCRIPTION"]):
        print("AFTER:", read(connection, f"show running-config interface {interface}"))

def main():
    host = input("Authorized lab management IP: ").strip()
    username = input("SSH username: ").strip()
    password = getpass("SSH password: ")
    secret = getpass("Enable secret (Enter if not needed): ")
    device = device_for(host, username, password)
    if secret:
        device["secret"] = secret
    try:
        with ConnectHandler(**device) as connection:
            if secret:
                connection.enable()
            task(connection, host)
    except NetmikoAuthenticationException:
        print("Authentication failed: check credentials, AAA and enable privileges.")
    except NetmikoTimeoutException:
        print("Connection failed: check address, routing, SSH port and device state.")
    except ReadTimeout:
        print("Command read timed out: inspect device prompt and command completion.")
    except (OSError, ValueError) as exc:
        print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")

if __name__ == "__main__":
    main()

Verify the result

Description changes without altering addressing or shutting an interface. Restore the previous description, or remove it only if none existed.

4. Running Multiple Commands Sequentially

READ ONLY

Label each show-command result so failures are easier to locate.

Python · 04_running_multiple_commands_sequentially.py
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout

ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"

def device_for(host, username, password):
    return dict(device_type="cisco_ios", host=host, username=username,
                password=password, conn_timeout=10, auth_timeout=15,
                banner_timeout=20)

def read(connection, command):
    return connection.send_command(command, read_timeout=60)

def apply_lab(connection, commands):
    # Review the existing object before calling this helper.
    print("PROPOSED LAB COMMANDS:")
    print("\n".join(commands))
    if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
        print("Cancelled. No configuration sent.")
        return False
    print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
    print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
    return True

def task(connection, host):
    commands = ["show version", "show ip route", "show ip interface brief"]
    for command in commands:
        print(f"\n===== {host}: {command} =====")
        print(read(connection, command))

def main():
    host = input("Authorized lab management IP: ").strip()
    username = input("SSH username: ").strip()
    password = getpass("SSH password: ")
    secret = getpass("Enable secret (Enter if not needed): ")
    device = device_for(host, username, password)
    if secret:
        device["secret"] = secret
    try:
        with ConnectHandler(**device) as connection:
            if secret:
                connection.enable()
            task(connection, host)
    except NetmikoAuthenticationException:
        print("Authentication failed: check credentials, AAA and enable privileges.")
    except NetmikoTimeoutException:
        print("Connection failed: check address, routing, SSH port and device state.")
    except ReadTimeout:
        print("Command read timed out: inspect device prompt and command completion.")
    except (OSError, ValueError) as exc:
        print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")

if __name__ == "__main__":
    main()

Verify the result

Three labeled outputs appear in order. This is sequential execution, not concurrency.

5. Backup Device Configuration

READ ONLY

Create a unique timestamped running-configuration backup.

Python · 05_backup_device_configuration.py
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout

ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"

def device_for(host, username, password):
    return dict(device_type="cisco_ios", host=host, username=username,
                password=password, conn_timeout=10, auth_timeout=15,
                banner_timeout=20)

def read(connection, command):
    return connection.send_command(command, read_timeout=60)

def apply_lab(connection, commands):
    # Review the existing object before calling this helper.
    print("PROPOSED LAB COMMANDS:")
    print("\n".join(commands))
    if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
        print("Cancelled. No configuration sent.")
        return False
    print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
    print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
    return True

def task(connection, host):
    output = read(connection, "show running-config")
    if not output.strip() or "% Invalid input" in output:
        raise ValueError("No usable running configuration returned")
    folder = Path("backups")
    folder.mkdir(exist_ok=True)
    stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%S%fZ")
    safe_host = "".join(c if c.isalnum() or c in ".-_" else "_" for c in host)
    path = folder / f"{safe_host}_{stamp}.txt"
    path.write_text(output, encoding="utf-8")
    print(f"Saved {path}; inspect content and restrict access.")

def main():
    host = input("Authorized lab management IP: ").strip()
    username = input("SSH username: ").strip()
    password = getpass("SSH password: ")
    secret = getpass("Enable secret (Enter if not needed): ")
    device = device_for(host, username, password)
    if secret:
        device["secret"] = secret
    try:
        with ConnectHandler(**device) as connection:
            if secret:
                connection.enable()
            task(connection, host)
    except NetmikoAuthenticationException:
        print("Authentication failed: check credentials, AAA and enable privileges.")
    except NetmikoTimeoutException:
        print("Connection failed: check address, routing, SSH port and device state.")
    except ReadTimeout:
        print("Command read timed out: inspect device prompt and command completion.")
    except (OSError, ValueError) as exc:
        print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")

if __name__ == "__main__":
    main()

Verify the result

A new backup file is created. Inspect its content and keep secrets out of public repositories.

6. VLAN Configuration

LAB CHANGE

Create two clearly named practice VLANs on an isolated switch.

Python · 06_vlan_configuration.py
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout

ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"

def device_for(host, username, password):
    return dict(device_type="cisco_ios", host=host, username=username,
                password=password, conn_timeout=10, auth_timeout=15,
                banner_timeout=20)

def read(connection, command):
    return connection.send_command(command, read_timeout=60)

def apply_lab(connection, commands):
    # Review the existing object before calling this helper.
    print("PROPOSED LAB COMMANDS:")
    print("\n".join(commands))
    if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
        print("Cancelled. No configuration sent.")
        return False
    print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
    print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
    return True

def task(connection, host):
    print("BEFORE:", read(connection, "show vlan brief"))
    print("VLANs 310 and 320 must be unused practice VLANs; check VTP mode and switch support.")
    commands = ["vlan 310", "name NJ_LAB_310", "exit", "vlan 320", "name NJ_LAB_320", "exit"]
    if apply_lab(connection, commands):
        print("AFTER:", read(connection, "show vlan brief"))

def main():
    host = input("Authorized lab management IP: ").strip()
    username = input("SSH username: ").strip()
    password = getpass("SSH password: ")
    secret = getpass("Enable secret (Enter if not needed): ")
    device = device_for(host, username, password)
    if secret:
        device["secret"] = secret
    try:
        with ConnectHandler(**device) as connection:
            if secret:
                connection.enable()
            task(connection, host)
    except NetmikoAuthenticationException:
        print("Authentication failed: check credentials, AAA and enable privileges.")
    except NetmikoTimeoutException:
        print("Connection failed: check address, routing, SSH port and device state.")
    except ReadTimeout:
        print("Command read timed out: inspect device prompt and command completion.")
    except (OSError, ValueError) as exc:
        print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")

if __name__ == "__main__":
    main()

Verify the result

VLANs 310/320 appear on a supported switch. No access ports are assigned. Remove only objects newly created for this exercise after verifying they are unused.

7. Looping Through Multiple Devices

READ ONLY

Collect interfaces from a device list while isolating expected failures.

Python · 07_looping_through_multiple_devices.py
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout

ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"

def device_for(host, username, password):
    return dict(device_type="cisco_ios", host=host, username=username,
                password=password, conn_timeout=10, auth_timeout=15,
                banner_timeout=20)

def read(connection, command):
    return connection.send_command(command, read_timeout=60)

def apply_lab(connection, commands):
    # Review the existing object before calling this helper.
    print("PROPOSED LAB COMMANDS:")
    print("\n".join(commands))
    if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
        print("Cancelled. No configuration sent.")
        return False
    print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
    print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
    return True

def task(connection, host):
    # This example overrides main() below with a multi-device runner.
    print(read(connection, "show ip interface brief"))

def main():
    hosts = [x.strip() for x in input("Lab IPs, comma-separated: ").split(",") if x.strip()]
    username = input("SSH username: ").strip()
    password = getpass("SSH password: ")
    for host in hosts:
        try:
            with ConnectHandler(**device_for(host, username, password)) as connection:
                print(f"\n===== {host} =====")
                task(connection, host)
        except NetmikoAuthenticationException:
            print(f"{host}: authentication failed")
        except NetmikoTimeoutException:
            print(f"{host}: connection failed")
        except ReadTimeout:
            print(f"{host}: command read timed out")
        except (OSError, ValueError) as exc:
            print(f"{host}: failed: {exc}")

if __name__ == "__main__":
    main()

Verify the result

Each host is labeled; a failed host does not stop later hosts. Shared credentials are used only for this small lab example.

8. Interface Status Check

READ ONLY

Collect switch status or router interface state without assuming identical CLI support.

Python · 08_interface_status_check.py
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout

ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"

def device_for(host, username, password):
    return dict(device_type="cisco_ios", host=host, username=username,
                password=password, conn_timeout=10, auth_timeout=15,
                banner_timeout=20)

def read(connection, command):
    return connection.send_command(command, read_timeout=60)

def apply_lab(connection, commands):
    # Review the existing object before calling this helper.
    print("PROPOSED LAB COMMANDS:")
    print("\n".join(commands))
    if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
        print("Cancelled. No configuration sent.")
        return False
    print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
    print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
    return True

def task(connection, host):
    kind = input("Device role: switch or router: ").strip().lower()
    if kind not in {"switch", "router"}:
        raise ValueError("Choose switch or router")
    command = "show interfaces status" if kind == "switch" else "show ip interface brief"
    print(read(connection, command))

def main():
    host = input("Authorized lab management IP: ").strip()
    username = input("SSH username: ").strip()
    password = getpass("SSH password: ")
    secret = getpass("Enable secret (Enter if not needed): ")
    device = device_for(host, username, password)
    if secret:
        device["secret"] = secret
    try:
        with ConnectHandler(**device) as connection:
            if secret:
                connection.enable()
            task(connection, host)
    except NetmikoAuthenticationException:
        print("Authentication failed: check credentials, AAA and enable privileges.")
    except NetmikoTimeoutException:
        print("Connection failed: check address, routing, SSH port and device state.")
    except ReadTimeout:
        print("Command read timed out: inspect device prompt and command completion.")
    except (OSError, ValueError) as exc:
        print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")

if __name__ == "__main__":
    main()

Verify the result

A switch port-status table or router interface table appears. The script reports state; it does not infer a complete network health verdict.

9. Applying ACLs

LAB CHANGE

Create a named lab ACL and inspect it without attaching it to an interface.

Python · 09_applying_acls.py
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout

ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"

def device_for(host, username, password):
    return dict(device_type="cisco_ios", host=host, username=username,
                password=password, conn_timeout=10, auth_timeout=15,
                banner_timeout=20)

def read(connection, command):
    return connection.send_command(command, read_timeout=60)

def apply_lab(connection, commands):
    # Review the existing object before calling this helper.
    print("PROPOSED LAB COMMANDS:")
    print("\n".join(commands))
    if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
        print("Cancelled. No configuration sent.")
        return False
    print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
    print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
    return True

def task(connection, host):
    name = "NJ_LAB_ACL"
    print("BEFORE:", read(connection, f"show ip access-lists {name}"))
    print("Use a new, unused ACL name; abort if this object already exists or is referenced.")
    commands = [f"ip access-list extended {name}", "remark ISOLATED LAB EXAMPLE", "permit icmp host 192.0.2.10 host 192.0.2.11", "deny ip any any log", "exit"]
    if apply_lab(connection, commands):
        print("AFTER:", read(connection, f"show ip access-lists {name}"))

def main():
    host = input("Authorized lab management IP: ").strip()
    username = input("SSH username: ").strip()
    password = getpass("SSH password: ")
    secret = getpass("Enable secret (Enter if not needed): ")
    device = device_for(host, username, password)
    if secret:
        device["secret"] = secret
    try:
        with ConnectHandler(**device) as connection:
            if secret:
                connection.enable()
            task(connection, host)
    except NetmikoAuthenticationException:
        print("Authentication failed: check credentials, AAA and enable privileges.")
    except NetmikoTimeoutException:
        print("Connection failed: check address, routing, SSH port and device state.")
    except ReadTimeout:
        print("Command read timed out: inspect device prompt and command completion.")
    except (OSError, ValueError) as exc:
        print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")

if __name__ == "__main__":
    main()

Verify the result

The ACL exists but is not enforced until attached. Do not apply it to management traffic. If newly created and unreferenced, cleanup is no ip access-list extended NJ_LAB_ACL.

10. BGP Configuration

LAB CHANGE

Configure a lab peer and verify session state separately from configuration acceptance.

Python · 10_bgp_configuration.py
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout

ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"

def device_for(host, username, password):
    return dict(device_type="cisco_ios", host=host, username=username,
                password=password, conn_timeout=10, auth_timeout=15,
                banner_timeout=20)

def read(connection, command):
    return connection.send_command(command, read_timeout=60)

def apply_lab(connection, commands):
    # Review the existing object before calling this helper.
    print("PROPOSED LAB COMMANDS:")
    print("\n".join(commands))
    if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
        print("Cancelled. No configuration sent.")
        return False
    print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
    print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
    return True

def task(connection, host):
    print("BEFORE BGP CONFIG:", read(connection, "show running-config | section router bgp"))
    print("BEFORE STATE:", read(connection, "show ip bgp summary"))
    print("Prerequisites: fresh isolated R1, no conflicting BGP ASN; peer 198.51.100.2 reachable on transit, peer ASN 65002 configured there.")
    commands = ["router bgp 65001", "neighbor 198.51.100.2 remote-as 65002", "exit"]
    if apply_lab(connection, commands):
        print("AFTER:", read(connection, "show ip bgp summary"))

def main():
    host = input("Authorized lab management IP: ").strip()
    username = input("SSH username: ").strip()
    password = getpass("SSH password: ")
    secret = getpass("Enable secret (Enter if not needed): ")
    device = device_for(host, username, password)
    if secret:
        device["secret"] = secret
    try:
        with ConnectHandler(**device) as connection:
            if secret:
                connection.enable()
            task(connection, host)
    except NetmikoAuthenticationException:
        print("Authentication failed: check credentials, AAA and enable privileges.")
    except NetmikoTimeoutException:
        print("Connection failed: check address, routing, SSH port and device state.")
    except ReadTimeout:
        print("Command read timed out: inspect device prompt and command completion.")
    except (OSError, ValueError) as exc:
        print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")

if __name__ == "__main__":
    main()

Verify the result

An established peer shows a numeric received-prefix count, possibly 0. Idle/Active is not success; check peer reachability, ASN, TCP/179 and address-family requirements. Restore captured BGP baseline afterward.

11. Checking Device Uptime

READ ONLY

Read uptime without depending on a fragile numeric parser.

Python · 11_checking_device_uptime.py
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout

ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"

def device_for(host, username, password):
    return dict(device_type="cisco_ios", host=host, username=username,
                password=password, conn_timeout=10, auth_timeout=15,
                banner_timeout=20)

def read(connection, command):
    return connection.send_command(command, read_timeout=60)

def apply_lab(connection, commands):
    # Review the existing object before calling this helper.
    print("PROPOSED LAB COMMANDS:")
    print("\n".join(commands))
    if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
        print("Cancelled. No configuration sent.")
        return False
    print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
    print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
    return True

def task(connection, host):
    print(read(connection, "show version | include uptime"))

def main():
    host = input("Authorized lab management IP: ").strip()
    username = input("SSH username: ").strip()
    password = getpass("SSH password: ")
    secret = getpass("Enable secret (Enter if not needed): ")
    device = device_for(host, username, password)
    if secret:
        device["secret"] = secret
    try:
        with ConnectHandler(**device) as connection:
            if secret:
                connection.enable()
            task(connection, host)
    except NetmikoAuthenticationException:
        print("Authentication failed: check credentials, AAA and enable privileges.")
    except NetmikoTimeoutException:
        print("Connection failed: check address, routing, SSH port and device state.")
    except ReadTimeout:
        print("Command read timed out: inspect device prompt and command completion.")
    except (OSError, ValueError) as exc:
        print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")

if __name__ == "__main__":
    main()

Verify the result

A platform-specific uptime line appears. Empty output means inspect full show version and adjust the filter.

12. Loopback Interface Configuration

LAB CHANGE

Create a dedicated lab loopback with documented addressing.

Python · 12_loopback_interface_configuration.py
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout

ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"

def device_for(host, username, password):
    return dict(device_type="cisco_ios", host=host, username=username,
                password=password, conn_timeout=10, auth_timeout=15,
                banner_timeout=20)

def read(connection, command):
    return connection.send_command(command, read_timeout=60)

def apply_lab(connection, commands):
    # Review the existing object before calling this helper.
    print("PROPOSED LAB COMMANDS:")
    print("\n".join(commands))
    if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
        print("Cancelled. No configuration sent.")
        return False
    print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
    print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
    return True

def task(connection, host):
    print("BEFORE:", read(connection, "show running-config interface Loopback100"))
    print("Abort if Loopback100 or 203.0.113.100 is already in use.")
    commands = ["interface Loopback100", "description NJ_AUTOMATION_LAB", "ip address 203.0.113.100 255.255.255.255", "no shutdown"]
    if apply_lab(connection, commands):
        print("AFTER:", read(connection, "show ip interface brief | include Loopback100"))

def main():
    host = input("Authorized lab management IP: ").strip()
    username = input("SSH username: ").strip()
    password = getpass("SSH password: ")
    secret = getpass("Enable secret (Enter if not needed): ")
    device = device_for(host, username, password)
    if secret:
        device["secret"] = secret
    try:
        with ConnectHandler(**device) as connection:
            if secret:
                connection.enable()
            task(connection, host)
    except NetmikoAuthenticationException:
        print("Authentication failed: check credentials, AAA and enable privileges.")
    except NetmikoTimeoutException:
        print("Connection failed: check address, routing, SSH port and device state.")
    except ReadTimeout:
        print("Command read timed out: inspect device prompt and command completion.")
    except (OSError, ValueError) as exc:
        print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")

if __name__ == "__main__":
    main()

Verify the result

Loopback100 appears with its configured address. If newly created only for this exercise, cleanup is no interface Loopback100; otherwise restore its original configuration.

13. Monitoring CPU Usage

READ ONLY

Take several timestamped CPU snapshots; output syntax varies by platform.

Python · 13_monitoring_cpu_usage.py
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout

ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"

def device_for(host, username, password):
    return dict(device_type="cisco_ios", host=host, username=username,
                password=password, conn_timeout=10, auth_timeout=15,
                banner_timeout=20)

def read(connection, command):
    return connection.send_command(command, read_timeout=60)

def apply_lab(connection, commands):
    # Review the existing object before calling this helper.
    print("PROPOSED LAB COMMANDS:")
    print("\n".join(commands))
    if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
        print("Cancelled. No configuration sent.")
        return False
    print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
    print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
    return True

def task(connection, host):
    from time import sleep
    for sample in range(3):
        print(datetime.now(timezone.utc).isoformat(), "sample", sample + 1)
        print(read(connection, "show processes cpu"))
        if sample < 2:
            sleep(5)

def main():
    host = input("Authorized lab management IP: ").strip()
    username = input("SSH username: ").strip()
    password = getpass("SSH password: ")
    secret = getpass("Enable secret (Enter if not needed): ")
    device = device_for(host, username, password)
    if secret:
        device["secret"] = secret
    try:
        with ConnectHandler(**device) as connection:
            if secret:
                connection.enable()
            task(connection, host)
    except NetmikoAuthenticationException:
        print("Authentication failed: check credentials, AAA and enable privileges.")
    except NetmikoTimeoutException:
        print("Connection failed: check address, routing, SSH port and device state.")
    except ReadTimeout:
        print("Command read timed out: inspect device prompt and command completion.")
    except (OSError, ValueError) as exc:
        print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")

if __name__ == "__main__":
    main()

Verify the result

Three snapshots appear. This is periodic sampling, not streaming telemetry or historical monitoring. Connection cleanup is managed by the context manager.

14. NTP Server Configuration

LAB CHANGE

Add a reachable lab time source and inspect synchronization state.

Python · 14_ntp_server_configuration.py
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout

ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"

def device_for(host, username, password):
    return dict(device_type="cisco_ios", host=host, username=username,
                password=password, conn_timeout=10, auth_timeout=15,
                banner_timeout=20)

def read(connection, command):
    return connection.send_command(command, read_timeout=60)

def apply_lab(connection, commands):
    # Review the existing object before calling this helper.
    print("PROPOSED LAB COMMANDS:")
    print("\n".join(commands))
    if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
        print("Cancelled. No configuration sent.")
        return False
    print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
    print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
    return True

def task(connection, host):
    print("BEFORE:", read(connection, "show running-config | include ^ntp"))
    print("Lab NTP server 192.0.2.20 must be reachable and serving NTP. Do not overwrite existing time policies.")
    if apply_lab(connection, ["ntp server 192.0.2.20"]):
        print(read(connection, "show ntp associations"))
        print(read(connection, "show ntp status"))

def main():
    host = input("Authorized lab management IP: ").strip()
    username = input("SSH username: ").strip()
    password = getpass("SSH password: ")
    secret = getpass("Enable secret (Enter if not needed): ")
    device = device_for(host, username, password)
    if secret:
        device["secret"] = secret
    try:
        with ConnectHandler(**device) as connection:
            if secret:
                connection.enable()
            task(connection, host)
    except NetmikoAuthenticationException:
        print("Authentication failed: check credentials, AAA and enable privileges.")
    except NetmikoTimeoutException:
        print("Connection failed: check address, routing, SSH port and device state.")
    except ReadTimeout:
        print("Command read timed out: inspect device prompt and command completion.")
    except (OSError, ValueError) as exc:
        print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")

if __name__ == "__main__":
    main()

Verify the result

A configured association does not prove synchronized time. Allow protocol convergence and check status, reachability and authentication requirements. Remove only the lab-added server afterward.

15. DHCP Pool Configuration

LAB CHANGE

Build an isolated router DHCP pool with excluded addresses and verify service state.

Python · 15_dhcp_pool_configuration.py
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout

ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"

def device_for(host, username, password):
    return dict(device_type="cisco_ios", host=host, username=username,
                password=password, conn_timeout=10, auth_timeout=15,
                banner_timeout=20)

def read(connection, command):
    return connection.send_command(command, read_timeout=60)

def apply_lab(connection, commands):
    # Review the existing object before calling this helper.
    print("PROPOSED LAB COMMANDS:")
    print("\n".join(commands))
    if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
        print("Cancelled. No configuration sent.")
        return False
    print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
    print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
    return True

def task(connection, host):
    print("BEFORE DHCP:", read(connection, "show running-config | section ip dhcp"))
    print("Use a dedicated lab LAN on 203.0.113.0/24 with gateway .1. No existing overlapping pool or live clients may use it. Check service dhcp is enabled.")
    commands = ["ip dhcp excluded-address 203.0.113.1 203.0.113.20", "ip dhcp pool NJ_LAB_POOL", "network 203.0.113.0 255.255.255.0", "default-router 203.0.113.1", "exit"]
    if apply_lab(connection, commands):
        print(read(connection, "show ip dhcp pool NJ_LAB_POOL"))
        print(read(connection, "show ip dhcp binding"))

def main():
    host = input("Authorized lab management IP: ").strip()
    username = input("SSH username: ").strip()
    password = getpass("SSH password: ")
    secret = getpass("Enable secret (Enter if not needed): ")
    device = device_for(host, username, password)
    if secret:
        device["secret"] = secret
    try:
        with ConnectHandler(**device) as connection:
            if secret:
                connection.enable()
            task(connection, host)
    except NetmikoAuthenticationException:
        print("Authentication failed: check credentials, AAA and enable privileges.")
    except NetmikoTimeoutException:
        print("Connection failed: check address, routing, SSH port and device state.")
    except ReadTimeout:
        print("Command read timed out: inspect device prompt and command completion.")
    except (OSError, ValueError) as exc:
        print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")

if __name__ == "__main__":
    main()

Verify the result

Bindings appear only after a lab client requests an address and gateway/L2 connectivity works. DNS is intentionally omitted; add a valid lab resolver if needed. Restore the pool and exclusions to their baseline after practice.

Troubleshoot by failure stage

FailureInvestigation
Connection timeoutCheck routing, management address, SSH port and device state.
Authentication errorCheck login credentials, AAA and enable requirements.
ReadTimeoutCheck command completion and prompt detection before increasing read_timeout.
Configuration ValueErrorInspect returned output and partial changes. The error pattern covers common IOS errors, not every platform response.
Configuration accepted, service downVerify operational state: BGP peer, NTP synchronization or DHCP client behavior.
Backup file existsInspect content and secure it; a written file is not proof of a valid complete backup.

Frequently asked questions

What are the prerequisites?

Networking fundamentals, manual SSH and enough Python to follow functions, loops and exceptions. Run installation in a virtual environment.

Can Netmiko manage non-Cisco devices?

Yes, supported platforms use specific device_type values. Replace commands and validation for the actual platform; these 15 examples are Cisco-focused.

Are these production-ready scripts?

They are complete lab examples, not a production change platform. Production use needs reviewed inventory, host-key policy, a secret store, access controls, testing, recovery and change approval.

How are SSH connections closed?

Each script uses the ConnectHandler context manager, which handles disconnection when leaving the block, including normal exception paths.

What are alternatives or complementary tools?

Ansible can orchestrate repeatable tasks, NAPALM provides supported network APIs, and pyATS/Genie supports network-state parsing and validation.

Does the website execute the scripts?

No. Copy/download controls run in the browser. Python must run in your own authorized lab environment.

Continue from scripts to a validated workflow

Keep the same lab and inventory as you progress. Collect state, identify expected changes, deploy a reviewed configuration and rerun verification. Preserve the results so you can explain what succeeded and what still needs investigation.

Technical references: Netmiko documentation · Python virtual environments · EVE-NG platform requirements

Back to the main course page