Sandbox99 Chronicles

NETCONF Fundamentals: Automating Network Configuration with Confidence

NETCONF overview

Written by Jose Mendez

Hi, I’m Jose Mendez, the creator of sandbox99.cc. with a passion for technology and a hands-on approach to learning, I’ve spent more than fifteen years navigating the ever-evolving world of IT.

Published Jul 26, 2025 | Last updated on Jul 27, 2025 at 5:35AM

Reading Time: 9 minutes

Introduction

In today’s fast-paced IT landscape, network engineers are shifting from manual, CLI-based management to automated, programmable solutions. At the heart of this transformation is NETCONF (Network Configuration Protocol) — a powerful protocol designed to automate network device configuration and state management.

If you’re a network professional aiming to modernize your workflow, learning NETCONF is a great first step. In this post, we’ll explore NETCONF’s architecture, operations, security, tooling, and real-world usage in a vendor-neutral way.

What is NETCONF?

NETCONF is a network management protocol standardized by the IETF, designed specifically for managing device configurations and retrieving operational state. It enables reliable and structured communication between a network automation tool (client) and a network device (server), typically using XML for encoding and SSH for secure transport.

Key features of NETCONF:

  • 🔧 Programmatic configuration of network devices
  • 📦 Structured XML payloads for consistency
  • 🔁 Full lifecycle management (read, write, commit, rollback)
  • 💥 Atomic transactions to prevent partial updates

History and Standardization

NETCONF originated from the need to replace fragile CLI parsing and limited SNMP-based configuration. It was developed under the IETF Network Working Group and first published as RFC 4741 in 2006. This version was later replaced by the current standard, RFC 6241, which introduced improvements and clarified implementation requirements.

Other notable RFCs related to NETCONF include:

Together, these standards form a solid foundation for modern, model-driven network management.

NETCONF Architecture

At its core, NETCONF follows a client-server model:

  • The client is typically an automation tool or script.
  • The server is a network device (router, switch, firewall) exposing a NETCONF agent.

Communication happens over:

  • SSH (port 830) – secure and default transport for NETCONF
  • TLS (optional) – for environments requiring certificate-based authentication

NETCONF sessions use a Remote Procedure Call (RPC) pattern, where the client sends XML-based requests and the server replies with results or errors.

NETCONF RPCs (Remote Procedure Calls)

NETCONF uses Remote Procedure Calls (RPCs) to allow clients to interact with network devices. Each RPC is an XML-encoded message sent over the secure NETCONF session, and the server responds with structured data or error details.

  • Client-Server Architecture: NETCONF operates on a client-server model where clients initiate RPC requests to network devices (servers), which process these requests and return corresponding RPC responses.
  • Message Exchange: Communication occurs through XML-based RPC messages sent by clients (for configuration retrieval or modification), with servers providing XML-encoded responses.
  • RPC Operations: Each message specifies an operation such as get-config (configuration retrieval) or edit-config (configuration modification). Devices respond with either the requested data, confirmation, or error details.

Here are some commonly used NETCONF RPCs:

RPC CommandPurpose
<get>Retrieves current operational (non-config) state of the device.
<get-config>Fetches the configuration data from a specified datastore.
<edit-config>Sends configuration changes to the target datastore (e.g., running).
<copy-config>Copies configuration between datastores or loads a new config from a file or source.
<delete-config>Deletes the contents of a specified datastore (e.g., startup).
<lock> / <unlock>Locks a datastore to prevent concurrent edits; ensures atomic operations.
<commit>Commits changes in the candidate datastore to the running config.
<discard-changes>Cancels uncommitted changes in the candidate datastore.
<validate> (optional)Validates a configuration before committing it.
<close-session>Gracefully closes the NETCONF session.

YANG: The Data Modeling Language

NETCONF doesn’t work alone — it relies on YANG (Yet Another Next Generation), a high-level modeling language for defining structured configuration data.

Modern network devices are increasingly complex, featuring numerous configuration options and capabilities. This complexity requires a standardized approach for description and management. Yet Another Next Generation (YANG) serves as a data modeling language that works alongside NETCONF to define how configuration and operational data is structured on network devices. Together, YANG and NETCONF provide a consistent, standardized framework for representing device capabilities and configuration parameters, enabling unified management across different vendors and device types.

YANG organizes data in a hierarchical structure similar to XML. This hierarchical approach allows network operators to model complex configuration and state data in an organized, predictable manner—essential for automation and network engineering solutions built on this format.

Key YANG Structure Elements:

  • Modules: Collections of related data definitions representing configuration data, operational data, or both. Modules define the overall structure including containers, lists, and leaves.
  • Containers: Logical groupings of related data elements, such as interface configurations or routing protocol settings.
  • Lists: Ordered collections of similar data items, such as interface inventories or routing tables.
  • Leaf Nodes: Individual data points within containers or lists that store actual values, such as IP addresses or interface names.
  • RPCs and Notifications: YANG models can define remote procedure calls and notifications representing device actions (like ping or traceroute commands) or asynchronous device messages.

YANG data models and their elements can be visualized as hierarchical tree structures.

Using these fundamental elements, YANG models standardize how network devices expose their configuration and operational data. Each model defines the schema for structuring and accessing device data through NETCONF. The ecosystem includes both industry-standard YANG models (such as IETF models for common network concepts like IPv4/IPv6 addressing) and vendor-specific models from companies like Cisco that describe device-specific capabilities while maintaining the same structural framework. Network devices typically implement a combination of both standard and vendor-specific models, providing comprehensive coverage of device capabilities.

Why YANG matters:

  • 🧩 It defines what can be configured and how
  • 🛠️ It enables tools to auto-generate forms, APIs, and code
  • 🔄 It provides vendor-neutral and reusable models

Security Considerations

Security is built into NETCONF by design:

SSH-based transport – Ensures encrypted communication and authentication
Role-Based Access Control (RBAC) – Prevents unauthorized commands
Datastore locking – Prevents simultaneous conflicting edits
Auditing – NETCONF actions can be logged and monitored
Schema enforcement – Ensures only valid, structured data is accepted

Administrators should enforce strong SSH credentials, disable unused transport methods, and regularly audit NETCONF logs for anomalies.

Tooling and Libraries

Several tools make working with NETCONF easier:

🧰 Libraries & Clients

  • ncclient – Python library for NETCONF (ideal for scripting)
  • YDK (YANG Development Kit) – Auto-generates Python bindings from YANG models
  • ConfD – Model-driven management daemon with NETCONF support
  • Netopeer / Sysrepo – Open-source NETCONF server implementations

🧪 Lab Environments

  • Cisco DevNet Sandbox – Pre-built NETCONF labs
  • Juniper vLabs – Cloud-based JunOS devices
  • EVE-NG / GNS3 – Run NETCONF-enabled VM appliances

These environments are perfect for experimentation, learning, and building production-ready automation scripts.

NETCONF in Action

Note: I’m using Pop!_OS (based on Ubuntu 22.04) as my daily driver, so the instructions below are Linux-based. If you’re using Windows, the code will still run, but you’ll need to figure out how to install Python 3 and set up a virtual environment (which is considered best practice).

Step 1: Create your workspace folder and Python Virtual Environment (Optional)

mkdir ~/NETCONF-demo && cd NETCONF-demo
sudo apt install python3-venv
python3 -m venv netconf-env
source netconf-env/bin/activate

Step 2: Install python library

pip3 install ncclient

Step 3: Go to https://devnetsandbox.cisco.com/DevNet
– Sign up for free and look for NETCONF sandbox available
– Launch any sandbox with NETCONF and get the credentials
– By default NETCONF was already configured and running port 830

Step 4: Open any of your favorite editor (nano, vim, vscode) copy and paste this python code below.
– “Configure lines 10-12 with your devnetsandbox.cisco.com/DevNet account credentials.”

Here’s a quick Python example using ncclient to retrieve some valuable information of a device:

#! /usr/bin/env python3

from ncclient import manager
import socket
import xml.etree.ElementTree as ET
from xml.dom import minidom


# Global variables for NETCONF connection
HOST = "devnetsandboxiosxe.cisco.com"
USERNAME = "admin" # Create free lab in devnetsandbox.cisco.com/DevNet
PASSWORD = "12345" # Create free lab in devnetsandbox.cisco.com/DevNet


def parse_cisco_config(xml_data):
    """Parse Cisco NETCONF XML configuration into human-readable format"""
    try:
        # Parse the XML
        root = ET.fromstring(xml_data)
        
        # Define namespaces
        namespaces = {
            'native': 'http://cisco.com/ns/yang/Cisco-IOS-XE-native',
            'ietf-if': 'urn:ietf:params:xml:ns:yang:ietf-interfaces',
            'ietf-ip': 'urn:ietf:params:xml:ns:yang:ietf-ip'
        }
        
        print("=" * 60)
        print("CISCO ROUTER CONFIGURATION SUMMARY")
        print("=" * 60)
        
        # Basic device info
        native = root.find('.//native:native', namespaces)
        if native is not None:
            version = native.find('native:version', namespaces)
            hostname = native.find('native:hostname', namespaces)
            
            print(f"\n📋 DEVICE INFORMATION:")
            print(f"   IOS Version: {version.text if version is not None else 'Unknown'}")
            print(f"   Hostname: {hostname.text if hostname is not None else 'Unknown'}")
        
        # Interface information
        print(f"\n🔌 NETWORK INTERFACES:")
        interfaces = root.findall('.//ietf-if:interface', namespaces)
        
        for i, interface in enumerate(interfaces, 1):
            name = interface.find('ietf-if:name', namespaces)
            enabled = interface.find('ietf-if:enabled', namespaces)
            description = interface.find('ietf-if:description', namespaces)
            
            if name is not None:
                print(f"   Interface {i}: {name.text}")
                print(f"      Status: {'Enabled' if enabled is not None and enabled.text == 'true' else 'Disabled'}")
                
                if description is not None:
                    print(f"      Description: {description.text}")
                
                # IPv4 addresses
                ipv4_addresses = interface.findall('.//ietf-ip:address', namespaces)
                for addr in ipv4_addresses:
                    ip = addr.find('ietf-ip:ip', namespaces)
                    netmask = addr.find('ietf-ip:netmask', namespaces)
                    if ip is not None:
                        mask = f"/{netmask.text}" if netmask is not None else ""
                        print(f"      IP Address: {ip.text}{mask}")
                print()
        
        # User accounts
        print(f"🔐 USER ACCOUNTS:")
        usernames = native.findall('.//native:username', namespaces) if native is not None else []
        for user in usernames:
            name = user.find('native:name', namespaces)
            privilege = user.find('native:privilege', namespaces)
            if name is not None:
                priv_level = privilege.text if privilege is not None else "Unknown"
                print(f"   User: {name.text} (Privilege Level: {priv_level})")
        
        # Routing information
        print(f"\n🛣️  ROUTING PROTOCOLS:")
        if native is not None:
            # BGP
            bgp = native.find('.//native:bgp', namespaces)
            if bgp is not None:
                bgp_id = bgp.find('native:id', namespaces)
                print(f"   BGP AS: {bgp_id.text if bgp_id is not None else 'Configured'}")
            
            # OSPF
            ospf = native.find('.//native:router-ospf', namespaces)
            if ospf is not None:
                ospf_processes = ospf.findall('.//native:process-id', namespaces)
                for process in ospf_processes:
                    process_id = process.find('native:id', namespaces)
                    router_id = process.find('native:router-id', namespaces)
                    if process_id is not None:
                        rid = router_id.text if router_id is not None else "Not set"
                        print(f"   OSPF Process: {process_id.text} (Router-ID: {rid})")
            
            # EIGRP
            eigrp = native.find('.//native:router-eigrp', namespaces)
            if eigrp is not None:
                as_num = eigrp.find('.//native:autonomous-system', namespaces)
                print(f"   EIGRP AS: {as_num.text if as_num is not None else 'Configured'}")
        
        # VRFs
        print(f"\n🌐 VRF INSTANCES:")
        vrfs = native.findall('.//native:vrf/native:definition', namespaces) if native is not None else []
        for vrf in vrfs:
            vrf_name = vrf.find('native:name', namespaces)
            if vrf_name is not None:
                print(f"   VRF: {vrf_name.text}")
        
        # AAA Configuration
        print(f"\n🔒 AAA CONFIGURATION:")
        if native is not None:
            aaa = native.find('native:aaa', namespaces)
            if aaa is not None:
                print("   AAA new-model: Enabled")
                
                # TACACS servers
                tacacs_servers = native.findall('.//native:tacacs/native:server', namespaces)
                for server in tacacs_servers:
                    server_name = server.find('native:name', namespaces)
                    if server_name is not None:
                        print(f"   TACACS+ Server: {server_name.text}")
        
        print("=" * 60)
        
    except ET.ParseError as e:
        print(f"Error parsing XML: {e}")
    except Exception as e:
        print(f"Error processing configuration: {e}")


def main():
    """Main function to execute the NETCONF configuration retrieval"""
    print("Starting NETCONF configuration retrieval...")
    print(f"Connecting to: {HOST}")
    
    # Connect to the NETCONF server on a Cisco device
    try:
        with manager.connect(host=HOST, port=830, username=USERNAME, password=PASSWORD, hostkey_verify=False) as m:
            # Retrieve the running configuration
            config = m.get_config(source="running").data_xml
            print("Successfully connected to NETCONF server!")
            
            # Parse and display the configuration in human-readable format
            parse_cisco_config(config)
            
            # Optionally save raw XML to file
            with open('raw_config.xml', 'w') as f:
                # Pretty print the XML
                dom = minidom.parseString(config)
                f.write(dom.toprettyxml(indent="  "))
            print("\n💾 Raw XML configuration saved to 'raw_config.xml'")
            
    except Exception as e:
        # Check if it's an authentication error
        if "Authentication failed" in str(e) or "AuthenticationError" in str(type(e)):
            print(f"Authentication failed: Invalid username or password")
            print(f"Host: {HOST}")
            print(f"Username: {USERNAME}")
            print("Please check your credentials and try again.")
        
        # Check if it's a connection error
        elif "Connection" in str(e) or "SSH" in str(e) or "socket" in str(e).lower():
            print(f"Connection error: {e}")
            print("This could be due to network issues, SSH configuration problems, or the server being unreachable.")
        
        # Check if it's a timeout error
        elif "timeout" in str(e).lower() or "Timeout" in str(e):
            print(f"Timeout error: {e}")
            print("The server took too long to respond or is not reachable.")
        
        # General error handling
        else:
            print(f"An error occurred: {e}")
            print(f"Error type: {type(e).__name__}")
            print("Please check your network connection, credentials, and server availability.")


if __name__ == "__main__":
    main()

Step 5: Save the file and run.
– python3 ./netconf-get.py or
– chmod +x ./netconf-get.py
– ./netconf-get.py

Vendor Adoption and Ecosystem

NETCONF is widely supported across major vendors:

  • Cisco – IOS XE, IOS XR, NX-OS (with model-driven telemetry)
  • Juniper – Native support in JunOS
  • Arista – EOS supports YANG/NETCONF with OpenConfig
  • Nokia, Huawei, and others also support NETCONF in SDN-ready platforms

Adoption is growing with the rise of:

  • Network Function Virtualization (NFV)
  • Intent-Based Networking (IBN)
  • CI/CD pipelines for NetDevOps

Real-World Use Cases

🔁 Automated Configuration Pushes
Use NETCONF to consistently apply configuration changes across multiple devices with scripts or orchestration tools like Ansible or custom Python scripts.

📤 Configuration Backups & Snapshots
Easily retrieve and store the full device configuration as structured XML, making it simple to version, audit, or restore in disaster recovery scenarios.

🔍 Operational State Monitoring
Use <get> operations to pull real-time status (interface status, CPU, memory, etc.) for dashboards, health checks, or dynamic decision-making.

🧪 Validation and Pre-checks for CI/CD
Automate the testing and validation of device configurations in a pipeline, ensuring no broken configs are pushed to production.

🔐 Consistent Policy Enforcement
Enforce network-wide settings like AAA, logging, or ACLs by pushing and validating policies programmatically across all devices.

🌐 Multi-vendor Interoperability using OpenConfig models
Standardized YANG models allow NETCONF to configure different vendors’ devices with the same automation logic, reducing complexity in multi-vendor environments.

Final Thoughts

NETCONF is a cornerstone of modern network automation — offering the reliability, structure, and security that traditional CLI methods lack. As networks grow more complex and dynamic, mastering NETCONF becomes a critical skill for engineers embracing programmability and DevOps.

Whether you’re managing physical routers or automating cloud-native NFV devices, NETCONF is the protocol that bridges configuration with consistency.

📘 Up next: We’ll dive into RESTCONF, NETCONF’s JSON-speaking cousin. Stay tuned!

Calendar

September 2025
S M T W T F S
 123456
78910111213
14151617181920
21222324252627
282930  

Related Post