SSD Nodes Learn Hosting plans →
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-08-27

Ansible o Terraform: quale ti serve davvero?

Terraform crea il VPS, Ansible lo configura: scopri la differenza reale, i comandi per il passaggio e quando ti basta usare solo Ansible.

Ansible e Terraform in una frase

Ansible e Terraform non sono due strumenti alternativi che svolgono lo stesso compito. Terraform dichiara quale infrastruttura deve esistere: server, dischi, reti, record DNS. Ansible dichiara quale deve essere lo stato interno di una macchina già esistente: pacchetti, utenti, file di configurazione, servizi in esecuzione. Terraform crea il VPS. Ansible trasforma quel VPS in un web server.

Entrambi usano un approccio dichiarativo e rientrano nella categoria infrastructure as code (IaC). La differenza sostanziale riguarda ciò che memorizzano. Terraform scrive un file di stato che associa ogni risorsa definita nel codice a un oggetto reale creato tramite un'API. In questo modo può determinare che l'eliminazione di cinque righe richiede la distruzione di un server. Ansible non conserva informazioni tra un'esecuzione e l'altra. Si connette tramite SSH, ispeziona la macchina e modifica soltanto ciò che non corrisponde già al playbook.

Questa sola differenza spiega il resto della guida, compreso il motivo per cui affidare i due compiti a un unico strumento causa problemi.

Cosa fa realmente Terraform

Terraform comunica con un'API tramite un plugin provider. La pagina del registry del provider definisce i tipi di risorsa che è possibile dichiarare. Di conseguenza, un server su un host e un server su un altro host sono risorse diverse, con nomi e argomenti differenti.

resource "cloud_server" "web" {
  name  = "web1"
  image = "ubuntu-24.04"
  type  = "small"
}

output "web_ip" {
  value = cloud_server.web.ipv4_address
}

Sostituisci cloud_server con il tipo di risorsa documentato dal provider. Il blocco output è la parte importante per questa guida, perché definisce come l'indirizzo esce da Terraform.

terraform init
terraform fmt -check
terraform validate
terraform plan -out=tfplan
terraform apply tfplan

terraform init scarica il provider e scrive un file di lock. terraform plan mostra la differenza tra il codice e il file di stato e termina con una riga simile a Plan: 1 to add, 0 to change, 0 to destroy.. Leggi sempre quella riga. Alcuni argomenti non possono essere modificati direttamente. Il piano lo indica con # forces replacement accanto all'attributo, seguito da 1 to add, 0 to change, 1 to destroy. L'applicazione di quel piano elimina il server e ne crea uno nuovo e vuoto. In questo modo si perdono dati che si ritenevano al sicuro.

Salvare il piano in un file e applicare il file, invece di eseguire un terraform apply senza argomenti, garantisce che venga eseguito esattamente ciò che è stato verificato. Tra i due comandi, un'altra persona potrebbe aver modificato l'infrastruttura.

terraform.tfstate contiene lo stato. Se lo perdi, Terraform non sa più che quei server appartengono alla configurazione. Al successivo apply tenta quindi di crearne dei duplicati. Non appena più di una persona esegue questi comandi, conserva lo stato in un backend remoto. Se due persone eseguono apply contemporaneamente, si verifica questo:

Error: Error acquiring the state lock

OpenTofu è un fork di Terraform che usa gli stessi comandi e lo stesso formato di file. A luglio 2026, tutto ciò che è descritto in questa guida funziona se digiti tofu invece di terraform.

Cosa fa realmente Ansible

Ansible non richiede agent né API. Apre una connessione SSH, copia un piccolo modulo Python sul sistema di destinazione, lo esegue e lo elimina. Tutto ciò che puoi raggiungere tramite SSH e una password sudo può essere configurato con Ansible.

- 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: true
ansible -i inventory.ini web -m ansible.builtin.ping
ansible-playbook -i inventory.ini site.yml --check --diff
ansible-playbook -i inventory.ini site.yml

Il modulo ping verifica SSH, Python e sudo prima che inizi il debug di un playbook. Un risultato corretto è web1 | SUCCESS => {"ping": "pong"}. L'esecuzione --check --diff è l'equivalente più vicino a un piano in Ansible: indica quali modifiche verrebbero applicate senza eseguirle. Tuttavia, le attività che dipendono da attività precedenti possono restituire risultati errati in check mode, perché la modifica precedente non è stata realmente applicata.

Ogni esecuzione termina con un riepilogo come ok=6 changed=2 unreachable=0 failed=0. Esegui lo stesso playbook 2 volte. La seconda esecuzione dovrebbe restituire changed=0. Un'attività che segnala changed a ogni esecuzione non è idempotente. Di solito è un'attività command o shell che avrebbe dovuto usare un modulo reale. Se questo argomento è nuovo per te, inizia da un primo playbook Ansible su un singolo VPS e sviluppalo da lì.

Dove i due strumenti si sovrappongono e dove entrano in conflitto

Terraform può eseguire comandi su un nuovo server con il provisioner remote-exec. La documentazione ufficiale di HashiCorp definisce i provisioner come soluzione di ultima istanza. Ci sono validi motivi.

Un provisioner viene eseguito solo quando la risorsa viene creata. Se modifichi lo script, sul server esistente non accade nulla, perché dal punto di vista di Terraform la risorsa corrisponde già al codice. I passaggi del provisioner non compaiono in terraform plan, quindi la revisione non mostra alcuna traccia. Se lo script non riesce, Terraform contrassegna la risorsa come tainted e il successivo apply elimina e ricrea un server che probabilmente funzionava correttamente.

Anche il momento dell'errore è problematico. Il provider segnala il server come creato nel momento in cui l'API lo comunica, mentre il sistema operativo è ancora in fase di avvio e sshd non è ancora in ascolto.

Error: remote-exec provisioner error
timeout - last error: dial tcp 203.0.113.10:22: connect: connection refused

Ansible presenta la tentazione opposta. I moduli cloud possono creare server e, per un numero limitato di macchine, questa soluzione funziona. In cambio, però, perdi il grafo delle dipendenze e il file di stato. Ansible crea una risorsa senza problemi, ma se elimini il task dal playbook la risorsa resta in esecuzione e continua a generare costi, perché nulla ha registrato che fosse stata creata da Ansible.

La regola che ne deriva è questa: lascia a Terraform la gestione degli oggetti che un'API crea ed elimina, e lascia ad Ansible la gestione di tutto ciò che si trova all'interno di un sistema operativo avviato.

Il passaggio di consegne, in pratica

Il passaggio di consegne è un confine, non un'integrazione. Terraform termina, pubblica un indirizzo e si arresta. Ansible parte da quell'indirizzo.

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.yml

terraform output -raw stampa un valore senza virgolette e senza contenitore JSON, esattamente nel formato necessario all'interno di una sostituzione della shell. Per più server, usa terraform output -json e crea l'inventory a partire da quel risultato, perché -raw gestisce una sola stringa, un solo numero o un solo valore booleano.

Vale la pena mantenere il passaggio ping tra i due strumenti. Consente di distinguere il caso in cui «Terraform ha fornito l'indirizzo errato» dal caso in cui «il playbook contiene un bug». Quando il playbook è il primo componente a interagire con il nuovo server, i due problemi appaiono identici.

Lettura dello stato Terraform come inventario Ansible

Se si preferisce non scrivere affatto un file di inventario, la raccolta cloud.terraform legge direttamente lo stato.

ansible-galaxy collection install cloud.terraform

Scrivere terraform.yml accanto al playbook:

plugin: cloud.terraform.terraform_provider
project_path: /home/deploy/infra
ansible-inventory -i terraform.yml --graph
ansible-playbook -i terraform.yml site.yml

Prima di affidarsi a questa soluzione, è necessario conoscere due aspetti. Il plugin esegue terraform show su project_path, quindi la directory deve essere già inizializzata; in caso contrario, il plugin non funziona. Inoltre, il plugin non crea automaticamente gli host dalle risorse server: legge le risorse ansible_host e ansible_group, dichiarate nel codice Terraform tramite il provider Ansible. In ansible-inventory --graph non compare nulla finché non vengono aggiunte.

Un file di inventario generato manualmente è più semplice da sottoporre a debug e funziona con qualsiasi provider. Il plugin diventa utile quando l'inventario supera alcune macchine e la modifica manuale inizia a causare errori di battitura. È lo stesso punto in cui gestire diversi server Linux da un'unica macchina di controllo diventa un flusso di lavoro effettivo, non soltanto un'abitudine.

Ti serve davvero Terraform?

La maggior parte delle persone che leggono questa guida non ne ha bisogno, almeno non ancora. Terraform giustifica il proprio costo quando la creazione e la rimozione dell’infrastruttura sono attività ricorrenti. Se hai ordinato un solo VPS tramite un pannello di controllo e prevedi di mantenerlo per due anni, Terraform descrive un’operazione eseguita una sola volta e aggiunge un file di stato che non devi perdere.

Scegli Terraform quando ricrei spesso gli ambienti, quando l’ambiente di staging deve corrispondere esattamente a quello di produzione, quando più persone modificano l’infrastruttura e vuoi un piano verificabile prima che venga eliminato qualsiasi elemento, oppure quando gestisci non solo server, ma anche record DNS, load balancer e regole firewall disponibili tramite l’API di un provider.

Continua a usare soltanto Ansible quando i server hanno un ciclo di vita lungo e sono pochi, e quando la domanda quotidiana è «questo server è configurato correttamente?» invece di «questo server esiste?». Un singolo playbook che mette in sicurezza un server appena creato copre lo stesso ambito di primi dieci minuti su un nuovo VPS, con il vantaggio di essere eseguito nello stesso modo sul server successivo.

L’ordine di apprendimento deriva da questo. Ansible ripaga già sul primo server che gestisci. Terraform ripaga dal terzo ambiente che ricrei.

Cosa si rompe nel passaggio di consegne

Il server non è pronto. Terraform termina correttamente, ma Ansible non riesce a partire.

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}

L'API ha restituito un indirizzo prima che sshd fosse in ascolto. Attendi che la porta sia disponibile invece di aggiungere un'attesa fissa. Ansible dispone di ansible.builtin.wait_for_connection proprio per questo: eseguilo come prima attività del play. Quando lo stesso playbook viene eseguito su un gruppo anziché su un singolo host appena creato, stabilisci in anticipo cosa deve accadere quando un host resta irraggiungibile, perché Ansible esclude quell'host dal resto dell'esecuzione e la riga di riepilogo è l'unico punto in cui lo segnala.

La chiave dell'host è cambiata. Hai eliminato e ricreato il server, e il nuovo host risponde allo stesso indirizzo con una nuova chiave.

Host key verification failed.

Rimuovi la voce obsoleta con ssh-keygen -R 203.0.113.10. Questo accade spesso quando Terraform ricrea le risorse; per questo è opportuno limitare le ricreazioni sui computer che contengono dati.

Sudo non funziona. fatal: [web1]: FAILED! => {"msg": "Missing sudo password"} significa che become: true richiede una password su quell'host. Configura sudo senza password per l'utente di deployment oppure passa --ask-become-pass.

Terraform vuole eliminare una risorsa che non hai modificato. Il piano mostra modifiche che non hai mai scritto nel codice. Questo significa che l'infrastruttura reale non è più allineata al codice, in genere perché qualcuno ha modificato un'impostazione nel pannello web del provider. Esegui terraform plan -refresh-only per visualizzare solo quella differenza, quindi stabilisci se è errato il codice oppure la risorsa attiva. Non applicare mai un piano distruttivo che non sai spiegare riga per riga.

Ansible segnala modifiche a ogni esecuzione. Un'attività shell senza una condizione creates o when viene eseguita sempre. Non è un problema solo estetico, perché non puoi più usare changed=0 come indicatore del fatto che un server si trova nello stato richiesto.

FAQ

Terraform può sostituire Ansible?

Non per la configurazione interna a un server. Terraform può eseguire script tramite il provisioner remote-exec, ma questi vengono eseguiti solo durante la creazione della risorsa, non compaiono mai in terraform plan e, se falliscono, contrassegnano la risorsa come compromessa. Al successivo apply viene quindi pianificata la sua eliminazione e ricreazione. Terraform non dispone di un equivalente di un modulo che verifica se nginx è già installato e non esegue alcuna azione in tal caso. Usa Terraform per creare la macchina, quindi delega la configurazione.

Ansible può sostituire Terraform?

Per un numero ridotto di server di lunga durata, sì. Ansible dispone di moduli cloud per creare server e, se ordini due istanze VPS e le mantieni, questo può essere sufficiente. Perdi però il file di stato e il grafo delle dipendenze: se rimuovi un task dal playbook, la risorsa continua a essere in esecuzione e continua a generare costi, perché Ansible non registra mai di averla creata. Terraform avrebbe pianificato la sua eliminazione.

Quale dei due dovrei imparare per primo?

Ansible, se oggi gestisci già dei server. Offre un ritorno immediato sul primo server, richiede soltanto SSH e le competenze acquisite si applicano anche a un server ordinato manualmente. Terraform diventa utile in seguito, quando ricrei ripetutamente gli ambienti o gestisci risorse del provider oltre ai server, come record DNS e regole firewall.

Come trasferisco l'indirizzo IP del nuovo server da Terraform ad Ansible?

Dichiara un output nel codice Terraform, quindi leggilo dopo apply. terraform output -raw web_ip stampa il valore senza altro testo, per usarlo in una sostituzione nella shell, mentre terraform output -json restituisce tutti gli output contemporaneamente quando sono presenti più host. Scrivi il valore in un file di inventario oppure installa la collection cloud.terraform e indica a ansible-inventory -i terraform.yml --graph la directory del progetto.

Perché il mio playbook fallisce subito dopo il completamento di Terraform?

Il provider segnala che il server è stato creato non appena la relativa API lo comunica, mentre il sistema operativo sta ancora avviandosi. Per questo SSH viene rifiutato nei primi secondi. L'errore è UNREACHABLE! con Connection refused. Rendi ansible.builtin.wait_for_connection il primo task del play invece di impostare arbitrariamente una durata per sleep, perché il tempo di avvio varia in base all'immagine e al piano.