Cómo documentar y mantener sistemas en NetBox
Guía operativa para incorporar recursos nuevos y actualizar recursos existentes en el inventario de NetBox de iort.
Índice
- Antes de empezar
- Cómo autenticarse en la API
- Decidir qué objeto NetBox crear
- Campos obligatorios mínimos
- Custom fields disponibles
- Tags disponibles y cuándo usarlas
- Flujo de trabajo para LLMs y operadores
- Plantillas por tipo de sistema
- Qué no meter en NetBox
- Referencia de IDs útiles
- Checklist final para LLMs
1. Antes de empezar
NetBox en iort es el inventario estructurado, no la fuente de verdad operativa de todo.
Antes de crear cualquier objeto, responde estas preguntas:
| Pregunta | Fuente real |
|---|---|
| ¿Cómo se desplegó? | Pulumi stack |
| ¿Dónde están los secretos? | Google Secret Manager (proyecto iort-secrets) |
| ¿Dónde está el código? | Repo git local o GitHub |
| ¿Quién es el responsable operativo? | Definir owner en NetBox |
| ¿A qué tenant pertenece? | Ver tenants disponibles |
NetBox debe recoger: identidad, relaciones, owner, tenant, estado, clasificación y enlaces externos. No duplicar lo que ya está en esas otras fuentes.
2. Cómo autenticarse en la API
URL base
https://iort-netbox.tailf75db9.ts.net
Solo accesible desde la tailnet de Tailscale. Verificar conectividad con:
curl -Ik "https://iort-netbox.tailf75db9.ts.net/api/"
Comportamiento esperado sin autenticación:
curl -Ik "https://iort-netbox.tailf75db9.ts.net/api/"
Devuelve 403, que en este despliegue significa que la API está accesible por HTTPS dentro de la tailnet pero exige autenticación.
Autenticación por token v2 (recomendada)
En NetBox 4.5.2 los tokens v2 sí se usan por cabecera HTTP. El formato correcto es:
Authorization: Bearer nbt_<key>.<token>
Ejemplo de lectura:
NETBOX_TOKEN="nbt_<key>.<token>"
curl -s "https://iort-netbox.tailf75db9.ts.net/api/dcim/sites/" \
-H "Authorization: Bearer $NETBOX_TOKEN" \
-H "Accept: application/json" \
| python3 -m json.tool
Ejemplo de escritura:
curl -s -X POST "https://iort-netbox.tailf75db9.ts.net/api/dcim/sites/" \
-H "Authorization: Bearer $NETBOX_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{...}'
Con autenticación por token no hace falta X-CSRFToken.
Autenticación por sesión (opcional)
Si ya estás operando desde navegador o necesitas reproducir el flujo web con cookies, también puedes usar sesión:
CSRF="<valor_csrftoken>"
SESSION="<valor_sessionid>"
COOKIES="csrftoken=$CSRF; sessionid=$SESSION"
# Para peticiones de escritura (POST/PATCH/DELETE), añadir siempre:
# -H "X-CSRFToken: $CSRF"
# -H "Referer: https://iort-netbox.tailf75db9.ts.net/"
Patrón recomendado para LLMs y agentes
Dentro de la tailnet, el patrón correcto es este:
- usar
MCPpara leer, explorar y localizar el objeto canónico; - usar la
REST APIcon token v2 para crear o editar; - volver a leer tras la escritura y verificar que el resultado final es el esperado;
- añadir una entrada de
journalcon el contexto del cambio.
MCP en este despliegue es de solo lectura. No lo uses como canal de escritura.
Credenciales de admin
Las credenciales de admin están en GSM bajo el secreto iort-netbox-prod-netbox-admin-password del proyecto iort-secrets. Úsalas para entrar a la UI por Tailscale y, si hace falta, generar un token v2 dedicado para automatización.
gcloud secrets versions access latest \
--secret=iort-netbox-prod-netbox-admin-password \
--project=iort-secrets
3. Decidir qué objeto NetBox crear
| Tipo de sistema | Objeto NetBox a usar |
|---|---|
| VPS / instancia cloud | VirtualMachine dentro de un Cluster |
| Hardware físico o robot | Device |
| Web/app serverless (Cloudflare Pages, Firebase, etc.) | Site en grupo "Cloud" |
| Red / prefijo / VPN | Prefix, IPAddress, Tunnel |
| Componente sin identidad propia | InventoryItem |
| Puerto/endpoint publicado sobre una VM/Device | Service |
Regla clave: no crees VMs ficticias para representar aplicaciones serverless o funciones cloud. Usa Site como punto de anclaje para proveedores externos sin infraestructura propia.
4. Campos obligatorios mínimos
Todo objeto de producción debe tener como mínimo:
name— identificador humano, estableslug— kebab-case, sin cambiar después de crearlostatus: activetenant— a qué organización/dominio pertenecedescription— una línea, qué es y en qué entorno- al menos una
tagde clasificación (ver sección 6) custom_field.sot_system— quién gestiona la fuente de verdad operativa
Y en comments, contexto operativo relevante (acceso, dependencias, IaC, política de secretos).
5. Custom fields disponibles
Estos custom fields están disponibles en los objetos de tipo site, cluster y virtual_machine:
| Campo | Tipo | Qué poner |
|---|---|---|
sot_system |
texto | quién es la fuente de verdad operativa: pulumi, cloudflare, scaleway, manual... |
iac_stack |
texto | nombre del stack: cloudflare-pages, scaleway-vps, nombre del stack Pulumi... |
pulumi_stack_url |
URL | enlace directo al stack en Pulumi Cloud |
access_model |
texto | public, tailscale, vpn, private |
service_tier |
texto | prod, staging, dev |
gsm_secret_project |
texto | proyecto de GSM donde están los secretos: iort-secrets |
gsm_secret_prefix |
texto | prefijo del bundle de secretos: p.ej. iort-netbox-prod- |
backup_policy |
texto | descripción de la política de backup |
runbook_ref |
texto | ruta o URL al runbook operativo |
operator_portal_url |
URL | dashboard del proveedor o UI de gestión |
observed_hostname |
texto | hostname observado en runtime (solo VMs/Devices) |
observed_public_ipv4 |
texto | IP pública observada (solo VMs/Devices) |
tailscale_dns |
texto | FQDN de Tailscale (solo VMs/Devices) |
published_endpoints |
JSON | endpoints públicos en formato JSON |
6. Tags disponibles y cuándo usarlas
Tags de gestión (aplicar siempre una):
| Tag | Cuándo usar |
|---|---|
managed-by-pulumi |
El recurso se gestiona con Pulumi IaC |
managed-by-manual |
Documentado a mano, sin IaC |
Tags de acceso:
| Tag | Cuándo usar |
|---|---|
exposure-public |
Accesible desde Internet sin autenticación de red |
access-tailscale |
Accesible solo desde la tailnet |
Tags de entorno:
| Tag | Cuándo usar |
|---|---|
env-prod |
Producción |
Tags de proveedor/plataforma:
| Tag | Cuándo usar |
|---|---|
cloudflare |
Cualquier recurso en Cloudflare |
cloudflare-pages |
Desplegado en Cloudflare Pages |
scaleway |
Infraestructura en Scaleway |
tailscale |
Recurso con conectividad Tailscale |
Tags de proyecto (añadir la del proyecto correspondiente):
iort, custochef, visort, ferrol, ...
7. Flujo de trabajo para LLMs y operadores
Patrón general
Antes de escribir en NetBox:
- leer primero;
- buscar por varias claves antes de crear nada;
- editar el objeto canónico si ya existe;
- crear padres antes que hijos;
- no sobrescribir
commentsnicustom_fieldssin haber leído el estado actual; - dejar siempre journal de lo que se hizo.
Contrato de trabajo recomendado para un LLM
Antes de ejecutar una escritura, el LLM debería poder responder internamente a esto:
- qué tipo de objeto principal va a tocar;
- si va a
crearoeditar; - cuál es el objeto canónico o por qué no existe todavía;
- qué campos exactos va a modificar;
- qué datos siguen siendo inciertos y por tanto no debe inventar.
Si no puede responder esas cinco preguntas con claridad, no debería escribir todavía.
Información mínima que un LLM debe reunir
Antes de crear o editar, intenta reunir como mínimo:
- nombre estable del sistema o proyecto;
- tipo de objeto principal esperado:
Site,VirtualMachine,Device,Service,Prefixo similar; - tenant;
- owner o equipo responsable;
- entorno:
prod,staging,dev; - modelo de acceso:
public,tailscale,vpn,private; - proveedor o fuente de verdad operativa:
pulumi,cloudflare,scaleway,manual; - hostname, dominio, URL pública o FQDN de Tailscale si aplica;
- stack IaC, repo, runbook y prefijo de secretos si existen.
Si faltan varios de esos datos, es mejor parar y pedir contexto adicional que inventarlos.
Orden recomendado de modelado
Crear o editar en este orden cuando aplique:
tenant,owner,contactsi faltan;region,site group,site;cluster;deviceovirtual machine;- interfaces, IPs, prefijos o direccionamiento;
service;- tags, custom fields, comments y journal.
Errores comunes:
- crear una
VMsinsiteniclustercuando ambos existen; - modelar una web serverless como
VirtualMachine; - meter el endpoint como texto libre en
commentssin crearServicecuando sí hay host real; - crear objetos hijos antes de saber cuál es el padre correcto.
Cómo buscar sin duplicar
Si no estás seguro de si algo ya existe, busca varias veces con criterios distintos y compara resultados. Ejemplo con lectura completa y filtrado local:
curl -s "https://iort-netbox.tailf75db9.ts.net/api/dcim/sites/?limit=200" \
-H "Authorization: Bearer $NETBOX_TOKEN" \
| python3 - <<'PY'
import json, sys
needle = "nombre-o-fragmento".lower()
data = json.load(sys.stdin)
for item in data.get("results", []):
haystack = " ".join([
str(item.get("name") or ""),
str(item.get("slug") or ""),
str(item.get("description") or ""),
]).lower()
if needle in haystack:
print(item["id"], item.get("name"), item.get("slug"))
PY
Repite el patrón para sites, devices, virtual-machines o clusters antes de decidir crear.
Flujo A — documentar un proyecto nuevo
Paso 1 — Autenticarse
Exportar un token v2 según la sección 2.
export NETBOX_TOKEN="nbt_<key>.<token>"
Paso 2 — Buscar si ya existe algo relacionado
Antes de crear nada, buscar candidatos por:
- nombre;
- slug;
- hostname;
- dominio o URL;
- tenant;
- stack Pulumi;
- tags;
- comentarios existentes.
Patrón mínimo de lectura por API:
# Sites existentes
curl -s "https://iort-netbox.tailf75db9.ts.net/api/dcim/sites/" \
-H "Authorization: Bearer $NETBOX_TOKEN" \
| python3 -c "import sys,json; d=json.load(sys.stdin); [print(t['id'],t['name'],t['slug']) for t in d['results']]"
# Tenants
curl -s "https://iort-netbox.tailf75db9.ts.net/api/tenancy/tenants/" \
-H "Authorization: Bearer $NETBOX_TOKEN" \
| python3 -c "import sys,json; d=json.load(sys.stdin); [print(t['id'],t['name']) for t in d['results']]"
Si el LLM tiene acceso al MCP del workspace, este es el orden recomendado:
- consultar por MCP si ya existe un
Site,VirtualMachine,DeviceoServicerelacionado; - usar la API solo cuando ya esté claro qué objeto hay que crear o editar.
Paso 3 — Decidir si hay que crear o editar
Crear un objeto nuevo solo si no existe ya una identidad operativa equivalente.
Normalmente se edita si:
- el proyecto es el mismo y solo cambian descripción, tags, enlaces, comentarios o custom fields;
- la VM o device sigue siendo el mismo recurso lógico;
- cambió el dominio, el runbook o el stack, pero no la identidad principal.
Normalmente se crea un objeto nuevo si:
- aparece un nuevo entorno separado;
- el hostname o identidad operativa cambia de verdad;
- se sustituye un hardware por otro con lifecycle distinto;
- antes no existía el recurso en inventario.
Paso 4 — Crear el objeto principal
Crear primero el objeto principal y después sus relaciones o metadatos complementarios.
Ver plantillas en la sección 8.
Si el proyecto nuevo necesita más de un objeto, orden recomendado:
- proyecto cloud/serverless:
Sitey después, si aplica, servicios o enlaces operativos; - VPS o nodo virtual:
Site->Cluster->VirtualMachine->Service; - robot o hardware edge:
Site->Device-> interfaces/IPs ->Service.
Paso 5 — Añadir entrada en el journal
Siempre añadir una entrada de journal al objeto recién creado documentando el contexto de la creación:
curl -s -X POST "https://iort-netbox.tailf75db9.ts.net/api/extras/journal-entries/" \
-H "Authorization: Bearer $NETBOX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"assigned_object_type": "dcim.site",
"assigned_object_id": <ID_DEL_OBJETO>,
"kind": "info",
"comments": "## Registro inicial\n\nContexto de la creación, estado actual, pendientes."
}'
Los assigned_object_type válidos son: dcim.site, dcim.device, virtualization.virtualmachine, virtualization.cluster.
Paso 6 — Verificar
Tras crear:
- releer el objeto por API o MCP;
- comprobar
name,slug,tenant,tags,custom_fieldsycomments; - abrir la
display_urlsi hace falta validación visual; - comprobar que no se ha duplicado un objeto existente.
Flujo B — editar un proyecto existente
Paso 1 — Localizar el objeto canónico
No editar “a ciegas”. Primero identifica el objeto exacto y guarda su id.
Busca por más de una clave si hace falta:
nameslug- hostname
- FQDN de Tailscale
- URL pública
- tenant
- tags
- referencias en
commentsocustom_fields
Paso 2 — Leer el estado actual completo
Ejemplo para un Site existente:
SITE_ID="<id>"
curl -s "https://iort-netbox.tailf75db9.ts.net/api/dcim/sites/${SITE_ID}/" \
-H "Authorization: Bearer $NETBOX_TOKEN" \
| python3 -m json.tool
Haz lo mismo con la ruta del tipo correspondiente si es una VM, device o cluster.
Paso 3 — Definir el alcance del cambio
Antes de editar, separa:
- datos que deben mantenerse;
- datos que deben corregirse;
- datos nuevos que hay que añadir;
- datos dudosos que no deben tocarse sin confirmación.
Regla práctica: si no estás seguro de un campo, no lo “normalices” por intuición.
Además, decide si el cambio es:
descriptivo: descripción, comments, enlaces, tags, custom fields;estructural: cambiar padre, tenant, site, cluster, hostname o identidad principal;de limpieza: corregir duplicados, normalizar nombres, retirar datos erróneos.
Los cambios estructurales son los más peligrosos y deben hacerse con especial cuidado.
Paso 4 — Aplicar un PATCH mínimo
Editar solo los campos necesarios. Ejemplo de PATCH para un Site:
curl -s -X PATCH "https://iort-netbox.tailf75db9.ts.net/api/dcim/sites/${SITE_ID}/" \
-H "Authorization: Bearer $NETBOX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"description": "Descripción actualizada",
"comments": "Comentarios operativos actualizados",
"tags": [{"id": 34}, {"id": 35}, {"id": 38}],
"custom_fields": {
"sot_system": "cloudflare",
"service_tier": "prod",
"access_model": "public"
}
}'
Recomendaciones:
- no cambies
slugsi no hay una razón fuerte; - no borres
commentsútiles: léelos antes y reescríbelos de forma deliberada; - no elimines tags o custom fields válidos por omisión accidental;
- si el cambio es grande, haz varios
PATCHpequeños en vez de uno enorme.
Cómo preservar datos existentes al editar
Antes de hacer PATCH, inspecciona explícitamente estas partes:
commentstagscustom_fields- relaciones (
tenant,site,cluster,device role,platform)
Regla operativa:
- si un campo ya contiene información válida, fusiónalo;
- no lo sustituyas por una versión más corta salvo que sea claramente una corrección;
- si el campo tiene mezcla de información buena y basura, limpia solo la basura y conserva lo útil.
Especialmente importante:
commentsdebe seguir siendo un resumen operativo, no un dump enorme;custom_fieldsdebe quedar consistente y no perder claves válidas;tagsno deben reducirse por accidente al set mínimo del ejemplo.
Estrategia segura para comments
comments debe quedar legible para humanos y LLMs. Patrón recomendado:
## <nombre del sistema>
<qué es y para qué sirve>
### Acceso
- URL o hostname principal
- FQDN de Tailscale si aplica
- vía de acceso operativa
### IaC / Código
- stack o repo
- dashboard o URL relevante
### Secretos
- proyecto GSM
- prefijo de secretos
### Notas operativas
- dependencias
- límites
- pendientes relevantes
No metas en comments:
- secretos;
- logs completos;
- salidas enteras de CLI;
- tickets temporales o notas de sprint sin valor duradero.
Paso 5 — Registrar journal del cambio
Tras editar, añade una entrada de journal con:
- qué cambió;
- por qué cambió;
- fuente de verdad usada;
- pendientes o dudas que sigan abiertas.
Paso 6 — Verificación posterior
Después del PATCH:
- relee el objeto por API;
- confirma que no se han perdido tags ni custom fields;
- si el LLM usa MCP, vuelve a consultarlo por MCP para comprobar que el inventario leído coincide con el escrito.
Abrir en el navegador (desde Tailscale) la URL display_url devuelta en la respuesta de creación para confirmar visualmente.
8. Plantillas por tipo de sistema
Web/app en Cloudflare Pages (Site)
{
"name": "<nombre-legible> (Cloudflare Pages)",
"slug": "cf-pages-<proyecto>",
"status": "active",
"group": 1,
"tenant": 1,
"description": "Descripción breve. SPA/Web desplegada en Cloudflare Pages.",
"comments": "## <nombre> — Web\n\n**URL pública:** https://dominio.io\n\n### Cloudflare Pages\n- Proyecto: <nombre-proyecto>\n- Account ID: <id>\n- Zone ID: <id>\n\n### Código fuente\n- Repo: <ruta>\n- Build: npm run build\n- Deploy: wrangler pages deploy dist\n\n### DNS\n- dominio.io CNAME → <proyecto>.pages.dev",
"tags": [
{"id": 34},
{"id": 35},
{"id": 36},
{"id": 37},
{"id": 38},
{"id": 6},
{"id": 8}
],
"custom_fields": {
"iac_stack": "cloudflare-pages",
"sot_system": "cloudflare",
"access_model": "public",
"service_tier": "prod",
"gsm_secret_project": "iort-secrets",
"operator_portal_url": "https://dash.cloudflare.com/<account-id>/pages/view/<proyecto>"
}
}
IDs de tags en la plantilla:
34=managed-by-manual,35=cloudflare,36=cloudflare-pages,37=exposure-public,38=env-prod,6=iort,8=infra. Verificar IDs actuales antes de usar (pueden cambiar si se añaden tags).
VPS en Scaleway (VirtualMachine)
{
"name": "<hostname>",
"status": "active",
"cluster": 1,
"site": 1,
"tenant": 1,
"platform": null,
"description": "Descripción del servicio en una línea.",
"comments": "## <hostname>\n\n**IP pública:** x.x.x.x\n**Tailscale:** <hostname>.tailf75db9.ts.net\n**Acceso:** tailscale ssh root@<hostname>\n\n### IaC\n- Pulumi stack: mmiguez/prod en proyecto iort-netbox\n- URL stack: https://app.pulumi.com/mmiguez/...\n\n### Secretos\n- Prefijo GSM: <prefijo>-\n- Proyecto GSM: iort-secrets",
"tags": [
{"id": 1},
{"id": 2},
{"id": 3},
{"id": 38}
],
"custom_fields": {
"iac_stack": "<nombre-stack-pulumi>",
"sot_system": "pulumi",
"access_model": "tailscale",
"service_tier": "prod",
"gsm_secret_project": "iort-secrets",
"gsm_secret_prefix": "<prefijo>-prod-",
"tailscale_dns": "<hostname>.tailf75db9.ts.net"
}
}
Patch mínimo para editar un objeto existente
Cambiar solo la URL del detalle y el payload según el tipo de objeto:
curl -s -X PATCH "https://iort-netbox.tailf75db9.ts.net/api/<ruta-del-objeto>/<ID>/" \
-H "Authorization: Bearer $NETBOX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"description": "Nueva descripción",
"comments": "Comentarios actualizados",
"tags": [{"id": 1}, {"id": 2}, {"id": 38}],
"custom_fields": {
"sot_system": "pulumi",
"service_tier": "prod",
"access_model": "tailscale"
}
}'
Úsalo como plantilla para:
dcim/sitesdcim/devicesvirtualization/virtual-machinesvirtualization/clusters
Rutas API útiles
Lectura de listas:
/api/dcim/sites//api/dcim/devices//api/virtualization/virtual-machines//api/virtualization/clusters//api/tenancy/tenants//api/extras/journal-entries/
Lectura de detalle:
/api/dcim/sites/<id>//api/dcim/devices/<id>//api/virtualization/virtual-machines/<id>//api/virtualization/clusters/<id>/
Escritura habitual:
POSTa la colección para crear;PATCHal detalle para editar;POST /api/extras/journal-entries/para registrar contexto.
9. Qué no meter en NetBox
| ❌ No meter | ✅ Alternativa |
|---|---|
| Contraseñas, tokens, claves privadas | GSM (iort-secrets) |
| Outputs completos de CLI o logs | Journal con resumen, enlace al log |
| Configuración completa de un servicio | Repo git + enlace en runbook_ref |
| IPs y DNS de Cloudflare o CDN externo | Sí modelar el Site, no hacer IPAM de infra de terceros |
| VMs ficticias para funciones serverless | Site en grupo Cloud |
| Estado en tiempo real (uptime, métricas) | Grafana / observabilidad externa |
| Tickets efímeros o TODOs de sprint | Jira/GitHub Issues; en journal solo hitos relevantes |
10. Referencia de IDs útiles
Verificar con GET /api/.../?limit=50 antes de usarlos, pueden cambiar.
Tenants
| ID | Nombre |
|---|---|
| 1 | IORT Shared Services |
| 2 | CustoChef |
Sites
| ID | Nombre | Uso |
|---|---|---|
| 1 | ES-MAD-SCW-01 | VPS Scaleway Madrid (site principal para VMs) |
| 3 | scw-fr-par-1 | Scaleway París |
| 4 | iort.io (Cloudflare Pages) | Web corporativa iort.io |
Site Groups
| ID | Nombre |
|---|---|
| 1 | Cloud |
| 2 | Scaleway / Dedibox |
Clusters
| ID | Nombre | Tipo |
|---|---|---|
| 1 | es-mad-scw-vps-01 | VPS (Scaleway Madrid) |
| 2 | custochef111-service-hosts | Edge Service Host |
Tags más usadas
| ID | Slug |
|---|---|
| 1 | managed-by-pulumi |
| 2 | access-tailscale |
| 3 | backup-restic |
| 4 | secret-ref-gsm |
| 6 | iort |
| 8 | infra |
| 32 | scaleway |
| 33 | tailscale |
| 34 | managed-by-manual |
| 35 | cloudflare |
| 36 | cloudflare-pages |
| 37 | exposure-public |
| 38 | env-prod |
Documentos relacionados
../docs/netbox/information-model.md../docs/netbox/secrets.md../docs/netbox/mcp-access.md../docs/netbox/validation.md- Manual de uso completo
11. Checklist final para LLMs
Antes de terminar una tarea de documentación en NetBox, comprueba esto:
- he confirmado que el acceso es dentro de Tailscale;
- he identificado si la operación era
createoedit; - he buscado duplicados por más de una clave;
- he elegido el tipo de objeto correcto;
- he preservado información útil ya existente;
- he evitado meter secretos;
- he dejado tags y custom fields coherentes;
- he releído el objeto final;
- he añadido journal si hubo creación o cambio relevante.
Si una de esas respuestas es “no”, la tarea probablemente no está cerrada del todo.