Skip to content

Blog

Azure Firewall Premium: inspección TLS, Threat Intelligence y reglas avanzadas

Resumen

Azure Firewall Premium añade inspección TLS, Threat Intelligence y reglas personalizadas para proteger redes cloud y híbridas. Este post va directo: cómo activar TLS inspection, usar Threat Intelligence, crear reglas avanzadas y monitorizar tráfico. Para admins y arquitectos que quieren seguridad real en producción.

¿Qué es Azure Firewall Premium?

  • Firewall gestionado, escalable y centralizado
  • Inspección TLS (decrypted traffic)
  • Threat Intelligence (bloqueo IPs maliciosas)
  • FQDN filtering, IDPS, reglas de aplicación y red
  • Integración con Log Analytics y Sentinel

Arquitectura / Cómo funciona

flowchart LR
    Internet --> FW[Azure Firewall Premium]
    FW --> VNet1[Prod VNet]
    FW --> VNet2[Dev VNet]
    FW --> OnPrem[On-Premises]
    FW -.-> LogAnalytics[Log Analytics]
    FW -.-> Sentinel[Azure Sentinel]

Activar Firewall Premium y TLS Inspection

  1. Crear Firewall Premium:
    RESOURCE_GROUP="rg-fw-prod"
    LOCATION="westeurope"
    FW_NAME="fw-premium-prod"
    VNET_NAME="vnet-fw-prod"
    SUBNET_NAME="AzureFirewallSubnet"
    
    az network firewall create \
      --name $FW_NAME \
      --resource-group $RESOURCE_GROUP \
      --location $LOCATION \
      --sku Premium
    
  2. Crear certificado para TLS inspection:
    # Generar certificado raíz (usando OpenSSL)
    openssl req -x509 -newkey rsa:4096 -keyout tls.key -out tls.crt -days 365 -nodes -subj "/CN=AzureFirewallTLS"
    # Subir a Key Vault
    az keyvault certificate import \
      --vault-name kv-fw-prod \
      --name fw-tls-root \
      --file tls.crt
    
  3. Configurar TLS inspection:
    az network firewall policy create \
      --name fw-policy-premium \
      --resource-group $RESOURCE_GROUP \
      --location $LOCATION \
      --sku Premium
    az network firewall policy tls-inspection create \
      --policy-name fw-policy-premium \
      --resource-group $RESOURCE_GROUP \
      --key-vault-secret-id <KV_CERT_SECRET_ID>
    

Threat Intelligence

  • Dos modos: "Alert" (solo avisa) y "Deny" (bloquea IPs maliciosas)
    az network firewall policy threat-intel-set \
      --policy-name fw-policy-premium \
      --resource-group $RESOURCE_GROUP \
      --action Deny
    

Reglas avanzadas

  • Application rules: FQDN, URL, TLS
  • Network rules: IP, port, protocol
  • IDPS: detección y bloqueo de ataques
    # Application rule: solo permite https://github.com
    az network firewall policy rule-collection-group application-rule-collection create \
      --policy-name fw-policy-premium \
      --resource-group $RESOURCE_GROUP \
      --name app-allow-github \
      --priority 100 \
      --action Allow \
      --rules '[{"name":"AllowGithub","sourceAddresses":["*"],"protocols":[{"protocolType":"Https","port":443}],"targetFqdns":["github.com"]}]'
    
    # Network rule: solo permite TCP 443 a IP específica
    az network firewall policy rule-collection-group network-rule-collection create \
      --policy-name fw-policy-premium \
      --resource-group $RESOURCE_GROUP \
      --name net-allow-prod \
      --priority 200 \
      --action Allow \
      --rules '[{"name":"AllowProd443","sourceAddresses":["10.0.0.0/24"],"destinationAddresses":["20.30.40.50"],"destinationPorts":["443"],"protocols":["TCP"]}]'
    

Monitorización y logging

  • Integrar con Log Analytics:
    az monitor diagnostic-settings create \
      --resource /subscriptions/$SUB_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Network/azureFirewalls/$FW_NAME \
      --workspace $LOG_ANALYTICS_ID \
      --name fw-logs \
      --logs '[{"category":"AzureFirewallApplicationRule","enabled":true},{"category":"AzureFirewallNetworkRule","enabled":true},{"category":"AzureFirewallDnsProxy","enabled":true}]'
    
  • Queries KQL útiles:
    // Top 10 destinos bloqueados
    AzureDiagnostics
    | where Category == "AzureFirewallNetworkRule"
    | where action_s == "Deny"
    | summarize Count = count() by destination_ip_s
    | top 10 by Count desc
    
    // Detección de ataques IDPS
    AzureDiagnostics
    | where Category == "AzureFirewallIdpsSignature"
    | summarize Count = count() by signature_name_s
    | top 10 by Count desc
    

Buenas prácticas

  • Usar TLS inspection solo para tráfico interno/confidencial
  • Mantener certificados actualizados en Key Vault
  • Threat Intelligence en modo "Deny" para entornos críticos
  • Revisar logs y alertas semanalmente
  • Segmentar reglas por entorno (dev, prod)
  • Integrar con Sentinel para correlación de incidentes

Costes

  • Firewall Premium: ~$1,500/mes por unidad
  • Log Analytics: ~$2.30/GB
  • Sentinel: ~$2.46/GB

Referencias

Azure Bicep avanzado: testing, linting y despliegue seguro multi-entorno

Resumen

Bicep permite IaC declarativo en Azure, pero en producción necesitas testing, linting y despliegue seguro por entorno. Este post va al grano: cómo testear plantillas, usar linting, parametrizar para dev/prod y evitar errores comunes.

¿Qué es Bicep avanzado?

  • Modularización y reutilización
  • Testing de plantillas antes de deploy
  • Linting para calidad y seguridad
  • Parametrización por entorno (dev, prod, test)
  • Integración con pipelines CI/CD

Arquitectura / Cómo funciona

flowchart LR
    Dev[Dev] --> CI[CI Pipeline]
    CI --> Lint[Lint]
    CI --> Test[Test]
    CI --> Deploy[Deploy]
    Deploy --> Azure[Azure]
    Azure --> RG1[Resource Group Dev]
    Azure --> RG2[Resource Group Prod]

Testing de plantillas Bicep

  1. Validar sintaxis:
    bicep build main.bicep
    
  2. Test de despliegue (dry-run):
    az deployment sub validate \
      --location westeurope \
      --template-file main.bicep \
      --parameters environment="dev"
    
  3. Test unitario con PSRule for Azure:
    pwsh -c "Invoke-PSRule -Path ./main.bicep"
    

Linting y calidad

  • Usar bicep linter:
    bicep build main.bicep
    bicep linter main.bicep
    
  • Reglas custom en .bicepconfig.json:
    {
      "analyzers": {
        "core": {
          "rules": {
            "no-hardcoded-secrets": "warning",
            "secure-parameters": "error"
          }
        }
      }
    }
    

Parametrización multi-entorno

  • Usar parámetros y archivos por entorno:
    az deployment group create \
      --resource-group rg-dev \
      --template-file main.bicep \
      --parameters @dev.parameters.json
    az deployment group create \
      --resource-group rg-prod \
      --template-file main.bicep \
      --parameters @prod.parameters.json
    
  • Ejemplo de parámetros:
    {
      "environment": {"value": "dev"},
      "adminPassword": {"value": "SuperSecret123!"}
    }
    

Integración CI/CD

  • Pipeline YAML ejemplo:
    trigger:
      - main
    pool:
      vmImage: 'ubuntu-latest'
    steps:
      - script: bicep build main.bicep
        displayName: 'Build Bicep'
      - script: bicep linter main.bicep
        displayName: 'Lint Bicep'
      - script: pwsh -c "Invoke-PSRule -Path ./main.bicep"
        displayName: 'Test Bicep'
      - script: az deployment group create --resource-group rg-dev --template-file main.bicep --parameters @dev.parameters.json
        displayName: 'Deploy Dev'
    

Buenas prácticas

  • Nunca hardcodear secretos en plantillas
  • Usar Key Vault para parámetros sensibles
  • Validar y testear antes de cada despliegue
  • Mantener módulos reutilizables y versionados
  • Revisar resultados de linting y testing en cada PR

Costes

  • Bicep: gratis
  • PSRule: gratis
  • Azure DevOps: desde $0 (hasta 1,800 min/mes)

Referencias

Azure Policy: Governance automatizado y remediación de recursos

Resumen

Azure Policy es el pilar de governance en Azure que permite definir estándares organizacionales (tags obligatorios, regiones permitidas, SKUs aprobados) y evaluar compliance automáticamente en todos los recursos. Cuando detecta recursos no conformes, puede auditar, denegar nuevos despliegues, o remediar recursos existentes con deployIfNotExists/modify effects. En este post veremos cómo crear policies, asignarlas con initiative sets, configurar remediation tasks para traer recursos a conformidad, y usar Azure Policy para implementar governance escalable en entornos multi-subscripción.

Azure Bastion Developer SKU: Acceso seguro gratuito a VMs

Resumen

Azure Bastion Developer es la opción gratuita del servicio Bastion que permite conectar de forma segura a una VM por vez directamente desde Azure Portal, sin necesidad de IPs públicas, agentes instalados ni configuración de VPN. Es ideal para escenarios Dev/Test donde no necesitas características avanzadas como scale units, file upload/download o shareable links. En este post veremos cómo funciona, cuándo usarlo, cómo desplegarlo, y las diferencias críticas con los SKUs Basic/Standard/Premium.

Azure Application Gateway: Reglas personalizadas de WAF v2

Resumen

El Web Application Firewall (WAF) v2 de Azure Application Gateway incluye el Core Rule Set (CRS) de OWASP para protección automática contra amenazas comunes. Sin embargo, cuando necesitas lógica de seguridad específica para tu aplicación (bloquear IPs concretas, filtrar User-Agents maliciosos, implementar rate limiting por geografía), las reglas personalizadas (custom rules) son la solución. En este post veremos cómo crear y gestionar custom rules que se evalúan antes que las managed rules, con ejemplos prácticos de bloqueo de bots, geofiltrado y rate limiting.

Azure Key Vault: RBAC vs Access Policies

Resumen

Azure Key Vault soporta dos modelos de autorización: Azure RBAC (recomendado) y Access Policies (legacy). RBAC proporciona control granular, separación de responsabilidades y soporte para Privileged Identity Management (PIM), mientras que Access Policies permite que roles con permisos de escritura (Contributor) se otorguen a sí mismos acceso al plano de datos, representando un riesgo de seguridad. Este post explica las diferencias, cómo migrar de Access Policies a RBAC, y las buenas prácticas de seguridad.

¿Por qué migrar a RBAC?

Problema con Access Policies

Riesgo de elevación de privilegios:

  • Usuarios con rol Contributor o Key Vault Contributor pueden modificar Access Policies
  • Pueden otorgarse acceso completo a secrets/keys/certificates sin aprobación
  • No hay separación entre gestión del vault y acceso a datos sensibles
  • No soporta PIM para acceso just-in-time

Ejemplo del problema:

# Usuario con rol Contributor puede hacer esto:
az keyvault set-policy \
    --name my-vault \
    --upn malicious-user@company.com \
    --secret-permissions get list \
    --key-permissions get list \
    --certificate-permissions get list

# Ahora tiene acceso a todos los secrets sin auditoría

Ventajas de Azure RBAC

Seguridad mejorada:

  • Solo roles Owner y User Access Administrator pueden asignar permisos de datos
  • Separación clara entre management plane y data plane
  • Soporte completo para PIM (acceso temporal con aprobación)
  • Auditoría centralizada en Activity Log

Control granular:

  • Scope a nivel de vault, secret individual, key o certificate
  • Roles predefinidos específicos (Key Vault Secrets User, Key Vault Crypto User)
  • Integración con Conditional Access policies
  • Managed identities nativo

Arquitectura de permisos

flowchart TB
    subgraph "Access Policies (Legacy)"
        A[Contributor Role] -->|puede modificar| B[Access Policy]
        B -->|otorga acceso| C[Secrets/Keys/Certs]
    end

    subgraph "Azure RBAC (Recomendado)"
        D[Owner/UAA Role] -->|único que asigna| E[Data Plane Role]
        E -->|acceso controlado| F[Secrets/Keys/Certs]
        G[Contributor] -.->|NO puede modificar| E
    end

    style A fill:#e74c3c
    style D fill:#2ecc71
    style G fill:#95a5a6

Roles RBAC predefinidos

Management Plane (Control Plane)

Rol Descripción Uso
Owner Control completo del vault + asignación de roles Administradores principales
Contributor Gestión del vault SIN asignación de roles data plane DevOps, operadores
Reader Solo lectura de propiedades del vault Auditoría, monitorización

Data Plane (Acceso a datos)

Rol Permisos Uso típico
Key Vault Administrator Acceso completo a secrets/keys/certs Break-glass, emergencias
Key Vault Secrets User Get secrets Aplicaciones, managed identities
Key Vault Secrets Officer CRUD secrets Administradores de secrets
Key Vault Crypto User Operaciones criptográficas (sign, verify, encrypt) Apps con cifrado
Key Vault Crypto Officer Crear/eliminar keys Gestión de claves
Key Vault Certificates Officer CRUD certificados Gestión de TLS/SSL
Key Vault Reader Listar secrets/keys/certs (no leer valores) Inventario, discovery

Migración de Access Policies a RBAC

Paso 1: Auditar permisos actuales

# Listar access policies existentes
az keyvault show \
    --name my-vault \
    --resource-group my-rg \
    --query "properties.accessPolicies" \
    -o table

Ejemplo output:

ObjectId                              Permissions
------------------------------------  -----------------
12345678-1234-1234-1234-123456789012  Secrets: get, list
87654321-4321-4321-4321-210987654321  Keys: all, Secrets: all

Paso 2: Mapear Access Policies a roles RBAC

Mapeo típico:

Access Policy Rol RBAC equivalente
Secrets: get, list Key Vault Secrets User
Secrets: all Key Vault Secrets Officer
Keys: encrypt, decrypt, sign, verify Key Vault Crypto User
Keys: all Key Vault Crypto Officer
Certificates: get, list Key Vault Certificates User (custom)
All permissions Key Vault Administrator

Paso 3: Habilitar RBAC en el vault

# Cambiar permission model a RBAC
az keyvault update \
    --name my-vault \
    --resource-group my-rg \
    --enable-rbac-authorization true

⚠️ Importante: Tras habilitar RBAC, las Access Policies dejan de funcionar inmediatamente.

Paso 4: Asignar roles RBAC

# Obtener object ID del principal (usuario, grupo, managed identity)
PRINCIPAL_ID=$(az ad user show --id user@company.com --query id -o tsv)

# Asignar rol a nivel de vault
az role assignment create \
    --role "Key Vault Secrets User" \
    --assignee $PRINCIPAL_ID \
    --scope $(az keyvault show --name my-vault --resource-group my-rg --query id -o tsv)

# Asignar rol a un secret específico
SECRET_ID=$(az keyvault secret show \
    --vault-name my-vault \
    --name db-password \
    --query id -o tsv)

az role assignment create \
    --role "Key Vault Secrets User" \
    --assignee $PRINCIPAL_ID \
    --scope $SECRET_ID

Paso 5: Configurar PIM (opcional pero recomendado)

# Crear eligible assignment (requiere activación)
az role assignment create \
    --role "Key Vault Administrator" \
    --assignee $ADMIN_PRINCIPAL_ID \
    --scope $(az keyvault show --name my-vault -g my-rg --query id -o tsv) \
    --assignee-object-type User \
    --assignee-principal-type User \
    # Nota: Configuración de PIM requiere Azure Portal o API de PIM

Configurar en Portal:

  1. Key Vault → Access control (IAM) → Privileged roles
  2. Add assignment → Select role → Key Vault Administrator
  3. Assignment type: Eligible
  4. Settings:
  5. Max activation duration: 8 hours
  6. Require justification: Yes
  7. Require approval: Yes (añadir approvers)
  8. Require MFA: Yes

Configuración con Managed Identity

System-assigned MI

# Crear VM con system-assigned identity
az vm create \
    --name app-vm \
    --resource-group my-rg \
    --image Ubuntu2204 \
    --assign-identity \
    --role "Key Vault Secrets User" \
    --scope $(az keyvault show --name my-vault -g my-rg --query id -o tsv)

# Obtener identity para verificar
az vm identity show --name app-vm --resource-group my-rg

User-assigned MI

# Crear user-assigned identity
az identity create \
    --name app-identity \
    --resource-group my-rg

IDENTITY_ID=$(az identity show -n app-identity -g my-rg --query id -o tsv)
PRINCIPAL_ID=$(az identity show -n app-identity -g my-rg --query principalId -o tsv)

# Asignar role
az role assignment create \
    --role "Key Vault Secrets User" \
    --assignee $PRINCIPAL_ID \
    --scope $(az keyvault show --name my-vault -g my-rg --query id -o tsv)

# Asignar identity a App Service
az webapp identity assign \
    --name my-app \
    --resource-group my-rg \
    --identities $IDENTITY_ID

Código de aplicación con RBAC

.NET (C#)

using Azure.Identity;
using Azure.Security.KeyVault.Secrets;

// Usar DefaultAzureCredential (soporta Managed Identity automáticamente)
var client = new SecretClient(
    new Uri("https://my-vault.vault.azure.net/"),
    new DefaultAzureCredential()
);

// Obtener secret
KeyVaultSecret secret = await client.GetSecretAsync("db-password");
string connectionString = secret.Value;

Python

from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient

credential = DefaultAzureCredential()
client = SecretClient(
    vault_url="https://my-vault.vault.azure.net/",
    credential=credential
)

# Obtener secret
secret = client.get_secret("db-password")
connection_string = secret.value

Azure CLI en scripts

# Autenticación con managed identity en Azure VM/App Service
export AZURE_CLIENT_ID=<user-assigned-mi-client-id>  # Solo si es user-assigned

# Obtener secret
az keyvault secret show \
    --vault-name my-vault \
    --name db-password \
    --query value \
    -o tsv

Casos de uso avanzados

1. Acceso granular por entorno

# Production: solo lectura para apps
az role assignment create \
    --role "Key Vault Secrets User" \
    --assignee $PROD_APP_IDENTITY \
    --scope $(az keyvault show --name prod-vault -g prod-rg --query id -o tsv)

# Development: lectura y escritura para devs
az role assignment create \
    --role "Key Vault Secrets Officer" \
    --assignee $DEV_GROUP_ID \
    --scope $(az keyvault show --name dev-vault -g dev-rg --query id -o tsv)

2. Rotation de secrets con Function App

# Function App con system MI y permiso de escritura
FUNCTION_IDENTITY=$(az functionapp identity show \
    --name secret-rotator \
    --resource-group my-rg \
    --query principalId -o tsv)

az role assignment create \
    --role "Key Vault Secrets Officer" \
    --assignee $FUNCTION_IDENTITY \
    --scope $(az keyvault secret show \
        --vault-name my-vault \
        --name api-key \
        --query id -o tsv)

3. Break-glass access con PIM

# Rol permanente: Key Vault Reader (solo lista)
az role assignment create \
    --role "Key Vault Reader" \
    --assignee $ADMIN_ID \
    --scope $(az keyvault show --name my-vault -g my-rg --query id -o tsv)

# Rol eligible (activable): Key Vault Administrator
# Configurar via Portal → IAM → Privileged access
# Activación requiere:
# - Justificación de negocio
# - Aprobación de 2 owners
# - MFA
# - Duración máxima: 4 horas

Seguridad adicional

Firewall y Private Endpoint

# Deshabilitar acceso público
az keyvault update \
    --name my-vault \
    --resource-group my-rg \
    --public-network-access Disabled

# Crear private endpoint
az network private-endpoint create \
    --name my-vault-pe \
    --resource-group my-rg \
    --vnet-name my-vnet \
    --subnet private-endpoints \
    --private-connection-resource-id $(az keyvault show --name my-vault -g my-rg --query id -o tsv) \
    --group-id vault \
    --connection-name my-vault-connection

Conditional Access

Configurar en Azure AD:

  1. Azure AD → Security → Conditional Access
  2. New policy:
  3. Users: Grupo de admins Key Vault
  4. Cloud apps: Azure Key Vault (API: cfa8b339-82a2-471a-a3c9-0fc0be7a4093)
  5. Conditions: Location = Fuera de oficina
  6. Grant: Require MFA + Compliant device

Auditoría y alertas

# Habilitar diagnostic settings
az monitor diagnostic-settings create \
    --name key-vault-audit \
    --resource $(az keyvault show --name my-vault -g my-rg --query id -o tsv) \
    --logs '[{"category": "AuditEvent", "enabled": true}]' \
    --workspace $(az monitor log-analytics workspace show -n my-la-ws -g my-rg --query id -o tsv)

# Crear alerta de acceso no autorizado
az monitor metrics alert create \
    --name unauthorized-access \
    --resource-group my-rg \
    --scopes $(az keyvault show --name my-vault -g my-rg --query id -o tsv) \
    --condition "count ActivityType_s == 'SecretGet' and ResultType == 'Unauthorized' > 5" \
    --window-size 5m \
    --evaluation-frequency 1m \
    --action action-group-id

Troubleshooting

Error: "Forbidden" tras habilitar RBAC

# Verificar roles asignados
az role assignment list \
    --scope $(az keyvault show --name my-vault -g my-rg --query id -o tsv) \
    --output table

# Si no tienes acceso, pedir a Owner/UAA que asigne rol

Error: "Network restricted"

# Añadir tu IP al firewall temporalmente
MY_IP=$(curl -s ifconfig.me)
az keyvault network-rule add \
    --name my-vault \
    --resource-group my-rg \
    --ip-address $MY_IP

Buenas prácticas

  1. Usar RBAC siempre: Migrar todos los vaults a RBAC authorization
  2. Principle of least privilege: Asignar solo permisos mínimos necesarios
  3. Scope granular: Preferir scope a secrets individuales sobre vault completo
  4. PIM para admins: Acceso eligible con aprobación y MFA para roles privilegiados
  5. Managed Identities: Nunca usar service principals con secrets en código
  6. Private Endpoints: Deshabilitar acceso público en production
  7. Auditoría: Habilitar diagnostic logs y revisar periódicamente
  8. Rotación: Implementar rotación automática de secrets con Azure Functions/Logic Apps
  9. Conditional Access: Requerir MFA y compliant device para admins
  10. Separación de vaults: Vaults separados por entorno (prod/dev/test)

Referencias

Azure Monitor Managed Prometheus y Grafana

Resumen

Azure Monitor Managed Prometheus es un servicio totalmente gestionado que simplifica la recopilación, almacenamiento y consulta de métricas Prometheus sin mantener tu propia infraestructura. Integrado nativamente con Azure Managed Grafana, proporciona observabilidad completa para clústeres Kubernetes (AKS, Arc), máquinas virtuales y aplicaciones cloud-native, con retención de 18 meses, escalado automático y SLA empresarial.

¿Por qué Managed Prometheus?

Problemas del Prometheus self-hosted:

  • Gestión manual de servidor Prometheus
  • Escalado complejo para alta carga
  • Backup y retention limitados
  • Alta disponibilidad requiere configuración custom
  • Costes de almacenamiento no optimizados

Ventajas del servicio gestionado:

  • Fully managed: Actualizaciones automáticas, sin gestión de infraestructura
  • Retención 18 meses: Sin coste adicional de almacenamiento
  • Escalado automático: Soporta millones de series temporales
  • SLA 99.9%: Alta disponibilidad garantizada
  • Integración nativa: AKS, Arc, Grafana, Container Insights
  • PromQL completo: 100% compatible con queries Prometheus

Arquitectura

flowchart LR
    A[AKS Cluster] -->|metrics scraping| B[Azure Monitor Agent]
    C[Arc-enabled K8s] -->|metrics scraping| B
    D[VMs] -->|metrics scraping| B
    B -->|ingestion| E[Azure Monitor Workspace]
    E -->|data source| F[Azure Managed Grafana]
    E -->|PromQL queries| G[Metrics Explorer]
    E -->|alerts| H[Azure Monitor Alerts]

    style E fill:#2ecc71
    style F fill:#3498db

Componentes clave:

  • Azure Monitor Workspace: Almacén de métricas Prometheus
  • Azure Monitor Agent: Scraper de métricas (pods ama-metrics)
  • Azure Managed Grafana: Visualización con dashboards preconstruidos
  • Data Collection Rules: Configuración de qué métricas recopilar

Configuración paso a paso

Paso 1: Crear Azure Monitor Workspace

# Variables
RESOURCE_GROUP="monitoring-rg"
LOCATION="westeurope"
PROMETHEUS_WORKSPACE="aks-prometheus-ws"

# Crear workspace
az monitor account create \
    --resource-group $RESOURCE_GROUP \
    --name $PROMETHEUS_WORKSPACE \
    --location $LOCATION

Paso 2: Crear Azure Managed Grafana

GRAFANA_NAME="aks-grafana"

# Crear Grafana workspace
az grafana create \
    --resource-group $RESOURCE_GROUP \
    --name $GRAFANA_NAME \
    --location $LOCATION \
    --zone-redundancy Enabled

Paso 3: Habilitar Prometheus en AKS

AKS_CLUSTER_NAME="my-aks-cluster"

# Opción A: Habilitar con workspace y Grafana existentes
az aks update \
    --name $AKS_CLUSTER_NAME \
    --resource-group $RESOURCE_GROUP \
    --enable-azure-monitor-metrics \
    --azure-monitor-workspace-resource-id $(az monitor account show \
        --resource-group $RESOURCE_GROUP \
        --name $PROMETHEUS_WORKSPACE \
        --query id -o tsv) \
    --grafana-resource-id $(az grafana show \
        --resource-group $RESOURCE_GROUP \
        --name $GRAFANA_NAME \
        --query id -o tsv)

# Opción B: Crear workspace automáticamente
az aks update \
    --name $AKS_CLUSTER_NAME \
    --resource-group $RESOURCE_GROUP \
    --enable-azure-monitor-metrics

Paso 4 (opcional): Configurar métricas custom

# Filtrar labels específicos de kube-state-metrics
az aks update \
    --name $AKS_CLUSTER_NAME \
    --resource-group $RESOURCE_GROUP \
    --enable-azure-monitor-metrics \
    --ksm-metric-labels-allow-list "namespaces=[app,environment,team]" \
    --ksm-metric-annotations-allow-list "pods=[prometheus.io/scrape,prometheus.io/port]"

Configuración para Arc-enabled Kubernetes

# Habilitar en cluster Arc
az k8s-extension create \
    --name azuremonitor-metrics \
    --cluster-name $CLUSTER_NAME \
    --resource-group $RESOURCE_GROUP \
    --cluster-type connectedClusters \
    --extension-type Microsoft.AzureMonitor.Containers.Metrics \
    --configuration-settings \
        azure-monitor-workspace-resource-id=$(az monitor account show \
            --resource-group $RESOURCE_GROUP \
            --name $PROMETHEUS_WORKSPACE \
            --query id -o tsv) \
        grafana-resource-id=$(az grafana show \
            --resource-group $RESOURCE_GROUP \
            --name $GRAFANA_NAME \
            --query id -o tsv)

Scraping de métricas custom

Service Monitor (scrape custom apps)

apiVersion: azmonitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: my-app-metrics
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: my-app
  endpoints:
  - port: metrics
    interval: 30s
    path: /metrics
    scheme: http

Aplicar configuración:

kubectl apply -f service-monitor.yaml

Pod Monitor

apiVersion: azmonitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: pod-metrics
  namespace: monitoring
spec:
  selector:
    matchLabels:
      role: backend
  podMetricsEndpoints:
  - port: metrics
    path: /actuator/prometheus

Consultas PromQL típicas

Dashboard de nodos AKS

# CPU usage por nodo
sum(rate(node_cpu_seconds_total{mode!="idle"}[5m])) by (instance) * 100

# Memoria disponible
node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100

# Disk I/O
rate(node_disk_read_bytes_total[5m]) + rate(node_disk_written_bytes_total[5m])

Métricas de pods

# CPU usage por pod
sum(rate(container_cpu_usage_seconds_total{namespace="production"}[5m])) by (pod)

# Memory working set
container_memory_working_set_bytes{namespace="production"} / 1024 / 1024

# Restart count
kube_pod_container_status_restarts_total{namespace="production"}

Métricas de aplicación (custom)

# Request rate
sum(rate(http_requests_total{job="my-app"}[5m])) by (status_code)

# 95th percentile latency
histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))

# Error rate
sum(rate(http_requests_total{status_code=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))

Integración con Grafana

Configurar data source automáticamente

Al crear Grafana con --grafana-resource-id durante el update de AKS, el data source se configura automáticamente.

Verificar:

# Listar data sources
az grafana data-source list \
    --name $GRAFANA_NAME \
    --resource-group $RESOURCE_GROUP

Importar dashboards preconstruidos

Azure proporciona dashboards oficiales:

  1. Kubernetes / Compute Resources / Cluster
  2. Kubernetes / Compute Resources / Namespace (Pods)
  3. Kubernetes / Compute Resources / Node (Pods)
  4. Kubernetes / Networking / Cluster

Importar desde UI:

  1. Abrir Grafana workspace en Azure Portal
  2. Click en "Endpoint" → Abre Grafana UI
  3. Dashboards → Import → ID del dashboard community
  4. Seleccionar data source: Azure Monitor Workspace

Dashboard IDs populares:

  • 15760: Kubernetes Cluster Monitoring
  • 15761: Kubernetes API Server
  • 15762: Node Exporter Full

Remote write desde Prometheus self-hosted

Configuración con Managed Identity

# prometheus.yml
global:
  external_labels:
    cluster: on-prem-k8s

remote_write:
  - url: https://<INGESTION_ENDPOINT>/dataCollectionRules/<DCR_ID>/streams/Microsoft-PrometheusMetrics/api/v1/write?api-version=2023-04-24
    azuread:
      cloud: AzurePublic
      managed_identity:
        client_id: <MANAGED_IDENTITY_CLIENT_ID>

Configuración con Service Principal

remote_write:
  - url: https://<INGESTION_ENDPOINT>/dataCollectionRules/<DCR_ID>/streams/Microsoft-PrometheusMetrics/api/v1/write?api-version=2023-04-24
    azuread:
      cloud: AzurePublic
      client_id: <APP_CLIENT_ID>
      tenant_id: <TENANT_ID>
      client_secret: <CLIENT_SECRET>

Alertas basadas en métricas

Crear alerta de uso de CPU

# Crear action group (email/SMS)
az monitor action-group create \
    --name critical-alerts \
    --resource-group $RESOURCE_GROUP \
    --short-name critical \
    --email-receiver email=ops@company.com

# Crear alerta PromQL
az monitor metrics alert create \
    --name high-cpu-alert \
    --resource-group $RESOURCE_GROUP \
    --scopes $(az monitor account show -n $PROMETHEUS_WORKSPACE -g $RESOURCE_GROUP --query id -o tsv) \
    --condition "avg(rate(node_cpu_seconds_total{mode!='idle'}[5m])) > 0.8" \
    --window-size 5m \
    --evaluation-frequency 1m \
    --action critical-alerts \
    --severity 2 \
    --description "CPU usage > 80%"

Cost optimization

Pricing: - Ingestion: ~$0.40 per million samples - Query: $0.16 per million samples returned - Storage: incluido sin coste adicional (18 meses)

Optimización:

# Reducir frecuencia de scraping
az aks update \
    --name $AKS_CLUSTER_NAME \
    --resource-group $RESOURCE_GROUP \
    --enable-azure-monitor-metrics \
    --scrape-interval 60s  # Default: 30s

# Filtrar namespaces irrelevantes
# Crear ConfigMap para excluir namespaces
kubectl apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: ama-metrics-prometheus-config
  namespace: kube-system
data:
  prometheus-config: |
    scrape_configs:
    - job_name: 'kubernetes-pods'
      kubernetes_sd_configs:
      - role: pod
      relabel_configs:
      - source_labels: [__meta_kubernetes_namespace]
        regex: kube-system|azure-system
        action: drop
EOF

Estimación de costes:

  • Cluster AKS 3 nodos: ~150K samples/min → ~$260/mes
  • Cluster AKS 10 nodos: ~500K samples/min → ~$860/mes
  • Queries Grafana dashboards: ~$50/mes adicionales

Monitorización avanzada

Multi-workspace para multi-cluster

{
  "properties": {
    "grafanaIntegrations": {
      "azureMonitorWorkspaceIntegrations": [
        {
          "azureMonitorWorkspaceResourceId": "/subscriptions/<SUB>/resourceGroups/<RG1>/providers/Microsoft.Monitor/accounts/ws-prod"
        },
        {
          "azureMonitorWorkspaceResourceId": "/subscriptions/<SUB>/resourceGroups/<RG2>/providers/Microsoft.Monitor/accounts/ws-dev"
        }
      ]
    }
  }
}

Recording rules (pre-agregación)

Configurar via Grafana UI → Alerting → Recording rules:

# Rule name: cluster:cpu_usage:rate5m
sum(rate(node_cpu_seconds_total{mode!="idle"}[5m])) by (cluster)

# Rule name: namespace:pod_cpu:sum
sum(rate(container_cpu_usage_seconds_total[5m])) by (namespace, pod)

Troubleshooting

Problema: Métricas no aparecen

# Verificar pods ama-metrics
kubectl get pods -n kube-system -l rsName=ama-metrics

# Revisar logs
kubectl logs -n kube-system -l rsName=ama-metrics -c ama-metrics

# Validar ConfigMap
kubectl get configmap ama-metrics-prometheus-config -n kube-system -o yaml

Problema: Grafana no muestra data source

# Verificar link entre Grafana y workspace
az grafana show \
    --name $GRAFANA_NAME \
    --resource-group $RESOURCE_GROUP \
    --query "properties.grafanaIntegrations"

Buenas prácticas

  1. Retention: Usa 18 meses incluidos, luego exporta históricos a Azure Data Explorer para análisis largo plazo
  2. Labels: Limita cardinalidad de labels (evita user IDs, timestamps como labels)
  3. Scraping: Ajusta intervalos según criticidad (30s crítico, 60s+ no crítico)
  4. Alertas: Usa alertas multi-window para reducir false positives
  5. Dashboards: Importa dashboards community antes de crear custom
  6. Multi-tenant: Usa workspaces separados por entorno (prod/dev) o equipo

Referencias