Aller au contenu
Infrastructure as Code medium

Inventaire dynamique NetBox avec netbox.netbox.nb_inventory

40 min de lecture

Logo Ansible

NetBox est la référence open-source en IPAM/DCIM : il gère vos racks, vos devices physiques, vos VMs, vos VLANs, vos prefixes IP. Si NetBox est déjà déployé chez vous, il est la source de vérité naturelle pour Ansible, pas la peine de dupliquer l'inventaire ailleurs.

Le plugin netbox.netbox.nb_inventory transforme votre instance NetBox en inventaire Ansible dynamique avec des filtres très fins (par site, par role, par status, par tag, par tenant). C'est le pattern recommandé dans les moyennes/grandes entreprises qui ont déjà investi dans NetBox.

  • Configurer le plugin avec un token API NetBox.
  • Filtrer les hôtes par site, role, status, tag NetBox.
  • Grouper automatiquement par les attributs NetBox.
  • Récupérer les IPs depuis l'IPAM NetBox.
  • Combiner devices physiques et virtualization dans un même inventaire.
  • Une instance NetBox 3.x ou 4.x opérationnelle.
  • Token API NetBox créé.
  • Collection netbox.netbox installée.
  • pynetbox Python : pip install pynetbox.

Sur l'UI NetBox : User → API Tokens → Add a token. Trois réglages de ce formulaire séparent un token correct d'un token à risque, et ce sont les trois que l'on laisse le plus souvent aux valeurs par défaut. Write enabled est coché par défaut alors qu'un inventaire n'écrit jamais dans NetBox. Allowed IPs est vide par défaut, ce qui rend le token utilisable depuis n'importe où. Et Expires vide crée un token éternel, donc impossible à faire tourner sans casser quelque chose un jour.

ChampValeur recommandée
Useransible-readonly (créer ce user au préalable)
DescriptionInventaire dynamique Ansible
Write enabled❌ Décocher (lecture seule)
Allowed IPsIP du control node Ansible (restriction réseau)
ExpiresDate d'expiration (ex: 1 an)

Copier le token généré, il sera utilisé une seule fois (mais peut être recréé).

Deux composants sont nécessaires et ils s'installent par des canaux différents. La collection netbox.netbox apporte le plugin d'inventaire, elle vient de Galaxy. La bibliothèque pynetbox est une dépendance Python du côté des modules de la collection ; elle s'installe avec pip, dans l'environnement Python qui exécute Ansible. Épinglez les deux versions, sinon un ansible-galaxy lancé six mois plus tard ramènera une collection dont le comportement de regroupement peut avoir changé.

Fenêtre de terminal
ansible-galaxy collection install netbox.netbox:==3.23.0
pip install 'pynetbox==7.8.0'

Activer dans ansible.cfg :

[inventory]
enable_plugins = host_list, script, auto, yaml, ini, netbox.netbox.nb_inventory

Le fichier d'inventaire d'un plugin dynamique n'est pas une liste d'hôtes, c'est une configuration de source. Son nom doit se terminer par .yml ou .yaml et sa première clé doit être plugin:, sinon le plugin auto ne reconnaît pas le fichier et Ansible le traite comme un inventaire YAML classique, donc vide. Le champ validate_certs: true est le défaut, gardez-le explicite : c'est la ligne qu'on est tenté de passer à false le jour d'un certificat auto-signé, et le rendre visible force à en faire un choix conscient.

Créer inventory/netbox.yml :

---
plugin: netbox.netbox.nb_inventory
api_endpoint: https://netbox.example.com
token: !vault |
$ANSIBLE_VAULT;1.1;AES256
3736...
validate_certs: true

Le token chiffré avec Ansible Vault :

Fenêtre de terminal
ansible-vault encrypt_string '<le-token>' --name token

Tester :

Fenêtre de terminal
ansible-inventory -i inventory/netbox.yml --graph

Sortie : tous vos devices NetBox apparaissent comme des hôtes Ansible.

Sans filtre, le plugin retourne toutes les machines de NetBox. Pour ne garder que certaines :

plugin: netbox.netbox.nb_inventory
api_endpoint: https://netbox.example.com
token: !vault | ...
# Filtres NetBox (équivalent des filtres URL de l'API REST)
query_filters:
- status: active
- role: webserver
- tag: managed-by-ansible

Avantage : la liste retournée par NetBox est déjà filtrée côté API, moins de payload, plus rapide.

# Multi-site
query_filters:
- status: active
- site: paris

NetBox a déjà ses propres attributs structurés (site, role, manufacturer, status). Le plugin les expose automatiquement :

plugin: netbox.netbox.nb_inventory
api_endpoint: https://netbox.example.com
token: !vault | ...
group_by:
- device_roles # groupe par role: webserver, dbserver, switch
- sites # groupe par site: paris, londres
- device_types # groupe par modèle: dell-r740, hp-dl380
- tags # groupe par tag NetBox
- status # groupe par status: active, planned, decommissioning
- region # groupe par région (toujours au singulier)
- tenants # groupe par tenant (multi-tenant NetBox)

Attention à la forme des clés : l'option plurals vaut true par défaut, ce qui impose les formes plurielles sites, tenants, tags, device_roles, device_types, manufacturers, platforms, racks. Quatre clés font exception et restent au singulier quelle que soit la valeur de plurals : region, status, location, site_group. Écrire regions fait échouer la lecture de l'inventaire avec une erreur de valeur non autorisée. Les noms de groupes sont construits à partir du slug NetBox, pas du nom d'affichage : un site nommé « Paris DC1 » de slug paris-dc1 donne le groupe sites_paris-dc1.

Résultat :

@all:
|--@device_roles_webserver:
| |--web1.paris.example.com
| |--web2.paris.example.com
|--@device_roles_dbserver:
| |--db1.paris.example.com
|--@sites_paris:
| |--web1.paris.example.com
| |--db1.paris.example.com
|--@status_active:
| |--... (toutes les machines actives)

Vous pouvez maintenant cibler device_roles_webserver:&sites_paris pour les webservers du site Paris uniquement.

NetBox distingue les devices physiques (/api/dcim/devices/) des VMs (/api/virtualization/virtual-machines/). Le plugin interroge les deux points d'accès systématiquement : il n'existe aucune option pour activer les VMs, elles sont là par défaut. Ce que vous pouvez régler, c'est la façon de les distinguer et le volume de données rapatriées.

plugin: netbox.netbox.nb_inventory
api_endpoint: https://netbox.example.com
token: !vault | ...
config_context: false # ne pas charger les config_contexts (alourdit)
group_by:
- is_virtual # crée un groupe is_virtual contenant les seules VMs
- status
# Filtres distincts pour les devices et pour les VMs
device_query_filters:
- has_primary_ip: 'true'
vm_query_filters:
- status: active

Le groupe is_virtual ne contient que les machines virtuelles, le plugin ne crée volontairement pas le groupe inverse. Pour ne viser que le matériel physique, ciblez donc all:!is_virtual. Pour ne filtrer que d'un côté, utilisez device_query_filters: et vm_query_filters: plutôt que query_filters:, qui s'applique aux deux requêtes à la fois. Un piège classique en découle : un query_filters: avec role: webserver retourne zéro VM si vos VMs ne portent pas de rôle NetBox.

NetBox stocke une IP primaire sur chaque device et chaque VM. Le plugin la lit et alimente ansible_host automatiquement, sans configuration, en retirant lui-même le masque : un primary_ip NetBox à 10.20.0.14/24 donne ansible_host: 10.20.0.14. Rien à composer pour le cas courant.

Deux options changent ce comportement quand votre parc ne s'adresse pas par IP. ansible_host_dns_name: true fait pointer ansible_host sur le champ DNS Name de l'IP primaire. oob_ip_as_primary_ip: true utilise l'adresse out-of-band, ce qui correspond au cas des équipements réseau que l'on administre par leur interface de management.

plugin: netbox.netbox.nb_inventory
api_endpoint: https://netbox.example.com
token: !vault | ...
ansible_host_dns_name: false # laisser ansible_host sur l'IP primaire
oob_ip_as_primary_ip: false # passer à true pour les équipements réseau
compose:
ansible_user: "'admin' if 'switch' in tags else 'ansible'"

compose: calcule des variables Ansible à partir des données déjà présentes dans les host vars. L'exemple ci-dessus choisit le compte SSH selon la présence d'un tag ; il fonctionne parce que le host var tags est une liste de slugs, donc directement testable avec in.

Le plugin ne recopie pas les objets NetBox tels quels, il les aplatit en host vars. Deux règles gouvernent les noms et il faut les connaître avant d'écrire un template. D'abord, l'option plurals valant true par défaut, la plupart des attributs de relation arrivent sous forme de liste à un seul élément : sites, device_roles, device_types, manufacturers, platforms, tenants. On y accède donc par [0]. Ensuite, la valeur stockée est le slug NetBox, pas le nom d'affichage.

Trois host vars échappent à cette règle : status reste un dictionnaire (value et label), tags est une liste de slugs de longueur variable, et primary_ip4 / primary_ip6 sont des chaînes d'adresse sans masque.

- name: Afficher les attributs NetBox de l'hôte
ansible.builtin.debug:
msg: |
Device {{ inventory_hostname }} :
- Site : {{ sites[0] }}
- Role : {{ device_roles[0] }}
- Manufacturer : {{ manufacturers[0] }}
- Model : {{ device_types[0] }}
- Serial : {{ serial }}
- Status : {{ status.label }}
- Primary IP : {{ primary_ip4 }}
- Tags : {{ tags | join(', ') }}

Pour obtenir la structure NetBox complète du site au lieu de son seul slug, activez site_data: true ; sites[0] devient alors le dictionnaire renvoyé par l'API et sites[0].name redevient valide. C'est ce qui rend NetBox utile ici : vos playbooks lisent la donnée IPAM/DCIM sans la dupliquer ailleurs.

NetBox peut être lent sur de gros parcs (300+ devices). Cache obligatoire :

plugin: netbox.netbox.nb_inventory
api_endpoint: https://netbox.example.com
token: !vault | ...
cache: true
cache_plugin: jsonfile
cache_timeout: 600
cache_connection: /tmp/ansible_netbox_cache

Pour rafraîchir après modification dans NetBox :

Fenêtre de terminal
ansible-inventory -i inventory/netbox.yml --list --refresh-cache

L'intersection :& de deux groupes générés par NetBox remplace la liste de machines qu'on maintenait à la main. Le tiret dans dell-r740 vient du slug NetBox : Ansible accepte ce caractère dans un nom de groupe mais émet un avertissement, réglé par TRANSFORM_INVALID_GROUP_CHARS. Vérifiez toujours la cible avec --list-hosts avant de lancer le playbook pour de bon.

Fenêtre de terminal
ansible-playbook -i inventory/netbox.yml \
--limit 'sites_paris:&device_types_dell-r740' \
patch-rhel.yml

Configurer uniquement les machines en statut "planned"

Section intitulée « Configurer uniquement les machines en statut "planned" »
Fenêtre de terminal
ansible-playbook -i inventory/netbox.yml \
--limit 'status_planned' \
bootstrap.yml

NetBox suit le cycle de vie : plannedactivedecommissioningdecommissioned. Cibler les planned permet de bootstrap uniquement les nouvelles machines.

Fenêtre de terminal
ansible-playbook -i inventory/netbox.yml \
--limit 'tags_pci-dss' \
audit-pci.yml

Toutes les machines taggées pci-dss dans NetBox sont auditées. Aucune liste à maintenir, la source de vérité est NetBox.

La moitié de ces symptômes se diagnostique avec la même commande, ansible-inventory -i inventory/netbox.yml --list -vvv, qui affiche les URL réellement appelées et le code HTTP de chaque réponse. Commencez par là plutôt que par modifier la configuration au hasard : un inventaire vide sans erreur vient presque toujours d'un query_filters trop strict, pas d'un problème de plugin.

SymptômeCauseFix
pynetbox not foundModule Python manquantpip install 'pynetbox==7.8.0'
401 UnauthorizedToken expiré ou mal écritRecréer le token, vérifier les permissions
Inventaire vide sans erreurquery_filters trop strict, ou slug inexistantTester le filtre sur l'API REST NetBox
Requêtes très nombreusesfetch_all: true (défaut) sur un parc filtréfetch_all: false groupe les requêtes par lots
HTTP 414 URI Too Longfetch_all: false sur beaucoup d'hôtesBaisser max_uri_length (défaut 4000)
Lent (>10s)Pas de cachecache: true mandatory
Allowed IPs bloqueToken configuré avec IP allowedAjouter l'IP du control node
value of group_by must be one of...Clé inexistante, par exemple regionsUtiliser region, toujours au singulier
group_by option "site" is not validClé au singulier alors que plurals vaut trueÉcrire sites, ou passer plurals: false

La question n'est pas « NetBox est-il un bon inventaire », mais « avez-vous déjà NetBox et le tenez-vous à jour ». Un NetBox alimenté à la main et jamais relu produit un inventaire faux, et un inventaire faux est pire qu'un fichier statique qu'on sait incomplet. Lisez donc la première ligne du tableau comme une condition d'entrée, pas comme un critère parmi d'autres.

CritèreNetBox recommandéAutre solution
Source de vérité IPAM/DCIM existanteSans objet
Petit parc (< 50 hôtes)SurdimensionnéStatique YAML
Cloud public (AWS/Azure/GCP)Plugin cloud directNetBox redondant
Mix on-prem + cloudPossible (NetBox sync)Plusieurs -i
Audit conformité (tags, owners)✅ excellenceGalère sans NetBox
  • NetBox = source de vérité IPAM/DCIM unifiée, pas de duplication d'inventaire.
  • netbox.netbox.nb_inventory = plugin officiel maintenu par NetBox Labs.
  • Token chiffré Vault + restriction Allowed IPs au token.
  • query_filters: = filtrer côté API (status, role, site, tag).
  • group_by: = grouper par attributs NetBox natifs.
  • ansible_host est déjà rempli par le plugin depuis l'IP primaire, compose: sert aux variables restantes (ansible_user, etc.).
  • plurals: true par défaut : les host vars de relation sont des listes à un élément, accès en [0].
  • Cache mandatory dès qu'on dépasse quelques centaines de devices.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn