Automating ACI using Cisco Nexus as Code

Cisco released a nice toolset based on Terraform to help us automate our fabrics. The mayor benefit of this toolset is that no programming knowledge is required. You don’t even need any Terraform knowledge to get started. It is awesome. I was honored to be able to talk about this solution with Cisco in the Cisco Champion Radio episode about this. That episode can be listened to here:

CCR Automate ACI Deployments with Terraform

This post will go into the getting started with this solution. I’ll check whether I can do another post using this to implement a complete CI/CD pipeline, but there are many blogposts available about creating such a pipeline. For now I wanted to focus on the Cisco ACI specific part.

What is Nexus as Code?

Nexus as code is just a set of tools made available by Cisco to help you automate your network. As the name implies, it’s not limited to ACI deployments. However, to be fair, most of it is currently focussed on the ACI deployments. Cisco Nexus as Code is actually a wrapper around Terraform. The wrapper makes it easier to write Terraform playbooks (or whatever they are called). Because of this you only need to know about the ACI constructs. You don’t have to know how to write Terraform scripts.

Ok, nice. I don’t need to know about Terraform, but what do I need to know?

Actually, you only need to know the policy model of ACI. That means that if you have some experience managing ACI you’re already likely to have sufficient knowledge to manage ACI through Nexus as Code. If you have no experience with either ACI or Terraform you only need to learn ACI. Let’s look at an example, the difference between creating a Tenant, Application Profile, VRF, Bridge Domain and EPG using Terraform versus using Nexus as Code.

The code below is the Terraform code (HCL):

terraform {
  required_providers {
    aci = {
      source = "ciscodevnet/aci"
    }
  }
}

#configure provider with your cisco aci credentials.
provider "aci" {
  # cisco-aci user name
  username = "admin"
  # cisco-aci password
  password = "password"
  # cisco-aci url
  url      = "https://my-cisco-aci.com"
  insecure = true
}

resource "aci_tenant" "Tenant_Terraform_Demo" {
  name        = "Michael_Tenant"
}

resource "aci_vrf" "VRF_Terraform_Demo" {
  tenant_dn = aci_tenant.Michael_Tenant.id
  name      = "demo_vrf"
}

resource "aci_bridge_domain" "BD_Terraform_Demo" {
  tenant_dn          = aci_tenant.Michael_Tenant.id
  name               = "demo_bd"
  relation_fv_rs_ctx = aci_vrf.VRF_Terraform_Demo.name
}

resource "aci_application_profile" "APP_Terraform_Demo" {
  tenant_dn   = aci_tenant.Michael_Tenant.id
  name        = "demo_app"
}

resource "aci_application_epg" "EPG_Terraform_Demo" {
    application_profile_dn  = aci_application_profile.demo_app.id
    name                    = "demo_epg"
    relation_fv_rs_bd       = aci_bridge_domain.demo_bd.id
}

Now compare this to the following code:

apic:
  tenants:
    - name: Michael_Tenant
      vrfs:
        - name: demo_vrf
      bridge_domains:
        - name: demo_bd
          vrf: demo_vrf
      application_profiles:
        - name: demo_app
          endpoint_groups:
            - name: demo_epg
              bridge_domain: demo_bd

Isn’t that a lot simpler? It really looks like what you would do within the GUI. Because it’s nested you don’t have to create references between resources as you would need to do in Terraform.

But wait a minute. In the first paragraph I described it was based on Terraform. Why doesn’t it look like HCL? That’s because the Nexus as Code actually is a wrapper. It creates the corresponding HCL code for you when you execute it.

What can I do with it?

As far as I’ve been able to discover the tool is pretty feature complete. It is possible to stage a complete fabric from scratch. The only thing you need to do first is ensure the APICs go through their initial setup wizard. Afterwards you can add switches, policies, tenents and whatever you can imagine to your fabric using Nexus as code.

Let’s give some examples:

Adding a switch to the fabric:

apic:
  node_policies:
    nodes:
      - id: 1101
        pod: 1
        role: spine
        serial_number: FDO2XXXXXX
        name: ACILAB-SP1101
      - id: 1201
        pod: 1
        role: leaf
        serial_number: FDO2XXXXXX
        name: ACILAB-LF1201

Configuring NTP:

apic:
  fabric_policies:
    pod_policies:
      date_time_policies:
        - name: pool.ntp.org
          ntp_servers:
            - hostname_ip: pool.ntp.org
              Preferred: true
              Mgmt_epg: oob

Configuring vlan pools:

apic:
  access_policies:
    vlan_pools:
      - name: vlanp_mdc
        description: "Multi-Pod vlan pool"
        allocation: static
        ranges:
          - from: 4
            to: 4
            role: external
      - name: vlanp_physical
        description: "On the wire physical vlans"
        allocation: static
        ranges:
          - from: 100
            to: 1999
            role: external
      - name: vlanp_l3out
        description: "L3out vlan pool"
        allocation: static
        ranges:
          - from: 3600
            to: 3699
            role: external
      - name: vlanp_vmware
        description: "VMware vlan pool"
        allocation: dynamic
        ranges:
          - from: 2000
            to: 2999
            role: external

And like you saw earlier you can configure tenants, vrfs, bridge domains and more using Nexus as Code.

Are there any downsides?

There are always downsides to any technology. The biggest one to using Nexus as Code is that, because it uses Terraform, it’s a declarative way of working. What does this mean? That means that Terraform keeps state for your environment. Every time you perform a change it will check the state to see whether the environment is still in it’s intended state before applying the change.

Let’s use an example to clarify that somewhat. Say for example I define an EPG using Nexus as code like this:

apic:
  tenants:
    - name: Michael_Tenant

      application_profiles:
        - name: demo_app
          endpoint_groups:
            - name: EPG1
              bridge_domain: BD1
            - name: EPG2
              bridge_domain: BD1

As you can see I made the mistake of configuring BD1 under EPG2, instead of configuring BD2. Say I notice that error while in the GUI. It’s an easy change to modify the BD in the GUI. Let’s say I do that. Next time I run the Nexus as Code it will however overwrite my manually changed configuration. So BD1 will again be configured under EPG2.

That means that these kinds of changes must be done in the configuration files of Nexus as Code. When you configure something with Nexus as Code you either keep using Nexus as Code for that configuration, or you stop using Nexus as Code at all.

For lab environments this behaviour might actually be something you really want. As it’s easy to revert to a known configuration. For production environments it’s a good idea to consider this behaviour.

Another possible downside is the state file. Since there might be a lot of configuration options in your fabric, your state file might be very big. A big state file slows down the operation of Terraform quite a bit. That means that you have to handle your state files with care. It might be smart to create multiple different statefiles. Maybe one for each tenant, or even one for each VRF in a tenant.

Where to learn more?

Cisco created a very good website to help you get started with Nexus as Code. You can find it here: