A network of 400 devices managed by SSH and human fingers has 400 opportunities per change for a typo, and no authoritative record of what the configuration should be. Automation reframes the whole activity: the network's intended state lives in files (versioned in git, reviewed like code), tools render it into device configuration, and the running network is continuously compared against intent. The engineer's job moves up a level — from typing configuration to defining it.
The interfaces
- Screen-scraping SSH — netmiko and friends driving the CLI; the pragmatic floor, since it works on everything, and the ceiling is parsing text meant for humans.
- REST APIs — controllers and modern devices expose HTTP endpoints returning JSON: GET for state, POST/PUT/DELETE for change, authenticated by token. One curl away from scriptable.
- NETCONF (SSH, port 830) and RESTCONF (HTTP) — model-driven management: configuration and state defined in YANG models, transported as XML or JSON, with the transactional semantics the CLI never had — candidate datastores, validate, commit, rollback.
- Telemetry — instead of SNMP polling, devices stream structured counters and events (gRPC dial-out) to collectors; the read path catching up to the write path.
Python: the lingua franca
Python won networking automation because the libraries exist for every interface above and the language reads like the runbook it replaces. The canonical starter pattern — inventory in, per-device action, structured results out — is a dozen lines with netmiko, and the same shape scales to REST with requests or NETCONF with ncclient:
from netmiko import ConnectHandlerfor host in inventory: conn = ConnectHandler(device_type='cisco_xe', host=host, **creds) facts = conn.send_command('show ip ospf neighbor', use_textfsm=True) missing = expected[host] - {n['neighbor_id'] for n in facts} if missing: alert(host, missing)
The use_textfsm argument is the quiet hero: community templates parse CLI output into dictionaries, making even screen-scraping yield structured data. Structured data is the entire game — once state is dictionaries rather than text, comparing intent to reality is a set operation, not a squint.
Ansible: declarative and agentless
Where Python scripts are imperative (do these steps), Ansible playbooks are declarative (this state should exist) and idempotent — run twice, changed nothing the second time. Modules for every major NOS render YAML intent into device-specific operations over SSH or API, no agent on the device. Playbooks plus git plus a CI pipeline is the minimum viable network-as-code practice: intent reviewed in pull requests, rendered by pipeline, pushed on merge, with the diff as the change record. Configuration drift stops being discovered during outages and starts being flagged by the nightly compliance run.
- name: Ensure NTP servers hosts: ios_routers tasks: - cisco.ios.ios_ntp_global: servers: - {server: 10.255.0.1} - {server: 10.255.0.2} state: merged
Start where the risk is lowest and the pain is highest: read-only compliance checks (are all NTP/SNMP/AAA configs correct?), then config generation from templates, then closed-loop remediation once trust is earned. The skills stack cleanly — Python basics, one structured-data format (JSON/YAML), one library at a time — and every hour invested compounds across every device you will ever manage. It is also, not incidentally, the skill line separating the last decade's network engineer from the next one's.