Skip to main content

Command Palette

Search for a command to run...

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

Published
β€’9 min read
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:

  1. 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.

  2. Data Types: YAML supports a variety of data types, including scalars (strings, numbers, Booleans), lists, and dictionaries (also known as maps or hashes).

  3. 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.

  4. Comments: YAML permits comments, which can be added using the # symbol. This feature is beneficial for including explanations or notes within the configuration files.

  5. 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!

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