Skip to content
Network Automation — NETCONF/YANG, Ansible, Python Netmiko, Intent-Based Networking & CI/CD

Network Automation — NETCONF/YANG, Ansible, Python Netmiko, Intent-Based Networking & CI/CD

DodaTech Updated Jun 20, 2026 10 min read

Network automation replaces manual SSH sessions to network devices with programmable, version-controlled, and testable workflows — instead of typing configure terminal on every router, you push validated configurations through NETCONF, Ansible, or Python scripts that guarantee consistency across thousands of devices.

What You’ll Learn

  • NETCONF and YANG for structured network data modeling and operations
  • RESTCONF for RESTful network device access
  • Ansible network automation with ios_config, vyos, and junos modules
  • Python automation using Netmiko and NAPALM for multi-vendor support
  • Intent-based networking (IBN) concepts
  • Network configuration testing with Batfish
  • CI/CD pipelines with GitOps for network config management
  • Monitoring integration for closed-loop remediation

Why Network Automation Matters

Manual network configuration is the leading cause of outages — a single typo in a BGP configuration or ACL rule can bring down an entire data center. Network automation eliminates human error, enforces consistent configurations, and enables rapid change at scale. Modern networks at companies like Google, Meta, and Netflix operate with fully automated change pipelines.

Doda Browser uses network automation-inspired patterns for managing its proxy configuration across multiple regions. Durga Antivirus Pro applies intent-based checking to validate that security rules are correctly enforced across distributed networks.

Learning Path

    flowchart LR
  A["Network Protocols"] --> B["Network Automation<br/>You are here"]
  B --> C["NETCONF/YANG"]
  B --> D["Ansible for Network"]
  B --> E["Python Netmiko/NAPALM"]
  C --> F["CI/CD & GitOps"]
  D --> F
  E --> F
  style B fill:#f90,color:#fff
  

NETCONF and YANG

NETCONF is an IETF protocol (RFC 6241) for installing, manipulating, and deleting network device configurations. YANG (RFC 7950) is the data modeling language that defines the structure of NETCONF data.

NETCONF Operations

OperationDescription
<get>Retrieve running config and state data
<get-config>Retrieve specified configuration datastore
<edit-config>Edit configuration (merge, replace, create, delete)
<copy-config>Copy configuration between datastores
<delete-config>Delete a configuration datastore
<lock> / <unlock>Lock configuration to prevent concurrent changes
<commit>Commit candidate configuration to running
<discard-changes>Discard uncommitted candidate changes

YANG Data Model Example

module example-vlan {
  namespace "urn:example:vlan";
  prefix vlan;

  container vlans {
    list vlan {
      key "vlan-id";
      leaf vlan-id {
        type uint16 {
          range "1..4094";
        }
      }
      leaf name {
        type string;
      }
      leaf status {
        type enumeration {
          enum active;
          enum suspended;
        }
        default active;
      }
    }
  }
}

NETCONF RPC Example

<!-- Get all VLANs from device -->
<rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <get>
    <filter type="subtree">
      <vlans xmlns="urn:example:vlan">
        <vlan>
          <vlan-id/>
          <name/>
          <status/>
        </vlan>
      </vlans>
    </filter>
  </get>
</rpc>

RESTCONF

RESTCONF (RFC 8040) provides a RESTful HTTP interface to NETCONF datastores. Use standard HTTP methods to manipulate YANG-defined data.

GET /restconf/data/example-vlan:vlans HTTP/1.1
Host: 192.168.1.1
Accept: application/yang-data+json

POST /restconf/data/example-vlan:vlans HTTP/1.1
Content-Type: application/yang-data+json
{
  "vlan": [
    {
      "vlan-id": 100,
      "name": "users",
      "status": "active"
    }
  ]
}
import requests
import json

url = "https://192.168.1.1/restconf/data/example-vlan:vlans"
headers = {
    "Accept": "application/yang-data+json",
    "Content-Type": "application/yang-data+json"
}
auth = ("admin", "password")

response = requests.get(url, headers=headers, auth=auth, verify=False)
print(json.dumps(response.json(), indent=2))

Expected output:

{
  "example-vlan:vlans": {
    "vlan": [
      {"vlan-id": 100, "name": "users", "status": "active"},
      {"vlan-id": 200, "name": "servers", "status": "active"}
    ]
  }
}

Ansible for Network Automation

Ansible is agentless — it connects to network devices via SSH or API and pushes configuration using specialized network modules.

Inventory File

# inventory/networks.yml
routers:
  hosts:
    router01:
      ansible_host: 192.168.1.1
      ansible_network_os: ios
    router02:
      ansible_host: 192.168.1.2
      ansible_network_os: vyos

switches:
  hosts:
    switch01:
      ansible_host: 192.168.1.10
      ansible_network_os: junos

Playbook — Cisco IOS Configuration

---
- name: Configure Cisco VLANs
  hosts: routers
  gather_facts: false
  tasks:
    - name: Configure VLAN 100
      cisco.ios.ios_config:
        lines:
          - vlan 100
          - name users
        parents: "interface GigabitEthernet0/1"
      
    - name: Configure OSPF
      cisco.ios.ios_config:
        lines:
          - router ospf 1
          - network 10.0.0.0 0.255.255.255 area 0
        save_when: modified
      
    - name: Verify configuration
      cisco.ios.ios_command:
        commands:
          - show ip ospf neighbor
          - show vlan brief
      register: output

    - name: Display OSPF neighbors
      debug:
        var: output.stdout_lines

Expected output:

TASK [Display OSPF neighbors] **********************
ok: [router01] => {
    "output.stdout_lines": [
        ["Neighbor ID     Pri   State"],
        ["10.0.0.2         1   FULL/BDR"],
        ["10.0.0.3         1   FULL/DR"]
    ]
}

Playbook — VyOS Configuration

- name: Configure VyOS router
  hosts: router02
  tasks:
    - name: Set interface address
      vyos.vyos.vyos_config:
        lines:
          - set interfaces ethernet eth0 address 10.0.1.1/24
          - set service ssh port 22
    
    - name: Configure BGP
      vyos.vyos.vyos_config:
        src: bgp_config.j2

Playbook — Juniper Configuration

- name: Configure Juniper switch
  hosts: switches
  tasks:
    - name: Configure interfaces
      junipernetworks.junos.junos_config:
        lines:
          - set interfaces ge-0/0/0 unit 0 family inet address 10.0.0.1/24
          - set vlans users vlan-id 100
        comment: "Automated config push via Ansible"

Python Network Automation with Netmiko

Netmiko simplifies SSH connections to network devices. It handles authentication, SSH keys, and output parsing consistently across vendors.

from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoTimeoutException, NetmikoAuthenticationException

device = {
    "device_type": "cisco_ios",
    "host": "192.168.1.1",
    "username": "admin",
    "password": "password",
    "port": 22,
}

try:
    connection = ConnectHandler(**device)
    connection.enable()
    
    # Send commands
    output = connection.send_command("show ip interface brief")
    print("=== Interface Brief ===")
    print(output)
    
    # Configure VLAN
    config_commands = [
        "vlan 100",
        "name users",
        "interface GigabitEthernet0/1",
        "switchport access vlan 100",
    ]
    output = connection.send_config_set(config_commands)
    print("\n=== Config Output ===")
    print(output)
    
    # Save config
    connection.save_config()
    
    connection.disconnect()

except NetmikoTimeoutException:
    print("ERROR: Device unreachable")
except NetmikoAuthenticationException:
    print("ERROR: Authentication failed")

Expected output:

=== Interface Brief ===
Interface              IP-Address      OK? Method Status
GigabitEthernet0/0     192.168.1.1     YES NVRAM  up
GigabitEthernet0/1     unassigned      YES NVRAM  up

=== Config Output ===
config term
Enter configuration commands, one per line.
router(config)#vlan 100
router(config-vlan)#name users
router(config-vlan)#interface GigabitEthernet0/1
router(config-if)#switchport access vlan 100

NAPALM — Multi-Vendor Abstraction

NAPALM (Network Automation and Programmability Abstraction Layer with Multivendor support) provides a unified API across vendors.

from napalm import get_network_driver

driver = get_network_driver("ios")
device = driver("192.168.1.1", "admin", "password")
device.open()

# Get facts
facts = device.get_facts()
print(f"Model: {facts['model']}")
print(f"Serial: {facts['serial_number']}")
print(f"OS Version: {facts['os_version']}")

# Get interfaces
interfaces = device.get_interfaces()
for iface, details in interfaces.items():
    print(f"{iface}: {'UP' if details['is_up'] else 'DOWN'} "
          f"({details['mac_address']})")

# Get BGP neighbors
bgp = device.get_bgp_neighbors()
print(f"\nBGP Neighbors: {len(bgp['global']['peers'])}")

device.close()

Intent-Based Networking

IBN means you declare what the network should do, not how to configure it.

# intent-policy.yaml
policy_name: "isolate-pci-segment"
description: "All PCI traffic must be isolated from general traffic"
rules:
  - match:
      source_tags: ["pci-server"]
      destination: "any"
    action: "permit"
    constraints:
      - vrf: "pci-vrf"
      - encrypted: true
  - match:
      source: "any"
      destination_tags: ["pci-server"]
      action: "deny"
      unless:
        - source_tags: ["pci-app"]
        - protocol: "https"

IBN tools continuously validate that the actual network state matches the intent policy.

Testing with Batfish

Batfish statically analyzes network configurations to detect errors before deployment — no live devices needed.

from pybatfish.client.session import Session

bf = Session(host="localhost")
bf.set_network("production")
bf.set_snapshot("snapshots/latest")

# Check for BGP session issues
bgp_issues = bf.q.bgpSessionStatus().answer().frame()
print("BGP sessions not established:")
print(bgp_issues[bgp_issues["Status"] != "ESTABLISHED"])

# Detect unreachable routes
unreachable = bf.q.routes().answer().frame()
unreachable = unreachable[unreachable["Ambiguous"]]
print(f"\nAmbiguous routes: {len(unreachable)}")

# Check ACL reachability
acl_result = bf.q.searchFilters(
    path="acl_line",
    filters="deny any any"
).answer().frame()

Expected output:

BGP sessions not established:
        Node     Interface           VRF   Status
0  router01  GigabitEthernet0/1  default  IDLE

Ambiguous routes: 0

CI/CD for Network Configs (GitOps)

Network GitOps treats configuration as code — all changes go through pull requests with automated validation.

# .github/workflows/network-ci.yml
name: Network Config Validation
on:
  pull_request:
    paths:
      - 'configs/**'
      - 'ansible/**'

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Syntax check Ansible playbooks
        run: ansible-playbook --syntax-check site.yml
      
      - name: Validate with Batfish
        run: |
          docker run --rm -d -p 9997:9997 batfish/batfish
          python scripts/batfish_validate.py
      
      - name: Validate YANG models
        run: |
          pip install pyang
          pyang --lint models/*.yang
      
      - name: Check VLAN consistency
        run: python scripts/check_vlan_overlap.py

Monitoring Integration

Network automation closes the loop — monitoring detects issues, automation remediates them.

# Simulate closed-loop remediation
def check_and_fix_bgp(device_ip):
    from netmiko import ConnectHandler
    
    device = {"device_type": "cisco_ios", "host": device_ip,
              "username": "monitor", "password": "secret"}
    
    conn = ConnectHandler(**device)
    bgp_status = conn.send_command("show ip bgp summary")
    
    if "down" in bgp_status.lower():
        print(f"⚠ BGP neighbor down on {device_ip}, attempting reset...")
        conn.send_command("clear ip bgp *")
        print(f"✓ BGP reset on {device_ip}")
        # Log to monitoring system
        return {"action": "bgp_reset", "device": device_ip, "status": "completed"}
    return {"action": "none", "device": device_ip, "status": "healthy"}

result = check_and_fix_bgp("192.168.1.1")
print(f"Remediation: {result['action']}")

Common Errors

1. NETCONF connection refused — port 830

The device doesn’t have NETCONF enabled. Enable it: netconf-yang set server enable or ip ssh netconf depending on vendor.

2. YANG model not installed on device

You defined a model but the device doesn’t support it. Check with show yang models | include example-vlan. You may need to install the model via RPM/JAM package.

3. Ansible “invalid module” for network operations

The cisco.ios.ios_config module requires the cisco.ios collection. Install it: ansible-galaxy collection install cisco.ios. Similar for vyos.vyos and junipernetworks.junos.

4. Netmiko authentication failure with SSH key

Netmiko defaults to password auth. Use use_keys=True and provide key_file= for SSH key authentication.

5. Batfish snapshot parsing errors

Config files must be in the correct directory structure: snapshots/<name>/configs/. Use the correct filename convention (e.g., router01.cfg).

6. CI/CD pipeline push causes outage

Automated pushes can break routing. Implement pre-checks (ping tests, BGP status checks) before applying config and post-checks after. Use canary deployments.

7. RESTCONF HTTPS certificate validation

Self-signed certificates on lab devices cause SSL errors. Use requests.packages.urllib3.disable_warnings() in dev, but never skip verification in production.

Practice Questions

  1. What is the difference between NETCONF and RESTCONF? NETCONF uses XML-based RPCs over SSH port 830 with transaction support (candidate/commit). RESTCONF uses HTTP methods (GET/POST/PUT/DELETE) over HTTPS port 443 with JSON or XML and is simpler to use from web applications.

  2. Why use NAPALM instead of direct Netmiko? NAPALM provides a vendor-agnostic API — the same get_facts() call works on Cisco, Juniper, Arista, and more. Netmiko is vendor-specific but gives more granular control over SSH sessions.

  3. How does Batfish help prevent network outages? Batfish analyzes configuration files offline to detect issues like BGP session mismatches, unreachable routes, ACL gaps, and VLAN conflicts before any change is deployed.

  4. What is intent-based networking? IBN replaces imperative configuration (set this IP, enable OSPF) with declarative policies (this segment must be isolated and encrypted). The network translates intent into device configs and continuously validates compliance.

  5. How does GitOps apply to network automation? Network configs are stored in Git. Changes require pull requests with automated validation (syntax check, Batfish analysis, VLAN overlap check). Merged changes are automatically deployed to devices.

Challenge: Design a GitOps-based network automation pipeline for a three-tier data center network (spine, leaf, border). Include: (1) directory structure for configs per device type, (2) Ansible playbook structure for each layer, (3) Batfish validation step, (4) GitHub Actions workflow with pre-post deployment checks, (5) rollback strategy if connectivity is lost after deployment.

FAQ

Do I need to know programming for network automation?
Yes — Python is the most common language. Understanding data structures (lists, dicts), functions, and error handling is essential. Ansible (YAML) reduces the programming barrier but complex logic still needs Python.
What is the best starting point for network automation?
Start with Ansible for simple configuration tasks, then add Netmiko for more complex Python scripts. NETCONF/YANG is the most powerful but has the steepest learning curve.
Can I automate legacy devices that don’t support NETCONF?
Yes — Netmiko and NAPALM work over SSH and support hundreds of legacy device types. SNMP can also be used for monitoring and basic configuration.
How do I manage secrets in network automation?
Use Ansible Vault, HashiCorp Vault, or your CI/CD platform’s secrets manager. Never hardcode passwords in playbooks or inventory files.
What is the role of Terraform in network automation?
Terraform providers (Cisco ACI, Juniper Junos) can manage network device state declaratively, similar to how Terraform manages cloud resources. It’s complementary to Ansible.
How do I test network changes without physical devices?
Use virtual devices (Cisco IOSv, Juniper vMX, Arista vEOS) or containerized network emulators (Containerlab, GNS3, EVE-NG). Batfish validates configs without running devices.

Try It Yourself

Simulate a multi-vendor network inventory check with Python:

# Simulate network device inventory automation
devices = [
    {"hostname": "core-01", "vendor": "cisco", "model": "ASR1001", "os": "IOS-XE 17.3"},
    {"hostname": "edge-01", "vendor": "juniper", "model": "MX204", "os": "Junos 21.2"},
    {"hostname": "spine-01", "vendor": "arista", "model": "DCS-7280", "os": "EOS 4.28"},
]

def check_version(device):
    os_min_versions = {
        "cisco": "17.0",
        "juniper": "20.0",
        "arista": "4.25",
    }
    min_ver = os_min_versions.get(device["vendor"], "0.0")
    device_ver = device["os"].split(" ")[-1]
    compliant = device_ver >= min_ver
    return compliant, min_ver

print("Network Device Inventory Report")
print("=" * 50)
for device in devices:
    compliant, min_ver = check_version(device)
    status = "✓ COMPLIANT" if compliant else "✗ NON-COMPLIANT"
    print(f"{device['hostname']:15} {device['vendor']:10} "
          f"v{device['os'].split()[-1]:8} {status}")

Expected output:

Network Device Inventory Report
==================================================
core-01         cisco      v17.3     ✓ COMPLIANT
edge-01         juniper    v21.2     ✓ COMPLIANT
spine-01        arista     v4.28     ✓ COMPLIANT

What’s Next

TutorialWhat You’ll Learn
VoIP Basics GuideVoice network fundamentals
RESTful APIsAPI design patterns for network automation
Docker FundamentalsContainerized network tools

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Updated 2026-06-20.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro