Getting Started with Network Automation: NetBox + Ansible

When you combine NetBox and Ansible, your network management is more efficient, reliable, and scalable. These tools work together to provide you with a cohesive solution for inventory management and network automation.

Here are some key reasons why you will benefit from using NetBox and Ansible together:

As the centralized platform for documenting and organizing your network infrastructure, NetBox is your single source of truth. Integrating it with Ansible means no more transposing data; it’s part of your automation. This streamlines your automation and improves your scalability by integrating a dynamic inventory into your tools while helping keep your configuration stay up to date and consistent.

With this integration, your network engineers can automate repetitive tasks, such as deploying standard configurations, updating firmware, or running compliance checks, by simply writing Ansible playbooks.

In this blog post, you’ll see how easy it is to integrate NetBox with Ansible. We’ll set up a NetBox instance as a dynamic inventory for Ansible, add a host, and then demonstrate a playbook to take advantage of the integrated inventory.

Integrating NetBox and Ansible

For this tutorial I’ll be providing examples and screenshots from Linux. The configuration and commands will also work with macOS and WSL on Windows.

Starting NetBox

Running NetBox as a docker container is a great way to spin up an instance quickly, especially for testing purposes. NetBox requires several programs to work together, each needing its own Docker container. So, the easiest way to run it is with Docker Compose. The NetBox community provides a working configuration in a GitHub repository here. Clone this repo and follow the README to get a local NetBox instance running.

The basic steps look like this:

git clone -b release https://github.com/netbox-community/netbox-docker.git
cd netbox-docker
tee docker-compose.override.yml <<EOF
version: '3.4'
services:
  netbox:
    ports:
      - 8000:8080
EOF
docker compose pull
docker compose up

Then, add the admin user from the command line.

$ docker compose exec netbox /opt/netbox/netbox/manage.py createsuperuser
🧬 loaded config '/etc/netbox/config/configuration.py'
🧬 loaded config '/etc/netbox/config/extra.py'
🧬 loaded config '/etc/netbox/config/logging.py'
🧬 loaded config '/etc/netbox/config/plugins.py'
Username (leave blank to use 'unit'): admin
Email address: foo@foo.com
Password: 
Password (again): 
Superuser created successfully.

NetBox Configuration

Point a browser at your server and login with the Superuser you added. You’ll see an empty inventory screen:

Before you integrate Ansible with NetBox, you’ll want to add at least one system in your inventory. But first, devices must be added to sites, and require a device role and device type. So, let’s start by adding these entities to NetBox.

First, navigate to Organization and add a site:

Next, under Devices, add a manufacturer.

Now that you have a manufacturer, you can use it to create a Device Type:

Next, you need a Device Role. You’re going to use this role later, so make it easy to type, with underscores (_) instead of dashes in the Slug field.

Add a Platform

Finally, you’ll need a Platform to pass extra information to Ansible. Create one named Cisco with a slug that contains ios.

Add a Device to NetBox

Now, with the scaffolding created, you can add a device from the Devices menu. Be sure to add a device that you’ll be able to manage via Ansible. In this case, its a router named sandbox-iosxr-1.cisco.com. This router is part of Cisco DevNet Sandbox. Be sure to add the Platform you created above.

Install Ansible and NetBox Libraries

Next, create a new directory, create a Python virtual environment, and activate it:

[egoebelbecker@ares src]$ mkdir ansible-netbox
[egoebelbecker@ares src]$ cd ansible-netbox/
[egoebelbecker@ares ansible-netbox]$ python3 -m venv .
[egoebelbecker@ares ansible-netbox]$ source bin/activate
(ansible-netbox) [egoebelbecker@ares ansible-netbox]$ 

Upgrade pip, and install the Python library for NetBox.

(ansible-netbox) [egoebelbecker@ares ansible-netbox]$ python3 -m pip install --upgrade pip
Requirement already satisfied: pip in ./lib64/python3.11/site-packages (22.2.2)
Collecting pip
  Using cached pip-23.1-py3-none-any.whl (2.1 MB)
Installing collected packages: pip
  Attempting uninstall: pip
    Found existing installation: pip 22.2.2
    Uninstalling pip-22.2.2:
      Successfully uninstalled pip-22.2.2
Successfully installed pip-23.1
(ansible-netbox) [egoebelbecker@ares ansible-netbox]$ pip3 install pynetbox
Collecting pynetbox
  Using cached pynetbox-7.0.1-py3-none-any.whl (33 kB)
Collecting requests<3.0,>=2.20.0 (from pynetbox)
  Using cached requests-2.28.2-py3-none-any.whl (62 kB)
Collecting charset-normalizer<4,>=2 (from requests<3.0,>=2.20.0->pynetbox)
  Using cached charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (197 kB)
Collecting idna<4,>=2.5 (from requests<3.0,>=2.20.0->pynetbox)
  Using cached idna-3.4-py3-none-any.whl (61 kB)
Collecting urllib3<1.27,>=1.21.1 (from requests<3.0,>=2.20.0->pynetbox)
  Using cached urllib3-1.26.15-py2.py3-none-any.whl (140 kB)
Collecting certifi>=2017.4.17 (from requests<3.0,>=2.20.0->pynetbox)
  Using cached certifi-2022.12.7-py3-none-any.whl (155 kB)
Installing collected packages: urllib3, idna, charset-normalizer, certifi, requests, pynetbox
Successfully installed certifi-2022.12.7 charset-normalizer-3.1.0 idna-3.4 pynetbox-7.0.1 requests-2.28.2 urllib3-1.26.15

Now install ansible, netaddr, ansible-pylibssh, and pytz.

(ansible-netbox) [egoebelbecker@ares ansible-netbox]$ pip install ansible
Collecting ansible
  Using cached ansible-7.4.0-py3-none-any.whl (43.3 MB)
Collecting ansible-core~=2.14.4 (from ansible)
  Using cached ansible_core-2.14.4-py3-none-any.whl (2.2 MB)
Collecting jinja2>=3.0.0 (from ansible-core~=2.14.4->ansible)
  Using cached Jinja2-3.1.2-py3-none-any.whl (133 kB)
Collecting PyYAML>=5.1 (from ansible-core~=2.14.4->ansible)
  Using cached PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (757 kB)
Collecting cryptography (from ansible-core~=2.14.4->ansible)
  Using cached cryptography-40.0.2-cp36-abi3-manylinux_2_28_x86_64.whl (3.7 MB)
Collecting packaging (from ansible-core~=2.14.4->ansible)
  Using cached packaging-23.1-py3-none-any.whl (48 kB)
Collecting resolvelib<0.9.0,>=0.5.3 (from ansible-core~=2.14.4->ansible)
  Using cached resolvelib-0.8.1-py2.py3-none-any.whl (16 kB)
Collecting MarkupSafe>=2.0 (from jinja2>=3.0.0->ansible-core~=2.14.4->ansible)
  Using cached MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (27 kB)
Collecting cffi>=1.12 (from cryptography->ansible-core~=2.14.4->ansible)
  Using cached cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (462 kB)
Collecting pycparser (from cffi>=1.12->cryptography->ansible-core~=2.14.4->ansible)
  Using cached pycparser-2.21-py2.py3-none-any.whl (118 kB)
Installing collected packages: resolvelib, PyYAML, pycparser, packaging, MarkupSafe, jinja2, cffi, cryptography, ansible-core, ansible
Successfully installed MarkupSafe-2.1.2 PyYAML-6.0 ansible-7.4.0 ansible-core-2.14.4 cffi-1.15.1 cryptography-40.0.2 jinja2-3.1.2 packaging-23.1 pycparser-2.21 resolvelib-0.8.1
(ansible-netbox) [egoebelbecker@ares ansible-netbox]$ pip install netaddr
Collecting netaddr
  Using cached netaddr-0.8.0-py2.py3-none-any.whl (1.9 MB)
Installing collected packages: netaddr
Successfully installed netaddr-0.8.0
(ansible-netbox) [egoebelbecker@ares ansible-netbox]$ pip install pytz
Collecting pytz
  Using cached pytz-2023.3-py2.py3-none-any.whl (502 kB)
Installing collected packages: pytz
Successfully installed pytz-2023.3

Install the Ansible Galaxy collection for NetBox.

(ansible-netbox) [egoebelbecker@ares ansible-netbox]$ ansible-galaxy collection install netbox.netbox
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Downloading https://galaxy.ansible.com/download/netbox-netbox-3.12.0.tar.gz to /home/egoebelbecker/.ansible/tmp/ansible-local-36452697hvb5zem/tmpdi7f7dxm/netbox-netbox-3.12.0-u4hhif8l
Installing 'netbox.netbox:3.12.0' to '/home/egoebelbecker/.ansible/collections/ansible_collections/netbox/netbox'
netbox.netbox:3.12.0 was installed successfully

You’ll also need the ansible collection to manage your device.

(new-ansible) [egoebelbecker@ares new-ansible]$ ansible-galaxy collection install cisco.nxos
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Downloading https://galaxy.ansible.com/download/cisco-nxos-4.3.0.tar.gz to /home/egoebelbecker/.ansible/tmp/ansible-local-15912379codqg99/tmp98h0esq6/cisco-nxos-4.3.0-o9_ontrg
Installing 'cisco.nxos:4.3.0' to '/home/egoebelbecker/.ansible/collections/ansible_collections/cisco/nxos'
Downloading https://galaxy.ansible.com/download/ansible-netcommon-5.1.0.tar.gz to /home/egoebelbecker/.ansible/tmp/ansible-local-15912379codqg99/tmp98h0esq6/ansible-netcommon-5.1.0-hdg2odq3
cisco.nxos:4.3.0 was installed successfully
Installing 'ansible.netcommon:5.1.0' to '/home/egoebelbecker/.ansible/collections/ansible_collections/ansible/netcommon'
Downloading https://galaxy.ansible.com/download/ansible-utils-2.9.0.tar.gz to /home/egoebelbecker/.ansible/tmp/ansible-local-15912379codqg99/tmp98h0esq6/ansible-utils-2.9.0-95eft0ci
ansible.netcommon:5.1.0 was installed successfully
Installing 'ansible.utils:2.9.0' to '/home/egoebelbecker/.ansible/collections/ansible_collections/ansible/utils'
ansible.utils:2.9.0 was installed successfully

NetBox API Key

Ansible needs a token to use the NetBox API. Select API Tokens from the user menu.

Select Add a Token from the API Tokens screen.

Fill out the form, leaving the Key and Allowed IPs blank. In a production environment, you would want to limit the addresses that can use this token.

Click Create, and you have a token.

You will use the key in the next step.

Add NetBox to Ansible Inventory

Now it’s time to get Ansible and NetBox working together. Set environment variables for your API token and the URL for your NetBox instance.

(ansible-netbox) [egoebelbecker@ares ansible-netbox]$ export NETBOX_TOKEN=*API TOKEN*
(ansible-netbox) [egoebelbecker@ares ansible-netbox]$ export NETBOX_API=http://127.0.0.1:8000

Now, it’s finally time to add NetBox to your Ansible inventory. Create a file named netbox_inventory.yml.

---
plugin: netbox.netbox.nb_inventory
validate_certs: false
config_context: false

This tells Ansible to use the NetBox API to retrieve your inventory. Try it with ansible-inventory -i netbox_inventory.yml –list

(ansible-netbox) [egoebelbecker@ares ansible-netbox]$ ansible-inventory --list -i netbox_inventory.yml
{
 "_meta": {
     "hostvars": {
         "sandbox-iosxr-1.cisco.com": {
             "asset_tag": "123456",
             "custom_fields": {},
             "device_roles": [
                 "core_router"
             ],
             "device_types": [
                 "cisco-ios-xrv-9000"
             ],
             "is_virtual": false,
             "local_context_data": [
                 null
             ],
             "locations": [],
             "manufacturers": [
                 "cisco"
             ],
             "regions": [],
             "serial": "B550ED1D0D9",
             "services": [],
             "site_groups": [],
             "sites": [
                 "trick-of-the-tale"
             ],
             "status": {
                 "label": "Active",
                 "value": "active"
             },
             "tags": []
         }
     }
 },
 "all": {
     "children": [
         "ungrouped"
     ]
 },
 "ungrouped": {
     "hosts": [
         "sandbox-iosxr-1.cisco.com"
     ]
 }
}

Ansible and NetBox are integrated. If you don’t want to pass the inventory file in for each command, you can create a config file and make it the default. Create an empty config file in your working directory. Ansible will use it for the command you run from here.

ansible-config init --disabled > ansible.cfg

Then, open it in an editor and find the inventory setting:

# (pathlist) Comma separated list of Ansible inventory sources 
;inventory=/etc/ansible/hosts

Override it with the complete path to your netbox_inventory.yml and uncomment the line:

# (pathlist) Comma separated list of Ansible inventory sources
inventory=/home/egoebelbecker/src/ansible-netbox/netbox_inventory.yml

Now, when you run ansible-inventory –list you see your NetBox inventory.

Automating Network Operations

Add Custom Variables to Inventory

Before you can use Ansible to manage the router, you need to tell it that it’s running Cisco ios. You’ll use the Platform. Update netbox_inventory.yml with two new lines at the end.

---
plugin: netbox.netbox.nb_inventory
validate_certs: false
config_context: false
compose:
  ansible_network_os: platform.slug

The compose field will map each hosts Platform slug to a variable named ansible_network_os. Ansible will pick this up and use it.

Ansible Playbook

Let’s finish with a simple playbook to backup the configuration on sandbox-iosxr-1.cisco.com.

Here’s the playbook:

---
- name: backup configuration
  hosts: sandbox-iosxr-1.cisco.com
  gather_facts: false
  connection: network_cli

  vars:
 backup_root: ./backups

 cli:
   host: "{{ inventory_hostname }}"
   authorize: true

  tasks:
 - name: run show running-config on remote devices
   ios_command:
     commands: "show running-config"
   register: config

 - name: ensure backup folder is created
   file:
     path: "{{ backup_root }}"
     state: directory
   run_once: yes

 - name: ensure device folder is created
   file:
     path: "{{ backup_root }}/{{ inventory_hostname }}"
     state: directory

 - name: get timestamp
   command: date +%Y%m%d
   register: timestamp

 - name: get/copy backup
   copy:
     content: "{{ config.stdout[0] }}"
     dest: "{{ backup_root }}/{{ inventory_hostname }}/running-config_{{ timestamp.stdout }}"

It will create a backups directory if needed, and save the running configuration there.

Since we don’t want to install keys on a public router, run this playbook with the -u and -k options for ssh authentication.

$ ansible-playbook cisco_config.yaml -u admin -k
SSH password:

PLAY [backup configuration] ***********************************************************************************************************************************************

TASK [run show running-config on remote devices] **************************************************************************************************************************
ok: [sandbox-iosxr-1.cisco.com]

TASK [ensure backup folder is created] ************************************************************************************************************************************
ok: [sandbox-iosxr-1.cisco.com]

TASK [ensure device folder is created] ************************************************************************************************************************************
ok: [sandbox-iosxr-1.cisco.com]

TASK [get timestamp] ******************************************************************************************************************************************************
changed: [sandbox-iosxr-1.cisco.com]

TASK [get/copy backup] ****************************************************************************************************************************************************
changed: [sandbox-iosxr-1.cisco.com]

PLAY RECAP ****************************************************************************************************************************************************************
sandbox-iosxr-1.cisco.com  : ok=5 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0   

Take a look in the backups directory:

$ ls -lr backups/
total 0
drwxr-xr-x. 1 egoebelbecker egoebelbecker 46 May  5 17:04 sandbox-iosxr-1.cisco.com
(new-ansible) [egoebelbecker@ares new-ansible]$ ls -lR backups/
backups/:
total 0
drwxr-xr-x. 1 egoebelbecker egoebelbecker 46 May  5 17:04 sandbox-iosxr-1.cisco.com

backups/sandbox-iosxr-1.cisco.com:
total 8
-rw-r--r--. 1 egoebelbecker egoebelbecker 7193 May  5 17:04 running-config_20230505

You were able to manage a host based on its name and platform in NetBox.

Wrap Up

In this post, you built a NetBox server using Docker Compose. Then you added a host to it and integrated Ansible for automated management. From there, you were able to manage that host using its group entries in a dynamic inventory sourced from NetBox.

This is only the beginning of what you can do with NetBox and Ansible. Ansible has access to all your device information in NetBox, and can use it to generate configurations, validate host information, build reports, and more.

Want to see more?

Eric Goebelbecker has worked in the financial markets in New York City for 25 years, developing infrastructure for market data and financial information exchange (FIX) protocol networks. He loves to talk about what makes teams effective (or not so effective!).

Share the Post:

Related Posts