How to Generate Device Configurations with NetBox

A common experience for a network engineer starting a new job is spending time getting acclimated to the environment. How is the network designed? What tools are in place? How do we deploy new hardware? inevitably they are pointed to the repository of network configuration “templates”.

Sometimes this is a shared folder filled with text documents titled something similar to “access-switch-template (Copy 4) updated on 2019-02-28 by jim - USE THIS ONE.txt” that have been handed down from engineer to engineer over the years.

Here’s a simple example of what one of those might look like:

hostname [HOSTNAME]

vlan [WORKSTATION VLAN ID] name Workstations

int range eth [RANGE OF EDGE PORTS] switchport mode access switchport access vlan [WORKSTATION VLAN ID]

int range eth [RANGE OF UPLINK PORTS] description Uplink channel-group [PORT CHANNEL GROUP NUMBER] mode on

int po [UPLINK PORT
CHANNEL NUMBER] description Uplink switchport mode trunk switchport
trunk allowed vlan all channel-group 10 mode on

int vlan [MANAGEMENT VLAN NUMBER] ip address [MANAGEMENT VLAN IP] [MANAGEMENT VLAN SUBNET]

While it can feel simple to keep a folder filled with text files, it enviably fails over time.

Taking advantage of Jinja2 templating has become surprisingly simple and is far superior to doing a search-and-replace in text documents. Generating switch templates using Jinja2, with NetBox as the source of truth, has been possible for quite some time. Here is a great example of this from 2021 showing off how templates can be generated in conjunction with NetBox. As of version 3.5, which shipped in late April, configuration templating is a native feature within NetBox.

For our example, we’re going to take a small switch and replicate the above template, using nothing but NetBox data and built-in features.

Our first step is going to be setting our HQ-IT-Staging-Switch up so Jinja2 has the right information to use when filling in our template.

HQ IT Staging Switch

Breaking our switch down we have:

  • GigabitEthernet0/0 unused and shut down.
  • GigabitEthernet1/0/1 enabled and configured for the voice VLAN (200)
  • GigabitEthernet1/0/2 – 5 enabled and configured for the data VLAN (100)
  • TenGigabitEthernet1/1/1 – 2 enabled and configured to be part of Port-Channel10
  • Port-Channel10 enabled and configured to be a trunk port for our two VLANs
  • VLAN900 our management VLAN configured with an IP address

Let’s Build a Basic Template

Next, we’ll set up the first iteration of our Jinja2 template by navigating to Provisioning > Config Templates and then click the Add button.

From there, we’ll use the following code for our basic template.

hostname {{ device.name }}

{% for interface in
device.interfaces.all() %} interface {{ interface.name }} switchport
mode access switchport access vlan {{ interface.untagged_vlan.vid }} {%
endfor %}

Breaking down our template:

  • {{ device.name }} extracts the name of our device
  • {% for interface in device.interfaces.all() %} begins a loop to examine every interface on the device
  • {{ interface.name }} allows us to access each interface configuration
  • switchport mode access configures our switchport mode
  • {{ interface.untagged_vlan.vid }} extracts the VLAN id that is assigned

    Once saved, we’ll go back to our device and select this new template for use.

    Once we’ve applied the template, and choose Render Config from the device.

We’re going to see that template come to life and generate our basic configuration.

We can see this was successful for our access ports, but we have some clear issues here.

  • GigabitEthernet0/0 is supposed to be shut down
  • TenGigabitEthernet1/1/1-2 are supposed to be part of Port-Channel10
  • Port-Channel10 is supposed to be a trunked uplink port
  • VLAN900 is supposed to be our management VLAN

This means we need to improve our template to take all of these scenarios into consideration.

Let’s Build a Better Template!

Jinja2 templating can be feel daunting at first. Rather than take a step by step approach to teaching all the ins-and-outs of templating, we’re going to jump directly into a fully created template. I’ll step through the template, each of its functions, what they are doing, and reference each action by line number.

hostname {{ device.name }}

{% for vl in device.site.vlans.all() %} vlan {{vl.vid }} name {{ vl.name }} {%- endfor %}

{% for interface in
device.interfaces.all() %} {%- if interface.mgmt_only == false %}
interface {{ interface.name }} {%- if interface.enabled == false %}
shutdown {%- elif interface.enabled == true %} no shutdown {%- endif
%} {%- if interface.mode == "access" %} switchport mode access
switchport access vlan {{ interface.untagged_vlan.vid }} {%- elif
interface.mode == "tagged" %} switchport mode trunk {%- for vlan in
interface.tagged_vlans.all() %} switchport trunk allowed vlan add {{
vlan.vid }} {%- endfor %} {%- elif "tagged-all" in interface.mode %}
switchport mode trunk switchport trunk allowed vlan all {%- else %} {%-
if interface.lag != None %} channel-group {% for char in
interface.lag.name %}{%- if char.isdigit() %}{{ char }}{%- endif %}{%-
endfor %} mode on {%- endif %} {%- endif %} {% else %} interface {{
interface.name }} description Management VLAN {%- if interface.enabled
== false %} shutdown {%- elif interface.enabled == true %} no
shutdown {%- endif %} ip address {{ device.primary_ip.address }} {{
device.primary_ip.address.netmask }} {%- endif %} {%- endfor %}

  • Line 1: Set the host name
  • Line 3: Iterate through every VLAN that exists at the site where the device resides
  • Line 4 and 5: Create the Layer 2 VLAN on the switch
  • Line 8 – 42: A loop that Iterates through every interface on the device
  • Line 9 – 31: Apply the following to interfaces that are NOT marked as management
  • Lines 11 and 12: If the interface is not enabled, shutdown
  • Lines 13 and 14: If the interface is enabled, no shutdown
  • Lines 16 – 18: If the interface is an access port, set the appropriate mode and untagged VLAN
  • Lines 19 – 23: If the interface is a trunk port, set the appropriate mode
  • Lines 21 and 22: With the trunk port, add each assigned VLAN
  • Lines 24 – 27: If the interface is a trunk port with all VLANs, set the appropriate configuration
  • Lines 28 – 30: If the interface is configured with any LAG information, set the appropriate channel-group X mode on
  • Line 32: By this point we’ve sorted out all non-management interfaces and are only left with the configuration for management marked interfaces
  • Lines 32 – 41: Configure our management VLAN interface
  • Lines 35 – 39: Configure the interface operational status accordingly
  • Line 40: Assign the management IP to the interface

When we use this updated template, we now see a much better representation of a configuration.

And there you have it!

Once you’ve mastered this process you can create templates for different device functions, different device types, different stages of operation, and even totally different vendors.

A Closing Note About Whitespace

White space with Jinja2 templating can be handled in multiple different ways. For our example I used manual whitespace trimming. Any place you see a percent sign combined with a minus sign {%- or -%} I’m manually trimming and setting spacing for the configuration so that it represents typical “show run” output

Share the Post:

Related Posts