This is **exactly** the kind of tactical, network-focused Jinja2 guide I wish I had when I started automating configs. Let’s sharpen it further with battle-tested patterns and CLI-ready examples. Here’s 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`** ```yaml interfaces: - name: Eth1/1 description: "Server Farm Uplink" mode: trunk vlans: [100, 200] mtu: 9214 ``` **`templates/cisco_interface.j2`** ```jinja 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`** ```jinja 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:** ```bash # 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`** ```yaml 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`** ```jinja 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: ```jinja {% 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`** ```python 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`** ```jinja hostname {{ device.name }} {% for iface in device.interfaces %} interface {{ iface.name }} description {{ iface.description }} {% endfor %} ``` **Render All Devices:** ```bash 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`** ```yaml portchannel: id: 10 members: [ "Eth1/1", "Eth1/2" ] mode: "active" vlan: 100 ``` **`templates/portchannel.j2`** ```jinja 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`** ```yaml - 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** ```bash # 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)** ```yaml # Encrypted vars.yml ansible-vault encrypt vars/secrets.yml ``` **Template:** ```jinja 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.