Mastering Dynamic Configurations: A Beginner's Guide to Jinja2 - Part 1

In contemporary network automation, the ability to dynamically generate configurations is essential for network engineers operating in multi-vendor environments. Manual configuration is prone to errors, lacks consistency, and is challenging to scale. Jinja2 templates serve as a powerful solution in this context. By integrating structured data (YAML/JSON) with reusable templates, network engineers can produce standardized and validated configurations across numerous devices in a safe and efficient manner.
This week, we are making significant progress by generating configurations dynamically. Rather than manually creating configuration files for each device, we will utilize Jinja2 templates to produce hundreds of configurations from a single data file.
In a real-world scenario, consider the task of configuring 100 new customer sites across Nokia SR OS, Cisco IOS XR, and Juniper routers. Each site requires unique VLANs, IP addresses, and routing configurations. Manually completing this task would take several days. However, using templates, it can be accomplished in just minutes.
Why Choose Jinja2 for Network Automation?
Jinja2 is a versatile and efficient templating engine commonly employed in automation frameworks like Ansible, as well as in custom Python automation scripts. It allows engineers to develop dynamic templates incorporating variables, loops, and conditional logic. This capability makes it particularly well-suited for generating configurations in multi-vendor environments.
Think of it like this:
A mail merge in Microsoft Word
A form letter where you fill in the blanks
A recipe where you substitute ingredients
The Traditional Method (Manual Configuration)
# Customer 1 - Nokia SROS router
/configure service customer 100 create
description "Customer CodedNetwork Corp"
exit
# Customer 2 - Nokia SROS router
/configure service customer 200 create
description "Lazyprogrammer INC"
exit
#...... Repeat for a 100 customers π«
The Modern Approach (Jinja2 Template)
Jinja2 Template:
# Template only once !!! π
/configure service customer {{ customer_id }} create
description "{{ customer_name }}"
exit
Data (YAML file):
customers:
- customer_id: 100
customer_name: "Customer CodedNetwork Corp"
- customer_id: 200
customer_name: "Lazyprogrammer INC"
Result: Generate 100 configurations in seconds.
Understanding YAML
YAML (YAML Ain't Markup Language) is a human-readable data serialization format frequently utilized for configuration files and data exchange between languages with varying data structures. It is crafted to be straightforward to read and write, which makes it a favored option for configuration files in numerous applications, including network automation.
Key features of YAML include:
Simplicity: YAML is crafted to be straightforward to read and write, with a syntax that is more user-friendly compared to other data serialization formats like JSON or XML.
Data Types: YAML supports a variety of data types, including scalars (strings, numbers, Booleans), lists, and dictionaries (also known as maps or hashes).
Indentation: YAML uses indentation to denote the structure of data. The level of indentation indicates the hierarchy of data, similar to how Python uses indentation for code blocks.
Comments: YAML permits comments, which can be added using the
#symbol. This feature is beneficial for including explanations or notes within the configuration files.Compatibility: YAML is compatible with JSON, meaning that any valid JSON file is also a valid YAML file.
In the context of Jinja2 templates, YAML is frequently employed to store structured data that can be integrated into the templates to produce dynamic configurations. This approach facilitates a clear distinction between the data and the template logic, thereby simplifying the management and updating of configurations.
# Key-Value Pairs (Basic Structure)
hostname: core-r1
vendor: nokia
os: sros
# Lists( Arrays)
interfaces:
- name: ethernet-1/1
ip: 10.0.0.1/24
- name: ethernet-1/2
ip: 10.0.1.1/24
# Nested Structures
device:
hostname: core-r1
vendor: nokia
routing:
bgp:
asn: 65001
router_id: 1.1.1.1
# Indentation (Critical Rule in YAML)
router:
bgp:
asn: 65001
# Strings, Integers, and Booleans
hostname: core-r1 # string
asn: 65001 # integer
enabled: true # boolean
# Comments in YAML starts with "#"
hostname: core-r1
vendor: nokia # SR OS platform
Importance of YAML Validation
A YAML validator is a tool or process that ensures a YAML file is syntactically correct, well-structured, and compliant before being utilized in automation pipelines. In network automation, where YAML is used for configuration generation, inventory, and deployments, validation serves as a critical safety layer.
Prevents Automation Failures Prior to Deployment
Automation tools such as Python scripts, Jinja2 templates, and Ansible heavily depend on YAML files. If the YAML is invalid, it can cause the entire pipeline to fail.
Prevents Incorrect Configuration Generation (High Risk)
When utilizing Jinja2 templates, YAML serves as the authoritative source. If the structure is incorrect, the templates may produce incomplete or erroneous configurations.
Safeguards Production Networks Against Human Errors
Most YAML errors result from human mistakes, including:
Incorrect indentation
Duplicate keys
Missing quotes
Typographical errors
In service provider environments, including core routers, software upgrades, and automation health checks, even a minor YAML error can lead to disruptions in:
Routing policies
Interface provisioning
Telemetry pipelines
A validator functions as a pre-change safety control before deployment
Ensures Data Consistency Across Multi-Vendor Environments
In multi-vendor automation environments (such as Nokia SR OS, Cisco IOS XR, and Junos), YAML is used to model device data. Validators ensure:
Ensures correct schema structure
Verifies the presence of required fields (ASN, interfaces, hostname)
Confirms no missing attributes for specific vendors
This is particularly crucial when creating vendor-specific configurations from a unified template.
Supports CI/CD and Git-Based Automation Pipelines
Modern network teams maintain YAML files in Git repositories to facilitate:
Version control
Change tracking
Automated deployments
Validation in CI/CD Pipelines:
Git Commit β YAML Validation β Jinja2 Render β Lab Test β Production Deploy
If validation fails:
Deployment is automatically halted
Prevents incorrect configurations from being applied to devices
Detects Indentation and Syntax Errors (A Common Issue)
YAML is sensitive to indentation, requiring spaces only and not tabs.
A validator promptly identifies:
Incorrect indentation
Invalid nesting
Structural inconsistencies
Enhances Automation Reliability and Scalability
For large-scale automation involving hundreds of routers:
Valid YAML ensures predictable automation behavior.
Invalid YAML leads to unpredictable failures.
Benefits include:
Stable template rendering
Faster troubleshooting
Cleaner automation logs
Reduced rollback scenarios
Essential for Jinja2 Template Rendering (Direct Impact)
Jinja2 consumes YAML as input variables:
data = yaml.safe_load(open("devices.yaml"))
template.render(data)
Security and Compliance Advantages
Validated YAML helps prevent:
Misconfigured access policies
Incorrect ACL deployments
Faulty automation scripts that push unsafe configurations
My preferred YAML Validator is YAML Lint:
Best Practice Workflow for Your Automation Stack
YAML Inventory File
|
v
YAML Validator (yamllint)
|
+-------+--------+
| |
Valid Invalid
| |
v v
Jinja2 Templates Fix Errors
|
v
Config Generation β Device Deployment β Health Checks
Jinja2 Basics
1. Variables
Variables in Jinja2 are represented by double curly braces {{ }}. These act as placeholders, which are substituted with actual data during the template rendering process.
hostname {{ hostname }}
interface {{ interface_name }}
ip address {{ ip_address }}
2. Loops
Loops enable you to iterate over lists such as interfaces, VLANs, BGP neighbors, or services. This functionality is particularly beneficial when configuring multiple interfaces or services on a routers
{% for intf in interfaces %}
interface {{ intf.name }}
description {{ intf.description }}
ip address {{ intf.ip }}
{% endfor %}
3. Conditionals
Conditionals incorporate logic into templates, enabling configurations to adapt dynamically based on factors such as vendor, device role, or feature flags. This is essential in multi-vendor environments where syntax varies across platforms..
{% if vendor == "cisco" %}
router bgp {{ asn }}
{% elif vendor == "nokia" %}
configure router bgp {{ asn }}
{% elif vendor == "juniper" %}
set protocols bgp group external type external
{% endif %}
4. Filters
Filters in Jinja2 are used to transform or format data before rendering it into the final configuration. They help clean, modify, or standardize values automatically.
Common Networking Use Cases:
Uppercase interface names
Formatting IP addresses
Default values for missing fields
hostname {{ hostname | upper }}
interface {{ interface | default("GigabitEthernet0/0") }}
Advanced Jinja2 Features
1. Macros (Reusable Blocks)
{# Define a macro #}
{% macro interface_config(port, vlan, description) %}
interface {{ port }}
description "{{ description }}"
vlan {{ vlan }}
no shutdown
{% endmacro %}
{# Use the macro #}
{% for intf in interfaces %}
{{ interface_config(intf.port, intf.vlan, intf.desc) }}
{% endfor %}
2. Include Other Templates
{# base_template.j2 #}
/configure
{% include 'system_config.j2' %}
{% include 'interface_config.j2' %}
{% include 'routing_config.j2' %}
exit
3. Custom Filters
def ip_increment(ip, increment=1):
"""Increment IP address"""
parts = ip.split('.')
parts[3] = str(int(parts[3]) + increment)
return '.'.join(parts)
# Add to Jinja2 environment
env.filters['ip_increment'] = ip_increment
4. Usage in template
neighbor {{ base_ip|ip_increment(1) }}
neighbor {{ base_ip|ip_increment(2) }}
5. Conditional Includes
{% if device_type == 'nokia' %}
{% include 'nokia_specific.j2' %}
{% elif device_type == 'cisco' %}
{% include 'cisco_specific.j2' %}
{% endif %}
Detailed Internal Jinja2 Logic Flow
How Templates Process Data
Device Inventory (devices.yaml)
|
v
+----------------------+
| Load Data in Python |
| (dict / variables) |
+----------+-----------+
|
v
+----------------------+
| Pass Data to Jinja2 |
| template.render() |
+----------+-----------+
|
+----------+-----------+
| |
v v
+-------------------+ +-------------------+
| Variables | | Conditionals |
| {{ hostname }} | | if vendor == SR OS|
| {{ interfaces }} | | vendor logic |
+-------------------+ +-------------------+
| |
+----------+-----------+
|
v
+----------------------+
| Loops |
| for interface in list|
+----------+-----------+
|
v
+----------------------+
| Filters |
| upper, default, join |
+----------+-----------+
|
v
+----------------------+
| Final Config Output |
| Ready for Deployment |
+----------------------+
Jinja2 + YAML Workflow (Advanced Automation Process)
devices.yaml (YAML Inventory)
|
v
Python Script (Loader)
|
v
Jinja2 Template Engine
|
v
Rendered Router Configuration
|
v
Deployment (SSH / NETCONF / API)
Whatβs Next?
In Part 1, we examined the principles of YAML and Jinja2 and their application in dynamic configuration generation. This enables network engineers to transition from static, manual CLI configurations to automated, data-driven deployments. This methodology ensures consistency, minimizes human error, and expedites deployment in production networks.
In Part 2 , we are going to show practical examples of dynamic configuration generation. We will go through a few use cases :
Use Case 1 : Nokia SROS service Configuration
Use Case 2 : Nokia SROS Interface Configuration
Use Case 3: Multivendor BGP Configuration ( Nokia SROS, Juniper, Cisco)
Questions ?
Reach out on LinkedIn!





