Files
the_information_nexus/tech_docs/Jinja2_guide.md

10 KiB
Raw Blame History

This is exactly the kind of tactical, network-focused Jinja2 guide I wish I had when I started automating configs. Lets sharpen it further with battle-tested patterns and CLI-ready examples. Heres your playbook:


1. Immediate Wins: Stop Hand-Editing Configs

A. Interface Templating (Multi-Vendor)

Problem: Vendor-specific syntax for the same logical interface.
Solution: One YAML → Cisco/Juniper/Arista templates.

vars/interface.yml

interfaces:
  - name: Eth1/1
    description: "Server Farm Uplink"
    mode: trunk
    vlans: [100, 200]
    mtu: 9214

templates/cisco_interface.j2

interface {{ interface.name }}
 description {{ interface.description }}
 {% if interface.mode == 'trunk' %}
 switchport mode trunk
 switchport trunk allowed vlan {{ interface.vlans | join(',') }}
 {% endif %}
 mtu {{ interface.mtu }}

templates/juniper_interface.j2

interfaces {
    {{ interface.name | replace('Eth','ge-') }} {
        description "{{ interface.description }}";
        {% if interface.mode == 'trunk' %}
        unit 0 {
            family ethernet-switching {
                vlan members [ {{ interface.vlans | join(' ') }} ];
            }
        }
        {% endif %}
        mtu {{ interface.mtu }};
    }
}

Render Both:

# Cisco
jinja2 templates/cisco_interface.j2 vars/interface.yml
# Juniper
jinja2 templates/juniper_interface.j2 vars/interface.yml

B. BGP Config Generation (With Error-Prone Logic)

Problem: Complex BGP configs with neighbor policies.
Solution: Template + YAML with validation.

vars/bgp.yml

bgp:
  asn: 65001
  neighbors:
    - ip: 10.0.0.2
      remote_as: 65002
      policies: [ "PREVENT_LEAK" ]
    - ip: 192.168.1.1
      remote_as: 65123
      policies: [ "CUSTOMER_ROUTES" ]

templates/bgp.j2

router bgp {{ bgp.asn }}
  {% for neighbor in bgp.neighbors %}
  neighbor {{ neighbor.ip }} remote-as {{ neighbor.remote_as }}
  {% if "PREVENT_LEAK" in neighbor.policies %}
  neighbor {{ neighbor.ip }} route-map BLOCK_DEFAULT in
  {% endif %}
  {% endfor %}

Key Trick: Use | selectattr to filter neighbors:

{% for neighbor in bgp.neighbors | selectattr("policies", "contains", "CUSTOMER_ROUTES") %}
...customer-specific config...
{% endfor %}

2. Advanced: Network-as-Code Patterns

A. Generate Device-Specific Configs from NetBox API

Problem: NetBox has device data, but CLI configs are manual.
Solution: Fetch NetBox data → Jinja2.

fetch_netbox_data.py

import requests
import json

devices = requests.get("https://netbox/api/dcim/devices/").json()
with open('vars/netbox_devices.yml', 'w') as f:
    json.dump(devices, f)

templates/netbox_cisco.j2

hostname {{ device.name }}
{% for iface in device.interfaces %}
interface {{ iface.name }}
 description {{ iface.description }}
{% endfor %}

Render All Devices:

python fetch_netbox_data.py
jinja2 templates/netbox_cisco.j2 vars/netbox_devices.yml

B. Auto-Generate Port-Channel Configs (LACP)

Problem: Port-channel members change frequently.
Solution: Dynamic YAML + template.

vars/portchannel.yml

portchannel:
  id: 10
  members: [ "Eth1/1", "Eth1/2" ]
  mode: "active"
  vlan: 100

templates/portchannel.j2

interface Port-channel{{ portchannel.id }}
  channel-group {{ portchannel.id }} mode {{ portchannel.mode }}
  switchport access vlan {{ portchannel.vlan }}

{% for member in portchannel.members %}
interface {{ member }}
  channel-group {{ portchannel.id }} mode {{ portchannel.mode }}
{% endfor %}

3. Pro Tooling: Network-Specific Tricks

A. Ansible + Jinja2 for Zero-Touch Deployment

ansible_playbook.yml

- name: Push Interface Configs
  hosts: switches
  tasks:
    - name: Generate Config
      template:
        src: templates/cisco_interface.j2
        dest: /tmp/{{ inventory_hostname }}.cfg
    - name: Deploy Config
      cisco.ios.ios_config:
        src: /tmp/{{ inventory_hostname }}.cfg

B. Validate Configs Before Deployment

# Check for missing variables
jinja2 --format vars:yaml templates/interface.j2 vars/device.yml

# Dry-run with Ansible
ansible-playbook playbook.yml --check

C. Secret Management (Ansible Vault)

# Encrypted vars.yml
ansible-vault encrypt vars/secrets.yml

Template:

username {{ vaulted_username }}
password {{ vaulted_password }}

4. Your Battle Plan

  1. Start Tonight:

    • Pick 1 repetitive config (VLANs/ACLs) → template it.
    • Render with jinja2-cli locally.
  2. Next Week:

    • Integrate with NetBox/Python to auto-generate configs.
    • Use Ansible to push to devices.
  3. Go Pro:

    • Build a GitLab pipeline that auto-generates configs on NetBox changes.

Example Repo Structure:

network-automation/
├── templates/       # .j2 files
├── vars/            # .yml device vars
├── scripts/         # Python/Ansible helpers
└── outputs/         # Rendered configs

Key Takeaway: Jinja2 turns network configs from manual CLI work into version-controlled code. The more you template now, the more time you save later.


Great pivot! Rego (Open Policy Agents policy language) and Jinja2 serve different but complementary roles in infrastructure automation. Lets break down how they compare, overlap, and where youd use each—with gritty examples tailored to infrastructure/security use cases.


1. Quick Comparison: Jinja2 vs. Rego

Feature Jinja2 Rego
Primary Use Templating (generate configs/docs) Policy enforcement (validate inputs)
Input YAML/JSON/CSV JSON/YAML (often from APIs)
Output Rendered text (configs, CLI commands) Policy decisions (allow/deny + detailed reasons)
Context "Generate this network config" "Is this network config compliant?"
Key Strength Flexibility in text generation Logic-based evaluation with auditing

2. Where Jinja2 and Rego Overlap

Both operate on structured data (YAML/JSON), but solve different problems in the pipeline:

Example Workflow: Network Change Automation

  1. Jinja2: Generates a candidate BGP config from YAML variables.
    router bgp {{ bgp.asn }}
      neighbor {{ neighbor.ip }} remote-as {{ neighbor.asn }}
    
  2. Rego: Validates the generated config before deployment.
    # Prevent BGP peers in untrusted ASNs
    deny[msg] {
      input.kind == "bgp_config"
      not input.neighbor.asn in trusted_asns
      msg := sprintf("BGP peer ASN %v is not trusted", [input.neighbor.asn])
    }
    

3. Rego Use Cases (Where It Shines Over Jinja2)

A. Pre-Deployment Validation

Problem: Ensure Jinja2-generated configs meet security/compliance rules.
Rego Policy (policies/networking.rego):

package networking

# Deny firewall rules that allow SSH from the internet
deny[msg] {
    input.kind == "firewall_rule"
    input.action == "allow"
    input.port == 22
    input.source == "0.0.0.0/0"
    msg := "SSH must not be open to the internet!"
}

# Require VLAN descriptions for auditability
deny[msg] {
    input.kind == "vlan_config"
    not input.description
    msg := "VLANs must have a description"
}

How to Use:

# Validate a Jinja2-rendered config against Rego
opa eval --data policies/networking.rego --input rendered_config.json "data.networking.deny"

B. Multi-Cloud Policy Enforcement

Problem: Enforce tagging standards across AWS/Azure/GCP.
Rego Policy (policies/cloud.rego):

package cloud

# Require 'CostCenter' tag on all resources
deny[msg] {
    input.resource.tags["CostCenter"] == ""
    msg := "All resources must have a CostCenter tag"
}

# Block public S3 buckets
deny[msg] {
    input.kind == "aws_s3_bucket"
    input.acl == "public-read"
    msg := "Public S3 buckets are prohibited"
}

Input (JSON from Terraform/Cloud API):

{
  "kind": "aws_s3_bucket",
  "acl": "public-read",
  "tags": { "CostCenter": "" }
}

Output:

{
  "deny": [
    "All resources must have a CostCenter tag",
    "Public S3 buckets are prohibited"
  ]
}

C. Network Topology Auditing

Problem: Validate that Jinja2-generated network designs follow best practices.
Rego Policy (policies/topology.rego):

package topology

# Core switches must have redundant links
deny[msg] {
    input.device.role == "core"
    count(input.device.links) < 2
    msg := "Core devices must have redundant links"
}

# VLAN 1 should not be used (security best practice)
deny[msg] {
    input.vlan.id == 1
    msg := "VLAN 1 is prohibited for security reasons"
}

4. When to Use Jinja2 vs. Rego

Scenario Tool Why
Generating Cisco/Juniper configs Jinja2 Best at text templating.
Validating configs pre-deploy Rego Logic-based policy checks.
Creating API payloads Jinja2 Structure dynamic JSON/YAML.
Auditing existing infra Rego Query live state for violations.
Documenting network designs Jinja2 Render Markdown/HTML.
Enforcing security rules Rego Evaluate against compliance rules.

5. Pro Tips for Rego in Infrastructure

  1. Test Policies Early: Use opa test to unit test your Rego rules.
    opa test policies/ -v
    
  2. Integrate with CI/CD:
    # GitLab CI example
    validate_policy:
      image: openpolicyagent/opa
      script:
        - opa eval --data policies/ --input $CHANGES "data.deny"
    
  3. Debug with trace:
    # Add to your policy to see evaluation steps
    trace(sprintf("Input: %v", [input]))
    

6. Example End-to-End Flow

  1. Jinja2 Generates Config:
    jinja2 bgp_template.j2 vars.yml > bgp_config.json
    
  2. Rego Validates It:
    opa eval --data policies/ --input bgp_config.json "data.networking.deny"
    
  3. Only deploy if Rego returns no denials.

7. Key Takeaway

  • Jinja2: Your "build" tool (creates configs/scripts/docs).
  • Rego: Your "guardrail" tool (ensures compliance/safety).

Together, they form a powerful pipeline:
Jinja2 (generate) → Rego (validate) → Ansible/Terraform (deploy)

Want a deep dive on a specific Rego use case (e.g., network security policies or cloud governance)? I can break it down with real code.