Skip to content

Blog

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

Azure Container Apps Dynamic Sessions

Resumen

Azure Container Apps Dynamic Sessions proporciona acceso rápido a entornos sandbox seguros y aislados, ideales para ejecutar código generado por IA o aplicaciones que requieren aislamiento. Con inicio en milisegundos, gestión automática del ciclo de vida y escalado masivo, es la solución perfecta para escenarios de code interpreter, ejecución de agentes de IA o sandboxing de código no confiable.

¿Qué son las Dynamic Sessions?

Las Dynamic Sessions son entornos sandboxed que se ejecutan dentro de un session pool y proporcionan:

Características clave:

  • Aislamiento Hyper-V: Cada sesión aislada en sandbox independiente
  • Inicio ultra rápido: Nuevas sesiones en milisegundos (warm pool)
  • Acceso REST API: Gestión simple vía HTTP endpoints
  • Gestión automática: Limpieza automática tras inactividad
  • Alta escalabilidad: Cientos/miles de sesiones concurrentes
  • Network isolation opcional: Seguridad adicional para workloads críticos

Tipos de sesiones

1. Code Interpreter Sessions (Built-in)

Sesiones pre-configuradas para ejecutar código Python/Node.js sin gestionar infraestructura:

# Crear session pool con code interpreter
az containerapp sessionpool create \
    --name my-code-interpreter-pool \
    --resource-group my-rg \
    --location eastasia \
    --max-sessions 100 \
    --container-type PythonLTS \
    --cooldown-period 300

Casos de uso: - Código generado por LLMs (ChatGPT, Claude, etc.) - Evaluación de código de usuarios en SaaS apps - Jupyter notebooks serverless - Herramientas de IA (LangChain, LlamaIndex, Semantic Kernel)

2. Custom Container Sessions

Sesiones con contenedores personalizados para entornos especializados:

{
  "type": "Microsoft.App/sessionPools",
  "apiVersion": "2024-08-02-preview",
  "name": "custom-session-pool",
  "location": "eastus",
  "properties": {
    "environmentId": "/subscriptions/<SUB_ID>/resourceGroups/<RG>/providers/Microsoft.ContainerApps/environments/<ENV>",
    "poolManagementType": "Dynamic",
    "containerType": "CustomContainer",
    "scaleConfiguration": {
      "maxConcurrentSessions": 50,
      "readySessionInstances": 10
    },
    "customContainerTemplate": {
      "containers": [{
        "image": "myregistry.azurecr.io/custom-sandbox:1.0",
        "name": "sandbox",
        "resources": {
          "cpu": 0.5,
          "memory": "1Gi"
        }
      }],
      "ingress": {
        "targetPort": 8080
      }
    },
    "dynamicPoolConfiguration": {
      "executionType": "Timed",
      "cooldownPeriodInSeconds": 600
    }
  }
}

Arquitectura y flujo

flowchart TB
    A[Aplicación cliente] -->|REST API| B[Session Pool]
    B -->|Asigna| C[Ready Session]
    C -->|Hyper-V Isolation| D[Sandbox Container]
    D -->|Ejecuta código| E[Results]
    E -->|Devuelve| A
    C -.->|Cooldown| F[Auto cleanup]

    style C fill:#2ecc71
    style D fill:#3498db
    style F fill:#e74c3c

Flujo de ejecución:

  1. Cliente solicita sesión vía identifier
  2. Si no existe → pool asigna sesión ready del warm pool
  3. Código se ejecuta en sandbox Hyper-V aislado
  4. Resultados se devuelven al cliente
  5. Tras cooldown sin actividad → limpieza automática

Configuración práctica

Paso 1: Crear Azure Container Apps environment

# Variables
RESOURCE_GROUP="aca-sessions-rg"
LOCATION="eastasia"
ENVIRONMENT_NAME="aca-sessions-env"

# Crear resource group y environment
az group create --name $RESOURCE_GROUP --location $LOCATION

az containerapp env create \
    --name $ENVIRONMENT_NAME \
    --resource-group $RESOURCE_GROUP \
    --location $LOCATION

Paso 2: Crear session pool

SESSION_POOL_NAME="ai-code-interpreter"

az containerapp sessionpool create \
    --name $SESSION_POOL_NAME \
    --resource-group $RESOURCE_GROUP \
    --location $LOCATION \
    --max-sessions 100 \
    --container-type PythonLTS \
    --cooldown-period 300

Paso 3: Obtener management endpoint

# Obtener endpoint del pool
SESSION_POOL_ENDPOINT=$(az containerapp sessionpool show \
    -n $SESSION_POOL_NAME \
    -g $RESOURCE_GROUP \
    --query "properties.poolManagementEndpoint" \
    -o tsv)

echo "Pool Management Endpoint: $SESSION_POOL_ENDPOINT"

Paso 4: Ejecutar código en sesión

POST https://<REGION>.dynamicsessions.io/subscriptions/<SUB_ID>/resourceGroups/<RG>/sessionPools/<POOL_NAME>/identifier/<SESSION_ID>/code/execute?api-version=2024-02-02-preview
Content-Type: application/json
Authorization: Bearer <TOKEN>

{
    "properties": {
        "codeInputType": "inline",
        "executionType": "synchronous",
        "code": "import pandas as pd\ndf = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})\nprint(df.describe())"
    }
}

Respuesta:

{
    "status": "Success",
    "result": {
        "stdout": "       A    B\ncount  3.0  3.0\nmean   2.0  5.0\nstd    1.0  1.0\nmin    1.0  4.0\n25%    1.5  4.5\n50%    2.0  5.0\n75%    2.5  5.5\nmax    3.0  6.0",
        "stderr": "",
        "executionTimeMs": 120
    }
}

Integración con frameworks de IA

LangChain

from langchain.agents import Tool
from langchain_azure_dynamic_sessions import SessionsPythonREPLTool

# Configurar tool con session pool
sessions_tool = SessionsPythonREPLTool(
    pool_management_endpoint=SESSION_POOL_ENDPOINT
)

# Crear agente con herramienta
tools = [
    Tool(
        name="Python REPL",
        func=sessions_tool.run,
        description="Ejecuta código Python en sandbox seguro"
    )
]

LlamaIndex

from llama_index.tools.azure_code_interpreter import AzureCodeInterpreterToolSpec
from llama_index.agent.react import ReActAgent

# Configurar tool
tool_spec = AzureCodeInterpreterToolSpec(
    pool_management_endpoint=SESSION_POOL_ENDPOINT
)

# Crear agente
agent = ReActAgent.from_tools(
    tool_spec.to_tool_list(),
    llm=llm,
    verbose=True
)

# Ejecutar query
response = agent.chat("Analiza este dataset CSV y crea un gráfico de tendencias")

Seguridad y network isolation

Habilitar network isolation

# Actualizar session pool con network isolation
az containerapp sessionpool update \
    --name $SESSION_POOL_NAME \
    --resource-group $RESOURCE_GROUP \
    --session-network-status EgressEnabled

Beneficios: - Bloquea acceso saliente no autorizado - Previene exfiltración de datos - Compatible con VNet integration - Logs de conexiones en Azure Monitor

Autenticación con Managed Identity

{
  "identity": {
    "type": "UserAssigned",
    "userAssignedIdentities": {
      "<IDENTITY_RESOURCE_ID>": {}
    }
  },
  "properties": {
    "managedIdentitySettings": [{
      "identity": "<IDENTITY_RESOURCE_ID>",
      "lifecycle": "None"
    }]
  }
}

Casos de uso reales

1. Agente de análisis de datos con GPT-4

# Prompt del usuario
user_query = "Analiza ventas_2024.csv y muestra top 5 productos"

# Agent genera y ejecuta código
code_generated = """
import pandas as pd
df = pd.read_csv('/data/ventas_2024.csv')
top_products = df.groupby('producto')['ventas'].sum().nlargest(5)
print(top_products.to_string())
"""

# Ejecutar en dynamic session
response = execute_in_session(
    session_id="analysis-001",
    code=code_generated
)

2. Sandbox para código de usuarios (SaaS app)

# Usuario envía código no confiable
user_code = request.json.get('code')

# Ejecutar en sesión aislada con timeout
result = execute_code_safely(
    code=user_code,
    session_id=f"user-{user_id}",
    timeout=30,
    max_memory="512Mi"
)

if result['status'] == 'Success':
    return jsonify(result['output'])
else:
    return jsonify({'error': result['error']}), 400

3. Jupyter notebooks serverless

# Crear sesión interactiva para notebook
notebook_session = create_session(
    pool_name="jupyter-pool",
    session_id=f"notebook-{session_token}",
    idle_timeout=3600  # 1 hora
)

# Ejecutar celdas secuencialmente
for cell in notebook_cells:
    result = execute_cell(
        session_id=notebook_session,
        code=cell['code']
    )
    cell['output'] = result

Limitaciones y consideraciones

Limitaciones actuales:

  • Max 100 sesiones concurrentes (configurable hasta 1000)
  • Cooldown mínimo: 60 segundos
  • Timeouts: 30 min por ejecución (synchronous), sin límite (asynchronous)
  • Network isolation no soporta inbound connections
  • Storage efímero (no persistente entre sesiones)

Pricing: - Pago por uso: tiempo de sesión activa + vCPU/memory - Warm pool incluido sin coste adicional - Estimado: ~$0.03/hora por sesión estándar (0.25 vCPU, 0.5 Gi)

Monitorización

# Logs de sesiones en Log Analytics
az monitor log-analytics query \
    --workspace $WORKSPACE_ID \
    --analytics-query "ContainerAppSystemLogs_CL
    | where ContainerAppName_s == '$SESSION_POOL_NAME'
    | where TimeGenerated > ago(1h)
    | project TimeGenerated, Log_s
    | order by TimeGenerated desc"

Buenas prácticas

  1. Dimensionamiento del pool:
  2. readySessionInstances: 10-20% del maxConcurrentSessions
  3. Ajustar cooldownPeriod según patrón de uso (300-600s típico)

  4. Gestión de sesiones:

  5. Usar session IDs únicos y descriptivos
  6. Implementar cleanup explícito tras finalizar
  7. Monitorizar métricas de pool saturation

  8. Seguridad:

  9. Habilitar network isolation para código no confiable
  10. Validar inputs antes de ejecutar código
  11. Limitar recursos (CPU/memory) por sesión
  12. Revisar logs de ejecución regularmente

  13. Cost optimization:

  14. Escalar readySessionInstances dinámicamente según demanda
  15. Implementar timeout agresivo para sesiones idle
  16. Evaluar custom containers vs built-in (PythonLTS más económico)

Referencias

Azure Automation Runbooks: PowerShell y Python para automatización IT

Resumen

Azure Automation proporciona Process Automation mediante runbooks (scripts PowerShell 7.2, Python 3.10, Graphical) ejecutables en cloud (3-hour fair share limit) o Hybrid Runbook Workers (sin límite, on-premises/multicloud). Webhooks permiten triggers desde Logic Apps, Functions, ITSM, monitoring alerts. Ideal para: VM lifecycle management, scheduled maintenance, incident response, configuration drift remediation.

Resumen

Azure Private Link permite acceder a servicios Azure (Storage, SQL Database, Key Vault) y servicios de terceros desde VNets privadas sin exponerlos a internet mediante private endpoints (IPs privadas en tu subnet). Integración DNS automática con private DNS zones (privatelink.*.core.windows.net) resuelve FQDNs a IPs privadas. Escenarios: VNet workloads, peered VNets, on-premises con DNS forwarder, hub-spoke topology.

Microsoft Defender for Cloud CSPM: Governance y Attack Paths

Resumen

Defender CSPM (Cloud Security Posture Management) proporciona visibilidad avanzada de riesgos en entornos Azure, AWS y GCP mediante governance rules (asignación automática de owners y SLAs), attack path analysis (modelado de rutas de ataque explotables), Cloud Security Explorer (queries para detectar misconfiguraciones) y risk prioritization de recommendations. Plan Defender CSPM ~$12/recurso/mes.

Azure Front Door Standard/Premium: WAF, Caching y Rules Engine

Resumen

Azure Front Door Standard/Premium es un CDN global con capacidades integradas de WAF (Web Application Firewall), caching avanzado, rules engine para manipulación de requests/responses y anycast routing desde 118+ PoPs Microsoft. Proporciona aceleración de aplicaciones HTTP/HTTPS, balanceo global, failover automático y protección DDoS L7, reemplazando a Front Door Classic y CDN from Microsoft (classic).