Ansible o Terraform: ¿cuál necesitas?
Terraform crea el VPS y Ansible lo configura. Compara sus funciones, el traspaso con comandos, los problemas de provisioners y cuándo basta con Ansible.
Ansible frente a Terraform en una frase
Ansible frente a Terraform no es una elección entre dos herramientas que hacen el mismo trabajo. Terraform declara qué infraestructura existe: servidores, discos, redes y registros DNS. Ansible declara qué debe ser cierto dentro de una máquina que ya existe: paquetes, usuarios, archivos de configuración y servicios en ejecución. Terraform crea el VPS. Ansible convierte ese VPS en un servidor web.
Ambos son declarativos y los dos se consideran infraestructura como código (IaC). La diferencia real es lo que recuerdan. Terraform escribe un archivo de estado que asigna cada recurso del código a un objeto real creado mediante una API. Así puede determinar que eliminar cinco líneas implica destruir un servidor. Ansible no conserva información entre ejecuciones. Se conecta mediante SSH, inspecciona la máquina y cambia sólo lo que todavía no coincide con el playbook.
Esa única diferencia explica el resto de esta guía, incluido el motivo por el que combinar ambos trabajos en una sola herramienta suele causar problemas.
Qué hace realmente Terraform
Terraform se comunica con una API mediante un complemento de provider. La página del registro de tu provider define los tipos de recursos que puedes escribir. Por eso, un servidor en un host y un servidor en otro host tienen nombres de recursos distintos y argumentos diferentes.
resource "cloud_server" "web" {
name = "web1"
image = "ubuntu-24.04"
type = "small"
}
output "web_ip" {
value = cloud_server.web.ipv4_address
}Sustituye cloud_server por el tipo de recurso que documenta tu provider. El bloque output es la parte importante de esta guía, porque define cómo sale la dirección de Terraform.
terraform init
terraform fmt -check
terraform validate
terraform plan -out=tfplan
terraform apply tfplanterraform init descarga el provider y escribe un archivo de bloqueo. terraform plan muestra la diferencia entre tu código y el archivo de estado. Termina con una línea como Plan: 1 to add, 0 to change, 0 to destroy.. Lee esa línea siempre. Algunos argumentos no se pueden cambiar en el mismo recurso. El plan lo indica con # forces replacement junto al atributo, seguido de 1 to add, 0 to change, 1 to destroy. Aplicar ese plan elimina el servidor y crea uno nuevo y vacío. Así es como se pierden datos que se creían protegidos.
Guardar el plan en un archivo y aplicar el archivo, en lugar de ejecutar un terraform apply sin más, garantiza que se ejecuta lo que revisaste. Entre ambos comandos, otra persona podría haber cambiado la infraestructura.
terraform.tfstate contiene el estado. Si lo pierdes, Terraform deja de saber que esos servidores te pertenecen. La siguiente aplicación intentará crear duplicados. Guárdalo en un backend remoto en cuanto más de una persona ejecute los comandos. Si dos personas aplican cambios al mismo tiempo, se produce esto:
Error: Error acquiring the state lockOpenTofu es un fork de Terraform con los mismos comandos y el mismo formato de archivo. En julio de 2026, todo lo incluido en esta guía funciona si escribes tofu en lugar de terraform.
Qué hace realmente Ansible
Ansible no necesita un agente ni una API. Abre una conexión SSH, copia un módulo pequeño de Python al destino, lo ejecuta y lo elimina. Todo lo que pueda alcanzar mediante SSH y una contraseña de sudo, Ansible puede configurarlo.
- name: Base web server
hosts: web
become: true
tasks:
- name: Install nginx
ansible.builtin.apt:
name: nginx
state: present
update_cache: true
- name: Ensure nginx is running at boot
ansible.builtin.service:
name: nginx
state: started
enabled: trueansible -i inventory.ini web -m ansible.builtin.ping
ansible-playbook -i inventory.ini site.yml --check --diff
ansible-playbook -i inventory.ini site.ymlEl módulo ping comprueba SSH, Python y sudo antes de empezar a depurar un playbook. Un resultado correcto es web1 | SUCCESS => {"ping": "pong"}. La ejecución de --check --diff es lo más parecido a un plan en Ansible: informa de los cambios que realizaría sin aplicarlos, aunque las tareas que dependen de tareas anteriores pueden informar incorrectamente en modo de comprobación, porque el cambio anterior nunca llegó a realizarse.
Cada ejecución termina con un resumen como ok=6 changed=2 unreachable=0 failed=0. Ejecute el mismo playbook dos veces. La segunda ejecución debería informar changed=0. Una tarea que informa de cambios en cada ejecución no es idempotente. Normalmente es una tarea command o shell que debería haberse implementado con un módulo real. Si este tema es nuevo para usted, empiece con su primer playbook de Ansible en un VPS individual y amplíelo a partir de ahí.
Dónde se solapan las dos herramientas y dónde entran en conflicto
Terraform puede ejecutar comandos en un servidor nuevo con el provisioner remote-exec. La documentación de HashiCorp recomienda usar los provisioners sólo como último recurso. Hay buenos motivos para ello.
Un provisioner se ejecuta únicamente cuando se crea el recurso. Si edita el script, no ocurre nada en el servidor existente, porque desde el punto de vista de Terraform el recurso ya coincide con el código. Los pasos del provisioner nunca aparecen en terraform plan, por lo que la revisión no muestra ninguna señal de ellos. Si el script falla, Terraform marca el recurso como tainted, y el siguiente apply destruye y vuelve a crear un servidor que probablemente funcionaba correctamente.
El fallo también se produce en un momento inadecuado. El provider informa de que el servidor se ha creado en cuanto la API lo indica, aunque el sistema operativo todavía se está iniciando y sshd aún no está escuchando.
Error: remote-exec provisioner error
timeout - last error: dial tcp 203.0.113.10:22: connect: connection refusedAnsible presenta la tentación opuesta. Los módulos para la nube pueden crear servidores, y esto funciona con un número reducido de máquinas. Pero se pierden el grafo de dependencias y el archivo de estado. Ansible crea recursos sin problemas, pero si elimina la tarea del playbook, el recurso sigue ejecutándose y generando costes, porque nada registró que alguna vez le perteneciera.
La regla resultante es la siguiente: deje que Terraform gestione los objetos que una API crea y elimina, y que Ansible gestione todo lo que se encuentra dentro de un sistema operativo ya iniciado.
La transferencia, en la práctica
La transferencia es un límite, no una integración. Terraform termina, publica una dirección y se detiene. Ansible comienza a partir de esa dirección.
terraform apply -auto-approve
terraform output -raw web_ip
printf '[web]\n%s ansible_user=root\n' "$(terraform output -raw web_ip)" > inventory.ini
ansible -i inventory.ini web -m ansible.builtin.ping
ansible-playbook -i inventory.ini site.ymlterraform output -raw imprime un valor sin comillas ni envoltorio JSON, que es lo que se necesita dentro de una sustitución de shell. Para varios servidores, use terraform output -json y genere el inventario a partir de ese resultado, porque -raw sólo admite una cadena, un número o un valor booleano.
Conviene conservar el paso ping entre ambas herramientas. Permite distinguir entre «Terraform proporcionó la dirección incorrecta» y «mi playbook tiene un error». Ambos problemas parecen idénticos cuando el playbook es lo primero que entra en contacto con el servidor nuevo.
Lectura del estado de Terraform como inventario de Ansible
Si prefiere no escribir ningún archivo de inventario, la colección cloud.terraform lee el estado directamente.
ansible-galaxy collection install cloud.terraformEscriba terraform.yml junto al playbook:
plugin: cloud.terraform.terraform_provider
project_path: /home/deploy/infraansible-inventory -i terraform.yml --graph
ansible-playbook -i terraform.yml site.ymlDebe conocer dos aspectos antes de depender de este método. El plugin ejecuta terraform show contra project_path, por lo que ese directorio ya debe estar inicializado. De lo contrario, el plugin falla. Tampoco crea hosts a partir de los recursos de sus servidores: lee los recursos ansible_host y ansible_group, que debe declarar en el código de Terraform mediante el proveedor de Ansible. No aparece nada en ansible-inventory --graph hasta que los añada.
Un archivo de inventario generado sin más es más fácil de depurar y funciona con cualquier proveedor. El plugin resulta útil cuando el inventario supera unas pocas máquinas y la edición manual empieza a introducir errores tipográficos. Ese es también el punto en el que administrar varios servidores Linux desde una máquina de control se convierte en un flujo de trabajo real y no sólo en un hábito.
¿Realmente necesita Terraform?
La mayoría de los lectores no lo necesitan, al menos todavía. Terraform compensa su coste cuando crear y destruir infraestructura es una tarea recurrente. Si ha contratado un VPS mediante un panel de control y piensa conservarlo durante dos años, Terraform describe una tarea que ocurre una sola vez y añade un archivo de estado que no debe perder.
Use Terraform cuando reconstruya entornos con frecuencia, cuando staging deba coincidir exactamente con producción, cuando varias personas cambien la infraestructura y quiera revisar un plan antes de que se elimine cualquier recurso, o cuando lo que administra incluya algo más que servidores, como registros DNS, balanceadores de carga y reglas de firewall gestionadas mediante la API de un proveedor.
Use sólo Ansible cuando los servidores tengan una vida útil larga y sean pocos, y cuando la pregunta diaria sea «¿está este servidor configurado correctamente?» en lugar de «¿existe este servidor?». Un único playbook que proteja un servidor nuevo cubre lo mismo que los primeros diez minutos en un VPS nuevo, con la ventaja de que se ejecuta del mismo modo en el siguiente servidor.
El orden de aprendizaje se deduce de esto. Ansible compensa desde el primer servidor que administre. Terraform compensa a partir del tercer entorno que reconstruya.
Qué falla en la transferencia
El servidor no está listo. Terraform finaliza correctamente, pero Ansible falla de inmediato.
fatal: [web1]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 203.0.113.10 port 22: Connection refused", "unreachable": true}La API devolvió una dirección antes de que sshd empezara a escuchar. Espere a que el puerto esté disponible en lugar de añadir una espera fija. Ansible tiene ansible.builtin.wait_for_connection para esto; ejecútelo como primera tarea del play.
La clave del host cambió. Destruyó y volvió a crear el servidor, y el nuevo responde en la misma dirección con una clave nueva.
Host key verification failed.Elimine la entrada obsoleta con ssh-keygen -R 203.0.113.10. Esto ocurre constantemente cuando Terraform se encarga de reconstruir servidores. Por eso conviene evitar reconstrucciones frecuentes en máquinas que contienen datos.
Sudo falla. fatal: [web1]: FAILED! => {"msg": "Missing sudo password"} significa que become: true necesita una contraseña en ese host. Configure sudo sin contraseña para el usuario de despliegue o pase --ask-become-pass.
Terraform quiere destruir algo que no modificó. El plan muestra cambios que nunca escribió. Esto significa que la infraestructura real se desvió del código, normalmente porque alguien cambió una opción en el panel web del proveedor. Ejecute terraform plan -refresh-only para ver sólo esa diferencia y decida si el código o el recurso activo es incorrecto. Nunca aplique un plan destructivo que no pueda explicar línea por línea.
Ansible informa de cambios en cada ejecución. Una tarea shell sin una condición creates o when se ejecuta siempre. No es un problema meramente estético, porque ya no podrá usar changed=0 como señal de que el servidor está en el estado solicitado.
FAQ
¿Puede Terraform sustituir a Ansible?
No para la configuración dentro de un servidor. Terraform puede ejecutar scripts con el provisioner remote-exec, pero estos sólo se ejecutan al crear el recurso, nunca aparecen en terraform plan y marcan el recurso como afectado cuando fallan. Esto programa su destrucción y reconstrucción en la siguiente ejecución de apply. Terraform no tiene un equivalente de un módulo que compruebe si nginx ya está instalado y no haga nada si lo está. Use Terraform para crear la máquina y después transfiera la configuración.
¿Puede Ansible sustituir a Terraform?
Para un número reducido de servidores de larga duración, sí. Ansible tiene módulos para la nube que crean servidores. Si solicita dos instancias VPS y las conserva, eso es suficiente. Lo que pierde es el archivo de estado y el grafo de dependencias. Si elimina una tarea del playbook, el recurso sigue ejecutándose y generando costes porque Ansible nunca registró que lo había creado. Terraform habría planificado su destrucción.
¿Cuál debería aprender primero?
Ansible, si actualmente administra servidores. Se amortiza desde el primer equipo, sólo necesita SSH y sus conocimientos se aplican a un servidor que haya solicitado manualmente. Terraform se amortiza más adelante, cuando reconstruye entornos de forma repetida o administra recursos del proveedor que no son servidores, como registros DNS y reglas del firewall.
¿Cómo paso la IP del servidor nuevo de Terraform a Ansible?
Declare un output en el código de Terraform y léalo después de apply. terraform output -raw web_ip muestra el valor sin formato para una sustitución de shell, y terraform output -json proporciona todas las salidas a la vez cuando hay varios hosts. Escriba ese valor en un archivo de inventario, o instale la colección cloud.terraform y apunte ansible-inventory -i terraform.yml --graph al directorio del proyecto.
¿Por qué falla mi playbook justo después de que Terraform termina?
El proveedor informa de que el servidor se ha creado en cuanto su API lo indica, mientras el sistema operativo todavía está arrancando. Por eso SSH rechaza las conexiones durante los primeros segundos. El error es UNREACHABLE! con Connection refused. Haga que ansible.builtin.wait_for_connection sea la primera tarea del play en lugar de adivinar una duración para sleep, porque el tiempo de arranque varía según la imagen y el plan.