Practical Example: Nested Network Device Configs
🏷️ Dictionaries / Nested Dictionaries
đź§ Context Introduction
As you begin working with network automation, you'll quickly discover that real-world device configurations are rarely flat and simple. A single switch or router contains multiple interfaces, each with its own IP address, VLAN assignment, and status. This hierarchical data is best represented using nested dictionaries—dictionaries inside other dictionaries. In this guide, we'll walk through a practical example of modeling a network device's configuration using nested dictionaries, then show how to access, update, and iterate over that data.
⚙️ Why Nested Dictionaries for Network Configs?
- Real-world structure: A device has interfaces; each interface has properties (IP, VLAN, status). This is a natural parent-child relationship.
- Efficient lookups: Instead of looping through flat lists, you can access specific interface details directly by key.
- Easy to update: Need to change the IP on interface GigabitEthernet0/1? You can do it in one line without touching other data.
- Readable and maintainable: Nested dictionaries mirror how engineers think about device configurations—organized by device, then interface, then property.
🛠️ Building a Nested Device Config
Let's model a single network switch with two interfaces. We'll create a dictionary where the top-level key is the device hostname, and the value is another dictionary containing interface names as keys, each pointing to a dictionary of properties.
Example structure (conceptual): - Top-level key: "switch-01" - Inside that, a key "interfaces" with value being another dictionary - Inside interfaces, keys like "GigabitEthernet0/1" and "GigabitEthernet0/2" - Each interface has keys: "ip_address", "vlan", "status"
How to build it in Python: - Start with an empty dictionary: device_config = {} - Add the device name as a key with an empty dictionary as value: device_config["switch-01"] = {} - Inside that, add an "interfaces" key with another empty dictionary: device_config["switch-01"]["interfaces"] = {} - Now add each interface: device_config["switch-01"]["interfaces"]["GigabitEthernet0/1"] = {"ip_address": "10.0.1.1", "vlan": 10, "status": "up"} - Repeat for the second interface: device_config["switch-01"]["interfaces"]["GigabitEthernet0/2"] = {"ip_address": "10.0.2.1", "vlan": 20, "status": "down"}
The final structure looks like this (written as a single assignment for clarity): - device_config = {"switch-01": {"interfaces": {"GigabitEthernet0/1": {"ip_address": "10.0.1.1", "vlan": 10, "status": "up"}, "GigabitEthernet0/2": {"ip_address": "10.0.2.1", "vlan": 20, "status": "down"}}}}
🕵️ Accessing Nested Data
To retrieve specific values, chain the keys in order from outer to inner.
Examples of accessing data: - Get the IP address of the first interface: device_config["switch-01"]["interfaces"]["GigabitEthernet0/1"]["ip_address"] → returns "10.0.1.1" - Get the VLAN of the second interface: device_config["switch-01"]["interfaces"]["GigabitEthernet0/2"]["vlan"] → returns 20 - Get the entire dictionary for the first interface: device_config["switch-01"]["interfaces"]["GigabitEthernet0/1"] → returns {"ip_address": "10.0.1.1", "vlan": 10, "status": "up"}
What happens if a key doesn't exist? - Python raises a KeyError. To avoid this, use the .get() method: device_config.get("switch-01", {}).get("interfaces", {}).get("GigabitEthernet0/3", "Not found") → returns "Not found" instead of crashing.
📊 Updating Nested Values
Updating a value in a nested dictionary is straightforward—just assign a new value to the full key path.
Examples of updates: - Change the IP address on GigabitEthernet0/1: device_config["switch-01"]["interfaces"]["GigabitEthernet0/1"]["ip_address"] = "10.0.1.100" - Change the status of GigabitEthernet0/2 to "up": device_config["switch-01"]["interfaces"]["GigabitEthernet0/2"]["status"] = "up" - Add a new property (e.g., description) to an interface: device_config["switch-01"]["interfaces"]["GigabitEthernet0/1"]["description"] = "Uplink to core"
Adding a new interface entirely: - device_config["switch-01"]["interfaces"]["GigabitEthernet0/3"] = {"ip_address": "10.0.3.1", "vlan": 30, "status": "admin down"}
🔄 Iterating Over Nested Data
To process all interfaces on a device, you'll need nested loops—one for devices, one for interfaces.
How to loop through all interfaces: - Outer loop: for device_name, device_data in device_config.items(): — this gives you each device name and its data dictionary - Inner loop: for interface_name, interface_props in device_data["interfaces"].items(): — this gives you each interface name and its properties dictionary - Inside the inner loop, you can access individual properties: interface_props["ip_address"], interface_props["vlan"], interface_props["status"]
Practical output example (not code, just the logic): - For each device, print the device name - For each interface on that device, print the interface name, its IP, VLAN, and status - This lets you generate a report of all interfaces across all devices in one go
đź§© Comparison: Flat vs. Nested Structures
| Feature | Flat Dictionary | Nested Dictionary |
|---|---|---|
| Example key | "switch-01_Gig0/1_ip" | ["switch-01"]["interfaces"]["Gig0/1"]["ip"] |
| Readability | Hard to parse visually | Mirrors real-world hierarchy |
| Adding new interface | Requires new key with prefix | Just add a new inner dictionary |
| Updating all interfaces | Must loop and filter by prefix | Directly iterate over inner dict |
| Error-prone? | Easy to mis-type long keys | Clearer path, but must ensure parent keys exist |
âś… Best Practices for Engineers
- Always use .get() for safe access when you're not sure a key exists—this prevents your script from crashing on unexpected data.
- Build nested dictionaries step by step rather than all at once—it's easier to debug and verify each level.
- Use meaningful key names like "interfaces", "vlan", "status"—avoid abbreviations that might confuse you later.
- Consider using a small helper function to safely drill into nested dictionaries: for example, a function that takes a list of keys and returns the value or a default.
- When iterating, unpack wisely—use descriptive variable names like device_name, interface_name, interface_props so the code reads like English.
🚀 Next Steps
Now that you understand nested dictionaries, try extending this example: - Add a second device (e.g., "switch-02") with its own interfaces - Write logic to find all interfaces that are "down" across all devices - Create a function that takes a device name and returns a list of all IP addresses configured on that device
Nested dictionaries are the foundation for working with JSON data from network devices, APIs, and configuration files. Master this pattern, and you'll be ready to automate real network tasks with confidence.
Nested dictionaries store device configurations where each device contains multiple configuration parameters as key-value pairs.
đź”§ Example 1: Basic nested dictionary for a single switch
This example creates a simple nested dictionary with one device and its configuration settings.
switch_config = {
"hostname": "SW-01",
"vlans": {
"10": "Management",
"20": "Data",
"30": "Voice"
},
"interfaces": {
"Gig0/1": "access",
"Gig0/2": "trunk"
}
}
print(switch_config["hostname"])
print(switch_config["vlans"]["10"])
print(switch_config["interfaces"]["Gig0/1"])
📤 Output: SW-01 Management access
đź”§ Example 2: Accessing nested values with multiple keys
This example shows how to retrieve specific configuration values from deeply nested dictionaries.
router_config = {
"hostname": "RTR-01",
"ospf": {
"process_id": 100,
"area": 0,
"networks": {
"192.168.1.0": "0.0.0.255",
"10.0.0.0": "0.255.255.255"
}
}
}
print(router_config["ospf"]["process_id"])
print(router_config["ospf"]["networks"]["192.168.1.0"])
📤 Output: 100 0.0.0.255
đź”§ Example 3: Adding new nested configuration to a device
This example demonstrates how to add new configuration parameters to an existing nested dictionary.
firewall_config = {
"hostname": "FW-01",
"interfaces": {
"eth0": "outside",
"eth1": "inside"
}
}
firewall_config["interfaces"]["eth2"] = "dmz"
firewall_config["nat_rules"] = {
"rule_1": {
"source": "10.0.0.0/24",
"destination": "any",
"action": "permit"
}
}
print(firewall_config["interfaces"])
print(firewall_config["nat_rules"]["rule_1"]["action"])
📤 Output: {'eth0': 'outside', 'eth1': 'inside', 'eth2': 'dmz'} permit
đź”§ Example 4: Looping through nested device configurations
This example shows how to iterate over all devices and their nested configuration parameters.
network_devices = {
"SW-01": {
"type": "switch",
"vlans": [10, 20, 30],
"status": "online"
},
"RTR-01": {
"type": "router",
"vlans": [10, 20],
"status": "online"
},
"FW-01": {
"type": "firewall",
"vlans": [10],
"status": "offline"
}
}
for device_name, device_info in network_devices.items():
print(f"Device: {device_name}")
print(f" Type: {device_info['type']}")
print(f" Status: {device_info['status']}")
print(f" VLANs: {device_info['vlans']}")
📤 Output: Device: SW-01 Type: switch Status: online VLANs: [10, 20, 30] Device: RTR-01 Type: router Status: online VLANs: [10, 20] Device: FW-01 Type: firewall Status: offline VLANs: [10]
đź”§ Example 5: Updating nested values across multiple devices
This example demonstrates how to modify nested configuration values for multiple devices at once.
device_configs = {
"SW-01": {
"hostname": "SW-01",
"mgmt_ip": "192.168.1.10",
"snmp": {
"community": "public",
"version": "v2c"
}
},
"SW-02": {
"hostname": "SW-02",
"mgmt_ip": "192.168.1.11",
"snmp": {
"community": "public",
"version": "v2c"
}
}
}
for device, config in device_configs.items():
config["snmp"]["community"] = "secure_community"
config["snmp"]["version"] = "v3"
print(device_configs["SW-01"]["snmp"])
print(device_configs["SW-02"]["snmp"])
📤 Output: {'community': 'secure_community', 'version': 'v3'} {'community': 'secure_community', 'version': 'v3'}
📊 Comparison Table
| Operation | Example | Key Feature |
|---|---|---|
| Access nested value | config["vlans"]["10"] |
Uses multiple keys |
| Add nested value | config["interfaces"]["eth2"] = "dmz" |
Creates new key-value pair |
| Loop through devices | for name, info in devices.items() |
Iterates all nested data |
| Update nested value | config["snmp"]["community"] = "new" |
Modifies existing nested key |
| Check device status | if device_info["status"] == "online" |
Conditional nested access |
đź§ Context Introduction
As you begin working with network automation, you'll quickly discover that real-world device configurations are rarely flat and simple. A single switch or router contains multiple interfaces, each with its own IP address, VLAN assignment, and status. This hierarchical data is best represented using nested dictionaries—dictionaries inside other dictionaries. In this guide, we'll walk through a practical example of modeling a network device's configuration using nested dictionaries, then show how to access, update, and iterate over that data.
⚙️ Why Nested Dictionaries for Network Configs?
- Real-world structure: A device has interfaces; each interface has properties (IP, VLAN, status). This is a natural parent-child relationship.
- Efficient lookups: Instead of looping through flat lists, you can access specific interface details directly by key.
- Easy to update: Need to change the IP on interface GigabitEthernet0/1? You can do it in one line without touching other data.
- Readable and maintainable: Nested dictionaries mirror how engineers think about device configurations—organized by device, then interface, then property.
🛠️ Building a Nested Device Config
Let's model a single network switch with two interfaces. We'll create a dictionary where the top-level key is the device hostname, and the value is another dictionary containing interface names as keys, each pointing to a dictionary of properties.
Example structure (conceptual): - Top-level key: "switch-01" - Inside that, a key "interfaces" with value being another dictionary - Inside interfaces, keys like "GigabitEthernet0/1" and "GigabitEthernet0/2" - Each interface has keys: "ip_address", "vlan", "status"
How to build it in Python: - Start with an empty dictionary: device_config = {} - Add the device name as a key with an empty dictionary as value: device_config["switch-01"] = {} - Inside that, add an "interfaces" key with another empty dictionary: device_config["switch-01"]["interfaces"] = {} - Now add each interface: device_config["switch-01"]["interfaces"]["GigabitEthernet0/1"] = {"ip_address": "10.0.1.1", "vlan": 10, "status": "up"} - Repeat for the second interface: device_config["switch-01"]["interfaces"]["GigabitEthernet0/2"] = {"ip_address": "10.0.2.1", "vlan": 20, "status": "down"}
The final structure looks like this (written as a single assignment for clarity): - device_config = {"switch-01": {"interfaces": {"GigabitEthernet0/1": {"ip_address": "10.0.1.1", "vlan": 10, "status": "up"}, "GigabitEthernet0/2": {"ip_address": "10.0.2.1", "vlan": 20, "status": "down"}}}}
🕵️ Accessing Nested Data
To retrieve specific values, chain the keys in order from outer to inner.
Examples of accessing data: - Get the IP address of the first interface: device_config["switch-01"]["interfaces"]["GigabitEthernet0/1"]["ip_address"] → returns "10.0.1.1" - Get the VLAN of the second interface: device_config["switch-01"]["interfaces"]["GigabitEthernet0/2"]["vlan"] → returns 20 - Get the entire dictionary for the first interface: device_config["switch-01"]["interfaces"]["GigabitEthernet0/1"] → returns {"ip_address": "10.0.1.1", "vlan": 10, "status": "up"}
What happens if a key doesn't exist? - Python raises a KeyError. To avoid this, use the .get() method: device_config.get("switch-01", {}).get("interfaces", {}).get("GigabitEthernet0/3", "Not found") → returns "Not found" instead of crashing.
📊 Updating Nested Values
Updating a value in a nested dictionary is straightforward—just assign a new value to the full key path.
Examples of updates: - Change the IP address on GigabitEthernet0/1: device_config["switch-01"]["interfaces"]["GigabitEthernet0/1"]["ip_address"] = "10.0.1.100" - Change the status of GigabitEthernet0/2 to "up": device_config["switch-01"]["interfaces"]["GigabitEthernet0/2"]["status"] = "up" - Add a new property (e.g., description) to an interface: device_config["switch-01"]["interfaces"]["GigabitEthernet0/1"]["description"] = "Uplink to core"
Adding a new interface entirely: - device_config["switch-01"]["interfaces"]["GigabitEthernet0/3"] = {"ip_address": "10.0.3.1", "vlan": 30, "status": "admin down"}
🔄 Iterating Over Nested Data
To process all interfaces on a device, you'll need nested loops—one for devices, one for interfaces.
How to loop through all interfaces: - Outer loop: for device_name, device_data in device_config.items(): — this gives you each device name and its data dictionary - Inner loop: for interface_name, interface_props in device_data["interfaces"].items(): — this gives you each interface name and its properties dictionary - Inside the inner loop, you can access individual properties: interface_props["ip_address"], interface_props["vlan"], interface_props["status"]
Practical output example (not code, just the logic): - For each device, print the device name - For each interface on that device, print the interface name, its IP, VLAN, and status - This lets you generate a report of all interfaces across all devices in one go
đź§© Comparison: Flat vs. Nested Structures
| Feature | Flat Dictionary | Nested Dictionary |
|---|---|---|
| Example key | "switch-01_Gig0/1_ip" | ["switch-01"]["interfaces"]["Gig0/1"]["ip"] |
| Readability | Hard to parse visually | Mirrors real-world hierarchy |
| Adding new interface | Requires new key with prefix | Just add a new inner dictionary |
| Updating all interfaces | Must loop and filter by prefix | Directly iterate over inner dict |
| Error-prone? | Easy to mis-type long keys | Clearer path, but must ensure parent keys exist |
âś… Best Practices for Engineers
- Always use .get() for safe access when you're not sure a key exists—this prevents your script from crashing on unexpected data.
- Build nested dictionaries step by step rather than all at once—it's easier to debug and verify each level.
- Use meaningful key names like "interfaces", "vlan", "status"—avoid abbreviations that might confuse you later.
- Consider using a small helper function to safely drill into nested dictionaries: for example, a function that takes a list of keys and returns the value or a default.
- When iterating, unpack wisely—use descriptive variable names like device_name, interface_name, interface_props so the code reads like English.
🚀 Next Steps
Now that you understand nested dictionaries, try extending this example: - Add a second device (e.g., "switch-02") with its own interfaces - Write logic to find all interfaces that are "down" across all devices - Create a function that takes a device name and returns a list of all IP addresses configured on that device
Nested dictionaries are the foundation for working with JSON data from network devices, APIs, and configuration files. Master this pattern, and you'll be ready to automate real network tasks with confidence.
Interactive Views
You are currently in 📚 All-in-One mode. Use the tabs at the top to switch to 📖 Theory Only or 💻 Code Only views.
Nested dictionaries store device configurations where each device contains multiple configuration parameters as key-value pairs.
đź”§ Example 1: Basic nested dictionary for a single switch
This example creates a simple nested dictionary with one device and its configuration settings.
switch_config = {
"hostname": "SW-01",
"vlans": {
"10": "Management",
"20": "Data",
"30": "Voice"
},
"interfaces": {
"Gig0/1": "access",
"Gig0/2": "trunk"
}
}
print(switch_config["hostname"])
print(switch_config["vlans"]["10"])
print(switch_config["interfaces"]["Gig0/1"])
📤 Output: SW-01 Management access
đź”§ Example 2: Accessing nested values with multiple keys
This example shows how to retrieve specific configuration values from deeply nested dictionaries.
router_config = {
"hostname": "RTR-01",
"ospf": {
"process_id": 100,
"area": 0,
"networks": {
"192.168.1.0": "0.0.0.255",
"10.0.0.0": "0.255.255.255"
}
}
}
print(router_config["ospf"]["process_id"])
print(router_config["ospf"]["networks"]["192.168.1.0"])
📤 Output: 100 0.0.0.255
đź”§ Example 3: Adding new nested configuration to a device
This example demonstrates how to add new configuration parameters to an existing nested dictionary.
firewall_config = {
"hostname": "FW-01",
"interfaces": {
"eth0": "outside",
"eth1": "inside"
}
}
firewall_config["interfaces"]["eth2"] = "dmz"
firewall_config["nat_rules"] = {
"rule_1": {
"source": "10.0.0.0/24",
"destination": "any",
"action": "permit"
}
}
print(firewall_config["interfaces"])
print(firewall_config["nat_rules"]["rule_1"]["action"])
📤 Output: {'eth0': 'outside', 'eth1': 'inside', 'eth2': 'dmz'} permit
đź”§ Example 4: Looping through nested device configurations
This example shows how to iterate over all devices and their nested configuration parameters.
network_devices = {
"SW-01": {
"type": "switch",
"vlans": [10, 20, 30],
"status": "online"
},
"RTR-01": {
"type": "router",
"vlans": [10, 20],
"status": "online"
},
"FW-01": {
"type": "firewall",
"vlans": [10],
"status": "offline"
}
}
for device_name, device_info in network_devices.items():
print(f"Device: {device_name}")
print(f" Type: {device_info['type']}")
print(f" Status: {device_info['status']}")
print(f" VLANs: {device_info['vlans']}")
📤 Output: Device: SW-01 Type: switch Status: online VLANs: [10, 20, 30] Device: RTR-01 Type: router Status: online VLANs: [10, 20] Device: FW-01 Type: firewall Status: offline VLANs: [10]
đź”§ Example 5: Updating nested values across multiple devices
This example demonstrates how to modify nested configuration values for multiple devices at once.
device_configs = {
"SW-01": {
"hostname": "SW-01",
"mgmt_ip": "192.168.1.10",
"snmp": {
"community": "public",
"version": "v2c"
}
},
"SW-02": {
"hostname": "SW-02",
"mgmt_ip": "192.168.1.11",
"snmp": {
"community": "public",
"version": "v2c"
}
}
}
for device, config in device_configs.items():
config["snmp"]["community"] = "secure_community"
config["snmp"]["version"] = "v3"
print(device_configs["SW-01"]["snmp"])
print(device_configs["SW-02"]["snmp"])
📤 Output: {'community': 'secure_community', 'version': 'v3'} {'community': 'secure_community', 'version': 'v3'}
📊 Comparison Table
| Operation | Example | Key Feature |
|---|---|---|
| Access nested value | config["vlans"]["10"] |
Uses multiple keys |
| Add nested value | config["interfaces"]["eth2"] = "dmz" |
Creates new key-value pair |
| Loop through devices | for name, info in devices.items() |
Iterates all nested data |
| Update nested value | config["snmp"]["community"] = "new" |
Modifies existing nested key |
| Check device status | if device_info["status"] == "online" |
Conditional nested access |