Terraform Azure AKS es la solución definitiva para desplegar y gestionar clústeres de Kubernetes en Microsoft Azure mediante infraestructura como código. En esta guía completa aprenderás a provisionar Azure Kubernetes Service (AKS) de forma automatizada, escalable y reproducible, siguiendo las mejores prácticas de DevOps e Infrastructure as Code en 2025.
Azure Kubernetes Service es el servicio de Kubernetes completamente gestionado de Microsoft Azure, y combinarlo con Terraform te permite versionar tu infraestructura cloud, replicar entornos consistentemente, y mantener un control total sobre tu stack de contenedores. Si también trabajas con otros servicios autohospedados, te recomendamos explorar nuestra guía de Docker Compose para complementar tu arquitectura.
¿Qué es Terraform?
Terraform es la herramienta líder de Infrastructure as Code (IaC) desarrollada por HashiCorp que permite definir, provisionar y gestionar recursos cloud mediante archivos de configuración declarativos. A diferencia de scripts imperativos, con esta herramienta describes el estado final deseado y el motor se encarga de alcanzarlo automáticamente.
Las principales ventajas son:
- Multi-cloud: Funciona con AWS, Azure, GCP, y más de 3000 providers
- Declarativo: Defines qué quieres, no cómo conseguirlo
- Versionable: El código IaC se almacena en Git
- Idempotente: Ejecutas múltiples veces con el mismo resultado
- Plan de ejecución: Previsualizas cambios antes de aplicarlos
- State management: Mantiene seguimiento del estado de tu infraestructura
En 2025, el provider azurerm tiene más de 1.8 mil millones de descargas, siendo el segundo más popular después de AWS. El 90% de empresas enterprise usan 3 o más cloud providers simultáneamente, y esta solución IaC facilita la gestión multi-cloud.
¿Qué es Azure Kubernetes Service (AKS)?
Azure Kubernetes Service (AKS) es el servicio administrado de Kubernetes en Microsoft Azure que simplifica el despliegue, gestión y operaciones de clústeres Kubernetes. Microsoft se encarga del control plane (API server, scheduler, etcd), mientras tú gestionas los nodos worker.
Características principales de AKS:
- Control plane gratuito: Solo pagas por los nodos worker (VMs)
- Actualizaciones automáticas: Microsoft gestiona parches de seguridad de Kubernetes
- Escalado automático: Cluster autoscaler y horizontal pod autoscaler integrados
- Integración Azure: Azure Active Directory, Azure Monitor, Azure Policy
- Networking avanzado: Azure CNI, Kubenet, load balancers integrados
- Node pools múltiples: Diferentes tipos de VMs para workloads especializados
- Windows y Linux: Soporte para contenedores Windows y Linux
Terraform Azure AKS: Fundamentos
Cuando utilizas Terraform Azure AKS, trabajas principalmente con dos recursos fundamentales del provider azurerm:
azurerm_kubernetes_cluster: Crea el clúster AKS con su configuración baseazurerm_kubernetes_cluster_node_pool: Añade node pools adicionales para workloads específicos
El flujo de trabajo típico incluye:
- Configurar provider azurerm: Autenticación y versión
- Crear Resource Group: Contenedor lógico de recursos
- Definir networking: VNet, subnets, NSG (opcional pero recomendado)
- Provisionar clúster AKS: Con node pool por defecto
- Configurar node pools adicionales: Para diferentes cargas de trabajo
- Obtener credenciales: Para kubectl mediante outputs
Requisitos Previos
Antes de comenzar con Terraform Azure AKS, necesitas:
- Terraform instalado: Versión 1.5 o superior (verifica con
terraform version) - Azure CLI: Para autenticación (
az login) - Suscripción Azure activa: Con permisos para crear recursos AKS
- kubectl instalado: Para interactuar con el clúster
- Service Principal o Managed Identity: Para autenticación del clúster
- Editor de código: VS Code con extensión HashiCorp Terraform recomendada
Para configurar Azure CLI:
# Autenticarse en Azure
az login
# Verificar suscripción activa
az account show
# Cambiar suscripción si es necesario
az account set --subscription "NOMBRE_O_ID"
# Crear service principal para Terraform
az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/SUBSCRIPTION_ID"
Guarda las credenciales del service principal (appId, password, tenant) para usarlas en tu configuración.
Configurar Provider AzureRM para Terraform Azure AKS
El primer paso es configurar el provider azurerm. En 2025, la versión más reciente es la 4.54.0, parte de la serie 4.x que incluye mejoras de rendimiento y soporte para las últimas APIs de Azure.
Crea un archivo versions.tf:
terraform {
required_version = ">= 1.5.0"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
}
}
provider "azurerm" {
features {
resource_group {
prevent_deletion_if_contains_resources = false
}
key_vault {
purge_soft_delete_on_destroy = true
recover_soft_deleted_key_vaults = true
}
}
# Opcional: usar service principal
# client_id = var.client_id
# client_secret = var.client_secret
# tenant_id = var.tenant_id
# subscription_id = var.subscription_id
}
El bloque features es obligatorio y permite personalizar comportamientos del provider. Si trabajas con secretos y certificados, también puedes integrar Vaultwarden como gestor de contraseñas seguro.
Crear Clúster Terraform Azure AKS Básico
Vamos a crear un clúster AKS funcional paso a paso siguiendo las mejores prácticas.
Paso 1: Definir Variables
Crea variables.tf:
variable "resource_group_name" {
description = "Nombre del Resource Group"
type = string
default = "rg-aks-terraform"
}
variable "location" {
description = "Región de Azure"
type = string
default = "East US"
}
variable "cluster_name" {
description = "Nombre del clúster AKS"
type = string
default = "aks-cluster-terraform"
}
variable "kubernetes_version" {
description = "Versión de Kubernetes"
type = string
default = "1.28.3"
}
variable "node_count" {
description = "Número de nodos en el default node pool"
type = number
default = 2
}
variable "vm_size" {
description = "Tamaño de VM para nodos"
type = string
default = "Standard_D2s_v3"
}
variable "environment" {
description = "Entorno (dev, staging, prod)"
type = string
default = "production"
}
Paso 2: Resource Group
En main.tf, comienza con el resource group:
resource "azurerm_resource_group" "aks" {
name = var.resource_group_name
location = var.location
tags = {
Environment = var.environment
ManagedBy = "Terraform"
Project = "AKS-Demo"
}
}
Paso 3: Clúster AKS Básico
Ahora el recurso principal para Terraform Azure AKS:
resource "azurerm_kubernetes_cluster" "aks" {
name = var.cluster_name
location = azurerm_resource_group.aks.location
resource_group_name = azurerm_resource_group.aks.name
dns_prefix = var.cluster_name
kubernetes_version = var.kubernetes_version
# Node pool por defecto (system)
default_node_pool {
name = "system"
node_count = var.node_count
vm_size = var.vm_size
os_disk_size_gb = 30
os_disk_type = "Managed"
type = "VirtualMachineScaleSets"
enable_auto_scaling = true
min_count = 2
max_count = 5
# Etiquetas para nodos
node_labels = {
"nodepool-type" = "system"
"environment" = var.environment
"nodepoolos" = "linux"
}
tags = {
"nodepool-type" = "system"
}
}
# Managed Identity (recomendado sobre service principal)
identity {
type = "SystemAssigned"
}
# Configuración de red (kubenet básico)
network_profile {
network_plugin = "kubenet"
load_balancer_sku = "standard"
network_policy = "calico"
}
# Azure AD Integration (RBAC)
azure_active_directory_role_based_access_control {
managed = true
azure_rbac_enabled = true
}
tags = {
Environment = var.environment
ManagedBy = "Terraform"
}
}
Paso 4: Outputs
Crea outputs.tf para obtener información del clúster:
output "resource_group_name" {
description = "Nombre del Resource Group"
value = azurerm_resource_group.aks.name
}
output "kubernetes_cluster_name" {
description = "Nombre del clúster AKS"
value = azurerm_kubernetes_cluster.aks.name
}
output "cluster_id" {
description = "ID del clúster AKS"
value = azurerm_kubernetes_cluster.aks.id
}
output "kube_config" {
description = "Configuración kubectl (sensible)"
value = azurerm_kubernetes_cluster.aks.kube_config_raw
sensitive = true
}
output "client_certificate" {
description = "Certificado cliente"
value = azurerm_kubernetes_cluster.aks.kube_config[0].client_certificate
sensitive = true
}
output "host" {
description = "Endpoint del API server"
value = azurerm_kubernetes_cluster.aks.kube_config[0].host
}
output "get_credentials_command" {
description = "Comando para obtener credenciales kubectl"
value = "az aks get-credentials --resource-group ${azurerm_resource_group.aks.name} --name ${azurerm_kubernetes_cluster.aks.name}"
}
Paso 5: Desplegar la Infraestructura
# Inicializar Terraform
terraform init
# Validar sintaxis
terraform validate
# Formatear código
terraform fmt
# Previsualizar cambios
terraform plan
# Aplicar cambios (tiempo estimado: 5-10 minutos)
terraform apply
# Confirmar con 'yes'
El despliegue del clúster tarda aproximadamente 5-10 minutos. Una vez completado, configura kubectl:
# Obtener credenciales
az aks get-credentials --resource-group rg-aks-terraform --name aks-cluster-terraform
# Verificar conectividad
kubectl cluster-info
# Ver nodos
kubectl get nodes
# Ver namespaces
kubectl get namespaces
Node Pools Avanzados en Terraform Azure AKS
Los node pools permiten tener diferentes tipos de VMs para workloads específicos. Por ejemplo, un pool para aplicaciones generales y otro con GPUs para machine learning.
User Node Pool para Aplicaciones
resource "azurerm_kubernetes_cluster_node_pool" "user" {
name = "userpool"
kubernetes_cluster_id = azurerm_kubernetes_cluster.aks.id
vm_size = "Standard_D4s_v3"
# Auto-scaling
enable_auto_scaling = true
min_count = 1
max_count = 10
node_count = 3
# Disk
os_disk_size_gb = 100
os_disk_type = "Managed"
# Taints para dedicar a workloads específicos
node_taints = [
"workload=user:NoSchedule"
]
node_labels = {
"nodepool-type" = "user"
"environment" = var.environment
"workload" = "applications"
}
tags = {
"nodepool-type" = "user"
"purpose" = "applications"
}
}
Node Pool con Spot Instances (Ahorro de Costos)
resource "azurerm_kubernetes_cluster_node_pool" "spot" {
name = "spot"
kubernetes_cluster_id = azurerm_kubernetes_cluster.aks.id
vm_size = "Standard_D2s_v3"
# Spot instances para cargas interrumpibles
priority = "Spot"
eviction_policy = "Delete"
spot_max_price = -1 # Precio máximo del mercado
enable_auto_scaling = true
min_count = 0
max_count = 5
# Taint para que solo pods tolerantes se ejecuten aquí
node_taints = [
"kubernetes.azure.com/scalesetpriority=spot:NoSchedule"
]
node_labels = {
"kubernetes.azure.com/scalesetpriority" = "spot"
"nodepool-type" = "spot"
}
}
Los Spot Instances pueden ahorrar hasta 80% en costos para cargas de trabajo tolerantes a interrupciones como batch processing o CI/CD.
Networking Avanzado con Terraform Azure AKS
Para entornos de producción, Azure CNI con networking personalizado es recomendado.
VNet y Subnets Dedicadas
resource "azurerm_virtual_network" "aks" {
name = "vnet-aks-terraform"
location = azurerm_resource_group.aks.location
resource_group_name = azurerm_resource_group.aks.name
address_space = ["10.1.0.0/16"]
tags = {
Environment = var.environment
}
}
resource "azurerm_subnet" "aks_nodes" {
name = "subnet-aks-nodes"
resource_group_name = azurerm_resource_group.aks.name
virtual_network_name = azurerm_virtual_network.aks.name
address_prefixes = ["10.1.0.0/20"]
}
resource "azurerm_subnet" "aks_pods" {
name = "subnet-aks-pods"
resource_group_name = azurerm_resource_group.aks.name
virtual_network_name = azurerm_virtual_network.aks.name
address_prefixes = ["10.1.16.0/20"]
}
Clúster AKS con Azure CNI
resource "azurerm_kubernetes_cluster" "aks_advanced" {
name = var.cluster_name
location = azurerm_resource_group.aks.location
resource_group_name = azurerm_resource_group.aks.name
dns_prefix = var.cluster_name
kubernetes_version = var.kubernetes_version
default_node_pool {
name = "system"
node_count = var.node_count
vm_size = var.vm_size
vnet_subnet_id = azurerm_subnet.aks_nodes.id
enable_auto_scaling = true
min_count = 2
max_count = 5
# Pod subnet para Azure CNI Overlay
pod_subnet_id = azurerm_subnet.aks_pods.id
}
identity {
type = "SystemAssigned"
}
network_profile {
network_plugin = "azure"
network_policy = "azure"
load_balancer_sku = "standard"
# Rangos de IPs
service_cidr = "10.2.0.0/16"
dns_service_ip = "10.2.0.10"
}
# Integración con Azure Monitor
oms_agent {
log_analytics_workspace_id = azurerm_log_analytics_workspace.aks.id
}
}
Monitoreo y Observabilidad
Integra Azure Monitor para observabilidad completa:
resource "azurerm_log_analytics_workspace" "aks" {
name = "log-aks-terraform"
location = azurerm_resource_group.aks.location
resource_group_name = azurerm_resource_group.aks.name
sku = "PerGB2018"
retention_in_days = 30
tags = {
Environment = var.environment
}
}
resource "azurerm_log_analytics_solution" "aks" {
solution_name = "ContainerInsights"
location = azurerm_resource_group.aks.location
resource_group_name = azurerm_resource_group.aks.name
workspace_resource_id = azurerm_log_analytics_workspace.aks.id
workspace_name = azurerm_log_analytics_workspace.aks.name
plan {
publisher = "Microsoft"
product = "OMSGallery/ContainerInsights"
}
}
Para monitoreo externo de disponibilidad de servicios, también puedes complementar con Uptime Kuma como herramienta de monitoreo adicional.
Seguridad en Terraform Azure AKS
Network Security Group (NSG)
resource "azurerm_network_security_group" "aks" {
name = "nsg-aks-terraform"
location = azurerm_resource_group.aks.location
resource_group_name = azurerm_resource_group.aks.name
security_rule {
name = "AllowKubernetesAPI"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "443"
source_address_prefix = "YOUR_IP/32" # Restringir a tu IP
destination_address_prefix = "*"
}
}
resource "azurerm_subnet_network_security_group_association" "aks" {
subnet_id = azurerm_subnet.aks_nodes.id
network_security_group_id = azurerm_network_security_group.aks.id
}
Azure Policy para AKS
resource "azurerm_kubernetes_cluster" "aks_secure" {
# ... configuración anterior ...
# Azure Policy Add-on
azure_policy_enabled = true
# Pod Security Policy (deprecated en K8s 1.25+, usar Pod Security Standards)
# Usar Azure Policy en su lugar
# Key Vault Secrets Provider
key_vault_secrets_provider {
secret_rotation_enabled = true
secret_rotation_interval = "2m"
}
}
Private Cluster
Para máxima seguridad, habilita private cluster:
resource "azurerm_kubernetes_cluster" "aks_private" {
# ... configuración anterior ...
private_cluster_enabled = true
# Private DNS Zone personalizada (opcional)
private_dns_zone_id = azurerm_private_dns_zone.aks.id
}
Módulo Oficial de Terraform Azure AKS
Para simplificar, usa el módulo oficial de Azure:
module "aks" {
source = "Azure/aks/azurerm"
version = "~> 9.0"
resource_group_name = azurerm_resource_group.aks.name
location = azurerm_resource_group.aks.location
cluster_name = var.cluster_name
kubernetes_version = var.kubernetes_version
# Node pool por defecto
agents_count = 2
agents_size = "Standard_D2s_v3"
agents_pool_name = "system"
# Networking
network_plugin = "azure"
vnet_subnet_id = azurerm_subnet.aks_nodes.id
# Identity
identity_type = "SystemAssigned"
# RBAC
rbac_aad = true
rbac_aad_managed = true
rbac_aad_azure_rbac_enabled = true
# Monitoreo
log_analytics_workspace_enabled = true
tags = {
Environment = var.environment
ManagedBy = "Terraform"
}
}
Este módulo encapsula mejores prácticas y reduce significativamente el código boilerplate.
CI/CD con Terraform Azure AKS
Integra con Azure DevOps o GitHub Actions para despliegues automatizados:
GitHub Actions Workflow
name: 'Terraform AKS Deploy'
on:
push:
branches:
- main
pull_request:
jobs:
terraform:
runs-on: ubuntu-latest
env:
ARM_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
ARM_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
ARM_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.6.0
- name: Terraform Init
run: terraform init
- name: Terraform Plan
run: terraform plan -out=tfplan
- name: Terraform Apply
if: github.ref == 'refs/heads/main'
run: terraform apply -auto-approve tfplan
State Management Remoto
Para equipos, almacena el state en Azure Storage:
terraform {
backend "azurerm" {
resource_group_name = "rg-terraform-state"
storage_account_name = "sttfstateaks123" # Debe ser único globalmente
container_name = "tfstate"
key = "aks.terraform.tfstate"
# State locking automático con Azure Storage
}
}
Crear la infraestructura de backend:
# Crear resource group
az group create --name rg-terraform-state --location eastus
# Crear storage account
az storage account create \
--name sttfstateaks123 \
--resource-group rg-terraform-state \
--location eastus \
--sku Standard_LRS \
--encryption-services blob
# Crear container
az storage container create \
--name tfstate \
--account-name sttfstateaks123
Optimización de Costos
- Spot Instances: Ahorro de hasta 80% para workloads tolerantes a interrupciones
- Auto-scaling: Escala a cero nodos user cuando no hay carga
- Reserved Instances: Descuentos de hasta 72% con compromiso de 1-3 años
- Azure Hybrid Benefit: Usa licencias Windows/SQL existentes
- Tamaños VM apropiados: B-series para dev/test, D-series para producción
- Shut down dev clusters: Detén clústeres fuera de horario laboral
- Monitoreo de costos: Azure Cost Management + Alerts
Ejemplo de auto-scaling agresivo para dev:
default_node_pool {
name = "system"
enable_auto_scaling = true
min_count = 1 # Mínimo permitido para system pool
max_count = 3
vm_size = "Standard_B2s" # Burstable para dev
}
Troubleshooting Común
Error: «Insufficient Regional Quota»
Causa: Tu suscripción no tiene suficiente cuota de vCPUs en la región.
Solución:
# Ver cuotas actuales
az vm list-usage --location eastus --output table
# Solicitar aumento en Azure Portal: Support → New Support Request
Error: «AuthorizationFailed»
Causa: El service principal o managed identity no tiene permisos.
Solución:
# Asignar rol Contributor
az role assignment create \
--assignee APP_ID \
--role Contributor \
--scope /subscriptions/SUBSCRIPTION_ID
Error: «PodSubnetIDNotDelegated»
Causa: La subnet de pods requiere delegación específica.
Solución:
resource "azurerm_subnet" "aks_pods" {
name = "subnet-aks-pods"
resource_group_name = azurerm_resource_group.aks.name
virtual_network_name = azurerm_virtual_network.aks.name
address_prefixes = ["10.1.16.0/20"]
delegation {
name = "aks-delegation"
service_delegation {
name = "Microsoft.ContainerService/managedClusters"
actions = [
"Microsoft.Network/virtualNetworks/subnets/join/action"
]
}
}
}
Debugging con TF_LOG
export TF_LOG=DEBUG
export TF_LOG_PATH=./terraform-debug.log
terraform apply
FAQ sobre Terraform Azure AKS
¿Cuánto cuesta un clúster AKS con Terraform?
El control plane de AKS es gratuito. Solo pagas por las VMs de los nodos. Un clúster básico con 2 nodos Standard_D2s_v3 (2 vCPU, 8GB RAM) cuesta aproximadamente $140/mes en región East US. Los Spot Instances pueden reducir esto hasta $28/mes.
¿Terraform Azure AKS vs Azure Portal vs Azure CLI?
El código de esta herramienta IaC es versionable, reproducible, y permite peer reviews. Azure Portal es útil para exploración inicial, pero no escala para múltiples entornos. Azure CLI es scriptable pero imperativo y no mantiene state. Para producción, siempre usa Infrastructure as Code.
¿Cómo actualizo la versión de Kubernetes?
Cambia kubernetes_version en tu configuración y ejecuta terraform apply. AKS realizará un rolling update sin downtime. Siempre verifica versiones soportadas con az aks get-versions --location eastus.
¿Puedo usar Terraform para desplegar aplicaciones en AKS?
Sí, usando el provider de Kubernetes puedes gestionar deployments, services, configmaps, etc. Sin embargo, para aplicaciones complejas, considera Helm o ArgoCD para GitOps. Si trabajas con aplicaciones de automatización del hogar, también puedes integrar Home Assistant en tu cluster.
¿Debo usar kubenet o Azure CNI?
Kubenet es más simple y consume menos IPs (NAT para pods). Azure CNI asigna IPs de la VNet directamente a pods, necesario para integración con Azure Firewall, Private Link, o comunicación directa pod-to-VM. Para producción enterprise, Azure CNI es recomendado.
Conclusión
Dominar Terraform Azure AKS te permite gestionar clústeres Kubernetes en Azure de forma automatizada, escalable y reproducible. Con Infrastructure as Code, mantienes consistencia entre entornos, facilitas colaboración mediante Git, y reduces errores humanos en configuraciones complejas.
Los próximos pasos recomendados incluyen:
- Implementar Helm provider para gestionar aplicaciones
- Configurar Application Gateway Ingress Controller
- Integrar con Azure Key Vault para secretos
- Implementar GitOps con ArgoCD o Flux
- Configurar disaster recovery con Azure Backup para AKS
Recuerda siempre ejecutar terraform plan antes de apply, usar backends remotos para state management, y documentar tus decisiones arquitectónicas. Para proyectos de IA local que puedan ejecutarse en tu cluster, también considera explorar Ollama con Docker Compose.
Recursos Adicionales
- Terraform Registry – azurerm_kubernetes_cluster
- Azure AKS Terraform Module Official
- Microsoft Azure AKS Documentation
- HashiCorp AKS Tutorial
- GitHub – AzureRM Provider
- AzureRM Provider Version History
