Cloud architecture14 min de lectura

Azure Landing Zones en producción: cómo diseñarlas sin convertir la nube en burocracia

Una Landing Zone madura no controla cada despliegue: establece guardrails claros y permite que los equipos trabajen con autonomía dentro de ellos.

David González
David GonzálezCEO · Cloud y DevOps
Detalle de infraestructura de servidor con indicadores azules, como metáfora de una Azure Landing Zone gobernada

Una Azure Landing Zone no debería ser una colección de policies, una jerarquía de Management Groups y una red Hub-Spoke dibujada en Visio.

Debería responder a una pregunta mucho más difícil:

¿cómo permitimos que los equipos desplieguen infraestructura de forma autónoma sin perder control sobre seguridad, red, identidad, costes y cumplimiento?

Ahí está realmente el problema.

Porque existen dos extremos bastante habituales.

En uno, prácticamente todo está permitido.

Cada equipo crea recursos, VNets, Key Vaults, Public IPs, Service Principals, Private Endpoints y Storage Accounts como considera conveniente.

Durante un tiempo funciona.

Hasta que hay que entender quién tiene acceso a qué, por qué existen siete redes diferentes, dónde terminan los logs, qué recursos están expuestos a Internet o quién es responsable de una suscripción que lleva seis meses generando coste.

El otro extremo tampoco funciona mucho mejor.

El equipo de plataforma intenta evitarlo imponiendo controles sobre absolutamente todo.

Crear una subscription necesita aprobación.

Crear una VNet necesita aprobación.

Abrir una ruta necesita aprobación.

Dar acceso necesita aprobación.

Crear un Private Endpoint necesita aprobación.

Y Azure termina convirtiéndose en una especie de datacenter tradicional con una API delante.

Eso tampoco es una Landing Zone bien diseñada.

Es burocracia automatizada.

La dificultad está en construir algo intermedio.

Una plataforma que establezca límites claros y fuertes allí donde exista riesgo real, pero que permita libertad dentro de esos límites.

La Landing Zone no empieza en la red

Uno de los errores más habituales al diseñar Azure es empezar dibujando VNets.

Hub.

Spokes.

Firewalls.

Peerings.

Subnets.

Private Endpoints.

Pero antes de decidir cómo circulará el tráfico hay una decisión más importante:

¿cuál será nuestra unidad real de aislamiento y gobierno?

En Azure, muchas veces esa unidad debería ser la subscription.

No simplemente el Resource Group.

Una subscription proporciona un límite administrativo mucho más útil para:

  • asignación de políticas;
  • presupuestos;
  • RBAC;
  • quotas;
  • separación de workloads;
  • lifecycle;
  • ownership;
  • aislamiento operacional.

Microsoft utiliza precisamente este principio dentro de Azure Landing Zones: las subscriptions actúan como unidades de gestión y escala.

Eso cambia bastante cómo debería organizarse un tenant.

No tendría demasiado sentido terminar con algo parecido a:

Subscription: Production

├── rg-app-a
├── rg-app-b
├── rg-data
├── rg-network
├── rg-monitoring
├── rg-security
├── rg-dev
├── rg-test
└── rg-random-things

Ese modelo parece sencillo inicialmente.

Pero con el tiempo empiezan los problemas.

Las políticas afectan a workloads que no deberían compartirlas.

Los permisos terminan siendo demasiado amplios.

Los costes son más difíciles de atribuir.

Los límites de servicio empiezan a compartirse.

Y mover después determinadas piezas resulta bastante más complicado.

En una plataforma que espera crecer resulta generalmente más sano pensar en términos similares a:

Tenant

├── Platform
│   ├── Identity
│   ├── Connectivity
│   └── Management

├── Landing Zones
│   ├── Corp
│   │   ├── workload-a-prod
│   │   └── workload-b-prod
│   │
│   └── Online
│       ├── ecommerce-prod
│       └── api-prod

└── Sandbox

No porque esta jerarquía tenga que copiarse literalmente.

Sino porque separa dos conceptos importantes:

la plataforma que habilita el cloud y los workloads que consumen esa plataforma.

Management Groups: no reflejes el organigrama

Otra tentación habitual consiste en replicar la estructura empresarial dentro de Management Groups.

Spain
├── Finance
├── Marketing
├── Engineering
└── Operations

Puede parecer lógico.

Pero los organigramas cambian.

Las arquitecturas deberían cambiar bastante menos.

Un Management Group es especialmente útil cuando representa requisitos técnicos o de gobierno compartidos.

Por ejemplo:

Landing Zones
├── Online
├── Corp
├── Regulated
└── Sandbox

La pregunta correcta no es:

¿qué departamento utiliza esta subscription?

Sino:

¿qué conjunto de controles debería heredar esta subscription?

Dos aplicaciones pertenecientes a departamentos completamente distintos pueden necesitar exactamente las mismas políticas.

Y dos aplicaciones pertenecientes al mismo departamento pueden necesitar modelos de conectividad radicalmente diferentes.

La jerarquía debería representar esas diferencias.

Azure Policy debería impedir estados peligrosos, no controlar todas las decisiones

Aquí empieza una de las partes más delicadas.

Azure Policy es tremendamente poderosa.

Y precisamente por eso es fácil utilizarla mal.

Podemos crear policies para:

  • impedir Public IPs;
  • exigir determinados SKUs;
  • restringir regiones;
  • exigir tags;
  • desplegar diagnósticos;
  • deshabilitar acceso público;
  • imponer TLS;
  • restringir tipos de recursos;
  • comprobar configuración;
  • desplegar automáticamente determinados componentes.

Pero técnicamente poder hacerlo no significa que debamos hacerlo.

Una policy debería existir porque hay un riesgo que merece ser controlado.

No porque “podemos estandarizarlo”.

Una clasificación que resulta útil es separar las políticas en cuatro grupos.

1. Guardrails de seguridad

Estados que simplemente no queremos permitir.

Por ejemplo:

Storage Account con acceso público
Key Vault accesible desde Internet
SQL Server con public network access habilitado
TLS antiguo
Public IP no autorizado

Aquí Deny puede tener mucho sentido.

2. Guardrails de gobierno

Regiones permitidas.

SKUs prohibidos.

Tipos de recursos restringidos.

Ownership obligatorio.

Estos controles dependen mucho más del contexto.

3. Configuración automática

Por ejemplo:

DeployIfNotExists:
    Diagnostic Settings
        -> Log Analytics

Puede reducir muchísimo el trabajo manual.

Pero hay que tener cuidado.

Una política que modifica recursos automáticamente se convierte prácticamente en otro sistema de despliegue.

Y eso tiene implicaciones sobre debugging, permisos y lifecycle.

4. Auditoría

Hay situaciones donde inicialmente interesa observar antes que bloquear.

Audit
AuditIfNotExists

Puede ser bastante más inteligente durante una migración que desplegar inmediatamente cientos de Deny.

Porque una cosa es diseñar gobierno para un entorno nuevo.

Y otra intentar introducirlo sobre cinco años de recursos existentes.

El error de empezar todo con Deny

Supongamos que decidimos:

Ningún Storage Account puede tener acceso público.

Perfecto.

Creamos:

effect = Deny

Y unas horas después falla un deployment.

El equipo de aplicación no sabe por qué.

Terraform devuelve un error.

El pipeline queda rojo.

Alguien abre un ticket al equipo de plataforma.

El equipo de plataforma descubre que una policy bloqueó el recurso.

Se crea una excepción.

Un mes después nadie recuerda por qué existe.

Una Landing Zone madura necesita también una estrategia para las excepciones.

No basta con tener políticas.

Hay que definir:

Policy

Violation

Can workload remediate?

    ├── Yes → Fix workload

    └── No

     Exception

     Owner
     Reason
     Expiration
     Compensating control

Una excepción permanente sin owner ni fecha de caducidad termina convirtiéndose simplemente en una policy que no se aplica.

RBAC: Owner no debería ser la solución universal

Otro problema recurrente aparece con permisos.

Cuando algo no funciona:

Assign Owner

Problema resuelto.

También acabamos de eliminar una buena parte del modelo de seguridad.

En Azure conviene separar claramente:

Control plane
Data plane

No es lo mismo poder configurar un Storage Account que poder leer sus blobs.

No es lo mismo administrar un Key Vault que poder recuperar sus secretos.

Y no es lo mismo desplegar una base de datos que poder acceder a los datos que contiene.

Un modelo sano intenta evitar permisos amplios persistentes.

Por ejemplo:

Platform Team
    |
    +-- Network Contributor      -> Connectivity
    +-- Policy Contributor       -> Governance
    +-- Security Reader          -> Tenant
    +-- Reader                   -> Workloads

Application Team
    |
    +-- Contributor              -> Own subscription
    +-- no RBAC delegation
    +-- no Policy changes
    +-- no shared network changes

Y para operaciones privilegiadas:

Microsoft Entra PIM

Eligible Role

Activation

MFA / justification

Time limited privilege

La idea fundamental es reducir el número de privilegios permanentes.

Especialmente:

Owner
User Access Administrator
Global Administrator

No deberían convertirse en permisos cotidianos.

El patrón tradicional es conocido:

                 Internet
                    |
               Azure Firewall
                    |
                  Hub
                 /   \
                /     \
            Spoke A   Spoke B

El hub suele concentrar:

  • Firewall;
  • VPN/ExpressRoute;
  • DNS;
  • routing;
  • servicios compartidos.

Mientras los spokes contienen workloads.

Microsoft sigue documentando Hub-Spoke como una topología fundamental para Landing Zones, manteniendo normalmente la conectividad central en una subscription dedicada.

El problema empieza cuando prácticamente todos los servicios PaaS pasan a utilizar Private Endpoints.

SQL.

Storage.

Key Vault.

Container Registry.

Cosmos DB.

App Configuration.

Service Bus.

Entonces aparece una pieza que muchas arquitecturas infravaloran:

DNS.

Private Endpoint sin diseño DNS es una incidencia esperando ocurrir

Supongamos:

mystorage.blob.core.windows.net

Normalmente resuelve a una IP pública.

Añadimos Private Endpoint.

Ahora queremos que internamente resuelva hacia:

10.20.3.17

Eso implica gestionar:

privatelink.blob.core.windows.net

y su Private DNS Zone.

El problema no es crear la zona.

El problema es determinar:

¿quién debería resolverla?

Tenemos varias opciones.

Podemos vincular Private DNS Zones directamente con cada spoke.

O centralizar resolución.

Por ejemplo:

On-prem
   |
Corporate DNS
   |
Azure DNS Private Resolver
   |
Private DNS Zones
   |
Private Endpoints

Azure DNS Private Resolver permite resolver namespaces privados entre Azure y redes externas sin tener que operar VMs dedicadas como DNS forwarders. Microsoft documenta tanto arquitecturas centralizadas como distribuidas para este escenario.

Y aquí aparece un problema que hemos visto demasiadas veces:

Network connectivity: OK
NSG: OK
Route: OK
Private Endpoint: Approved
Application: Cannot connect

El diagnóstico termina siendo:

nslookup database.database.windows.net

La aplicación está resolviendo la IP equivocada.

No era el firewall.

No era la ruta.

No era Kubernetes.

Era DNS.

Por eso el diseño DNS debería formar parte de la Landing Zone desde el principio.

No añadirse cuando empiezan a aparecer Private Endpoints.

¿Private Endpoints en el hub o en cada spoke?

Aquí no existe una respuesta universal.

Microsoft contempla ambos modelos dependiendo del escenario. Un Private Endpoint consumido por una única aplicación puede vivir razonablemente en su spoke, mientras que servicios compartidos por múltiples workloads pueden justificar centralización.

Modelo distribuido

Spoke App A
    └── Private Endpoint SQL A

Spoke App B
    └── Private Endpoint SQL B

Ventajas:

  • ownership claro;
  • lifecycle junto al workload;
  • menor dependencia del equipo central.

Inconvenientes:

  • proliferación;
  • mayor complejidad DNS;
  • más objetos de red.

Modelo centralizado

Hub
├── PE SQL
├── PE Storage
└── PE Key Vault

Ventajas:

  • control central;
  • inspección;
  • topología más uniforme.

Inconvenientes:

  • fuerte dependencia del equipo de plataforma;
  • mayor blast radius;
  • lifecycle desacoplado del workload;
  • posible cuello de botella organizativo.

Este tipo de decisiones son precisamente las que una Landing Zone debería documentar.

No únicamente desplegar.

El verdadero objetivo debería ser self-service

Una plataforma cloud madura no debería necesitar que alguien cree manualmente una subscription cada vez que aparece un proyecto.

Debería existir un proceso reproducible.

Algo parecido a:

name: payments-production

environment: production

landing_zone: online

region:
  primary: westeurope

network:
  enabled: true
  address_space: 10.40.0.0/16

budget:
  monthly: 10000

owners:
  - payments-platform-team

features:
  private_dns: true
  central_logging: true
  defender: true

Eso debería disparar una automatización.

Request

Validation

Subscription creation

Management Group assignment

Policy inheritance

RBAC

Budget

Network

Logging

Handover

Microsoft denomina este patrón subscription vending y lo plantea precisamente como una forma de estandarizar cómo los equipos solicitan y reciben subscriptions gobernadas.

El objetivo no debería ser que el equipo central cree recursos.

Debería ser que construya la plataforma que permita a otros equipos crearlos correctamente.

Hay una diferencia enorme.

¿Terraform debería gestionar absolutamente todo?

Una Landing Zone normalmente necesita Infrastructure as Code.

Terraform y OpenTofu son candidatos evidentes.

Pero hay otra decisión importante:

¿dónde termina el código de plataforma y dónde empieza el código del workload?

Evitaría un repositorio gigante parecido a:

azure-everything/

con todo el tenant gestionado por un único state.

El blast radius resulta demasiado grande.

Es más razonable separar estados y responsabilidades.

Por ejemplo:

platform/
├── management-groups
├── policy
├── identity
├── connectivity
└── management

workloads/
├── payments
├── analytics
└── ecommerce

Y dentro de plataforma:

state-management-groups
state-policy
state-connectivity-eu
state-connectivity-us

La pregunta importante es:

¿qué tendría que bloquearse si este state queda corrupto o el pipeline falla?

Si la respuesta es:

todo nuestro tenant Azure,

probablemente el boundary sea demasiado grande.

Bootstrap: el problema que suele esconderse

Todo esto genera una paradoja.

Terraform necesita permisos para crear la plataforma.

Pero la plataforma es precisamente la que debería controlar los permisos de Terraform.

Alguien tiene que crear inicialmente:

State Storage Account
Managed Identity / App Registration
Federated credentials
RBAC
Key Vault

Este proceso inicial debería estar claramente definido.

Y, sobre todo, debería ser reproducible.

No depender de:

David lo creó una vez desde el portal y nadie sabe exactamente cómo.

Idealmente:

bootstrap/

platform identity

remote state

platform deployment

Una vez completado el bootstrap, el acceso humano debería reducirse.

Los pipelines deberían autenticarse utilizando identidades federadas u otros mecanismos sin secretos persistentes siempre que sea posible.

Logging centralizado no significa enviar absolutamente todo

Otro error frecuente:

Vamos a enviar todos los logs a Log Analytics.

Se habilita Diagnostic Settings para todo.

Y al mes siguiente aparece una factura considerable.

Gobierno no significa almacenar indiscriminadamente cada evento generado por Azure.

Hay que distinguir entre:

Security telemetry
Operational telemetry
Audit telemetry
Debug telemetry

Y preguntarse:

  • ¿quién consume este log?
  • ¿cuánto tiempo necesitamos conservarlo?
  • ¿qué problema permite investigar?
  • ¿qué volumen produce?
  • ¿hay duplicación?
  • ¿debería acabar realmente en Log Analytics?

Una Landing Zone debería imponer que exista observabilidad.

No necesariamente imponer que absolutamente todo termine almacenado para siempre.

La Landing Zone también necesita FinOps desde el primer día

Una subscription nueva debería poder nacer ya con:

Owner
Cost center
Environment
Budget
Alert thresholds
Mandatory tags

No seis meses después.

Subscription vending permite precisamente integrar presupuestos y otros controles de coste durante el aprovisionamiento.

Porque intentar atribuir gastos posteriormente suele terminar con recursos similares a:

rg-prod-final
rg-prod-new
rg-test2
rg-shared
rg-temp

y nadie sabe quién es responsable.

El tagging ayuda.

Pero no debería utilizarse para intentar arreglar un modelo de ownership mal diseñado.

Un equipo de plataforma no debería convertirse en el departamento del “no”

Aquí está probablemente la parte más importante de todo el diseño.

Una Landing Zone puede ser técnicamente impecable y organizativamente terrible.

Si para desplegar algo tenemos que abrir:

NETWORK-381
SEC-224
CLOUD-991
IAM-182

esperar cuatro reuniones y obtener tres aprobaciones, no hemos creado una plataforma.

Hemos creado una cola de tickets.

Un buen modelo debería parecerse mucho más a:

             PLATFORM TEAM
                  |
       defines safe boundaries
                  |
       +----------+----------+
       |                     |
    Policies              Modules
       |                     |
       +----------+----------+
                  |
             paved road
                  |
         APPLICATION TEAM
                  |
             self-service

El equipo central mantiene los límites.

Los equipos de producto operan dentro de ellos.

Guardrails frente a gates

Esta distinción resume bastante bien el problema.

Un gate dice:

tienes que pedirme permiso.

Un guardrail dice:

puedes hacerlo tú mientras permanezcas dentro de estas condiciones.

Cloud funciona mucho mejor cuando utilizamos guardrails.

Por ejemplo:

Gate

Abre un ticket para solicitar una Storage Account.

Guardrail

El desarrollador puede desplegar la Storage Account directamente, pero:

Public Access     = Disabled
TLS               >= required version
Diagnostic logs   = Enabled
Region            = Approved
Encryption        = Required

No necesita pedir permiso.

Simplemente no puede crear una configuración que viole los límites establecidos.

Eso es muchísimo más escalable.

Qué debería entregar realmente una Landing Zone

Si mañana tuviéramos que evaluar una Azure Landing Zone, no comprobaríamos simplemente si existen Management Groups.

Buscaríamos algo bastante más amplio.

Organización

Management Group hierarchy
Subscription boundaries
Workload ownership
Platform subscriptions
Sandbox strategy

Identidad

Least privilege
PIM
Break-glass accounts
Workload identities
Federated CI/CD authentication

Gobierno

Policy initiatives
Exceptions
Deny strategy
Allowed regions
Resource restrictions

Red

Hub-Spoke / vWAN decision
Egress strategy
Firewall
Private Link
DNS architecture
Hybrid connectivity
IP addressing

Seguridad

Defender
Key management
Secrets
Public exposure
Security telemetry

Operación

Central monitoring
Alert ownership
Logs
Runbooks
Incident access

FinOps

Budgets
Ownership
Tagging
Cost attribution
Resource lifecycle

Automatización

Terraform/OpenTofu
State boundaries
CI/CD
Subscription vending
Reusable workload modules

Y probablemente la pregunta más importante:

¿puede un equipo crear un workload nuevo sin necesitar que el equipo de plataforma realice manualmente veinte operaciones?

Si la respuesta es no, todavía queda trabajo.

La mejor Landing Zone es la que deja de llamar la atención

Una Landing Zone buena no debería estar constantemente recordándonos que existe.

Un desarrollador debería poder desplegar.

Un equipo de seguridad debería tener visibilidad.

Finanzas debería poder atribuir costes.

El equipo de plataforma debería mantener estándares.

Y ninguno debería bloquear innecesariamente al otro.

Eso requiere mucha más ingeniería que simplemente desplegar un blueprint de referencia.

Porque el problema real nunca ha sido crear una estructura de Management Groups.

El problema es decidir:

qué centralizamos, qué delegamos y dónde ponemos los límites.

Azure nos proporciona todas las piezas.

Management Groups.

Policies.

RBAC.

PIM.

Subscriptions.

Private Link.

DNS Private Resolver.

Firewall.

Terraform.

Budgets.

Log Analytics.

Pero una lista de servicios no constituye una arquitectura.

La arquitectura aparece cuando decidimos cómo deben relacionarse.

Y especialmente cuando entendemos los trade-offs de esas decisiones.

Una Azure Landing Zone madura no intenta controlar cada deployment.

Construye un entorno donde la mayoría de deployments correctos puedan ocurrir sin intervención humana.

Y bloquea automáticamente los pocos estados que la organización simplemente no está dispuesta a aceptar.

Ese es el equilibrio difícil.

Gobierno sin fricción. Autonomía sin caos.