Esc
Type to search posts, tags, and more...
Skip to content

Using NetBox as the Source of Truth for Nornir Automation

Connecting Nornir to NetBox for dynamic inventory management, so your automation always reflects the actual state of your network.

Contents

The Inventory Problem

Every network automation tool needs to know what devices exist, how to reach them, and what role they play. The naive approach is a YAML file or CSV spreadsheet. This works until it does not — someone adds a switch and forgets to update the inventory, or a device gets renamed and half your playbooks break.

NetBox solves this by providing a single source of truth for your network. Devices, IP addresses, sites, VLANs — everything lives in one API-accessible database. The question is how to connect it to your automation framework.

Nornir and the nornir-netbox Plugin

Nornir is a Python automation framework that treats inventory as a first-class concept. Instead of a static file, you can plug in a dynamic inventory source. The nornir-netbox plugin does exactly this:

from nornir import InitNornir

nr = InitNornir(
    inventory={
        "plugin": "NBInventory",
        "options": {
            "nb_url": "https://netbox.example.com",
            "nb_token": "your-api-token",
            "ssl_verify": True,
        },
    },
    runner={
        "plugin": "threaded",
        "options": {"num_workers": 50},
    },
)

print(f"Loaded {len(nr.inventory.hosts)} devices from NetBox")

Every time you initialize Nornir, it pulls the current device list from NetBox. No stale YAML files.

Mapping NetBox Fields to Nornir Data

The real power comes from mapping NetBox’s rich data model into Nornir’s host data. Out of the box, the plugin maps basic fields: hostname, platform, IP address. But you can extend this with custom fields and config contexts.

For example, we add a custom field automation_tier to devices in NetBox. This lets us control which devices are eligible for automated changes:

# Filter to only tier-1 (fully automated) devices
automated = nr.filter(lambda host: host.data.get("custom_fields", {}).get("automation_tier") == 1)

result = automated.run(
    task=napalm_get,
    getters=["facts", "interfaces"]
)

Config Contexts for Per-Device Variables

NetBox config contexts are JSON blobs that attach to devices based on roles, sites, or tags. We use them to store automation-specific variables:

{
  "ntp_servers": ["10.0.0.1", "10.0.0.2"],
  "syslog_server": "10.0.0.10",
  "snmp_community": "readonly-community",
  "management_vrf": "MGMT"
}

These merge into Nornir’s host data automatically. Your task functions can reference them without hardcoding values:

def configure_ntp(task):
    ntp_servers = task.host.data["config_context"]["ntp_servers"]
    commands = [f"ntp server {s}" for s in ntp_servers]
    task.run(
        task=netmiko_send_config,
        config_commands=commands
    )

Handling Scale

At around 200 devices, the initial NetBox API pull starts to feel slow. A few optimizations:

  • Use the limit and offset parameters — paginate instead of pulling everything at once
  • Cache the inventory — pull from NetBox once per automation run, not once per task
  • Filter server-side — use NetBox API filters to only pull devices matching your criteria (site, role, status=active)
nr = InitNornir(
    inventory={
        "plugin": "NBInventory",
        "options": {
            "nb_url": "https://netbox.example.com",
            "nb_token": "your-api-token",
            "filter_parameters": {
                "status": "active",
                "site": "dc-east",
                "role": "leaf-switch",
            },
        },
    },
)

The Feedback Loop

The final piece is closing the loop. After Nornir makes changes, update NetBox to reflect the new state. We run a post-change task that updates the device’s last_automated custom field and tags it with the change ticket number.

This creates an audit trail: you can look at any device in NetBox and see when it was last touched by automation and why. When something breaks, the investigation starts with data instead of guesswork.

! Was this useful?