NetStacksNetStacks

Example Templates

TeamsEnterprise

Production-ready Jinja2 template examples for VLAN, ACL, BGP, OSPF, and interface configurations across Cisco IOS, NX-OS, Juniper JunOS, and Arista EOS.

Overview

This page provides production-ready template examples for common network configurations. Each example includes the complete Jinja2 template source, the required variable values, and the rendered output so you can see exactly what the template produces.

The examples cover multiple vendor platforms and configuration types:

ExamplePlatformUse Case
VLAN ConfigurationCisco IOSCreate VLANs with SVI gateways
Access Control ListCisco IOSExtended ACL with permit/deny rules
BGP NeighborCisco NX-OSeBGP peering with route maps
OSPF AreaJuniper JunOSOSPF area with interface costs
Interface ConfigurationArista EOSL3 interfaces with VRF assignment

Copy any example into a new template, adjust the variables for your environment, and deploy through a stack.

How It Works

Each example follows the same pattern:

  1. Template source — The raw Jinja2 code you paste into a new template
  2. Variables — A JSON object showing the expected variable names, types, and example values
  3. Rendered output — The configuration text produced when the template is rendered with the example variables

Examples are designed for specific vendor CLI syntax. When adapting an example for a different platform, you will need to change the command syntax. For example, Cisco IOS uses interface GigabitEthernet0/1 while Juniper JunOS usesset interfaces ge-0/0/0. The template logic (loops, conditionals, variable patterns) translates across platforms.

Multi-Vendor Templates

You can build a single template that supports multiple vendors by branching on a platform variable: {% if platform == 'cisco_ios' %} ... {% elif platform == 'juniper_junos' %} ... {% endif %}. See the Jinja2 Syntax page for conditional patterns.

Step-by-Step Guide

Follow these steps to use any example from this page.

Step 1: Choose an Example

Browse the examples below and find one that matches your configuration use case. Each example targets a specific vendor platform and configuration type.

Step 2: Create a New Template

In NetStacks, create a new template and paste the example's Jinja2 source into the template content field. Give it a descriptive name like vlan-config-ios or bgp-neighbor-nxos.

Step 3: Adjust Variables for Your Environment

The example variable values use realistic but generic data. Replace them with values specific to your network: your VLAN IDs, your ASN, your IP addressing scheme.

Step 4: Preview the Rendered Output

Use the render/preview feature to verify the output matches what you expect for your environment. Compare the rendered output to a known-good device config to catch any issues.

Step 5: Deploy to Target Devices

Create a stack with the template, assign target devices, provide variable values, and deploy. Use dry-run mode first to review the diff before pushing changes.

Code Examples

1. VLAN Configuration (Cisco IOS)

Creates VLANs and their corresponding SVI gateway interfaces on a Cisco IOS switch.

vlan-config-ios.j2jinja2
{# VLAN Configuration Template - Cisco IOS #}
{# Variables: vlans (list of objects with id, name, gateway, mask) #}

{% for vlan in vlans %}
vlan {{ vlan.id }}
 name {{ vlan.name }}
!
{% endfor %}
{% for vlan in vlans %}
interface Vlan{{ vlan.id }}
 description {{ vlan.name }} Gateway
 ip address {{ vlan.gateway }} {{ vlan.mask }}
{% if vlan.dhcp_helper is defined %}
 ip helper-address {{ vlan.dhcp_helper }}
{% endif %}
 no shutdown
!
{% endfor %}
vlan-variables.jsonjson
{
  "vlans": [
    { "id": 100, "name": "USERS", "gateway": "10.1.100.1", "mask": "255.255.255.0", "dhcp_helper": "10.0.0.50" },
    { "id": 200, "name": "VOICE", "gateway": "10.1.200.1", "mask": "255.255.255.0", "dhcp_helper": "10.0.0.50" },
    { "id": 300, "name": "PRINTERS", "gateway": "10.1.300.1", "mask": "255.255.255.0" },
    { "id": 999, "name": "NATIVE", "gateway": "10.1.999.1", "mask": "255.255.255.0" }
  ]
}
vlan-rendered.txttext
vlan 100
 name USERS
!
vlan 200
 name VOICE
!
vlan 300
 name PRINTERS
!
vlan 999
 name NATIVE
!
interface Vlan100
 description USERS Gateway
 ip address 10.1.100.1 255.255.255.0
 ip helper-address 10.0.0.50
 no shutdown
!
interface Vlan200
 description VOICE Gateway
 ip address 10.1.200.1 255.255.255.0
 ip helper-address 10.0.0.50
 no shutdown
!
interface Vlan300
 description PRINTERS Gateway
 ip address 10.1.300.1 255.255.255.0
 no shutdown
!
interface Vlan999
 description NATIVE Gateway
 ip address 10.1.999.1 255.255.255.0
 no shutdown
!

2. Access Control List (Cisco IOS)

Creates an extended ACL with permit and deny rules from a structured list.

acl-extended-ios.j2jinja2
{# Extended ACL Template - Cisco IOS #}
{# Variables: acl_name, acl_number, rules (list), enable_logging #}

ip access-list extended {{ acl_name }}
{% for rule in rules %}
 {{ rule.action }} {{ rule.protocol }} {{ rule.source }} {{ rule.source_wildcard }} {{ rule.destination }} {{ rule.destination_wildcard }}{% if rule.port is defined %} eq {{ rule.port }}{% endif %}{% if enable_logging | default(false) %} log{% endif %}

{% endfor %}
 deny ip any any{% if enable_logging | default(false) %} log{% endif %}

!
interface {{ apply_interface }}
 ip access-group {{ acl_name }} {{ acl_direction | default('in') }}
acl-variables.jsonjson
{
  "acl_name": "ACL-SERVERS-INBOUND",
  "rules": [
    { "action": "permit", "protocol": "tcp", "source": "10.1.100.0", "source_wildcard": "0.0.0.255", "destination": "10.1.50.0", "destination_wildcard": "0.0.0.255", "port": "443" },
    { "action": "permit", "protocol": "tcp", "source": "10.1.100.0", "source_wildcard": "0.0.0.255", "destination": "10.1.50.0", "destination_wildcard": "0.0.0.255", "port": "22" },
    { "action": "permit", "protocol": "icmp", "source": "10.1.0.0", "source_wildcard": "0.0.255.255", "destination": "10.1.50.0", "destination_wildcard": "0.0.0.255" }
  ],
  "enable_logging": true,
  "apply_interface": "GigabitEthernet0/1",
  "acl_direction": "in"
}
acl-rendered.txttext
ip access-list extended ACL-SERVERS-INBOUND
 permit tcp 10.1.100.0 0.0.0.255 10.1.50.0 0.0.0.255 eq 443 log
 permit tcp 10.1.100.0 0.0.0.255 10.1.50.0 0.0.0.255 eq 22 log
 permit icmp 10.1.0.0 0.0.255.255 10.1.50.0 0.0.0.255 log
 deny ip any any log
!
interface GigabitEthernet0/1
 ip access-group ACL-SERVERS-INBOUND in

3. BGP Neighbor Configuration (Cisco NX-OS)

Configures BGP neighbors with address families and route maps on Cisco NX-OS.

bgp-config-nxos.j2jinja2
{# BGP Configuration Template - Cisco NX-OS #}
{# Variables: bgp_asn, router_id, neighbors (list), networks (list) #}

feature bgp
!
router bgp {{ bgp_asn }}
  router-id {{ router_id }}
  log-neighbor-changes
{% for neighbor in neighbors %}
  neighbor {{ neighbor.ip }} remote-as {{ neighbor.remote_asn }}
    description {{ neighbor.description | default('BGP Peer') }}
    address-family ipv4 unicast
{% if neighbor.route_map_in is defined %}
      route-map {{ neighbor.route_map_in }} in
{% endif %}
{% if neighbor.route_map_out is defined %}
      route-map {{ neighbor.route_map_out }} out
{% endif %}
{% if neighbor.max_prefix is defined %}
      maximum-prefix {{ neighbor.max_prefix }} 80 restart 5
{% endif %}
{% endfor %}
  address-family ipv4 unicast
{% for network in networks %}
    network {{ network.prefix }}/{{ network.mask_length }}
{% endfor %}
bgp-variables.jsonjson
{
  "bgp_asn": "65001",
  "router_id": "10.255.0.1",
  "neighbors": [
    {
      "ip": "10.0.0.2",
      "remote_asn": "65002",
      "description": "Transit - Provider-A",
      "route_map_in": "RM-PROVIDER-A-IN",
      "route_map_out": "RM-PROVIDER-A-OUT",
      "max_prefix": 10000
    },
    {
      "ip": "10.0.0.6",
      "remote_asn": "65003",
      "description": "Peering - IX-East",
      "route_map_in": "RM-IX-EAST-IN",
      "max_prefix": 5000
    }
  ],
  "networks": [
    { "prefix": "10.1.0.0", "mask_length": 16 },
    { "prefix": "10.2.0.0", "mask_length": 16 }
  ]
}
bgp-rendered.txttext
feature bgp
!
router bgp 65001
  router-id 10.255.0.1
  log-neighbor-changes
  neighbor 10.0.0.2 remote-as 65002
    description Transit - Provider-A
    address-family ipv4 unicast
      route-map RM-PROVIDER-A-IN in
      route-map RM-PROVIDER-A-OUT out
      maximum-prefix 10000 80 restart 5
  neighbor 10.0.0.6 remote-as 65003
    description Peering - IX-East
    address-family ipv4 unicast
      route-map RM-IX-EAST-IN in
      maximum-prefix 5000 80 restart 5
  address-family ipv4 unicast
    network 10.1.0.0/16
    network 10.2.0.0/16

4. OSPF Area Configuration (Juniper JunOS)

Configures an OSPF area with interface assignments, costs, and passive flags using Juniper JunOS "set" syntax.

ospf-area-junos.j2jinja2
{# OSPF Area Configuration - Juniper JunOS #}
{# Variables: ospf_area, router_id, interfaces (list) #}

set routing-options router-id {{ router_id }}
set protocols ospf area {{ ospf_area }} area-type {{ area_type | default('normal') }}
{% for intf in interfaces %}
set protocols ospf area {{ ospf_area }} interface {{ intf.name }} interface-type {{ intf.type | default('p2p') }}
{% if intf.cost is defined %}
set protocols ospf area {{ ospf_area }} interface {{ intf.name }} metric {{ intf.cost }}
{% endif %}
{% if intf.passive | default(false) %}
set protocols ospf area {{ ospf_area }} interface {{ intf.name }} passive
{% endif %}
{% if intf.authentication_key is defined %}
set protocols ospf area {{ ospf_area }} interface {{ intf.name }} authentication md5 1 key {{ intf.authentication_key }}
{% endif %}
{% endfor %}
{% if bfd_enabled | default(false) %}
{% for intf in interfaces %}
{% if not intf.passive | default(false) %}
set protocols ospf area {{ ospf_area }} interface {{ intf.name }} bfd-liveness-detection minimum-interval 300
{% endif %}
{% endfor %}
{% endif %}
ospf-variables.jsonjson
{
  "ospf_area": "0.0.0.0",
  "router_id": "10.255.0.1",
  "area_type": "normal",
  "bfd_enabled": true,
  "interfaces": [
    { "name": "xe-0/0/0.0", "type": "p2p", "cost": 10, "authentication_key": "0spfK3y!" },
    { "name": "xe-0/0/1.0", "type": "p2p", "cost": 100 },
    { "name": "lo0.0", "type": "p2p", "passive": true }
  ]
}
ospf-rendered.txttext
set routing-options router-id 10.255.0.1
set protocols ospf area 0.0.0.0 area-type normal
set protocols ospf area 0.0.0.0 interface xe-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface xe-0/0/0.0 metric 10
set protocols ospf area 0.0.0.0 interface xe-0/0/0.0 authentication md5 1 key 0spfK3y!
set protocols ospf area 0.0.0.0 interface xe-0/0/1.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface xe-0/0/1.0 metric 100
set protocols ospf area 0.0.0.0 interface lo0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface lo0.0 passive
set protocols ospf area 0.0.0.0 interface xe-0/0/0.0 bfd-liveness-detection minimum-interval 300
set protocols ospf area 0.0.0.0 interface xe-0/0/1.0 bfd-liveness-detection minimum-interval 300

5. Interface Configuration (Arista EOS)

Configures Layer 3 interfaces with IP addressing, VRF assignment, and optional VRRP on Arista EOS switches.

interface-config-eos.j2jinja2
{# Interface Configuration Template - Arista EOS #}
{# Variables: interfaces (list with name, ip, mask, description, vrf, vrrp_enabled, vrrp_vip, vrrp_priority) #}

{% for intf in interfaces %}
interface {{ intf.name }}
   description {{ intf.description }}
{% if intf.vrf is defined %}
   vrf {{ intf.vrf }}
{% endif %}
   ip address {{ intf.ip }}/{{ intf.prefix_length }}
{% if intf.mtu is defined %}
   mtu {{ intf.mtu }}
{% endif %}
{% if intf.vrrp_enabled | default(false) %}
   vrrp {{ intf.vrrp_group | default(1) }} ipv4 {{ intf.vrrp_vip }}
   vrrp {{ intf.vrrp_group | default(1) }} priority {{ intf.vrrp_priority | default(100) }}
{% endif %}
   no shutdown
!
{% endfor %}
interface-variables.jsonjson
{
  "interfaces": [
    {
      "name": "Ethernet1/1",
      "description": "Uplink to Spine-01",
      "ip": "10.0.1.1",
      "prefix_length": 31,
      "mtu": 9214
    },
    {
      "name": "Ethernet1/2",
      "description": "Uplink to Spine-02",
      "ip": "10.0.1.3",
      "prefix_length": 31,
      "mtu": 9214
    },
    {
      "name": "Vlan100",
      "description": "Users Gateway",
      "vrf": "PROD",
      "ip": "10.1.100.2",
      "prefix_length": 24,
      "vrrp_enabled": true,
      "vrrp_vip": "10.1.100.1",
      "vrrp_priority": 110
    },
    {
      "name": "Vlan200",
      "description": "Voice Gateway",
      "vrf": "PROD",
      "ip": "10.1.200.2",
      "prefix_length": 24,
      "vrrp_enabled": true,
      "vrrp_vip": "10.1.200.1",
      "vrrp_priority": 100
    }
  ]
}
interface-rendered.txttext
interface Ethernet1/1
   description Uplink to Spine-01
   ip address 10.0.1.1/31
   mtu 9214
   no shutdown
!
interface Ethernet1/2
   description Uplink to Spine-02
   ip address 10.0.1.3/31
   mtu 9214
   no shutdown
!
interface Vlan100
   description Users Gateway
   vrf PROD
   ip address 10.1.100.2/24
   vrrp 1 ipv4 10.1.100.1
   vrrp 1 priority 110
   no shutdown
!
interface Vlan200
   description Voice Gateway
   vrf PROD
   ip address 10.1.200.2/24
   vrrp 1 ipv4 10.1.200.1
   vrrp 1 priority 100
   no shutdown
!

Questions & Answers

Q: Can I modify these examples for my environment?
A: Yes. These examples are starting points. Copy the template source into a new NetStacks template, then modify the variable names, default values, conditional logic, and command syntax to match your network standards. The template structure and Jinja2 patterns are the same regardless of your specific values.
Q: Do these examples work across different vendor platforms?
A: Each example is written for a specific platform (Cisco IOS, NX-OS, Juniper JunOS, or Arista EOS) because CLI syntax differs between vendors. The Jinja2 template logic (loops, conditionals, filters) works the same everywhere — you only need to change the configuration commands themselves.
Q: How do I handle vendor-specific syntax differences?
A: You have two options: create separate templates per vendor, or create a single multi-vendor template that branches on a platform variable using {% if platform == 'cisco_ios' %} conditionals. Separate templates are simpler to maintain; multi-vendor templates reduce duplication.
Q: Can I combine multiple examples into a single stack?
A: Yes. A Stack Template can include multiple templates as services, each with an execution order. For example, you might have a VLAN template (order 1), an interface template (order 2), and an OSPF template (order 3). They deploy sequentially to each target device.
Q: Where can I find more template examples?
A: This page covers the most common configuration types. For additional patterns, see the Jinja2 Syntax page for advanced template techniques like macros and inheritance. You can also export templates from existing deployments as examples for new configurations.
Q: Are these examples tested against real devices?
A: The examples use standard CLI syntax for each vendor platform and produce valid configuration output. However, your specific device software version may have minor syntax differences. Always use the preview and dry-run features to verify output before deploying to production devices.

Troubleshooting

Template produces wrong syntax for my device

Verify that the template matches your device platform and software version. Cisco IOS, IOS-XE, NX-OS, and IOS-XR all use different CLI syntax. The examples specify the target platform in the template comment header. If your device uses a different syntax, modify the template commands accordingly.

Variable name mismatches when adapting an example

If you rename variables in the template, make sure to update all references. For example, if you rename vlans to vlan_list, update both the {% for %} loop and the variable values you provide at deployment. Use the extracted variable list to verify all variable names match.

Loop syntax errors with nested data structures

When looping over objects with nested properties (like {{ vlan.id }}), make sure your variable values use the exact property names the template expects. JavaScript-style ID versus Python-style id will cause an undefined property error.

Debugging Variable Structures

If a template produces unexpected output, render it with a minimal set of variables first. Add complexity incrementally — start with one loop iteration, verify the output, then add more items.

Rendered output has extra whitespace or blank lines

Jinja2 preserves whitespace around control tags by default. Use whitespace control dashes ({%- -%}) on loop and conditional tags to strip extra blank lines from the output. See the Jinja2 Syntax page for whitespace control details.

Continue building your template library with these related resources: