# 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 ```text ┌──────────┐ │ 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 ```bash cd ansible uv run ansible-galaxy collection install netbox.netbox ``` **Or add to requirements.yml:** ```yaml --- collections: - name: netbox.netbox version: ">=3.0.0" ``` ```bash 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:** ```bash # 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` ```yaml --- 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 ```bash # 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:** ```yaml --- 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:** ```yaml query_filters: - role: docker-host - role: k8s-node - status: active ``` ### Custom Groups **Create custom groups based on NetBox data:** ```yaml --- 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:** ```yaml --- 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` ```yaml --- 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` ```yaml --- - 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:** ```bash cd ansible uv run ansible-playbook -i inventory/netbox.yml playbooks/configure-docker-hosts.yml ``` ### Example 2: Site-Specific Deployments **Inventory with site grouping:** ```yaml --- 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:** ```yaml --- - 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:** ```yaml --- 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:** ```yaml --- - 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:** ```yaml --- - 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` ```ini [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:** ```bash rm -rf /tmp/ansible-netbox-cache ``` ## Troubleshooting ### Authentication Errors **Error:** `Failed to query NetBox API` **Check:** ```bash # 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:** ```yaml # 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:** ```bash # 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:** ```bash 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:** ```yaml # 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: ```text 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: ```text Tag: ansible-managed ``` **Filter in inventory:** ```yaml query_filters: - tag: ansible-managed ``` ### 3. Set Primary IPs Always set primary_ip4 in NetBox for devices/VMs: ```text Device → Edit → Primary IPv4 ``` ### 4. Use Custom Fields Add custom fields to NetBox for Ansible-specific data: ```text ansible_user (Text) ansible_port (Integer) ansible_python_interpreter (Text) backup_enabled (Boolean) ``` ### 5. Test Before Running Always test inventory before running playbooks: ```bash # 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 - [NetBox Ansible Collection Documentation](https://docs.ansible.com/ansible/latest/collections/netbox/netbox/) - [Dynamic Inventory Plugin Guide](https://docs.ansible.com/ansible/latest/plugins/inventory.html) - [NetBox API Documentation](https://demo.netbox.dev/api/docs/)