Files
gh-basher83-lunar-claude-pl…/skills/netbox-powerdns-integration/workflows/ansible-dynamic-inventory.md
2025-11-29 18:00:21 +08:00

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

Further Reading