|

Crear VM en Azure con Terraform: guía real 2026

En pocas palabras: Con Terraform y el provider azurerm se crea una VM Ubuntu 22.04 en Azure declarando 8 recursos: resource group, VNet, subnet, IP pública, NSG, NIC, asociación NSG-NIC y la VM Linux, ejecutando terraform init, plan y apply, sin usar el portal web.

Armar una VM en Azure a mano —portal, clics, formularios— es lento y además difícil de repetir. Un desarrollador documentó en dev.to, el 24 de septiembre de 2026, cómo levantar desde cero una infraestructura completa en Azure usando Terraform: resource group, red virtual, subred, IP pública, Network Security Group y una VM Ubuntu 22.04 con acceso SSH, incluyendo los errores reales que le aparecieron en el camino.

Terraform es una herramienta de infraestructura como código (IaC) de HashiCorp que permite declarar recursos de nube en archivos de configuración y desplegarlos con un comando. Para crear una VM en Azure con Terraform se usa el provider azurerm, que traduce ese código en llamadas reales contra la API de Microsoft Azure, sin pasar por el portal web ni configurar nada a mano.

En 30 segundos

  • El proyecto desplegó 8 recursos de Azure con Terraform: resource group, VNet, subnet, IP pública, NSG, NIC, asociación NSG-NIC y VM Linux, según el artículo original de dev.to.
  • El terraform apply final devolvió “Apply complete! Resources: 5 added, 0 changed, 0 destroyed”, después de correr init, fmt, validate y plan.
  • La VM quedó con IP pública 4.221.66.140 e IP privada 10.0.1.4 dentro de la subred flackern-subnet (10.0.1.0/24).
  • El primer intento de SSH falló con “Permission denied (publickey)” antes de lograr la conexión con la clave correcta.
  • Otro error típico: poner el bloque security_rule fuera del recurso azurerm_network_security_group, lo que dispara “Unsupported block type”.

¿Qué recursos de Azure hay que declarar en Terraform para una infraestructura segura?

Hacen falta ocho recursos conectados entre sí: resource group, VNet, subnet, IP pública, NSG, NIC, la asociación NSG-NIC y la VM Linux. Cada uno depende del anterior, así que Terraform los crea en el orden correcto según las referencias que declares en el código.

En el proyecto documentado, la arquitectura quedó así:

  • Resource Group (flackern-rg): el contenedor lógico que agrupa todos los demás recursos en una misma ubicación.
  • Virtual Network (flackern-vnet): red privada con el rango 10.0.0.0/16.
  • Subnet (flackern-subnet): subred dentro de esa VNet, con el rango 10.0.1.0/24.
  • Public IP (flackern-ip): dirección pública estática para conectarse desde internet.
  • Network Security Group (flackern-nsg): el firewall que controla qué tráfico entra a la VM.
  • Network Interface (flackern-nic): conecta la VM a la subred y a la IP pública.
  • Asociación NSG-NIC: vincula explícitamente el firewall con la interfaz de red.
  • VM Linux (flackern-vm): Ubuntu 22.04 LTS con autenticación por clave SSH.

El tráfico fluye siempre en la misma dirección: internet, IP pública, NIC, NSG y recién ahí la VM. Si un solo eslabón de esa cadena falla, ya sea porque la IP no está asociada al NIC o porque el NSG no dejó el puerto abierto, la conexión SSH no llega nunca. Relacionado: conectarte por SSH a tu VM de Azure.

¿Cómo se configura el bloque terraform y el provider azurerm antes de crear recursos?

Todo Terraform arranca con un bloque terraform que fija qué provider vas a usar y con un bloque provider "azurerm" que autentica contra tu suscripción. Sin eso, ningún recurso de Azure se puede crear.

En el proyecto de dev.to, el código quedó así:

  • required_providers: declara azurerm con la versión ~> 5.5.0 del proveedor de HashiCorp.
  • provider “azurerm”: incluye el bloque vacío features {} (obligatorio aunque no configures nada adentro) y el subscription_id de la cuenta de Azure.

Ojo con esto: la guía oficial de Microsoft Learn usa una versión distinta del provider, ~>3.0, y suma un provider adicional (azapi) para generar las claves SSH desde el mismo Terraform. No es un error, son dos formas válidas de resolver lo mismo: una genera las claves con Azure, la otra las genera vos mismo con ssh-keygen antes de correr terraform apply. Si estás por copiar código de internet, fijate primero qué versión de provider trae, porque mezclar sintaxis de versiones distintas es una fuente clásica de errores raros.

¿Cómo se configura un Network Security Group para permitir SSH en Azure?

Se declara un recurso azurerm_network_security_group con un bloque security_rule adentro, que abre el puerto TCP 22 en sentido entrante. Sin esa regla explícita, ninguna conexión SSH llega a la VM, aunque tenga IP pública asignada.

La regla concreta usada en el proyecto (nombrada Allow-SSH) tiene priority = 100, direction = "Inbound", access = "Allow", protocol = "Tcp" y destination_port_range = "22". Crear el NSG no alcanza. Hace falta un segundo recurso, azurerm_network_interface_security_group_association, que vincula ese NSG con el NIC de la VM. Si te salteás este paso, el firewall existe pero no protege (ni permite) nada, porque no está conectado a ninguna interfaz de red.

¿Cómo se genera un par de claves SSH para autenticarse en una VM Linux de Azure?

Se genera con ssh-keygen -t ed25519 -C "terraform-azure-vm", un comando que crea dos archivos: uno privado y uno público. El archivo privado nunca se comparte ni se sube a ningún repositorio; el público es el que va dentro del código Terraform. Te puede servir nuestra cobertura de certificaciones de Azure según tu rol.

En el caso documentado, el comando produjo terraform-azure (clave privada, se queda en la máquina local) y terraform-azure.pub (clave pública, se referencia en el recurso de la VM). Dentro del bloque azurerm_linux_virtual_machine, esa clave pública se carga así:

  • admin_username: el usuario que vas a usar para conectarte, en este caso azureuser.
  • admin_ssh_key.public_key: apunta al archivo con file("~/.ssh/terraform-azure.pub"), así Terraform lee el contenido y lo instala en la VM al crearla.

¿Qué pasos sigue el flujo terraform init, validate, plan y apply para crear una VM en Azure con Terraform?

El flujo tiene cuatro comandos en orden fijo: init descarga el provider, validate chequea la sintaxis, plan muestra qué se va a crear y apply lo ejecuta de verdad. Ninguno de estos pasos se puede saltear sin arriesgarse a un error de configuración no detectado.

En el proyecto real, la secuencia fue terraform init, después terraform fmt para ordenar el formato, terraform validate (que devolvió “Success! The configuration is valid”), terraform plan para revisar los cambios propuestos y finalmente terraform apply. El resultado fue “Apply complete! Resources: 5 added, 0 changed, 0 destroyed”, y el output vm_public_ip devolvió la dirección 4.221.66.140 lista para usar. Ese output es la forma más práctica de no tener que ir a buscar la IP a mano en el portal cada vez que recreás la infraestructura.

¿Qué errores comunes aparecen al desplegar esta infraestructura y cómo se solucionan?

Los tres errores más frecuentes son de sintaxis, de autenticación SSH y de datos mal escritos en la configuración. Ninguno rompe la cuenta de Azure, pero todos frenan el despliegue hasta corregirlos. Para más detalles técnicos, mirá diferencias entre Microsoft y GitHub.

  • “Unsupported block type” en security_rule: pasa cuando el bloque security_rule queda fuera del recurso azurerm_network_security_group, como standalone. Terraform es jerárquico: ese bloque tiene que estar adentro de las llaves del NSG, no al lado.
  • “Permission denied (publickey)” al hacer SSH: en el primer intento documentado, la conexión falló así antes de lograrse. Las causas típicas son apuntar a la clave privada equivocada con -i, usar un usuario que no coincide con el admin_username declarado en la VM, o que la clave pública cargada en Terraform no sea la misma que estás usando localmente.
  • Locations mal escritas: en el código fuente, la región de Azure quedó tipeada como "south Africa North", con mayúsculas inconsistentes. Azure suele aceptarlo igual, pero es una fuente de confusión al revisar el código después.

¿Cómo se conecta por SSH a la VM una vez desplegada en Azure?

Se conecta con ssh azureuser@<ip_publica>, usando el usuario declarado en admin_username y la IP que devolvió el output de Terraform. La primera vez, SSH pide confirmar la huella digital del servidor.

En el caso documentado, apareció el mensaje “The authenticity of host ‘4.221.66.140’ can’t be established” y hubo que responder yes para agregar el host al archivo known_hosts local. Una vez adentro, el sistema mostró el mensaje de bienvenida de Ubuntu 22.04.5 LTS y el prompt quedó como azureuser@flackern-vm:~$. Ahí se ve la diferencia entre las dos direcciones: la IP pública (4.221.66.140) es la que usás para entrar desde afuera, mientras que la IP privada (10.0.1.4) es la que la VM reporta puertas adentro de la red virtual, dentro del rango 10.0.1.0/24 de la subred.

Recursos declarados: proyecto de dev.to vs. quickstart oficial de Microsoft

Ambos enfoques llegan al mismo resultado (una VM Linux accesible por SSH) pero con diferencias puntuales que conviene conocer antes de copiar código de cualquiera de los dos.

RecursoTipo de TerraformPropósito
Grupo de recursosazurerm_resource_groupContenedor lógico para todo lo demás
Red virtualazurerm_virtual_networkRed privada (10.0.0.0/16 en el proyecto de dev.to)
Subredazurerm_subnetSegmento dentro de la VNet (10.0.1.0/24)
IP públicaazurerm_public_ipAcceso desde internet a la VM
Grupo de seguridadazurerm_network_security_groupReglas de firewall, incluida la de SSH en puerto 22
Interfaz de redazurerm_network_interfaceConecta la VM a la subred y a la IP pública
Asociación NSG-NICazurerm_network_interface_security_group_associationAplica el firewall a la interfaz
Máquina virtualazurerm_linux_virtual_machineUbuntu 22.04 LTS con autenticación SSH
crear vm en azure con terraform diagrama explicativo

La diferencia grande está en cómo cada uno maneja las claves SSH. El proyecto de dev.to las genera afuera de Terraform, con ssh-keygen en Git Bash, y después las referencia con file(). El quickstart de Microsoft Learn usa el provider azapi para generarlas dentro del propio flujo de Terraform, con el recurso azapi_resource_action. Ninguna de las dos es “la correcta”: si ya tenés un flujo de claves SSH propio en tu equipo, el primer método es más simple; si querés que todo, incluidas las claves, quede versionado en un solo lugar, el segundo tiene más sentido. Lo explicamos a fondo en repos de GitHub comprometidos con malware.

¿Cuándo conviene este nivel de infraestructura y cuándo es demasiado?

Armar una VNet, un NSG y una VM desde cero con Terraform tiene sentido cuando necesitás control fino sobre la red, ya sea para levantar un ambiente de pruebas reproducible, un servidor con reglas de firewall específicas, o infraestructura que se vaya a recrear varias veces. Para un blog, un sitio institucional o una tienda chica, todo ese trabajo de red privada y SSH suele ser más de lo que hace falta: ahí un hosting administrado como donweb.com resuelve lo mismo sin tener que declarar ocho recursos y sin depender de que alguien del equipo sepa Terraform.

Dicho esto, si el proyecto ya vive en Azure porque hay otras cargas de trabajo ahí, aprender a declarar la infraestructura en código, en vez de repetir clics en el portal cada vez, ahorra tiempo real: la próxima VM se crea corriendo terraform apply de nuevo, no rehaciendo el proceso a mano.

Preguntas Frecuentes

¿Cómo se crea una VM en Azure usando Terraform?

Se declaran los recursos necesarios (resource group, red, subred, IP pública, NSG, NIC y la VM) en archivos .tf, y después se corre terraform init, terraform plan y terraform apply. Terraform lee esos archivos, calcula qué crear y ejecuta las llamadas contra la API de Azure en el orden correcto según las dependencias entre recursos.

¿Qué recursos de Azure hay que declarar en Terraform para una infraestructura segura?

Hacen falta ocho recursos: resource group, VNet, subnet, IP pública, NSG, NIC, la asociación NSG-NIC y la VM Linux. Faltar cualquiera de ellos, sobre todo la asociación NSG-NIC, deja la infraestructura sin protección de firewall aunque el resto esté bien configurado.

¿Cómo se configura un Network Security Group para permitir SSH en Azure?

Se agrega un bloque security_rule dentro del recurso azurerm_network_security_group, con protocol = "Tcp" y destination_port_range = "22". Ese NSG después tiene que asociarse explícitamente al NIC de la VM con el recurso azurerm_network_interface_security_group_association, o la regla no aplica.

¿Por qué da error “Permission denied (publickey)” al conectarse por SSH a una VM de Azure?

Ese error aparece cuando la clave privada que usás para conectarte no coincide con la clave pública que Terraform cargó en la VM, o cuando el usuario indicado en el comando SSH no es el mismo que el admin_username declarado en el recurso de la máquina virtual. Revisar la ruta al archivo de clave privada suele resolverlo.

¿Cómo se genera un par de claves SSH para autenticarse en una VM Linux de Azure?

Se genera con el comando ssh-keygen -t ed25519 -C "algún-comentario", que crea un archivo privado y uno con extensión .pub. El archivo .pub se referencia dentro del bloque admin_ssh_key del recurso azurerm_linux_virtual_machine, usando la función file() de Terraform.

Conclusión

Lo que muestra este caso es que declarar infraestructura en código no elimina los errores, los hace más visibles y más fáciles de repetir la corrección. El mismo error de sintaxis, el mismo “Permission denied (publickey)”, le va a pasar a cualquiera que arranque con Terraform en Azure, así que documentarlo tiene valor real más allá del proyecto puntual.

Si estás por hacer lo mismo, el orden importa: primero el bloque terraform y el provider, después los recursos de red, recién al final la VM y la clave SSH. Y antes de correr apply en un ambiente que te importa, corré terraform plan y leelo con atención, porque ahí es donde se detectan la mayoría de estos problemas antes de que cuesten tiempo real.

Fuentes

Te puede interesar...