Un semáforo de salud para tus recursos Azure con Resource Health
Daniel J. Saldaña- 3 de octubre de 2026
- Puntuación de feedback

Un dashboard puede enseñar CPU, coste y número de recursos. Pero hay una pregunta mucho más directa que un equipo necesita responder rápido: ¿Azure está teniendo un problema con este recurso?
No hace falta construir una plataforma de observabilidad para contestarla. En este artículo extraigo una integración pequeña de Goliat Dashboard: una tarjeta de estado que consulta Azure Resource Health y muestra un semáforo junto al recurso.
La pieza final es deliberadamente simple:
Usuario abre un recurso en el dashboard ↓API privada de nuestra aplicación ↓Azure Resource Health ↓Estado normalizado: verde, ámbar, rojo o desconocidoEl navegador nunca recibe un token de Azure ni decide qué resourceId puede consultar.
Qué problema resuelve Resource Health
Azure Resource Health informa de la disponibilidad de un recurso y, cuando existe, aporta contexto sobre una degradación, un incidente o una acción requerida. No sustituye a las métricas ni a las alertas:
| Necesidad | Servicio más apropiado |
|---|---|
| «¿Hay una incidencia de plataforma que afecta a este recurso?» | Resource Health |
| «¿La CPU lleva diez minutos por encima del 80 %?» | Azure Monitor |
| «¿Hay una incidencia de servicio que afecta a una región?» | Service Health |
Esa distinción evita un error frecuente: pintar en rojo un recurso sólo porque una métrica supera un umbral propio. El semáforo de este artículo comunica la disponibilidad que Azure conoce; los umbrales de negocio van en otra tarjeta.
Arquitectura mínima y segura
La identidad administrada o el principal de servicio vive en el backend. Concédele únicamente permisos de lectura en la suscripción, grupo de recursos o recurso que vaya a mostrar el dashboard. Si el usuario de la aplicación no puede ver el recurso, el endpoint tampoco debe preguntarle a Azure.
Browser ── sesión y ACL ──► API privada ── identidad administrada ──► ARM / Resource Health │ └── caché breve por resourceIdEn desarrollo local puedes usar DefaultAzureCredential; en Azure usará de forma transparente la identidad administrada si está configurada. La clave es que el identificador de Azure proceda de tu inventario interno, no de un valor libre enviado por el navegador.
Un servicio pequeño para pedir el estado actual
La API de Resource Health se consulta a través de Azure Resource Manager. Este ejemplo usa @azure/identity y fetch, de manera que no queda acoplado a un cliente específico:
import { DefaultAzureCredential } from '@azure/identity';
const credential = new DefaultAzureCredential();const armScope = 'https://management.azure.com/.default';
type HealthLevel = 'healthy' | 'warning' | 'critical' | 'unknown';
export type ResourceHealth = { level: HealthLevel; availabilityState: string; summary?: string; occurredAt?: string;};
function toHealthLevel(state?: string): HealthLevel { switch (state?.toLowerCase()) { case 'available': return 'healthy'; case 'degraded': case 'unknown': return 'warning'; case 'unavailable': return 'critical'; default: return 'unknown'; }}
export async function getResourceHealth(resourceId: string): Promise<ResourceHealth> { // Este valor debe venir de tu inventario/BD, no directamente de query params. if (!resourceId.startsWith('/subscriptions/')) { throw new Error('Azure resource id no válido'); }
const token = await credential.getToken(armScope); if (!token) throw new Error('No se pudo obtener un token para Azure');
const url = new URL( `https://management.azure.com${resourceId}/providers/Microsoft.ResourceHealth/availabilityStatuses/current` ); url.searchParams.set('api-version', '2025-05-01');
const response = await fetch(url, { headers: { Authorization: `Bearer ${token.token}` }, signal: AbortSignal.timeout(8_000), });
if (!response.ok) { throw new Error(`Resource Health devolvió ${response.status}`); }
const body = await response.json(); const properties = body.properties ?? {};
return { level: toHealthLevel(properties.availabilityState), availabilityState: properties.availabilityState ?? 'Unknown', summary: properties.summary, occurredAt: properties.occurredTime, };}El contrato no expone toda la respuesta de ARM. Normalizarla a un DTO pequeño hace que el componente de interfaz no dependa de campos internos de Azure y permite cambiar la implementación sin romperlo.
Protege el endpoint antes de consultar Azure
En Goliat el handler de Resource Health se encuentra detrás de la misma autorización que el resto de la información privada. En una aplicación Astro, Express o Next.js la forma exacta cambia, pero estas reglas no:
export async function GET({ locals, params }: ApiContext) { const user = await requireUser(locals); const resource = await resourcesRepository.findByDashboardId(params.id);
if (!resource || !(await canViewResource(user, resource))) { return Response.json({ error: 'No encontrado' }, { status: 404 }); }
const cached = await healthCache.get(resource.azureResourceId); if (cached) return Response.json(cached);
const health = await getResourceHealth(resource.azureResourceId); await healthCache.set(resource.azureResourceId, health, 60_000);
return Response.json(health);}Usar 404 para un recurso no visible evita convertir el endpoint en un oráculo de inventario. La caché de un minuto reduce latencia y llamadas, sin presentar un estado histórico como si fuese tiempo real.
La tarjeta: clara antes que espectacular
El componente sólo necesita un color, un texto y una alternativa accesible al color:
const labels = { healthy: 'Disponible', warning: 'Revisar estado', critical: 'No disponible', unknown: 'Estado no disponible',} as const;
export function HealthBadge({ health }: { health: ResourceHealth }) { return ( <span className={`health health--${health.level}`} role="status"> <span aria-hidden="true" className="health__dot" /> {labels[health.level]} </span> );}Incluye siempre el estado textual. Un círculo rojo por sí solo no es suficiente para lectores de pantalla, usuarios con daltonismo ni capturas que viajan por un chat.
Una buena experiencia es mostrar la última actualización y un enlace a Azure Portal sólo si tu usuario ya dispone de acceso allí. Evita inventar explicaciones: si summary no viene informado, muestra el estado y ofrece refrescar.
Casos que conviene tratar explícitamente
| Situación | Tratamiento recomendado |
|---|---|
Azure devuelve Available |
Verde, sin dramatizarlo como una alerta |
Azure devuelve Degraded o Unknown |
Ámbar y un texto que invita a revisar |
Azure devuelve Unavailable |
Rojo y el resumen de Azure si existe |
| Falta permiso o el proveedor no responde | Estado técnico independiente: «No se pudo consultar» |
| El tipo de recurso no proporciona datos | «Sin datos de salud»; no lo confundas con verde |
No conviertas un fallo de tu endpoint en una incidencia de Azure. Es una diferencia pequeña en la interfaz, pero enorme para quien recibe una alerta.
Lista de salida a producción
- La identidad de la aplicación tiene el mínimo permiso de lectura necesario.
- El
resourceIdse resuelve desde un recurso autorizado del dashboard. - La respuesta está normalizada y no expone el token ni detalles innecesarios de ARM.
- Hay timeout, mensajes de error diferenciados y caché breve.
- El color viene acompañado de texto y de la hora de actualización.
- Se prueban recursos disponibles, degradados, sin datos y sin permisos.
La documentación de Availability Statuses de Resource Health explica el contrato que consulta el servicio. A partir de esta tarjeta puedes añadir un panel de Service Health por región, pero mantener ambas señales separadas hace que el dashboard sea mucho más honesto.
Con una llamada protegida y un DTO de cinco campos, el dashboard gana una señal que resulta útil durante una incidencia sin convertirse en otro sistema de monitorización que debas mantener.


