Introduction
NetBox is the leading open-source platform for network infrastructure modeling — DCIM (data center infrastructure management) and IPAM (IP address management). The netbox.netbox Ansible collection provides a dynamic inventory plugin that pulls device and VM data directly from NetBox, plus modules to manage every NetBox object. This makes NetBox your single source of truth and Ansible your automation engine.
Prerequisites
# Install the collection
ansible-galaxy collection install netbox.netbox
# Install Python dependency
pip install pynetbox
NetBox API Token
- In NetBox: Admin → API Tokens → Add Token
- Set permissions (read-only for inventory, read-write for modules)
- Save the token
Dynamic Inventory from NetBox
Basic Configuration
# netbox_inventory.yml
plugin: netbox.netbox.nb_inventory
api_endpoint: https://netbox.example.com
token: "{{ lookup('env', 'NETBOX_TOKEN') }}"
validate_certs: true
# Group devices by these attributes
group_by:
- site
- role
- platform
- device_type
# Use primary IP as ansible_host
compose:
ansible_host: primary_ip4.address | default('') | ansible.utils.ipaddr('address')
# Set token
export NETBOX_TOKEN=abcdef1234567890
# Test inventory
ansible-inventory -i netbox_inventory.yml --list
# Ping all devices
ansible -i netbox_inventory.yml all -m ping
Advanced Inventory Configuration
# netbox_inventory.yml
plugin: netbox.netbox.nb_inventory
api_endpoint: https://netbox.example.com
token: "{{ lookup('env', 'NETBOX_TOKEN') }}"
validate_certs: true
# Filter devices
query_filters:
- status: active
- region: us-east
- has_primary_ip: true
# Exclude specific roles
exclude_query_filters:
- role: out-of-band
# Group by multiple attributes
group_by:
- site
- role
- platform
- tenant
- tags
# Custom groups from device attributes
groups:
webservers: "'web' in device_role.slug"
switches: "'switch' in device_role.slug"
firewalls: "'firewall' in device_role.slug"
production: "'production' in tags | map(attribute='slug')"
# Set connection variables from NetBox data
compose:
ansible_host: primary_ip4.address | ansible.utils.ipaddr('address')
ansible_network_os: platform.slug | default('')
ansible_user: "'admin' if 'network' in device_role.slug else 'ansible'"
# Include virtual machines too
virtual_machines: true
# Cache for performance
cache: true
cache_plugin: jsonfile
cache_connection: /tmp/netbox-inventory-cache
cache_timeout: 3600
Test Inventory Groups
# List all groups
ansible-inventory -i netbox_inventory.yml --graph
# Show specific group
ansible-inventory -i netbox_inventory.yml --graph webservers
# Show host variables
ansible-inventory -i netbox_inventory.yml --host switch01
Manage NetBox Objects with Ansible
Create Sites
---
- name: Manage NetBox sites
hosts: localhost
gather_facts: false
vars:
netbox_url: https://netbox.example.com
netbox_token: "{{ vault_netbox_token }}"
tasks:
- name: Create sites
netbox.netbox.netbox_site:
netbox_url: "{{ netbox_url }}"
netbox_token: "{{ netbox_token }}"
data:
name: "{{ item.name }}"
slug: "{{ item.slug }}"
status: active
region: "{{ item.region }}"
facility: "{{ item.facility }}"
time_zone: "{{ item.tz }}"
state: present
loop:
- { name: "US-East-1", slug: "us-east-1", region: "US East", facility: "EQX-DC5", tz: "America/New_York" }
- { name: "EU-West-1", slug: "eu-west-1", region: "EU West", facility: "EQX-LD8", tz: "Europe/London" }
Create Devices
- name: Create network devices
netbox.netbox.netbox_device:
netbox_url: "{{ netbox_url }}"
netbox_token: "{{ netbox_token }}"
data:
name: "{{ item.name }}"
device_type: "{{ item.type }}"
role: "{{ item.role }}"
site: "{{ item.site }}"
status: active
platform: "{{ item.platform }}"
serial: "{{ item.serial }}"
tags:
- production
- managed
state: present
loop:
- { name: "core-sw01", type: "Cisco Catalyst 9300", role: "Core Switch", site: "US-East-1", platform: "cisco-ios", serial: "FCW2345G0AB" }
- { name: "fw01", type: "Palo Alto PA-3260", role: "Firewall", site: "US-East-1", platform: "panos", serial: "012345678" }
Manage IP Addresses
- name: Create IP prefix
netbox.netbox.netbox_prefix:
netbox_url: "{{ netbox_url }}"
netbox_token: "{{ netbox_token }}"
data:
prefix: 10.0.0.0/24
site: US-East-1
vlan:
name: Management
status: active
description: "Management network"
state: present
- name: Assign IP addresses
netbox.netbox.netbox_ip_address:
netbox_url: "{{ netbox_url }}"
netbox_token: "{{ netbox_token }}"
data:
address: "{{ item.ip }}"
status: active
assigned_object:
device: "{{ item.device }}"
name: "{{ item.interface }}"
dns_name: "{{ item.dns }}"
state: present
loop:
- { ip: "10.0.0.1/24", device: "core-sw01", interface: "Vlan100", dns: "core-sw01.example.com" }
- { ip: "10.0.0.2/24", device: "fw01", interface: "ethernet1/1", dns: "fw01.example.com" }
Manage VLANs
- name: Create VLANs
netbox.netbox.netbox_vlan:
netbox_url: "{{ netbox_url }}"
netbox_token: "{{ netbox_token }}"
data:
name: "{{ item.name }}"
vid: "{{ item.vid }}"
site: US-East-1
status: active
description: "{{ item.desc }}"
state: present
loop:
- { name: "Management", vid: 100, desc: "Management network" }
- { name: "Servers", vid: 200, desc: "Server production" }
- { name: "Users", vid: 300, desc: "User workstations" }
- { name: "DMZ", vid: 400, desc: "DMZ network" }
Full Workflow: NetBox → Ansible → Network
---
# Step 1: Query NetBox for VLAN assignments
- name: Configure switches from NetBox source of truth
hosts: switches
gather_facts: false
vars:
netbox_url: https://netbox.example.com
netbox_token: "{{ vault_netbox_token }}"
tasks:
- name: Get VLANs from NetBox
netbox.netbox.netbox_vlan:
netbox_url: "{{ netbox_url }}"
netbox_token: "{{ netbox_token }}"
data:
site: "{{ site }}"
state: present
register: vlans
- name: Configure VLANs on switch
cisco.ios.ios_vlans:
config:
- name: "{{ item.name }}"
vlan_id: "{{ item.vid }}"
state: active
state: merged
loop: "{{ vlans.results | default([]) }}"
Lookup Plugin
Query NetBox from within playbooks:
- name: Get device info from NetBox
ansible.builtin.debug:
msg: "{{ lookup('netbox.netbox.nb_lookup', 'devices', api_filter='name=core-sw01', api_endpoint=netbox_url, token=netbox_token) }}"
Troubleshooting
"pynetbox" Not Found
pip install pynetbox
Empty Inventory
- Check
query_filters—has_primary_ip: trueexcludes devices without IPs - Verify API token permissions
- Check device
status: activein NetBox
SSL Certificate Errors
# For self-signed certs (dev only)
validate_certs: false
Best Practices
- NetBox is the source of truth — never hardcode IPs or device lists in Ansible
- Use caching — NetBox inventory queries can be slow; cache for 1 hour
- Read-only tokens for inventory — minimize API token permissions
- Tag-based groups — use NetBox tags for dynamic grouping
- Sync both ways — update NetBox after Ansible makes changes
- Version your NetBox data — use NetBox's changelog and Ansible to audit changes
Related Articles
- Ansible Dynamic Inventory Guide
- Ansible Network Automation
- Ansible Vault Guide
- Ansible Proxmox Automation
Conclusion
NetBox plus Ansible creates a closed loop: NetBox holds the intended state of your infrastructure (devices, IPs, VLANs, prefixes), the dynamic inventory plugin feeds that data to Ansible, and Ansible modules configure the actual network to match. Use the nb_inventory plugin with group_by and compose for automatic host grouping and connection variables, and the netbox_* modules to keep NetBox in sync as your infrastructure evolves.