Cómo crear un inventario de recursos Azure por tags para tu dashboard
Daniel J. Saldaña- 10 de octubre de 2026
- Puntuación de feedback

Una suscripción Azure se llena antes de lo que parece: recursos temporales, pruebas, servicios compartidos y despliegues que ya nadie recuerda. Un dashboard no necesita empezar mostrando todo. Puede empezar contestando una pregunta concreta:
¿Qué recursos pertenecen a este proyecto y a este entorno?
En Goliat Dashboard, las tags son una forma ligera de construir ese inventario. No sustituyen una CMDB ni un modelo de autorización, pero convierten una lista inmanejable de recursos en una vista con contexto.
Suscripción Azure ↓Grupos de recursos y recursos ↓Filtro por tags permitidas ↓DTO de inventario para el dashboardLa integración de este post cabe detrás de un endpoint privado y se puede adaptar a una aplicación existente sin rediseñar todo el producto.
Antes del código: define una taxonomía pequeña
Las tags sólo aportan valor si la gente las aplica de forma consistente. Mi punto de partida sería éste:
| Tag | Ejemplo | Para qué sirve |
|---|---|---|
organization |
goliat |
Separar unidades o clientes |
projectId |
portal-interno |
Agrupar una aplicación |
environment |
prod |
Diferenciar producción, pruebas y desarrollo |
owner |
platform@empresa.com |
Saber a quién preguntar |
costCenter |
cc-024 |
Enlazar con una vista FinOps |
No uses las tags como control de acceso. Que alguien pueda escribir environment=prod no le concede ni le quita permisos de Azure. La autorización debe seguir viviendo en Entra ID, Azure RBAC y la capa de tu aplicación.
Tampoco des por hecho que las tags de un grupo de recursos se heredan automáticamente en sus recursos. Azure no hace esa propagación por defecto. Puedes combinar ambas en la salida del dashboard como una convención de presentación, dejando claro cuál fue la fuente.
Permisos y flujo de datos
El backend necesita una identidad con lectura sobre el alcance que vaya a inventariar. En producción, una identidad administrada evita guardar secretos; en local, DefaultAzureCredential puede usar tu inicio de sesión de Azure CLI o de Visual Studio Code.
Browser ──► API privada ──► inventario conocido ──► Azure Resource Manager │ │ │ └── valida que el usuario puede verlo └── nunca expone credenciales de AzureLo importante es no recibir una subscriptionId arbitraria desde el navegador. Resuélvela desde la configuración de la organización o desde una conexión Azure que el usuario ya tenga autorizada.
Listar y filtrar recursos con el SDK
Instala las dependencias:
npm install @azure/identity @azure/arm-resourcesEste servicio conserva la idea de Goliat: recorrer los grupos, recoger sus tags, listar sus recursos y devolver un objeto plano útil para una tabla:
import { DefaultAzureCredential } from '@azure/identity';import { ResourceManagementClient } from '@azure/arm-resources';
type Tags = Record<string, string>;
type InventoryResource = { id: string; name: string; type: string; location?: string; resourceGroup: string; tags: Tags;};
function hasTags(tags: Tags, required: Tags) { return Object.entries(required).every(([key, value]) => tags[key]?.toLowerCase() === value.toLowerCase());}
export async function listTaggedResources(subscriptionId: string, requiredTags: Tags): Promise<InventoryResource[]> { const client = new ResourceManagementClient(new DefaultAzureCredential(), subscriptionId); const result: InventoryResource[] = [];
for await (const group of client.resourceGroups.list()) { if (!group.name) continue;
const groupTags = group.tags ?? {}; for await (const resource of client.resources.listByResourceGroup(group.name)) { if (!resource.id || !resource.name || !resource.type) continue;
// Es una convención del dashboard: el recurso prevalece sobre el grupo. const effectiveTags = { ...groupTags, ...(resource.tags ?? {}) }; if (!hasTags(effectiveTags, requiredTags)) continue;
result.push({ id: resource.id, name: resource.name, type: resource.type, location: resource.location, resourceGroup: group.name, tags: effectiveTags, }); } }
return result;}La fusión groupTags + resource.tags no modifica Azure ni pretende afirmar que el recurso heredó esas tags. Sólo permite que el dashboard aplique una política de organización común, con prioridad para una tag escrita de forma explícita en el recurso.
Un endpoint que no se convierte en un explorador de suscripciones
No publiques la función anterior tal cual. Recibe un identificador de proyecto o de conexión de Azure que conozca tu aplicación, comprueba permisos y construye las tags en el servidor:
export async function GET({ locals, params }: ApiContext) { const user = await requireUser(locals); const project = await projects.findById(params.projectId);
if (!project || !(await canViewProject(user, project))) { return Response.json({ error: 'No encontrado' }, { status: 404 }); }
const requiredTags = { organization: project.organizationSlug, projectId: project.slug, };
const cacheKey = `inventory:${project.id}`; const resources = await cache.getOrSet( cacheKey, () => listTaggedResources(project.azureSubscriptionId, requiredTags), 5 * 60_000 );
return Response.json({ resources });}Limitar las claves de filtrado en el backend también evita que una URL con parámetros desconocidos se transforme en una llamada cara contra toda la suscripción.
Una tabla que da contexto sin saturar
Para una primera interfaz basta con cinco columnas:
| Nombre | Tipo | Grupo de recursos | Región | Entorno |
|---|---|---|---|---|
api-prod |
Microsoft.Web/sites |
rg-portal-prod |
westeurope |
prod |
Muestra las tags adicionales en un panel de detalle o como chips, no todas dentro de la tabla. También merece la pena filtrar por tipo de recurso en memoria: muchos usuarios quieren ver primero aplicaciones, bases de datos, cuentas de almacenamiento y servicios de IA, no cada recurso auxiliar.
Cuándo pasar a Azure Resource Graph
El recorrido con ResourceManagementClient es fácil de entender y está bien para una suscripción pequeña o para sincronizaciones bajo demanda. Si tu inventario se extiende a muchas suscripciones o miles de recursos, Azure Resource Graph permite consultar recursos y tags de forma agregada.
No es necesario cambiar el contrato del frontend. Mantén este DTO:
type InventoryResource = { id: string; name: string; type: string; location?: string; resourceGroup: string; tags: Record<string, string>;};Después sustituye sólo el adaptador que lo rellena. Esa separación es lo que hace que una integración de ejemplo pueda crecer sin obligar a reescribir las tarjetas del dashboard.
Errores habituales
- Filtrar por tags desde el cliente. El navegador no debe ser quien enumera la suscripción ni quien decide sus alcances.
- Usar tags como autorización. Son metadatos editables; valida permisos por separado.
- Esperar herencia automática. Si necesitas tags obligatorias, usa Azure Policy además de la convención de tu aplicación.
- Mostrar datos sin caché. Una tabla que dispara una enumeración completa en cada recarga se vuelve lenta y cara.
- No definir responsables.
ownerycostCenterson tan valiosas comoenvironmentcuando llega una revisión de coste o seguridad.
Consulta la guía de uso de tags en recursos Azure para conocer límites y comportamientos de cada tipo de recurso. Con esa base, este inventario se puede extender con coste, salud, recomendaciones o métricas sin perder la pieza más importante: a qué proyecto y a qué equipo pertenece cada cosa.


