Skip to content

Blog

Resumen

Azure Cosmos DB for MongoDB vCore es MongoDB real en Azure, con arquitectura vCore (CPU + RAM) que escala vertical y horizontalmente sin downtime. Soporte nativo para vector search (DiskANN), sharding automático y 99.99% SLA con HA activado. Ideal para lift & shift de aplicaciones MongoDB existentes o nuevas apps que necesiten queries complejas, aggregations y transacciones distribuidas.

Ephemeral OS Disks en Azure Virtual Desktop

Resumen

Azure Virtual Desktop ahora soporta Ephemeral OS Disks (Public Preview desde octubre 2025), una característica que mejora significativamente el rendimiento y velocidad de aprovisionamiento en entornos de escritorios virtuales. Los discos efímeros almacenan el sistema operativo en el almacenamiento local de la VM en lugar de en Azure Storage remoto, proporcionando latencias más bajas y tiempos de reimagen más rápidos, ideales para cargas de trabajo stateless.

¿Qué son los Ephemeral OS Disks?

Los Ephemeral OS Disks son discos de sistema operativo creados en el almacenamiento local de la máquina virtual (SSD local, cache o disco temporal) en lugar de almacenarse en Azure Storage remoto. Esta arquitectura ofrece ventajas clave para entornos AVD donde la persistencia del sistema operativo no es crítica.

Características principales:

  • Almacenamiento en disco local (cache o temp disk de la VM)
  • Aprovisionamiento y reimagen ultra rápidos
  • Menor latencia en operaciones de lectura/escritura
  • Optimizado para host pools de tipo pooled
  • No persistente: reinicio/reimagen/delete únicamente (no soporta stop/deallocate)

¿Por qué usar Ephemeral OS Disks en AVD?

Beneficios concretos:

  1. Rendimiento mejorado: Operaciones I/O directas sobre almacenamiento local
  2. Provisioning más rápido: Creación de session hosts en menos tiempo
  3. Reimagen acelerado: Reset de VMs al estado original en segundos
  4. Coste optimizado: No se consumen recursos de Azure Storage remoto
  5. Ideal para VDI stateless: Los perfiles de usuario se persisten en FSLogix/Azure Files

Arquitectura y funcionamiento

flowchart LR
    A[Session Host VM] -->|OS Disk| B[Local SSD/Cache]
    A -->|User Profiles| C[FSLogix Container]
    C -->|Storage| D[Azure Files/NetApp]

    style B fill:#2ecc71
    style D fill:#3498db

Diferencia clave con discos persistentes:

Aspecto Ephemeral OS Disk Managed OS Disk
Ubicación Almacenamiento local VM Azure Storage remoto
Latencia I/O Muy baja Mayor (red)
Persistencia No persistente Persistente
Operaciones Restart/Reimage/Delete Start/Stop/Deallocate/Snapshot
Coste Incluido en VM Coste adicional storage

Placement options:

  • OS Cache: Para imágenes ≤ 127 GiB (ej: Windows Server)
  • Temp Disk: Para imágenes que requieren >127 GiB y VM con disco temporal suficiente

Requisitos y limitaciones

Requisitos

  • Host pool de tipo Pooled con Session Host Configuration
  • Tamaño de imagen ≤ cache/temp disk disponible en VM size elegida
  • Solo soportado en Azure (no Azure Government durante preview)

Limitaciones importantes

No soporta:

  • Deallocate de VMs (solo restart, reimage, delete)
  • VM snapshots
  • Azure Disk Encryption
  • Azure Backup
  • Azure Site Recovery
  • OS Disk Swap
  • NVMe y Premium SSD (durante preview)

Configuración de autoscaling:

  • Usar Dynamic Autoscaling con Minimum percentage of active hosts = 100%
  • Evita operaciones stop/deallocate incompatibles con ephemeral disks

Configuración práctica

Paso 1: Crear host pool con Ephemeral OS Disk (Portal)

# Configuración requerida en Azure Portal

Host Pool Type: Pooled
Management Type: Automated
Session Host Configuration: Enabled

# En la pestaña Virtual Machines:
Image: Windows 11 Enterprise multi-session
Virtual Machine Size: Standard_D8s_v3 (temp disk 200 GiB)
OS Disk Type: Standard SSD (auto-selected)
Ephemeral OS Disk: Enabled
Placement: Temp Disk

Paso 2: Crear session host configuration con PowerShell

# Variables del entorno
$resourceGroup = "avd-rg"
$hostPoolName = "avd-ephemeral-pool"
$location = "westeurope"
$vmPrefix = "avd-eph"
$vmSize = "Standard_D8s_v3"
$subnetId = "/subscriptions/<SUB_ID>/resourceGroups/<RG>/providers/Microsoft.Network/virtualNetworks/<VNET>/subnets/<SUBNET>"

# Crear session host configuration con ephemeral disk
$parameters = @{
    FriendlyName = "Ephemeral Session Hosts"
    HostPoolName = $hostPoolName
    ResourceGroupName = $resourceGroup
    VMNamePrefix = $vmPrefix
    VMLocation = $location
    ImageInfoType = "Marketplace"
    MarketplaceInfoPublisher = "MicrosoftWindowsDesktop"
    MarketplaceInfoOffer = "Windows-11"
    MarketplaceInfoSku = "win11-22h2-avd"
    MarketplaceInfoExactVersion = "latest"
    VMSizeId = $vmSize
    DiskInfoType = "Standard_LRS"  # Standard SSD requerido en preview
    NetworkInfoSubnetId = $subnetId
    DomainInfoJoinType = "AzureActiveDirectory"
    VMAdminCredentialsUsernameKeyVaultSecretUri = "https://<VAULT>.vault.azure.net/secrets/<SECRET>/..."
    VMAdminCredentialsPasswordKeyVaultSecretUri = "https://<VAULT>.vault.azure.net/secrets/<SECRET>/..."
}

New-AzWvdSessionHostConfiguration @parameters

Paso 3: Validar tamaños de VM compatibles

# Listar VM sizes con soporte ephemeral disk
$location = "westeurope"
$vmSizes = Get-AzComputeResourceSku -Location $location |
    Where-Object {$_.ResourceType -eq 'virtualMachines'}

foreach ($vmSize in $vmSizes) {
    foreach ($capability in $vmSize.Capabilities) {
        if ($capability.Name -eq "EphemeralOSDiskSupported" -and $capability.Value -eq "True") {
            [PSCustomObject]@{
                VMSize = $vmSize.Name
                EphemeralSupported = $capability.Value
                CacheSize = ($vmSize.Capabilities | Where-Object {$_.Name -eq "CachedDiskBytes"}).Value
                TempDiskSize = ($vmSize.Capabilities | Where-Object {$_.Name -eq "MaxResourceVolumeMB"}).Value
            }
        }
    }
}

Paso 4: Configurar autoscaling para ephemeral disks

# Crear scaling plan con configuración específica para ephemeral
$scalingParams = @{
    HostPoolName = $hostPoolName
    ResourceGroupName = $resourceGroup
    ScheduledDateTimeZone = "W. Europe Standard Time"
    UpdateLogOffDelayMinute = 15
    UpdateMaxVmsRemoved = 10
    UpdateDeleteOriginalVM = $true  # Importante: eliminar VMs en lugar de deallocate
    UpdateLogOffMessage = "El sistema se reiniciará para mantenimiento en 15 minutos"
}

New-AzWvdSessionHostManagement @scalingParams

Migración de host pools existentes

Para migrar un host pool con managed disks a ephemeral:

# 1. Actualizar session host configuration
$updateParams = @{
    HostPoolName = $hostPoolName
    ResourceGroupName = $resourceGroup
    VMSizeId = "Standard_D8s_v3"  # VM size con temp disk suficiente
    DiskInfoType = "Standard_LRS"
}

Update-AzWvdSessionHostConfiguration @updateParams

# 2. Programar update para aplicar cambios (reemplaza session hosts)
# Nota: Esto recreará los session hosts con ephemeral disks

# 3. Monitorizar progreso
$progress = Get-AzWvdSessionHostManagementsUpdateStatus @updateParams |
    Format-List PercentComplete, ProgressSessionHostsCompleted, EndTime

$progress

Buenas prácticas

Persistencia de datos:

  • Usar FSLogix Profile Containers para perfiles de usuario
  • Almacenar datos en Azure Files o Azure NetApp Files
  • Configurar redirección de carpetas conocidas (OneDrive, Documents)

Gestión de ciclo de vida:

  • Implementar lógica restart/delete en scripts de autoscaling
  • Evitar usar stop/deallocate (genera pérdida de datos)
  • Programar reimaging periódico para refresh de session hosts

Sizing correcto:

  • Verificar que image size ≤ cache/temp disk disponible
  • Para Windows 11 multi-session (127 GiB): usar VMs con temp disk ≥ 127 GiB
  • Preferir temp disk placement sobre cache para mayor espacio disponible

Monitorización:

  • Alertas sobre tasa de reimage/restart
  • Métricas de latencia de disco en Azure Monitor
  • Revisar logs de deployment failures relacionados con tamaño insuficiente

Troubleshooting común

Error: "Insufficient local storage"

# Causa: VM size sin suficiente cache/temp disk
# Solución: Cambiar a VM size mayor o con temp disk adecuado

# Verificar tamaño requerido
$imageSize = 127  # GiB para Windows 11 multi-session
Get-AzComputeResourceSku -Location $location |
    Where-Object {$_.Name -eq "Standard_D8s_v3"} |
    Select-Object Name, @{N="TempDiskGB";E={[math]::Round(($_.Capabilities |
        Where-Object {$_.Name -eq "MaxResourceVolumeMB"}).Value / 1024)}}

Error: "NVMe placement not supported"

Causa: Selección de NVMe como placement durante preview
Solución: Usar Temp Disk o Cache placement en Azure Portal

Pérdida de datos tras deallocate:

Causa: Operación stop/deallocate borra OS state en ephemeral disks
Solución: Configurar autoscaling con UpdateDeleteOriginalVM = $true
Usar restart en lugar de deallocate para operaciones manuales

Referencias

Analizando VNet Flow Logs en Azure

En este post explico qué son los VNet flow logs (logs de flujo de red de Azure), cómo habilitarlos y cómo analizarlos con un pequeño script PowerShell que desarrollé para facilitar la detección de anomalías y la generación de reportes.

Documentación oficial (Microsoft):

https://learn.microsoft.com/azure/network-watcher/vnet-flow-logs-overview

Resumen rápido

  • Los VNet flow logs capturan información sobre el tráfico IP que atraviesa subredes y recursos dentro de una Virtual Network. Almacenan datos similares a los NSG flow logs pero a nivel de VNet.
  • Se pueden enviar a una cuenta de almacenamiento, a Log Analytics o a Event Hub. El formato JSON resultante contiene un array records con campos que describen cada flujo y sus métricas (bytes, paquetes, acción, puertos, etc.).

Por qué analizarlos

  • Identificar hosts con alto volumen de tráfico.
  • Detectar intentos de escaneo de puertos o reintentos persistentes.
  • Encontrar conexiones denegadas o patrones sospechosos.

Herramienta: script PowerShell

He incluido un script llamado Analyze-VNETFlowLogs.ps1 que procesa archivos JSON con VNet flow logs y genera un análisis en consola. Además puede exportar dos ficheros CSV con el detalle y el resumen del análisis.

(Opcional) Descarga de Flow Logs previa

Si tus logs están aún en la cuenta de almacenamiento, puedes usar el script de apoyo Download-FlowLogs.ps1 para:

  • Descargar todos los blobs del contenedor (por defecto insights-logs-flowlogflowevent).
  • Manejar rutas largas en Windows (hash si usas -AutoShorten).
  • Reorganizar los archivos en una jerarquía YYYY-MM-DD/HH/FlowLog_<timestamp>_*.json.
  • Facilitar un patrón único de entrada para el analizador.

Parámetros clave: - -StorageAccount (obligatorio): Nombre de la storage account. - -ContainerName: Contenedor (default: insights-logs-flowlogflowevent). - -DownloadPath: Carpeta destino local (default: ./FlowLogs). - -AutoShorten: Acorta automáticamente rutas demasiado largas mediante hashing. - -Force: Omite confirmaciones interactivas.

Variables de entorno soportadas: - AZ_STORAGE_KEY: Si está definida se usa esa clave directamente. - AZ_RESOURCE_GROUP: Permite intentar descubrir la key si no se pasó manualmente.

Si no hay clave disponible o el intento de descubrimiento falla, se intentará usar automáticamente --auth-mode login para la autenticación.

Ejemplo de uso completo (descarga + análisis):

# 1. Descargar y organizar
pwsh -File ./Download-FlowLogs.ps1 -StorageAccount mystorageacct -DownloadPath ./FlowLogs -AutoShorten -Force

# 2. Analizar todos los logs resultantes
pwsh -File ./Analyze-VNETFlowLogs.ps1 -LogFiles "./FlowLogs/**/*.json" -ExportCSV -OutputPath ./reports -ShowGraphs

Si ya tienes los JSON en local (por AzCopy u otro método), puedes saltarte el paso 1.

Dónde está el script y ejemplos

Cómo usar el script

  1. Preparar archivos JSON descargados desde la cuenta de almacenamiento o exportados desde Log Analytics.
  2. Abrir PowerShell (Windows) o pwsh (Linux/macOS) y ejecutar (ejemplo):
pwsh -File "./Analyze-VNETFlowLogs.ps1" -LogFiles "./samples/*.json" -ExportCSV -OutputPath "./reports" -ShowGraphs

Explicación rápida de parámetros

  • -LogFiles: rutas a los JSON (acepta wildcards).
  • -ExportCSV: exporta VNetFlowAnalysis_Details_<timestamp>.csv y VNetFlowAnalysis_Summary_<timestamp>.csv.
  • -OutputPath: directorio para los CSV.
  • -SpecificIPs: array de IPs para análisis centrado en hosts concretos.
  • -ShowGraphs: muestra gráficas de barras simples en la consola.

Ejemplo práctico (archivo de ejemplo incluido)

  • He añadido samples/sample-flowlog.json con varios flujos que cubren casos típicos (permitidos, denegados, varios BEGIN para simular reintentos/posible escaneo).
  • Ejecutando el comando anterior sobre ese ejemplo se generan dos CSV de salida en reports/ y se muestra un resumen en consola.

Notas y recomendaciones

  • El script agrupa los flows en memoria para facilitar el análisis; para datasets muy grandes conviene procesar por lotes o filtrar por rango de tiempo antes de ejecutar.
  • Revisa la documentación oficial (enlace arriba) si tu flujo JSON tiene un formato distinto o campos adicionales; la función Parse-FlowTuple del script espera un orden concreto de campos, que puedes adaptar si es necesario.

Contribuciones y siguientes pasos

  • Si quieres, puedo añadir un conjunto de tests que ejecuten el script sobre el ejemplo y verifiquen los CSV generados automáticamente.
  • También puedo crear un pequeño tutorial paso a paso para habilitar VNet flow logs en Azure y obtener los JSON desde una cuenta de almacenamiento.

Archivos incluidos

  • Analyze-VNETFlowLogs.ps1 — script principal.
  • Download-FlowLogs.ps1 — utilidad para descargar y normalizar logs desde la cuenta de almacenamiento.
  • README.md — instrucciones y referencia a la doc oficial.
  • samples/sample-flowlog.json — ejemplo para pruebas.

Espero que esto te ayude a analizar flujos en tus VNets.

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.