Network Automation — NETCONF/YANG, Ansible, Python Netmiko and CI/CD
In this tutorial, you'll learn about Network Automation. We cover key concepts, practical examples, and best practices.
Network automation replaces manual device-by-device configuration with programmable, version-controlled, and testable workflows that reduce errors, enforce compliance, and accelerate changes across hundreds or thousands of network devices.
What You'll Learn
You will learn how to model network configurations with YANG, push and retrieve configurations via NETCONF and RESTCONF, automate multi-vendor tasks with Ansible and Python Netmiko, validate network state with Batfish, and implement GitOps CI/CD pipelines for infrastructure changes.
Why It Matters
Manual network configuration causes 60-80% of network outages according to industry studies. Automation eliminates typos, ensures consistency, enables peer review through version control, and reduces change deployment time from hours to minutes. Every major telecom operator and cloud provider uses network automation at scale.
Real-World Use
A tier-2 ISP with 500 routers across 50 points of presence needs to add a new BGP community to every router. A manual rollout would take two engineers three weeks with high risk of mistyped commands. An Ansible playbook running NETCONF edits completes the change on all 500 routers in under 10 minutes with zero errors.
Main Content
The Network Automation Stack
flowchart TD
A[Git Repository] -->|git push| B[CI/CD Pipeline]
B --> C[Validation Layer]
C -->|Batfish| D[Pre-change Validation]
D -->|Pass| E[Deployment]
D -->|Fail| F[Reject + Notify]
E -->|Ansible / Python| G[Network Devices]
G -->|Post-change Telemetry| H[Monitoring]
H -->|Validation| I[Update Inventory]
Expected behavior: Every change starts in Git. A CI/CD pipeline picks up the change, runs pre-validation with Batfish, deploys via Ansible or Python, and monitors the result. Any validation failure rejects the change and notifies the team.
YANG Data Modeling
YANG (Yet Another Next Generation) is a data modeling language used to describe configuration and state data for network devices. Models are defined in RFCs (standard YANG) or by vendors (vendor YANG).
YANG model example for an interface:
module example-interface {
yang-version 1.1;
namespace "urn:example:interface";
prefix if;
container interfaces {
list interface {
key "name";
leaf name {
type string;
}
leaf enabled {
type boolean;
default true;
}
leaf mtu {
type uint16 {
range "68..9216";
}
default 1500;
}
container ipv4 {
leaf address {
type string;
}
leaf netmask {
type string;
}
}
}
}
}
Expected behavior: This YANG model defines an interface with name, enabled status, MTU, and IPv4 address fields. NETCONF clients use this model to construct valid XML configuration data. A device that implements this model validates incoming config against the model before applying it.
NETCONF Operations
NETCONF uses XML-encoded RPCs over SSH (port 830) to manage device configuration.
| Operation | Purpose |
|---|---|
<get> |
Retrieve running configuration and state data |
<get-config> |
Retrieve a specific configuration datastore |
<edit-config> |
Modify a configuration datastore |
<copy-config> |
Replace one datastore with another |
<delete-config> |
Delete a configuration datastore |
<lock> / <unlock> |
Lock a datastore to prevent concurrent changes |
<commit> |
Commit a candidate configuration to running |
<discard-changes> |
Discard candidate configuration changes |
<validate> |
Validate a configuration against YANG models |
NETCONF XML RPC Example
<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<edit-config>
<target>
<running/>
</target>
<config>
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<name>GigabitEthernet0/1</name>
<description>WAN Link to Provider</description>
<enabled>true</enabled>
<ipv4 xmlns="urn:ietf:params:xml:ns:yang:ietf-ip">
<address>
<ip>203.0.113.1</ip>
<prefix-length>30</prefix-length>
</address>
</ipv4>
</interface>
</interfaces>
</config>
</edit-config>
</rpc>
Expected behavior: The device receives this RPC, validates the XML against its supported YANG models, applies the interface configuration, and returns an <ok> response. Any validation error returns an <rpc-error> with details about the failure.
Ansible Network Automation
Ansible uses idempotent modules to manage network devices without requiring agents on the devices.
---
- name: Configure BGP on all core routers
hosts: core_routers
gather_facts: no
vars:
bgp_as: 64500
bgp_peer: 203.0.113.2
tasks:
- name: Enable BGP feature
cisco.ios.ios_bgp:
config:
bgp_as: "{{ bgp_as }}"
neighbors:
- neighbor: "{{ bgp_peer }}"
remote_as: 64501
description: "Primary upstream peer"
activate: true
address_family:
- afi: ipv4
safi: unicast
address_family:
- afi: ipv4
safi: unicast
networks:
- prefix: "192.0.2.0"
masklen: 24
state: merged
Expected behavior: Ansible connects to each router in the core_routers group, applies the BGP configuration, and reports changed/success/failed per device. The state: merged directive ensures the configuration is added without removing existing BGP settings.
Python Netmiko for Multi-Vendor Automation
Netmiko is a Python library that simplifies SSH connections to network devices from different vendors.
from netmiko import ConnectHandler
import json
devices = [
{
"device_type": "cisco_ios",
"host": "192.168.1.1",
"username": "admin",
"password": "secure_password",
},
{
"device_type": "juniper_junos",
"host": "192.168.1.2",
"username": "admin",
"password": "secure_password",
},
]
commands = [
"snmp-server community public RO",
"snmp-server location DataCenter-NYC",
"snmp-server contact noc"@company".com",
]
for device in devices:
try:
connection = ConnectHandler(**device)
connection.enable()
output = connection.send_config_set(commands)
print(f"Configured {device['host']} successfully")
connection.disconnect()
except Exception as e:
print(f"Failed on {device['host']}: {str(e)}")
Expected output:
Configured 192.168.1.1 successfully
Configured 192.168.1.2 successfully
If a device is unreachable or authentication fails, the script reports the specific error without halting the entire batch.
Intent-Based Networking with Batfish
Batfish analyzes network configuration files to detect inconsistencies before deployment.
from pybatfish.client.session import Session
bf = Session()
bf.set_network("company-network")
bf.set_snapshot("snapshots/latest")
# Verify BGP sessions have correct peer configurations
bgp_status = bf.q.bgpSessionStatus().answer()
print(bgp_status.to_pandas())
# Check that no interface has an MTU mismatch
mtu_mismatch = bf.q.interfaceProperties(
properties="mtu"
).answer()
print(mtu_mismatch.to_pandas())
# Validate ACLs permit expected traffic
acl_reach = bf.q.searchFilters(
filters="ACL-CORE-IN",
headers=HeaderConstraints(srcIps="10.0.0.0/8", dstIps="192.168.0.0/16"),
).answer()
print(acl_reach.to_pandas())
Expected output: Batfish returns pandas DataFrames showing each BGP session status (ESTABLISHED, IDLE, ACTIVE), interface MTU values per device, and whether the specified ACL permits or denies the test traffic. This catches configuration errors that would otherwise cause production outages.
GitOps CI/CD Pipeline for Network Configs
A GitOps approach stores all network configurations in Git and uses a CI/CD pipeline to validate and deploy changes.
# .github/workflows/network-deploy.yml
name: Network Configuration Deployment
on:
push:
branches: [main]
paths:
- "configs/**"
- "ansible/**"
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate with Batfish
run: |
pip install pybatfish
python scripts/validate_configs.py
- name: Ansible syntax check
run: |
ansible-playbook --syntax-check playbooks/site.yml
deploy:
needs: validate
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Deploy configurations
run: |
ansible-playbook playbooks/site.yml -i inventory/production.yml
- name: Post-deployment verification
run: |
python scripts/verify_deployment.py
python scripts/notify_team.py
Expected behavior: Merging a pull request to main triggers the pipeline. The validate job runs Batfish checks and Ansible syntax validation. If validation passes, the deploy job applies changes to production devices and runs post-deployment verification. Any failure stops the pipeline and notifies the team.
Automation Tools Comparison
| Tool | Protocol | Language | Best For |
|---|---|---|---|
| NETCONF/YANG | XML over SSH | XML, YANG | Model-driven, standardized config management |
| RESTCONF | HTTP/JSON | JSON, YANG | Web-friendly NETCONF alternative |
| Ansible | SSH, NETCONF, API | YAML | Multi-vendor, idempotent, agentless |
| Python Netmiko | SSH | Python | Custom scripts, complex logic, legacy devices |
| NAPALM | NETCONF, SSH | Python | Multi-vendor abstraction layer |
| Batfish | Config analysis | Python | Pre-deployment validation |
| Terraform | API | HCL | Cloud networking, infrastructure as code |
Common Errors
1. YANG Model Version Mismatch
A NETCONF edit fails because the device runs an older YANG model revision than the client uses. Always verify revision-date in the YANG module and check the device-supported models with <get-schema>.
2. Ansible Become vs. Enable Mode
Cisco devices require enable mode for privileged commands. Ansible's become: yes does not trigger enable on IOS devices. Use connection: network_cli and <a href="/devops/ansible/">Ansible</a>_connection: <a href="/devops/ansible/">ansible</a>.netcommon.network_cli with the correct authorize and auth_pass settings.
3. Missing Idempotency
Ansible playbooks that use command or raw modules instead of dedicated ios_config or netconf_config modules are not idempotent. Running them twice may duplicate configuration lines or cause errors.
4. Hardcoded Credentials
Storing device passwords in Ansible variables or Python scripts exposes credentials in version control. Use Ansible Vault, environment variables, or a secrets manager like HashiCorp Vault.
5. No Dry-Run Mode
Deploying without a --check or --dry-run option risks applying broken configurations to production devices. Always run Ansible with --check --diff first and validate the proposed changes.
6. Ignoring Error Handling in Python Scripts
A Netmiko script that crashes on the first device failure leaves remaining devices unconfigured. Wrap device operations in try/except blocks and log errors per device instead of aborting the entire batch.
7. CI/CD Secrets Exposure
Pipeline logs that print device configurations or debug output may leak passwords and SNMP community strings. Set no_log: true on sensitive Ansible tasks and mask secrets in CI/CD variable configuration.
Practice Questions
Question 1
What is the difference between NETCONF and RESTCONF?
Answer: NETCONF uses XML-encoded RPCs over SSH (port 830) with datastore operations (candidate, running, startup). RESTCONF uses HTTP/JSON with RESTful API patterns (GET, POST, PUT, PATCH, DELETE) over HTTPS (port 443) and is compatible with web development tools.
Question 2
How does Ansible achieve idempotency in network configuration?
Answer: Ansible network modules (like ios_config, netconf_config) compare the desired state from the playbook against the current device state. If the configuration already matches, no change is made. Only lines that differ from the running config are applied.
Question 3
What is the role of Batfish in a network automation pipeline?
Answer: Batfish parses device configuration files and simulates the network to detect inconsistencies, routing loops, ACL misconfigurations, and BGP peering errors before the configuration is deployed to production devices.
Question 4
Why is GitOps important for network automation?
Answer: GitOps provides version control, peer review through pull requests, an audit trail of all changes, and a single source of truth for network configurations. Combined with CI/CD pipelines, it ensures every change is validated, tested, and deployable.
Challenge Question
Design a complete GitOps automation pipeline for a network with 200 routers from three vendors (Cisco, Juniper, Arista). Include YANG models for interface configuration, Ansible playbooks for BGP deployment, Batfish validation for pre-change analysis, and CI/CD workflows for automated deployment. Identify how you would handle vendor-specific configuration differences within a unified pipeline.
Mini Project
Automate Interface Configuration Across Multiple Devices
Write a Python script that uses Netmiko to configure interface descriptions, enable SNMP, and verify reachability across five simulated routers.
Requirements:
- Python 3.10+ with Netmiko installed (
pip install netmiko) - Five device definitions in a JSON inventory file
- Ability to roll back changes if verification fails
Steps:
- Create an inventory file
inventory.json:
[
{"device_type": "cisco_ios", "host": "192.168.1.11", "username": "admin", "password": "admin123"},
{"device_type": "cisco_ios", "host": "192.168.1.12", "username": "admin", "password": "admin123"},
{"device_type": "cisco_ios", "host": "192.168.1.13", "username": "admin", "password": "admin123"}
]
Write a Python script that:
- Reads the inventory
- Connects to each device and applies interface descriptions
- Enables SNMP read-only access
- Verifies the configuration by retrieving the running config and checking specific lines
- Logs success or failure per device
- Supports a
--rollbackflag to revert changes
Run the script, then verify the changes are applied correctly
Expected output:
Processing device 192.168.1.11: SUCCESS - Interface config applied
Processing device 192.168.1.12: SUCCESS - Interface config applied
Processing device 192.168.1.13: FAILED - Authentication error, skipping
Summary: 2 succeeded, 1 failed, 0 skipped
The failed device is logged for manual investigation while the successful devices continue normally. This pattern mirrors production Ansible and Python automation used by network operations teams at scale.
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro