Skip to content

Azure Services

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 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).

Azure Backup Immutable Vaults: Protección contra ransomware

Resumen

Los Immutable Vaults en Azure Backup bloquean operaciones que podrían eliminar recovery points (stop backup, delete backup data, disable soft delete), protegiendo contra ransomware y actores maliciosos. Al habilitar immutability con lock irreversible, se garantiza que los backups son intocables durante el periodo de retención, cumpliendo requisitos regulatorios (HIPAA, SOC 2, GDPR) y asegurando recuperación ante ataques.

Azure Arc GitOps con Flux v2 para Kubernetes

Resumen

Azure Arc-enabled Kubernetes con GitOps (Flux v2) permite gestionar configuraciones de clusters Kubernetes (on-premises, multicloud, edge) desde repositorios Git de forma declarativa. Flux v2 reconcilia automáticamente el estado deseado definido en Git con el estado real del cluster, proporcionando drift reconciliation, auditoría completa y despliegues automatizados.

Azure Virtual Network Manager: Illustrative relationship between components

graph TD
    subgraph "Azure Scope"
        MG["Management Group / Subscription"]
    end
    subgraph "Azure Virtual Network Manager (AVNM)"
        AVNM_Instance["AVNM Instance"] -- Manages --> MG
        AVNM_Instance -- Contains --> NG1["Network Group A (e.g., Production)"]
        AVNM_Instance -- Contains --> NG2["Network Group B (e.g., Test)"]
    end
    subgraph "Virtual Networks (VNets)"
        VNet1["VNet 1"]
        VNet2["VNet 2"]
        VNet3["VNet 3"]
        VNet4["VNet 4"]
    end
    subgraph "Membership"
        Static["Static Membership"] -- Manually Adds --> NG1
        Static -- Manually Adds --> NG2
        VNet1 -- Member Of --> Static
        VNet4 -- Member Of --> Static
        Dynamic["Dynamic Membership (Azure Policy)"] -- Automatically Adds --> NG1
        Dynamic -- Automatically Adds --> NG2
        Policy["Azure Policy Definition (e.g., 'tag=prod')"] -- Defines --> Dynamic
        VNet2 -- Meets Policy --> Dynamic
        VNet3 -- Meets Policy --> Dynamic
    end
    subgraph "Configurations"
        ConnConfig["Connectivity Config (Mesh / Hub-Spoke)"] -- Targets --> NG1
        SecConfig["Security Admin Config (Rules)"] -- Targets --> NG1
        SecConfig2["Security Admin Config (Rules)"] -- Targets --> NG2
    end
    subgraph "Deployment & Enforcement"
        Deploy["Deployment"] -- Applies --> ConnConfig
        Deploy -- Applies --> SecConfig
        Deploy -- Applies --> SecConfig2
        NG1 -- Receives --> ConnConfig
        NG1 -- Receives --> SecConfig
        NG2 -- Receives --> SecConfig2
        VNet1 -- Enforced --> NG1
        VNet2 -- Enforced --> NG1
        VNet3 -- Enforced --> NG1
        VNet4 -- Enforced --> NG2
    end
    %% Removed 'fill' for better dark mode compatibility, kept colored strokes
    classDef avnm stroke:#f9f,stroke-width:2px;
    classDef ng stroke:#ccf,stroke-width:1px;
    classDef vnet stroke:#cfc,stroke-width:1px;
    classDef config stroke:#ffc,stroke-width:1px;
    classDef policy stroke:#fcc,stroke-width:1px;
    class AVNM_Instance avnm;
    class NG1,NG2 ng;
    class VNet1,VNet2,VNet3,VNet4 vnet;
    class ConnConfig,SecConfig,SecConfig2 config;
    class Policy,Dynamic,Static policy;

Diagram Explanation

  1. Azure Scope: Azure Virtual Network Manager (AVNM) operates within a defined scope, which can be a Management Group or a Subscription. This determines which VNets AVNM can "see" and manage.
  2. AVNM Instance: This is the main Azure Virtual Network Manager resource. Network groups and configurations are created and managed from here.
  3. Network Groups:
    • These are logical containers for your Virtual Networks (VNets).
    • They allow you to group VNets with common characteristics (environment, region, etc.).
    • A VNet can belong to multiple network groups.
  4. Membership: How VNets are added to Network Groups:
    • Static Membership: You add VNets manually, selecting them one by one.
    • Dynamic Membership: Uses Azure Policy to automatically add VNets that meet certain criteria (like tags, names, locations). VNets matching the policy are dynamically added (and removed) from the group.
  5. Virtual Networks (VNets): These are the Azure virtual networks that are being managed.
  6. Configurations: AVNM allows you to apply two main types of configurations to Network Groups:
    • Connectivity Config: Defines how VNets connect within a group (or between groups). You can create topologies like Mesh (all connected to each other) or Hub-and-Spoke (a central VNet connected to several "spoke" VNets).
    • Security Admin Config: Allows you to define high-level security rules that apply to the VNets in a group. These rules can override Network Security Group (NSG) rules, enabling centralized and mandatory security policies.
  7. Deployment & Enforcement:

    • The created configurations (connectivity and security) must be Deployed.
    • During deployment, AVNM translates these configurations and applies them to the VNets that are members of the target network groups in the selected regions.
    • Once deployed, the VNets within the groups receive and apply (Enforced) these configurations, establishing the defined connections and security rules.

    And maybe this post will be published in the official documentation of Azure Virtual Network Manager, who knows? 😉