Skip to content

First Steps

This section provides a simple example of configuring Cisco NX-OS switches using Network-as-Code. It walks through a VXLAN EVPN fabric deployment to get familiar with the NX-OS NAC module and the YAML-driven approach.

The repository used in this example can be found at: https://github.com/netascode/nac-nxos-vxlan-example

This example assumes you have installed the following prerequisites.

NX-API must be enabled on each NX-OS device before Terraform can communicate with it. SSH into each device and run:

feature nxapi

Ensure the device management IP is reachable from the machine running Terraform.

The NX-OS provider authenticates using environment variables:

Terminal window
export NXOS_USERNAME=admin
export NXOS_PASSWORD=password
git clone https://github.com/netascode/nac-nxos-vxlan-example.git
cd nac-nxos-vxlan-example

Open the folder in your preferred editor / IDE.

Step 4: Understand the Module Configuration

Section titled “Step 4: Understand the Module Configuration”

The main.tf file contains the required providers and points to the YAML data and template directories:

terraform {
required_providers {
nxos = {
source = "CiscoDevNet/nxos"
}
}
}
module "nxos" {
source = "netascode/nac-nxos/nxos"
version = "0.3.0"
yaml_directories = ["data/"]
template_directories = ["templates/"]
}

The required_providers block specifies the NX-OS provider from CiscoDevNet. Authentication is handled via the environment variables set in Step 2. The module block points to the NAC NX-OS module, reads all YAML files from the data/ directory, and loads template files from the templates/ directory. Device URLs are defined in the YAML inventory, so no explicit provider block with a URL is needed.

The data/ directory contains YAML files that describe the desired state of the fabric, and the templates/ directory contains reusable configuration templates. Here are the key concepts:

The inventory.nac.yaml file defines global variables, device groups, and devices with their management URLs and per-device variables:

nxos:
global:
variables:
bgp_asn: 65000
anycast_rp_ip: 10.254.254.1
anycast_gw_mac: 00:00:22:22:33:33
device_groups:
- name: FABRIC
templates:
- global
- underlay_ip
- underlay_ospf
- underlay_multicast
- overlay_bgp
- name: LEAFS
templates:
- overlay_vxlan
- name: SPINES
devices:
- name: LEAF1
url: https://10.1.1.1
device_groups:
- FABRIC
- LEAFS
variables:
mgmt_ip: 10.1.1.1
leaf_id: 1
- name: SPINE1
url: https://10.1.1.3
device_groups:
- FABRIC
- SPINES
variables:
mgmt_ip: 10.1.1.3
spine_id: 1

Global variables (like bgp_asn) are shared across all devices. Device groups assign templates to sets of devices — for example, all devices in the FABRIC group receive the underlay and overlay templates. Per-device variables (like leaf_id) allow templates to generate unique configurations per device.

Update the device URLs to match your environment.

Device groups allow you to logically group devices and assign shared templates and variables. They are also used to define services. For example, service_prod_web.nac.yaml defines a Layer 2 service across two leaf switches:

nxos:
device_groups:
- name: SERVICE_PROD_WEB
devices:
- LEAF1
- LEAF2
templates: [service_l2]
variables:
vlan_id: 1001
vlan_name: PROD_WEB

This assigns the service_l2 template to both LEAF1 and LEAF2 with the specified variables. Adding more services is as simple as creating additional YAML files with different device groups and variables.

Templates are external .yaml.tftpl files that generate data model configuration using Terraform template syntax. They support variable substitution and have access to the full inventory via the GLOBAL object. Templates are registered in templates.nac.yaml:

nxos:
templates:
- name: global
type: file
file: templates/global.yaml.tftpl
- name: underlay_ospf
type: file
file: templates/underlay_ospf.yaml.tftpl
- name: service_l2
type: file
file: templates/service_l2.yaml.tftpl

A template file uses variables from the device and global scope, and can iterate over the inventory to discover peers. For example, the service_l2 template generates VLAN, NVE VNI, and EVPN configuration from just two variables:

templates/service_l2.yaml.tftpl
vlan:
vlans:
- id: ${vlan_id}
vn_segment: ${try(vni, try(l2vni_base, 30000) + vlan_id)}
name: ${vlan_name}
interfaces:
nve:
vnis:
- vni: ${try(vni, try(l2vni_base, 30000) + vlan_id)}
evpn:
vnis:
- vni: ${try(vni, try(l2vni_base, 30000) + vlan_id)}
rd: auto
route_target_both_auto: true

Variables like ${vlan_id} are resolved per device from the variables defined in the device group. The GLOBAL object provides access to the full inventory, enabling templates to discover spine or leaf peers dynamically for BGP neighbor or interface configuration.

Initialize Terraform to download all required providers and modules:

terraform init

Preview the changes:

terraform plan

Apply the configuration to push changes to the NX-OS devices:

terraform apply

Type yes to approve.

SSH into the NX-OS devices and verify that the configuration was applied:

show running-config | include hostname
show vlan brief
show ip bgp summary
show nve peers

Run terraform destroy to remove all configuration managed by Terraform:

terraform destroy

Type yes to approve. Verify on the devices that the configuration has been reverted.