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.
La red centralizada parece sencilla hasta que aparece Private Link
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.



