YAML Data Modelling
YAML Data Modelling for Network Automation¶
"From Hardcoded Values to Structured Data β The Foundation of Scalable Automation"¶
Every production network automation system relies on structured data. Device inventories, configuration templates, credential stores, testbedsβthey all use YAML (YAML Ain't Markup Language).
Why YAML dominates network automation:
- β Human-readable β Network engineers can read and edit without programming knowledge
- β Framework-native β Nornir inventories, PyATS testbeds, Ansible playbooks all use YAML
- β Version control friendly β Plain text format works perfectly with Git
- β Supports complex structures β Nested data, lists, dictionaries without JSON's noise
- β Comments allowed β Document your data inline (JSON can't do this)
If you're hardcoding IPs, credentials, or configuration values in Python scripts, you're doing it wrong. This tutorial teaches you the right way.
π― What You'll Learn¶
By the end of this tutorial, you'll understand:
- β YAML syntax fundamentals (scalars, lists, dictionaries, nested structures)
- β Reading and writing YAML in Python
- β Nornir inventory structure (hosts, groups, defaults)
- β PyATS testbed structure (devices, connections, credentials)
- β Configuration data modelling best practices
- β Common YAML pitfalls and how to avoid them
- β Real-world patterns used in production automation
- β Validating YAML structure programmatically
π Prerequisites¶
Required Knowledge¶
- β Completed Beginner Tutorials β Familiar with Python dictionaries and lists
- β Understanding of Python data structures (dict, list, str)
- β Basic text editor skills
Required Software¶
# Create a virtual environment
python -m venv yaml_venv
source yaml_venv/bin/activate
# Windows PowerShell: .\yaml_venv\Scripts\Activate.ps1
# Windows CMD: yaml_venv\Scripts\activate.bat
# Install required packages
pip install pyyaml netmiko nornir pyats
Required Access¶
- No device access required for this tutorial (we'll work with files and Python)
π YAML Fundamentals: Understanding the Syntax¶
What is YAML?¶
YAML is a data serialization languageβit converts human-readable text into data structures (dictionaries, lists) that programs can use.
Example: Instead of this Python code:
devices = {
'router1': {
'ip': '10.1.1.1',
'username': 'admin',
'device_type': 'cisco_ios'
},
'router2': {
'ip': '10.1.1.2',
'username': 'admin',
'device_type': 'cisco_ios'
}
}
Write this YAML file:
devices:
router1:
ip: 10.1.1.1
username: admin
device_type: cisco_ios
router2:
ip: 10.1.1.2
username: admin
device_type: cisco_ios
Result: Cleaner, more readable, editable without Python knowledge.
π§ YAML Syntax: Core Concepts¶
1. Scalars (Single Values)¶
Scalars are simple values: strings, numbers, booleans.
---
# String (no quotes needed for simple strings)
hostname: router1
# Number (integer)
vlan_id: 100
# Number (float)
timeout: 5.5
# Boolean
enabled: true
admin_mode: false
# String with special characters (quotes required)
password: "P@ssw0rd!"
# String with spaces (quotes required)
description: "Main office router"
# Null value
bgp_asn: null
Key rules:
- Strings usually don't need quotes (unless they contain special characters)
- Numbers are automatically typed (int vs float)
- Booleans:
true,false,yes,no,on,off nullrepresents no value
2. Lists (Arrays)¶
Lists are collections of items, denoted with - (dash).
---
# List of strings
vlans:
- 10
- 20
- 30
- 40
# List of dictionaries (more complex)
interfaces:
- name: GigabitEthernet0/0
ip: 10.1.1.1
subnet: 255.255.255.0
- name: GigabitEthernet0/1
ip: 10.1.2.1
subnet: 255.255.255.0
# Inline list syntax (less common)
allowed_vlans: [10, 20, 30, 40]
Python equivalent:
vlans = [10, 20, 30, 40]
interfaces = [
{'name': 'GigabitEthernet0/0', 'ip': '10.1.1.1', 'subnet': '255.255.255.0'},
{'name': 'GigabitEthernet0/1', 'ip': '10.1.2.1', 'subnet': '255.255.255.0'}
]
3. Dictionaries (Mappings)¶
Dictionaries are key-value pairs, using key: value format.
---
# Simple dictionary
device:
hostname: router1
ip: 10.1.1.1
os: ios
# Nested dictionary
device_config:
basic:
hostname: core-router-01
domain: example.com
interfaces:
management:
name: GigabitEthernet0/0
ip: 192.168.1.1
Python equivalent:
device = {
'hostname': 'router1',
'ip': '10.1.1.1',
'os': 'ios'
}
device_config = {
'basic': {
'hostname': 'core-router-01',
'domain': 'example.com'
},
'interfaces': {
'management': {
'name': 'GigabitEthernet0/0',
'ip': '192.168.1.1'
}
}
}
4. Nested Structures (Real-World Pattern)¶
Combine lists and dictionaries to model complex data:
---
# Network inventory with nested structure
network:
datacenters:
dc1:
location: "New York"
devices:
- hostname: nyc-core-01
ip: 10.1.1.1
type: router
vlans: [10, 20, 30]
- hostname: nyc-core-02
ip: 10.1.1.2
type: router
vlans: [10, 20, 30]
dc2:
location: "Los Angeles"
devices:
- hostname: la-core-01
ip: 10.2.1.1
type: router
vlans: [40, 50, 60]
What this represents:
- Network has multiple datacenters
- Each datacenter has location and devices
- Each device has hostname, IP, type, and VLANs
Python access:
data['network']['datacenters']['dc1']['devices'][0]['hostname']
# Returns: 'nyc-core-01'
π Reading YAML in Python¶
Basic YAML Loading¶
#!/usr/bin/env python3
"""
Read YAML file and convert to Python data structures
"""
import yaml
# Read YAML file
with open('devices.yaml', 'r') as file:
data = yaml.safe_load(file)
# safe_load() parses YAML and returns Python dict/list
# Always use safe_load() (not load()) for security
# Access the data
print(f"Router 1 IP: {data['router1']['ip']}")
print(f"Router 2 Username: {data['router2']['username']}")
# Iterate through devices
for device_name, device_info in data.items():
print(f"Device: {device_name}")
print(f" IP: {device_info['ip']}")
print(f" Type: {device_info['device_type']}")
Why safe_load() not load():
safe_load()only parses standard YAML (safe)load()can execute arbitrary Python code (security risk)- Always use
safe_load()unless you have a specific reason not to
Handling Complex Structures¶
#!/usr/bin/env python3
"""
Read nested YAML and extract specific data
"""
import yaml
# Sample YAML file: network_inventory.yaml
yaml_content = """
---
datacenters:
dc1:
location: "New York"
devices:
- hostname: nyc-core-01
ip: 10.1.1.1
vlans: [10, 20, 30]
- hostname: nyc-core-02
ip: 10.1.1.2
vlans: [10, 20, 30]
"""
# Parse YAML
data = yaml.safe_load(yaml_content)
# Navigate nested structure
dc1_location = data['datacenters']['dc1']['location']
print(f"DC1 Location: {dc1_location}")
# Access list items
for device in data['datacenters']['dc1']['devices']:
print(f"\nDevice: {device['hostname']}")
print(f" IP: {device['ip']}")
print(f" VLANs: {', '.join(map(str, device['vlans']))}")
Output:
DC1 Location: New York
Device: nyc-core-01
IP: 10.1.1.1
VLANs: 10, 20, 30
Device: nyc-core-02
IP: 10.1.1.2
VLANs: 10, 20, 30
π Writing YAML in Python¶
Creating YAML from Python Data¶
#!/usr/bin/env python3
"""
Convert Python data structures to YAML files
"""
import yaml
# Python data structure
devices = {
'router1': {
'ip': '10.1.1.1',
'username': 'admin',
'device_type': 'cisco_ios',
'vlans': [10, 20, 30]
},
'router2': {
'ip': '10.1.1.2',
'username': 'admin',
'device_type': 'cisco_ios',
'vlans': [10, 20, 30]
}
}
# Write to YAML file
with open('devices_output.yaml', 'w') as file:
yaml.dump(devices, file, default_flow_style=False, sort_keys=False)
# default_flow_style=False makes output readable (not inline)
# sort_keys=False preserves key order
print("β YAML file created: devices_output.yaml")
Generated YAML:
router1:
ip: 10.1.1.1
username: admin
device_type: cisco_ios
vlans:
- 10
- 20
- 30
router2:
ip: 10.1.1.2
username: admin
device_type: cisco_ios
vlans:
- 10
- 20
- 30
ποΈ Real-World Pattern 1: Nornir Inventory¶
Nornir uses YAML for device inventory management. Understanding this structure is critical for Nornir automation.
Nornir Inventory Structure¶
Nornir uses three YAML files:
- hosts.yaml β Individual device definitions
- groups.yaml β Device groupings with shared settings
- defaults.yaml β Default values applied to all devices
hosts.yaml¶
---
# Individual device definitions
router1:
hostname: 10.1.1.1
# IP address or DNS name for SSH connection
groups:
- ios_routers
# Device belongs to ios_routers group (inherits settings)
data:
# Custom data specific to this device
device_type: cisco_ios
location: "New York"
is_production: true
switch1:
hostname: 10.1.1.10
groups:
- ios_switches
data:
device_type: cisco_ios
location: "New York"
vlan_range: "1-100"
router2:
hostname: 10.2.1.1
groups:
- ios_routers
data:
device_type: cisco_ios
location: "Los Angeles"
is_production: false
Key concepts:
- Device name (e.g.,
router1) is the unique identifier hostnameis the actual IP/DNS for connectiongroupsassigns device to group (inherits credentials, settings)datastores custom fields (access viahost.data['location'])
groups.yaml¶
---
# Group definitions with shared settings
ios_routers:
username: admin
password: "" # Will be set at runtime (security)
data:
connection_timeout: 30
enable_password: ""
ios_switches:
username: switch_admin
password: ""
data:
connection_timeout: 20
enable_password: ""
nxos_devices:
username: nxos_admin
password: ""
data:
device_type: cisco_nxos
connection_timeout: 30
Key concepts:
- Groups define shared settings (credentials, timeouts)
- Devices inherit from groups they belong to
- Password left blank (set at runtime for security)
defaults.yaml¶
---
# Default values applied to ALL devices
data:
connection_timeout: 15
# Default SSH timeout if not specified elsewhere
auth_timeout: 10
# Default authentication timeout
read_timeout: 30
# Default command read timeout
ssh_config_file: null
# No custom SSH config by default
Key concepts:
- Applies to all devices unless overridden
- Useful for organisation-wide standards
Using Nornir Inventory in Python¶
#!/usr/bin/env python3
"""
Use Nornir inventory from YAML files
"""
from nornir import InitNornir
# Initialize Nornir with config file
nr = InitNornir(config_file="nornir_config.yaml")
# Access device data
router1 = nr.inventory.hosts['router1']
print(f"Device: {router1.name}")
print(f"Hostname: {router1.hostname}")
print(f"Location: {router1.data['location']}")
print(f"Username: {router1.username}") # Inherited from group
# Filter devices by group
ios_devices = nr.filter(F(groups__contains="ios_routers"))
print(f"\nFound {len(ios_devices.inventory.hosts)} IOS routers")
# Filter devices by custom data
prod_devices = nr.filter(lambda h: h.data.get('is_production') == True)
print(f"Found {len(prod_devices.inventory.hosts)} production devices")
π¬ Real-World Pattern 2: PyATS Testbed¶
PyATS uses YAML to define network testbeds (device topology and connectivity).
PyATS Testbed Structure¶
---
testbed:
name: "Production Network"
# Testbed identifier
devices:
csr1000v:
# Device name (referenced as testbed.devices['csr1000v'])
type: router
# Device type (router, switch, firewall, etc.)
os: iosxe
# Operating system (determines parser)
connections:
# Multiple connection types supported
cli:
# Command-line interface connection
protocol: ssh
ip: 192.168.1.10
port: 22
credentials:
default:
# Primary credential set
username: admin
password: !vault |
# Ansible Vault encrypted password
$ANSIBLE_VAULT;1.2;AES256;default
# Encrypted password here
netconf:
# NETCONF connection (if supported)
protocol: netconf
ip: 192.168.1.10
port: 830
credentials:
default:
username: admin
password: !vault |
# Same password, encrypted
nexus9k:
type: switch
os: nxos
connections:
cli:
protocol: ssh
ip: 192.168.1.20
port: 22
credentials:
default:
username: admin
password: !vault |
# Encrypted password
backup:
# Backup credential set
username: backup_user
password: !vault |
# Different credentials
Key concepts:
- Each device can have multiple connection types (cli, netconf, restconf)
- Credentials encrypted with Ansible Vault (secure)
- Multiple credential sets supported (primary, backup)
Using PyATS Testbed in Python¶
#!/usr/bin/env python3
"""
Load PyATS testbed and connect to devices
"""
from pyats.topology import loader
# Load testbed from YAML
testbed = loader.load('testbed.yaml')
# Access device
device = testbed.devices['csr1000v']
# Connect to device
device.connect()
print(f"β Connected to {device.name}")
# Parse device output
output = device.parse('show version')
print(f"Device version: {output['version']['version']}")
# Disconnect
device.disconnect()
π Real-World Pattern 3: Configuration Data Modelling¶
Model configuration data in YAML for template-driven automation.
VLAN Configuration Data¶
---
# VLAN definitions for network-wide deployment
vlans:
- id: 10
name: DATA
description: "Corporate data network"
enabled: true
- id: 20
name: VOICE
description: "VoIP phones"
enabled: true
- id: 30
name: GUEST
description: "Guest wireless"
enabled: true
- id: 99
name: MGMT
description: "Management network"
enabled: true
# Interface assignments
interface_assignments:
GigabitEthernet0/1:
mode: access
vlan: 10
description: "Data port - Workstation"
GigabitEthernet0/2:
mode: access
vlan: 20
description: "Voice port - IP Phone"
GigabitEthernet0/24:
mode: trunk
allowed_vlans: [10, 20, 30, 99]
description: "Trunk to distribution switch"
Using Configuration Data in Python¶
#!/usr/bin/env python3
"""
Generate Cisco configuration from YAML data
"""
import yaml
from netmiko import ConnectHandler
# Load YAML configuration data
with open('vlan_config.yaml', 'r') as file:
config_data = yaml.safe_load(file)
# Connect to device
device = ConnectHandler(
device_type='cisco_ios',
host='10.1.1.1',
username='admin',
password='password'
)
# Generate VLAN configuration commands
vlan_commands = []
for vlan in config_data['vlans']:
if vlan['enabled']:
vlan_commands.extend([
f"vlan {vlan['id']}",
f"name {vlan['name']}"
])
# Generate interface configuration commands
interface_commands = []
for interface, config in config_data['interface_assignments'].items():
interface_commands.append(f"interface {interface}")
interface_commands.append(f"description {config['description']}")
if config['mode'] == 'access':
interface_commands.extend([
"switchport mode access",
f"switchport access vlan {config['vlan']}"
])
elif config['mode'] == 'trunk':
vlans_str = ','.join(map(str, config['allowed_vlans']))
interface_commands.extend([
"switchport mode trunk",
f"switchport trunk allowed vlan {vlans_str}"
])
# Deploy configuration
print("Deploying VLAN configuration...")
output = device.send_config_set(vlan_commands)
print(output)
print("\nDeploying interface configuration...")
output = device.send_config_set(interface_commands)
print(output)
device.disconnect()
print("\nβ Configuration deployed successfully")
Why this pattern works:
- Configuration data separated from code
- Easy to modify VLANs without changing Python script
- Version control tracks configuration changes
- Non-programmers can edit YAML
β οΈ Common YAML Pitfalls & How to Avoid Them¶
Pitfall 1: Indentation Errors¶
Wrong:
devices:
router1:
hostname: 10.1.1.1 # β Wrong indentation
ip: 10.1.1.1
Right:
devices:
router1:
hostname: 10.1.1.1 # β Correct indentation
ip: 10.1.1.1
Rule: Always use 2 spaces for indentation (not tabs, not 4 spaces).
Pitfall 2: Unquoted Special Characters¶
Wrong:
password: P@ssw0rd! # β @ and ! are special characters
description: Device: Core Router # β Colon without quotes
Right:
password: "P@ssw0rd!" # β Quoted
description: "Device: Core Router" # β Quoted
Rule: Use quotes for strings with special characters (:, @, !, #, %, etc.).
Pitfall 3: Boolean Confusion¶
Wrong:
enabled: yes # Works but inconsistent
admin: on # Works but inconsistent
debug: true # Works but different style
Right:
enabled: true # β Consistent
admin: true # β Consistent
debug: true # β Consistent
Rule: Use true/false for consistency (avoid yes/no, on/off).
Pitfall 4: Missing Document Separator¶
Wrong:
device: router1 # No document separator
hostname: 10.1.1.1
Right:
---
device: router1 # β Document separator
hostname: 10.1.1.1
Rule: Always start YAML files with --- (document separator).
Pitfall 5: Duplicate Keys¶
Wrong:
device:
hostname: router1
hostname: router2 # β Duplicate key (only last value used)
Right:
device:
hostname: router1 # β Unique key
backup_hostname: router2
Rule: Each key must be unique within its scope.
π§ͺ Validating YAML Structure¶
Validation Script¶
#!/usr/bin/env python3
"""
Validate YAML files before using them in automation
"""
import yaml
import sys
def validate_yaml_file(filepath):
"""
Validate YAML file syntax and structure
Returns True if valid, False otherwise
"""
try:
with open(filepath, 'r') as file:
data = yaml.safe_load(file)
# Check if data was parsed
if data is None:
print(f"β {filepath}: File is empty or invalid")
return False
# Type check (expecting dict for most network configs)
if not isinstance(data, dict):
print(f"β {filepath}: Root element is not a dictionary")
print(f"β {filepath}: Valid YAML")
return True
except yaml.YAMLError as e:
print(f"β {filepath}: YAML syntax error")
print(f" Error: {e}")
return False
except FileNotFoundError:
print(f"β {filepath}: File not found")
return False
except Exception as e:
print(f"β {filepath}: Unexpected error")
print(f" Error: {e}")
return False
def validate_nornir_inventory(hosts_file, groups_file, defaults_file):
"""
Validate Nornir inventory structure
"""
print("Validating Nornir inventory...")
# Validate each file
if not validate_yaml_file(hosts_file):
return False
if not validate_yaml_file(groups_file):
return False
if not validate_yaml_file(defaults_file):
return False
# Load and check structure
with open(hosts_file) as f:
hosts = yaml.safe_load(f)
# Check hosts have required fields
for host_name, host_data in hosts.items():
if 'hostname' not in host_data:
print(f"β Host '{host_name}' missing 'hostname' field")
return False
print("β Nornir inventory structure valid")
return True
if __name__ == "__main__":
# Validate single file
validate_yaml_file('devices.yaml')
# Validate Nornir inventory
validate_nornir_inventory(
'inventory/hosts.yaml',
'inventory/groups.yaml',
'inventory/defaults.yaml'
)
Run validation:
python validate_yaml.py
π Best Practices for Network Automation¶
1. Separate Data from Code¶
Bad:
# Hardcoded in Python
devices = {
'router1': {'ip': '10.1.1.1', 'username': 'admin'},
'router2': {'ip': '10.1.1.2', 'username': 'admin'}
}
Good:
# devices.yaml
---
router1:
ip: 10.1.1.1
username: admin
router2:
ip: 10.1.1.2
username: admin
# Load from YAML
with open('devices.yaml') as f:
devices = yaml.safe_load(f)
2. Use Comments Liberally¶
---
# Production datacenter devices
# Last updated: 2026-03-12
# Contact: network-team@example.com
devices:
router1:
hostname: 10.1.1.1 # Primary WAN router
location: "NYC DC1"
router2:
hostname: 10.1.1.2 # Backup WAN router
location: "NYC DC1"
3. Version Control Your YAML¶
git add inventory/
git commit -m "Added router3 to NYC datacenter inventory"
git push
Benefits:
- Track changes over time
- Rollback if needed
- Audit trail for compliance
4. Validate Before Deployment¶
# Always validate YAML before using it
try:
with open('devices.yaml') as f:
devices = yaml.safe_load(f)
except yaml.YAMLError as e:
print(f"β YAML error: {e}")
sys.exit(1)
# Proceed with automation...
5. Never Store Passwords in Plain Text¶
Bad:
credentials:
username: admin
password: MyPassword123 # β Plain text password
Good:
credentials:
username: admin
password: "" # Set at runtime or use vault
import getpass
# Prompt for password at runtime
password = getpass.getpass("Enter device password: ")
Or use Ansible Vault:
credentials:
username: admin
password: !vault |
$ANSIBLE_VAULT;1.2;AES256
# Encrypted password
π Complete Working Example: Network Inventory System¶
Project Structure¶
network-inventory/
βββ inventory/
β βββ hosts.yaml
β βββ groups.yaml
β βββ defaults.yaml
βββ config/
β βββ vlan_config.yaml
βββ scripts/
β βββ load_inventory.py
β βββ validate_yaml.py
βββ README.md
inventory/hosts.yaml¶
---
# Network device inventory
router1:
hostname: 10.1.1.1
groups:
- ios_routers
- production
data:
location: "NYC-DC1"
rack: "A-01"
router2:
hostname: 10.1.1.2
groups:
- ios_routers
- production
data:
location: "NYC-DC1"
rack: "A-02"
switch1:
hostname: 10.1.1.10
groups:
- ios_switches
- production
data:
location: "NYC-DC1"
rack: "A-03"
inventory/groups.yaml¶
---
# Device groups with shared settings
ios_routers:
username: router_admin
data:
device_type: cisco_ios
connection_timeout: 30
ios_switches:
username: switch_admin
data:
device_type: cisco_ios
connection_timeout: 20
production:
data:
backup_schedule: "daily"
monitoring: true
scripts/load_inventory.py¶
#!/usr/bin/env python3
"""
Load and display network inventory from YAML
"""
import yaml
from pathlib import Path
def load_inventory():
"""Load Nornir-style inventory from YAML files"""
# Load hosts
with open('inventory/hosts.yaml') as f:
hosts = yaml.safe_load(f)
# Load groups
with open('inventory/groups.yaml') as f:
groups = yaml.safe_load(f)
# Load defaults
with open('inventory/defaults.yaml') as f:
defaults = yaml.safe_load(f)
return hosts, groups, defaults
def display_inventory(hosts, groups):
"""Display inventory in human-readable format"""
print("=" * 70)
print("NETWORK INVENTORY")
print("=" * 70)
for host_name, host_data in hosts.items():
print(f"\n{host_name}:")
print(f" Hostname: {host_data['hostname']}")
print(f" Groups: {', '.join(host_data.get('groups', []))}")
if 'data' in host_data:
print(" Data:")
for key, value in host_data['data'].items():
print(f" {key}: {value}")
def main():
"""Main function"""
try:
hosts, groups, defaults = load_inventory()
display_inventory(hosts, groups)
print(f"\n{'=' * 70}")
print(f"Total devices: {len(hosts)}")
print(f"Total groups: {len(groups)}")
print("=" * 70)
except FileNotFoundError as e:
print(f"β Error: {e}")
print(" Make sure inventory files exist")
except yaml.YAMLError as e:
print(f"β YAML error: {e}")
except Exception as e:
print(f"β Unexpected error: {e}")
if __name__ == "__main__":
main()
Run the script:
python scripts/load_inventory.py
Output:
======================================================================
NETWORK INVENTORY
======================================================================
router1:
Hostname: 10.1.1.1
Groups: ios_routers, production
Data:
location: NYC-DC1
rack: A-01
router2:
Hostname: 10.1.1.2
Groups: ios_routers, production
Data:
location: NYC-DC1
rack: A-02
switch1:
Hostname: 10.1.1.10
Groups: ios_switches, production
Data:
location: NYC-DC1
rack: A-03
======================================================================
Total devices: 3
Total groups: 2
======================================================================
π Troubleshooting¶
"YAMLError: mapping values are not allowed here"¶
Cause: Unquoted string with colon
Fix:
# Wrong
description: Device: Core Router
# Right
description: "Device: Core Router"
"YAMLError: expected , but found"¶
Cause: Indentation error
Fix: Use 2 spaces consistently (not tabs, not 4 spaces)
# Wrong
device:
hostname: router1 # 4 spaces
# Right
device:
hostname: router1 # 2 spaces
"AttributeError: 'NoneType' object has no attribute"¶
Cause: Empty or missing YAML file
Fix:
with open('devices.yaml') as f:
data = yaml.safe_load(f)
# Check if data was loaded
if data is None:
print("Error: YAML file is empty")
sys.exit(1)
π Additional Resources¶
- YAML Official Specification β Full YAML specification
- PyYAML Documentation β Python YAML library docs
- Nornir Inventory Guide β Nornir-specific YAML structure
- PyATS Testbed Documentation β PyATS testbed YAML structure
- YAML Validator Online β Validate YAML syntax online
π― Key Takeaways¶
- β YAML is the standard for network automation data modelling
- β Human-readable format that network engineers can edit without programming
- β Separation of concerns β Keep data (YAML) separate from code (Python)
- β Version control friendly β Track changes, rollback if needed
- β Framework-native β Nornir, PyATS, Ansible all use YAML
- β Security-conscious β Never store plaintext passwords, use vaults
- β Validate before deployment β Catch syntax errors early
π Next Steps¶
You've mastered YAML! Continue your data modelling journey:
- JSON Data Handling for Network Automation (Recommended Next)
- Learn JSON for API interactions
- REST API responses and structured logging
-
When to use JSON vs YAML
- Combine YAML data with Jinja2 templates
- Generate device configurations dynamically
-
Template-driven automation patterns
- Apply YAML inventory in real Nornir automation
-
Parallel execution at scale
- Use YAML testbeds for network validation
- Enterprise testing patterns
Remember: YAML is the foundation of structured network automation. Master it now, use it everywhere later.
β Back to Intermediate Tutorials | Continue to JSON Tutorial β
Need help applying this in a live Cisco environment?
If you want this pattern implemented, governed, or adapted for your estate, use the contact page to start a discovery conversation or review how Nautomation Prime delivers engagements.