NetBox is a hugely popular open-source IP address management (IPAM) and data centre infrastructure management (DCIM) tool. NetBox provides a range of automation features, one of which is the ability to update NetBox with Ansible using the Ansible NetBox collection.

In this short post, we'll walk through the process of creating a device and adding a primary IP address, along with assigning a management interface and IP to the device using Ansible.

Prerequisites

  1. A working NetBox installation.
  2. Ansible installed on your machine.
  3. The netbox.netbox Ansible collection. Install it using:
ansible-galaxy collection install netbox.netbox

NetBox API URL and Token

Create an API Token within NetBox

  1. Log in to NetBox.
  2. Access your profile by clicking on your username in the top right.
  3. Navigate to API Tokens.
  4. Click Add a Token.
  5. The key will be used in the .env, in the next step.

Create an .env

In your project directory, create a .env file:

NETBOX_URL=http://your-netbox-url
NETBOX_TOKEN=NETBOX_API_KEY

Load the .env

Load your environment variables so they will be available in your Ansible Playbook:

export $(cat .env)

Define Your Variables

Create a vars.yml file that contains the following:

tenant_name: Acme1
site_name: Site1

ip_prefixes:
  - prefix: 172.29.1.0/24

devices:
  - device: rtr-spine1
    interface: GigabitEthernet0/7
    primary_ip: 172.29.1.101/24
  - device: rtr-spine2
    interface: GigabitEthernet0/7
    primary_ip: 172.29.1.102/24
  - device: rtr-spine3
    interface: GigabitEthernet0/7
    primary_ip: 172.29.1.103/24

Create the Ansible Playbook

Create a Playbook named pb_create_device_w_primary_ip.yml.

Within this Playbook, we will, in NetBox:

  • Create the IP prefix for our management network.
  • Create the devices.
  • Assign the IP to the mgmt interface.
  • Set the required interface as mgmt
  • Set the required interface (mgmt) to the primary IP.

Note: This Playbook presumes you already have created the:

  • Tenant: Acme1
  • Site: Site1
  • Role: Spine
  • Device type: Cisco IOSv
  • Platform: ios
- name: Create Devices and Add Primary IP
  hosts: localhost
  gather_facts: no

  vars:
    NETBOX_ENDPOINT: "{{ lookup('env','NETBOX_ENDPOINT') }}"
    NETBOX_TOKEN: "{{ lookup('env','NETBOX_TOKEN') }}"

  vars_files:
    - "../vars.yml"

  tasks:
    - name: Create IP Prefixes
      netbox.netbox.netbox_prefix:
        netbox_url: "{{ NETBOX_ENDPOINT }}"
        netbox_token: "{{ NETBOX_TOKEN }}"
        data:
          prefix: "{{ item.prefix }}"
          site: "{{ site_name }}"
          tenant: "{{ tenant_name }}"
      loop: "{{ ip_prefixes }}"
      tags: prefix

    - name: Create Devices
      netbox.netbox.netbox_device:
        netbox_url: "{{ NETBOX_ENDPOINT }}"
        netbox_token: "{{ NETBOX_TOKEN }}"
        data:
          name: "{{ item.device }}"
          site: "{{ site_name }}"
          tenant: "{{ tenant_name }}"
          device_type: cisco-iosv
          device_role: spine
          platform: ios
      loop: "{{ devices }}"
      tags: device

    - name: Update Management IPs
      netbox.netbox.netbox_ip_address:
        netbox_url: "{{ NETBOX_ENDPOINT }}"
        netbox_token: "{{ NETBOX_TOKEN }}"
        data:
          address: "{{ item.primary_ip }}"
          assigned_object:
            device: "{{ item.device }}"
            name: "{{ item.interface }}"
      loop: "{{ devices }}"
      tags: mgmt

    - name: Update Management Interfaces
      netbox.netbox.netbox_device_interface:
        netbox_url: "{{ NETBOX_ENDPOINT }}"
        netbox_token: "{{ NETBOX_TOKEN }}"
        data:
          device: "{{ item.device }}"
          name: "{{ item.interface }}"
          mgmt_only: true
      loop: "{{ devices }}"
      tags: mgmt

    - name: Update Primary IP
      netbox.netbox.netbox_device:
        netbox_url: "{{ NETBOX_ENDPOINT }}"
        netbox_token: "{{ NETBOX_TOKEN }}"
        data:
          name: "{{ item.device }}"
          site: "{{ site_name }}"
          tenant: "{{ tenant_name }}"
          primary_ip4: "{{ item.primary_ip }}"
      loop: "{{ devices }}"
      tags: device

Note: We use the device module twice within this Playbook. This is because we need the device to be present to assign an IP against it before we can mark the IP on the device as primary.

Run the Playbook

Next, we run the Playbook, like so:

$ ansible-playbook pb_create_device_w_primary_ip.yml

PLAY [Create Devices and Add Primary IP] **************************************************************************************

TASK [Create IP Prefixes] *****************************************************************************************************
changed: [localhost] => (item={'prefix': '172.29.1.0/24'})

TASK [Create Devices] *********************************************************************************************************
changed: [localhost] => (item={'device': 'rtr-spine1', 'interface': 'GigabitEthernet0/7', 'primary_ip': '172.29.1.101/24'})
changed: [localhost] => (item={'device': 'rtr-spine2', 'interface': 'GigabitEthernet0/7', 'primary_ip': '172.29.1.102/24'})
changed: [localhost] => (item={'device': 'rtr-spine3', 'interface': 'GigabitEthernet0/7', 'primary_ip': '172.29.1.103/24'})

TASK [Update Management IPs] **************************************************************************************************
changed: [localhost] => (item={'device': 'rtr-spine1', 'interface': 'GigabitEthernet0/7', 'primary_ip': '172.29.1.101/24'})
changed: [localhost] => (item={'device': 'rtr-spine2', 'interface': 'GigabitEthernet0/7', 'primary_ip': '172.29.1.102/24'})
changed: [localhost] => (item={'device': 'rtr-spine3', 'interface': 'GigabitEthernet0/7', 'primary_ip': '172.29.1.103/24'})

TASK [Update Management Interfaces] *******************************************************************************************
changed: [localhost] => (item={'device': 'rtr-spine1', 'interface': 'GigabitEthernet0/7', 'primary_ip': '172.29.1.101/24'})
changed: [localhost] => (item={'device': 'rtr-spine2', 'interface': 'GigabitEthernet0/7', 'primary_ip': '172.29.1.102/24'})
changed: [localhost] => (item={'device': 'rtr-spine3', 'interface': 'GigabitEthernet0/7', 'primary_ip': '172.29.1.103/24'})

TASK [Update Primary IP] ******************************************************************************************************
changed: [localhost] => (item={'device': 'rtr-spine1', 'interface': 'GigabitEthernet0/7', 'primary_ip': '172.29.1.101/24'})
changed: [localhost] => (item={'device': 'rtr-spine2', 'interface': 'GigabitEthernet0/7', 'primary_ip': '172.29.1.102/24'})
changed: [localhost] => (item={'device': 'rtr-spine3', 'interface': 'GigabitEthernet0/7', 'primary_ip': '172.29.1.103/24'})

PLAY RECAP ********************************************************************************************************************
localhost                  : ok=5    changed=5    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Based on the Ansible output, we can see the changes to NetBox. Great!
But we can also see from NetBox that the devices are now present.

Closing Comments

I hope you`ve enjoyed this post. Populating NetBox using Ansible, coupled with YAML, offers many benefits. This approach accelerates the onboarding of data into a Source of Truth (SoT) and ensures that the process is more repeatable and less prone to errors. Great stuff! Until next time - Happy automating!

Ready to Master Network Automation? Start Your Journey Today!
Our membership provides:
  • Full deep-dive course library (inc. Batfish, pyATS, Netmiko)
  • Code repositories inc. full course code, scripts and examples
  • 24x7 multi-vendor labs (Arista, Cisco, Juniper)
  • Private online community
  • Live monthly tech sessions
  • Access to tech session library

Join Now ➜