Automating NX-OS with Netbox and Ansible Automation Platform

Automating NX-OS with Netbox and Ansible Automation Platform

This guide contains a few basic examples to get started automating NX-OS based networks with Netbox and Ansible Automation Platform. While this example is not part of the Nexus-as-Code project, it offers more seasoned Ansible users several examples to get started with driving network changes through Netbox.

Open source configuration management tools like Netbox are gaining traction in the industry. Netbox combines traditional IP address management (IPAM) with datacenter infrastructure management (DCIM). It offers a powerful API and event-driven webhooks which makes it ideal for network automation. Some organizations want to use Netbox as their source of truth for their datacenter infrastructure. Others prefer it as a system of records to store information reflecting the state of the infrastructure. In this example, Netbox is leveraged as a SSoT (Single Source of Truth) for network configuration.

This guide assumes a working installation of Netbox and Ansible Automation Platform. For installation instructions please visit:

It also assumes that the reader has the option to create a private repository to store the Ansible inventory and playbooks. This guide makes use of the SaaS version of Gitlab.

Examples covered here include:

  • Complete interface configuration
  • Switch Virtual Interface (SVI) configuration

It is worth noting that Netbox does not offer many of the Gitops benefits such as branching, versioning, etc. For those that are interested in this, Nautobot is available as an alternative. Nautobot was initially developed as a fork of Netbox. It offers native Git integration along with support for plugins that allow to sync inventory state between different DCIM tools. The examples below can also be used with Nautobot.

Adding device configuration and tags to Netbox

First, Netbox needs to have some device configuration in place so that Ansible Automation Platform can execute Playbooks against those devices. Tags will be used to prevent Ansible Automation Platform from running a job each time a change is made in Netbox. In the Netbox GUI, navigate to Customization > Tags and add a new tag named ansible-int.

Add at least one device to the Netbox inventory. This example assumes 4 Nexus switches as shown below. Tip: Many device types have already been added to the netbox-community devicetype-library.

Add interfaces to the device. Note that it is possible to use make use of ranges to support bulk creation. An example is the range Ethernet1/[1-48] with Type SFP+ (10GE) and Ethernet1/[49-54] with Type QSFP+ (40GE) for a typical 48-port top of rack switch. Keep in mind that the name of the interfaces should match the actual interface names on the device/switch.

Within the device overview window, navigate to Add Components > Interfaces:

Add a range of interfaces to the device:

Note: if the instance of Netbox already contains existing devices and interfaces, This step is optional.

Set up a repository

Now that the initial device configuration has been added to Netbox, the next step is to create a Git repository. As mentioned before, this guide leverages Gitlab. When doing so, be mindful of the visibility settings on the Gitlab project as this repository will store login credentials to the Nexus switches in an inventory.ini file.

Give the new repository a sensible name and select Create project.

Go ahead and clone the repository. The URL can be found in the clone dropdown menu.

git clone git@gitlab.com:<group>/<newrepo>.git
Cloning into '<newrepo>'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.

Populating the repository

Open the cloned folder in a text editor / IDE. Within this folder, create a new folder named collections. In that the collections folder, create a file named requirements.yml. Populate the requirements.yml file with the following:

collections:
- name: cisco.nxos

The cloned folder should now have the following structure:

The requirements.yml file will instruct Ansible Automation Platform which collection it should use.

Next up is the inventory file. Create a new file inventory.yml within the root of the cloned folder, and add the code below. Feel free to customize this based on the requirements and devices that are in scope. Make sure to update the device credentials in ansible_user and ansible_ssh_pass.

[all:vars] represents the variables that apply to all devices. As the management IP address for each device is likely unique, these are specified as group variables such as EVPN-LEAF-1. Make sure to substitute the group variables in the inventory.ini file with the correct device name as configured in Netbox. For example, substitute EVPN-LEAF-1 with my-prod-switch-1 if that matches the device name in Netbox. Lastly, make sure to update the management IP address for each device.

[all:vars]
ansible_connection=  network_cli
ansible_network_os= nxos
ansible_user= admin
ansible_ssh_pass= "password"
[EVPN-LEAF-1]
10.61.124.182
[EVPN-LEAF-2]
10.61.124.183
[EVPN-SPINE-1]
10.61.124.184
[EVPN-SPINE-2]
10.61.124.185

Creating the first playbook

Now that the environment has been set up with a requirements and inventory file, next is the creation of the first Ansible Playbook. This playbook will configure basic interface parameters such as setting the description, speed, duplex, access VLAN, etc.

  1. Create a file within the root of the cloned folder named nxos-int.yml, and add the following code:
- name: Netbox Webhook Ansible Interface
  hosts: 
    - "{{ device_id }}"
  gather_facts: no

  tasks:

  - name: Nxos L2 Interface VLAN
    cisco.nxos.nxos_l2_interfaces:
      config:
      - name: "{{ interface_id }}"
        access:
          vlan: "{{ vlan_id }}"
      state: replaced
    tags:
      - ansible-int

  - name: Nxos Interface
    cisco.nxos.nxos_interfaces:
      config:
      - name: "{{ interface_id }}"
        description: "{{ description }}"
        duplex: "{{ duplex }}"
        mtu: "{{ mtu }}"
        speed: "{{ speed }}"
        enabled: "{{ enabled }}"
      state: merged
    tags:
      - ansible-int
  1. Save the netbox-interfaces.yml file.

Note that variable values such as {{ vlan_id }} will be passed to Ansible Automation Platform through a webhook. These variables will be substituted. When configuring a webhook in Netbox in a later step, the payload is modified in such a way that only the fields that are relevant for the Ansible Playbook are sent. In order to better understand which fields can be passed in the body of the webhook, it can be useful to make use of Postman and the Netbox API. When doing this, make sure to provide the Netbox API key in the authorization tab. An example POST request is shown below. Make sure to substitute device.name with the correct device name and name with any interface.

The folder should now contain the following files and look like this:

  1. The next steps are pushing the local changes to the remote repository. This is done by adding the changes to staging, writing a commit message, followed by pushing the changes to the remote git repository.
~/netbox-aap-nxos main ?3 > git add .             
~/netbox-aap-nxos main +3 > git status #optional command for verification            
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   collections/requirements.yml
        new file:   inventory.ini
        new file:   nxos-int.yml

~/netbox-aap-nxos main +3 > git commit -am "initial code"
[main bec08bf] initial code
 3 files changed, 15 insertions(+)
 create mode 100644 collections/requirements.yml
 create mode 100644 inventory.ini
 create mode 100644 nxos-int.yml

~/netbox-aap-nxos main >1 > git push
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 12 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (6/6), 601 bytes | 601.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0), pack-reused 0
To gitlab.com:ecvd/netbox-aap-nxos.git
   9a65793..bec08bf  main -> main

Setting up Ansible Automation Platform

In order for AAP to read and access the contents of the previously created Git repository it is required to add credentials. In the Ansible Automation Platform GUI, navigate to Resources > Credentials, and select Add. In the Create New Credential window, perform the following actions:

  1. Provide a Name for the credentials.
  2. Optionally, provide a description.
  3. In the Credential Type field, select Source Control.
  4. In the Type Details section, provide a Username and Password.
  5. Select Save.

Add a new project by navigating to Resources > Projects and selecting Add: In this window, perform the following actions:

  1. Provide a name for the new project.
  2. Select Git as the SCM type.
  3. Provide the SCM URL. This is the URL to access the Git repository. (an example is https://gitlab.com/your-org/netbox-aap-nxos.git)

After saving the new project configuration, Ansible Automation Platform will trigger an SCM update to read the contents of the repository. The status can be observed in the detailed view or by navigating to Views > Jobs.

If repository is read successfully by Ansible Automation Platform, the contents of inventory.ini can be used to create a new inventory. Navigate to Resources > Inventories > Add and select Add Inventory to add a new inventory:

In the Create new inventory window, select Save and navigate to the Sources menu. Within this menu, select add to add a new source. In this window, perform the following actions:

  1. Provide a Name for the inventory.

  2. Select Sourced from a Project in the Source dropdown menu.

  3. In Source Details > Project. Select the previously created project.

  4. In Source Details > Inventory File. Select inventory.ini.

  5. Select Save.

    Note that if there are frequent changes to the remote repository it can be helpful to check the update on launch and update variables update options to refresh the inventory file to the most recent version whenever a job using this inventory is launched.

  6. Navigate to the bottom of the Details view and select Sync to trigger a one time inventory sync.

  7. Ensure that this job runs successfully.

The next step is to create a job template. The job template is where the inventory, playbook and other settings are linked together. Navigate to Resources > Templates and select Add > Add job template to add a new job template. In the Create New Job Template window, perform the following actions:

  1. Provide a Name for the job template.
  2. In the Inventory menu, select the previously created inventory.
  3. In the Project menu, select the previously created project.
  4. In the Playbook menu, ensure that the nxos-int.yml playbook is populated and select it.
  5. In the variables configuration area, select Prompt on Launch.
  6. Navigate the the bottom and check Prompt on Launch for Job Tags.

    Note: the prompt on launch flags are important as this instructs the job to prompt for extra variables and tags. The variables will get substituted with the values in the webhook sent by Netbox. The tags are needed to determine when the playbook should run.

  7. Save the template and take note of the URL to find the Job Template ID. For example, If this is the URL: https://url/#/templates/job_template/15/details. The Job ID is 15.

The final steps are adding an Application and Token to authorize the POST requests (webhooks) sent by Netbox to Ansible Automation Platform.

Navigate to Administration > Applications and select Add to add a new application. In the Create New Application window, perform the following actions:

  1. Provide a Name for the new application.
  2. In the Authorization grant type menu, select Resource owner password-based.
  3. In the Client type menu, select Public.
  4. Select Save to save the new application.

Navigate to Access > Users and select the admin user. Alternatively, add or use another user. In the Users detailed view, perform the following actions:

  1. Navigate to the Tokens menu tab and select Add to add a new token.
  2. In the Application field, select the previously created application.
  3. In the Scope field, select Write.
  4. Select Save and take note of the Token and Refresh Token as this is only displayed once.

Setting up the webhook

In the Netbox GUI, navigate to Other > Integrations > Webhooks and select Add to add a new Webhook. In the Add a new webhook window, perform the following actions:

  1. Provide a Name for the Webhook.
  2. In the URL field, provide the job template url with the launch parameter. Job ID 15 is used as example. This should match the respective job ID as noted earlier.
  3. In the Additional headers add Authorization: Bearer <your-token> to authenticate with Ansible Automation Platform. This is the Token from previous step.
  4. In the Body template field, provide the json formatted text from the following snippet:
{   "extra_vars":   {
        "device_id": "{{ data['device']['name'] }}",
        "interface_id": "{{ data['name'] }}",
        "vlan_id": "{{ data['untagged_vlan']['vid'] }}",
        "description": "{{ data['description'] }}",
        "duplex": "{{ data['duplex']['value'] }}",
        "mtu": "{{ data['mtu'] }}",
        "speed": "{{ data['speed'] }}",
        "enabled": "{{ data['enabled'] }}"
                    },
    "job_tags": "{{ data['tags'][0]['name'] }}"
}

Note: the Body template field is a Jinja2 template that allows the user to customise what is presented in the payload of the webhook. For basic interface configuration, the body should carry at least some basic parameters. In this configuration example, it should also carry a job tag so that the tag can be used by Ansible Automation Platform whether it should run a playbook.

Note: The line "job_tags": "{{ data['tags'][0]['name'] }}" only passes the first instance [0] of a tag applied to an interface. This does not allow passing multiple tags.

  1. Optionally, disable SSL verification.

    Note: This setting should be left enabled for production usage.

Creating event rules

Now that the Webhook is created, event rules are required to determine when to run the webhook. In the Netbox GUI, navigate to Integrations > Event Rules, and select Add to add a new event rule. In this window, perform the following actions:

  1. In the Name field, provide a name for the new event rule.

  2. In the Object Types, select DCIM > Interface

  3. In the Tags field, select the previously created tag.

  4. In the Events configuration section, select Updates.

  5. In the Action configuration section, for Action Type, select Webhook

  6. In the Action configuration section, for Webhook, select the webhook created in previous step.

  7. Select Save to save the event rule.

Modifying an interface

With the webhook and event rule for interfaces configured, generate an dcim, interface update event by modifying some of the configuration on an interface. In the Netbox GUI, Navigate to Devices, select a Device, and select any of the interfaces from the Interfaces tab.

In the interface overview, select edit and perform the following actions:

  1. In the Speed field, provide an interface speed in Kbps.
  2. In the Duplex field, select Full.
  3. In the Description field, provide a description.
  4. In the Tags menu, select the previously created tag.
  5. In the MTU field, provide the MTU.
  6. In the Operation configuration section, check Enabled.
  7. In the 802.1Q Switching menu, select Access.
  8. In the Untagged VLAN menu, select a VLAN. (if no VLANs exist, add a VLAN)

Before saving the configuration, open up a terminal and connect to the switch that owns the interface that is about to be modified, so that the changes can be verified later.

  1. Connect to the respective leaf over SSH and execute the following show command on the repsective interface.
EVPN-LEAF-1# show run int eth1/15

!Command: show running-config interface Ethernet1/15
!Running configuration last done at: Wed Jun 19 09:00:29 2024
!Time: Tue Jun 25 16:00:15 2024

interface Ethernet1/15
  switchport mode trunk
  switchport trunk allowed vlan none
  spanning-tree port type edge trunk
  mtu 9216
  1. Navigate back to Netbox and select Save to save the interface configuration. This will trigger the Webhook and this will attempt to launch a job in Ansible Automation Platform.
  2. In Ansible Automation Platform, navigate to Views > Jobs and select the job to confirm the configuration by ensuring the job exists, and the right job template, inventory, and playbook are shown.
  3. Connect or return to the respective leaf over SSH and execute the following show command on the repsective interface.
LEAF-1(config-if)# show run int eth1/15
!Command: show running-config interface Ethernet1/15

interface Ethernet1/15
  description Configured through Ansible Automation Platform
  switchport access vlan 5
  spanning-tree port type edge trunk
  speed 10000
  duplex full

Switch Virtual Interface (SVI) creation

An example to create an SVI will be provided here, but without the step by step guidance that was provided up to this point. Create a new job template for each new playbook.

The following Ansible playbook can be used to create an SVI on a Nexus switch:

cat nxos-l3-interface.yaml
---

- name: Netbox Webhook Ansible Interface
  hosts: 
    - "{{ device_id }}"
  gather_facts: no

  tasks:

  - name: Nxos L3 Interface
    cisco.nxos.nxos_l3_interfaces:
      config:
      - name: "{{ interface_id }}"
        ipv4:
          - address: "{{ address }}"
      state: replaced
    tags:
      - ansible-l3-int

Note: the current state for the task is set to replaced. Feel free to modify this to meet other requirements. Also make sure to create the Tag in Netbox.

Following the Playbook above, the webhooks Body Template should be the following:

{   "extra_vars":   {
        "device_id": "{{ data['assigned_object']['device']['name'] }}",
        "address": "{{ data['address'] }}",
        "interface_id": "{{ data['assigned_object']['name'] }}"
},
    "job_tags": "{{ data['tags'][0]['name'] }}"
}

The Webhook should be configured to trigger on Create and Update events. The Event Content Type is IPAM > IP Address.