11 KiB
Ansible Dynamic Inventory from NetBox
Overview
Use NetBox as a dynamic inventory source for Ansible, eliminating the need for static inventory files and ensuring your automation always has up-to-date infrastructure data.
Architecture
┌──────────┐
│ NetBox │ (Source of Truth)
│ IPAM │
└────┬─────┘
│
│ API Query
│
▼
┌────────────────┐
│ nb_inventory │ (Ansible Plugin)
│ Plugin │
└────┬───────────┘
│
│ Generates Dynamic Inventory
│
▼
┌────────────────┐
│ Ansible │ (Uses inventory for playbooks)
│ Playbooks │
└────────────────┘
Prerequisites
Install NetBox Ansible Collection
cd ansible
uv run ansible-galaxy collection install netbox.netbox
Or add to requirements.yml:
---
collections:
- name: netbox.netbox
version: ">=3.0.0"
uv run ansible-galaxy collection install -r requirements.yml
NetBox API Token
Create read-only API token in NetBox:
NetBox UI: Admin → API Tokens → Add
- User: ansible (create service user)
- Key: Generated automatically
- Write enabled: No (read-only)
Save token securely:
# Option 1: Environment variable
export NETBOX_API_TOKEN="your-token-here"
# Option 2: Ansible Vault
ansible-vault create group_vars/all/vault.yml
# Add: netbox_token: "your-token-here"
Basic Configuration
Create Inventory File
File: ansible/inventory/netbox.yml
---
plugin: netbox.netbox.nb_inventory
# NetBox API connection
api_endpoint: https://netbox.spaceships.work
token: !vault |
$ANSIBLE_VAULT;1.1;AES256
...
# Validate SSL (set to false for self-signed certs)
validate_certs: true
# Group hosts by these NetBox attributes
group_by:
- device_roles
- tags
- sites
- platforms
# Set ansible_host variable from primary_ip4
compose:
ansible_host: primary_ip4
# Only include active devices/VMs
query_filters:
- status: active
Test Inventory
# List all hosts
ansible-inventory -i ansible/inventory/netbox.yml --list
# View in YAML format
ansible-inventory -i ansible/inventory/netbox.yml --list --yaml
# View specific host
ansible-inventory -i ansible/inventory/netbox.yml --host docker-01-nexus
# Graph inventory
ansible-inventory -i ansible/inventory/netbox.yml --graph
Advanced Configuration
Filter by Tags
Only include hosts with specific tag:
---
plugin: netbox.netbox.nb_inventory
api_endpoint: https://netbox.spaceships.work
token: !vault |
$ANSIBLE_VAULT;...
# Only hosts tagged with "ansible-managed"
query_filters:
- tag: ansible-managed
- status: active
group_by:
- tags
Filter by Device Role
Only include specific device roles:
query_filters:
- role: docker-host
- role: k8s-node
- status: active
Custom Groups
Create custom groups based on NetBox data:
---
plugin: netbox.netbox.nb_inventory
api_endpoint: https://netbox.spaceships.work
token: !vault |
$ANSIBLE_VAULT;...
group_by:
- device_roles
- tags
- sites
# Custom group mappings
keyed_groups:
- key: tags
prefix: tag
- key: device_role.name
prefix: role
- key: platform.name
prefix: platform
compose:
ansible_host: primary_ip4
ansible_user: ansible
ansible_become: true
Include Custom Fields
Use NetBox custom fields in inventory:
---
plugin: netbox.netbox.nb_inventory
api_endpoint: https://netbox.spaceships.work
token: !vault |
$ANSIBLE_VAULT;...
compose:
ansible_host: primary_ip4
# Use custom fields from NetBox
backup_schedule: custom_fields.backup_schedule
monitoring_enabled: custom_fields.monitoring_enabled
application_owner: custom_fields.owner
group_by:
- tags
- custom_fields.environment
Usage Examples
Example 1: Configure All Docker Hosts
Inventory: ansible/inventory/netbox.yml
---
plugin: netbox.netbox.nb_inventory
api_endpoint: https://netbox.spaceships.work
token: !vault |
$ANSIBLE_VAULT;...
query_filters:
- tag: docker-host
- status: active
group_by:
- tags
compose:
ansible_host: primary_ip4
ansible_user: ansible
ansible_become: true
Playbook: ansible/playbooks/configure-docker-hosts.yml
---
- name: Configure Docker hosts from NetBox inventory
hosts: tag_docker_host
become: true
tasks:
- name: Ensure Docker is running
ansible.builtin.systemd:
name: docker
state: started
enabled: true
- name: Update Docker daemon config
ansible.builtin.copy:
dest: /etc/docker/daemon.json
content: |
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
notify: Restart Docker
handlers:
- name: Restart Docker
ansible.builtin.systemd:
name: docker
state: restarted
Run playbook:
cd ansible
uv run ansible-playbook -i inventory/netbox.yml playbooks/configure-docker-hosts.yml
Example 2: Site-Specific Deployments
Inventory with site grouping:
---
plugin: netbox.netbox.nb_inventory
api_endpoint: https://netbox.spaceships.work
token: !vault |
$ANSIBLE_VAULT;...
group_by:
- sites
- tags
compose:
ansible_host: primary_ip4
query_filters:
- status: active
Playbook targeting specific site:
---
- name: Update hosts at primary site
hosts: site_homelab # Automatically grouped by site name
become: true
tasks:
- name: Update all packages
ansible.builtin.apt:
upgrade: dist
update_cache: true
when: ansible_os_family == "Debian"
Example 3: Platform-Specific Configuration
Inventory:
---
plugin: netbox.netbox.nb_inventory
api_endpoint: https://netbox.spaceships.work
token: !vault |
$ANSIBLE_VAULT;...
group_by:
- platforms
compose:
ansible_host: primary_ip4
keyed_groups:
- key: platform.name
prefix: platform
Playbook with platform-specific tasks:
---
- name: Platform-specific configuration
hosts: all
become: true
tasks:
- name: Configure Ubuntu hosts
ansible.builtin.apt:
name: netbox-agent
state: present
when: "'ubuntu' in group_names"
- name: Configure Rocky hosts
ansible.builtin.dnf:
name: netbox-agent
state: present
when: "'rocky' in group_names"
Integration with Secrets Management
Use with Infisical
Combine dynamic inventory with Infisical secrets:
---
- name: Deploy app with NetBox inventory and Infisical secrets
hosts: tag_app_server
become: true
vars:
infisical_project_id: "7b832220-24c0-45bc-a5f1-ce9794a31259"
infisical_env: "prod"
infisical_path: "/app-config"
tasks:
- name: Retrieve database password
ansible.builtin.include_tasks: "{{ playbook_dir }}/../tasks/infisical-secret-lookup.yml"
vars:
secret_name: 'DB_PASSWORD'
secret_var_name: 'db_password'
- name: Deploy application config
ansible.builtin.template:
src: app-config.j2
dest: /etc/app/config.yml
owner: app
group: app
mode: '0600'
vars:
db_host: "{{ hostvars[groups['tag_database'][0]]['ansible_host'] }}"
db_password: "{{ db_password }}"
Caching for Performance
Enable Inventory Caching
File: ansible/ansible.cfg
[defaults]
inventory_plugins = /usr/share/ansible/plugins/inventory
[inventory]
enable_plugins = netbox.netbox.nb_inventory
# Enable caching
cache = true
cache_plugin = jsonfile
cache_timeout = 3600 # 1 hour
cache_connection = /tmp/ansible-netbox-cache
Benefits:
- Faster playbook runs
- Reduced API calls to NetBox
- Works offline (for cache duration)
Clear cache:
rm -rf /tmp/ansible-netbox-cache
Troubleshooting
Authentication Errors
Error: Failed to query NetBox API
Check:
# Test API token
curl -H "Authorization: Token $NETBOX_API_TOKEN" \
https://netbox.spaceships.work/api/dcim/devices/ | jq
# Verify token permissions
# Token must have read access to: DCIM, IPAM, Virtualization
SSL Certificate Errors
Error: SSL: CERTIFICATE_VERIFY_FAILED
Solutions:
# Option 1: Add CA certificate
validate_certs: true
ssl_ca_cert: /path/to/ca-bundle.crt
# Option 2: Disable for self-signed (dev only!)
validate_certs: false
No Hosts Found
Error: Inventory is empty
Check:
# List all devices in NetBox
curl -H "Authorization: Token $NETBOX_API_TOKEN" \
https://netbox.spaceships.work/api/dcim/devices/ | jq '.count'
# Check query filters
# Ensure devices match your filters (status, tags, etc.)
Debug inventory plugin:
ansible-inventory -i ansible/inventory/netbox.yml --list -vvv
Primary IP Not Set
Error: ansible_host is undefined
Cause: Devices/VMs in NetBox don't have primary_ip4 set
Solution:
# Fallback to custom field or use DNS name
compose:
ansible_host: primary_ip4 | default(custom_fields.management_ip) | default(name + '.spaceships.work')
Best Practices
1. Use Service Account
Create dedicated NetBox user for Ansible:
Username: ansible-automation
Permissions: Read-only (DCIM, IPAM, Virtualization)
Token: Never expires (or set appropriate expiration)
2. Tag for Inventory
Tag devices/VMs intended for Ansible management:
Tag: ansible-managed
Filter in inventory:
query_filters:
- tag: ansible-managed
3. Set Primary IPs
Always set primary_ip4 in NetBox for devices/VMs:
Device → Edit → Primary IPv4
4. Use Custom Fields
Add custom fields to NetBox for Ansible-specific data:
ansible_user (Text)
ansible_port (Integer)
ansible_python_interpreter (Text)
backup_enabled (Boolean)
5. Test Before Running
Always test inventory before running playbooks:
# Verify hosts
ansible-inventory -i inventory/netbox.yml --graph
# Test connectivity
ansible all -i inventory/netbox.yml -m ping
6. Document in NetBox
Use NetBox description fields to document:
- Ansible playbooks that manage this host
- Special configuration requirements
- Dependencies on other hosts