Using Velocity to create Plug and Play templates in DNA Center – Part 4

October 4, 2018 - 6 Comments

Earlier blog posts in this Cisco DNA Center series gave an overview of PnP, how to use the API and an introduction to the configuration template editor.  This blog post explores creating templates in velocity.  Velocity is a template programming language, used in Cisco Prime Infrastructure and APIC-EM.

Template Engine

The template engine can be used for either Day-0 (PnP) or Day-N (provisioning) changes.  There are a couple of subtle implications of this.

  1. All templates are rendered on DNAC. This means the velocity language does not have access to the current configuration of the device.
  2. PnP templates are pulled from DNAC as a single configuration file and copied to running configuration. Note that composite templates are not currently supported.
  3. Day-N configuration pushes the rendered configuration to the device. This mode supports interactive commands as well as exec commands. EEM scripts can be used to address this limitation for PnP.

Basic Variables

At the heart of any template is the concept of a “variable”.  Variables allow parts of a configuration to be customized for a specific device, while ensuring other parts are standardized across all devices.   A single configuration template is required, but it can be applied to many devices.  In velocity, variables begin with “$”.   A trivial example follows.

hostname  $hname 

To configure a hostname for a network device, the cli command “hostname adam-router” is used. “adam-router” is the name of the device. When applying this template to a set of devices, the only thing that changes is the variable ( $hname ).

Advanced Variables

In the example above, there may be a requirement to always end the hostname with “-router”.  The “{}” can be used to specify which part of a string is a variable and which is a string.  In the example below, the suffix will always be “-router” and $hname would be set to “adam” to achieve a similar result to the first example.

hostname  ${hname}-router

When the variable $hname is set to “adam” the template renders as

hostname adam-router

Escaping Variables

Occasionally “$” is required to be used in the configuration for something other than a variable. This is often seen in encrypted passwords for example.  It is possible to mark  these as “not a variable” in the UI.

It is also possible to create a dummy variable for “$” and use that instead.

#set ($d = "$")
enable secret 5 werw${d}2dsf3423${d}dd${d}

In the example above, a variable “d” is defined as “$”.  The “${d}” syntax is used where ever a “$” is required.  The template above renders as

enable secret 5 werw$2dsf3423$dd$


Macros are a great way of encapsulating a block of CLI. For example, define a configuration block for an uplink. For example, define a configuration block for an uplink.  Once the macro “uplink_interface” has been defined, it can be referenced as #uplink_interface

switchport trunk native vlan 999
switchport mode trunk
channel-group 1 mode active

interface Ten1/0/1

This will render as:

interface Ten1/0/1
switchport trunk native vlan 999
switchport mode trunk
channel-group 1 mode active


Loops are a powerful control construct that can be used in templates.  One common application is when combined with macros.  The macro “uplink_interface” was defined above, and it needs to be applied to every uplink in a switch stack.  A variable called stack_count is provided to the template which contains the number of switches in the stack.

#set ($stack_member_count = $stack_count)

#foreach($switch in [1..${stack_member_count}])
interface Ten${switch}/0/1

When the variable “stack_count” is set to “2”, the template renders as shown:

interface Ten1/0/1
switchport trunk native vlan 999
switchport mode trunk
channel-group 1 mode active

interface Ten2/0/1
switchport trunk native vlan 999
switchport mode trunk
channel-group 1 mode active

It is important to define the data type of $stack_count as an integer.  An integer will be treated as a number so the range [1..4] is shorthand for [1,2,3,4] for example.  The earlier blog showed how to change the type of a variable.  Here is a reminder

Manipulating IP Addresses

The final example shows how to manipulate IP addresses.  A common example is supplying an IP address and needing to create the default gateway in a configuration file.    The variable $ip has been defined as a string with IP address syntax.  The split command breaks the string up on octet boundaries and returns an array.  This is assigned to the variable $octets.

#set ($octets = $ip.split('\.'))

gateway $octets[0].$octets[1].$octets[2].1

If $ip is set to “”, the template will render as


Optionally the data type of “$ip” can be set to ipAddress. This will do input validation in the UI of the template.  If left as Data Type string, then any text could be entered.

What Next?

This blog shows more detail on the template language – velocity.  The next blog in the series will cover some advanced use cases

In the meantime, if you would like to learn more about this, you could visit  Cisco DevNet. DevNet has further explanations about this. In addition, we have a Github repository where you can get examples related to PnP.

Thanks for reading!


In an effort to keep conversations fresh, Cisco Blogs closes comments after 60 days. Please visit the Cisco Blogs hub page for the latest content.


  1. new to working with dnac and python etc

    with Mgig cards and non mgig cards specifying int ranges makes it more difficult. Is there a way to automate this with running commands against the switch or without user input?

    Is there a way to call on a csv file for variables so I do not have to input them every time I bring up a switch in my environment?

  2. Looking forward for advanced use cases. I am very new to VTL and DNAC, but am familiar with Python and Jinja (I was a bit disappointed that DNAC doesn't support Jinja templates). What I am trying to understand is this…
    Let's say I have a number of Regular Templates defined, such as AAA-TACACS and AAA-LocalCredentials… both templates have if/elseif/else block that use the same variable ($region). In my understanding, once I combine both into a Composite template, I will have to define region value TWICE…

    Is there a way to use variables that SPAN across multiple templates, bu tonly define their values ONCE? Thank you!

    • at this point there is no concept of global variables.
      There is the concept of system variables, where i can get access to site specific settings. This is not available in PnP as the device has not been assigned to a site.

  3. Looking for more advanced examples