Terraform vs. CloudFormation: la guida definitiva

Pubblicato: 2022-03-11

Se, come me, hai setacciato Internet per aiutarti a scegliere tra CloudFormation e Terraform come prossimo strumento di infrastruttura come codice (IaC) senza trovare una risposta definitiva, ho condiviso il tuo dolore per molto tempo. Ora ho una notevole esperienza con entrambi gli strumenti e posso prendere una decisione informata su quale utilizzare.

TL; DR

Per il tuo progetto IaC su AWS, scegli CloudFormation, perché:

  1. CloudFormation fa una distinzione tra codice (es. modelli) e istanze del codice (es. stack). In Terraform, non esiste tale distinzione. Maggiori informazioni su questo nella prossima sezione.
  2. Terraform non gestisce molto bene la gestione delle dipendenze di base. Maggiori informazioni su questo in una sezione successiva.

Differenziazione tra codice e istanze

Una differenza tra CloudFormation e Terraform è il modo in cui codice e istanze si relazionano tra loro all'interno di ciascun servizio.

CloudFormation ha il concetto di stack , che è l'istanza di un modello. Lo stesso modello può essere istanziato all'infinito da un determinato cliente in un determinato account, tra più account o da diversi clienti.

Terraform non ha tale concetto e richiede una relazione uno-a-uno tra il codice e la sua istanziazione. Sarebbe simile alla duplicazione del codice sorgente di un server Web per ogni server che si desidera eseguire o alla duplicazione del codice ogni volta che è necessario eseguire un'applicazione invece di eseguire la versione compilata.

Questo punto è abbastanza banale nel caso di una configurazione semplice, ma diventa rapidamente un punto dolente per le operazioni su scala medio-grande. In Terraform, ogni volta che devi creare un nuovo stack da codice esistente, devi duplicare il codice. E copiare/incollare file di script è un modo molto semplice per sabotare te stesso e corrompere risorse che non intendevi toccare.

Terraform in realtà non ha un concetto di stack come CloudFormation, che mostra chiaramente che Terraform è stato costruito da zero per avere una corrispondenza uno a uno tra il codice e le risorse che gestisce. Ciò è stato successivamente parzialmente rettificato dal concetto di ambienti (che da allora sono stati rinominati "spazi di lavoro"), ma il modo in cui li utilizzano rende incredibilmente facile la distribuzione in un ambiente indesiderato. Questo perché è necessario eseguire terraform workspace select prima della distribuzione e dimenticare questo passaggio verrà distribuito nell'area di lavoro precedentemente selezionata, che potrebbe essere o meno quella desiderata.

In pratica, è vero che questo problema è mitigato dall'uso dei moduli Terraform, ma anche nel migliore dei casi si richiederebbe una notevole quantità di codice standard. In effetti, questo problema era così grave che le persone avevano bisogno di creare uno strumento wrapper attorno a Terraform per affrontare questo problema: Terragrunt.

Gestione dello stato e autorizzazioni

Un'altra importante differenza tra CloudFormation e Terraform è il modo in cui ciascuno gestisce lo stato e le autorizzazioni.

CloudFormation gestisce gli stati dello stack per te e non ti offre alcuna opzione. Ma gli stati dello stack di CloudFormation sono stati solidi nella mia esperienza. Inoltre, CloudFormation consente agli utenti meno privilegiati di gestire gli stack senza disporre di tutte le autorizzazioni necessarie richieste dallo stack stesso. Questo perché CloudFormation può ottenere le autorizzazioni da un ruolo del servizio collegato allo stack anziché le autorizzazioni dall'utente che esegue l'operazione dello stack.

Terraform richiede di fornirgli alcuni back-end per gestire gli stati. L'impostazione predefinita è un file locale, che è totalmente insoddisfacente, dato:

  1. La robustezza del tuo file di stato è interamente legata alla robustezza della macchina su cui è archiviato.
  2. Questo rende praticamente impossibile il lavoro di squadra.

Quindi hai bisogno di uno stato solido e condiviso, che in AWS viene solitamente ottenuto utilizzando un bucket S3 per archiviare il file di stato, accompagnato da una tabella DynamoDB per gestire la concorrenza.

Ciò significa che devi creare manualmente un bucket S3 e una tabella DynamoDB per ogni stack di cui desideri creare un'istanza e anche gestire manualmente le autorizzazioni per questi due oggetti per impedire agli utenti meno privilegiati di accedere ai dati a cui non dovrebbero avere accesso. Se hai solo un paio di stack, non sarà un grosso problema, ma se hai 20 stack da gestire, diventa molto ingombrante.

A proposito, quando si utilizzano gli spazi di lavoro Terraform, non è possibile avere una tabella DynamoDB per spazio di lavoro. Ciò significa che se desideri che un utente IAM con autorizzazioni minime esegua le distribuzioni, quell'utente sarà in grado di armeggiare con i blocchi di tutti gli spazi di lavoro perché le autorizzazioni DynamoDB non sono granulari a livello di elemento.

Gestione delle dipendenze

A questo punto, sia CloudFormation che Terraform possono essere un po' complicati. Se modifichi l'ID logico (cioè il nome) di una risorsa, entrambi considereranno che la vecchia risorsa deve essere distrutta e ne deve essere creata una nuova. Quindi è generalmente una cattiva idea modificare l'ID logico delle risorse in uno degli strumenti, specialmente per gli stack nidificati in CloudFormation.

Come accennato nella prima sezione, Terraform non gestisce le dipendenze di base. Sfortunatamente, gli sviluppatori di Terraform non stanno prestando molta attenzione al problema di vecchia data, nonostante l'apparente mancanza di soluzioni alternative.

Dato che una corretta gestione delle dipendenze è assolutamente fondamentale per uno strumento IaC, tali problemi in Terraform mettono in discussione la sua idoneità non appena vengono coinvolte operazioni business-critical, come l'implementazione in un ambiente di produzione. CloudFormation offre un'atmosfera molto più professionale e AWS è sempre molto attento ad assicurarsi di offrire strumenti di livello produttivo ai propri clienti. In tutti gli anni in cui utilizzo CloudFormation, non ho mai riscontrato problemi con la gestione delle dipendenze.

CloudFormation consente a uno stack di esportare alcune delle sue variabili di output, che possono quindi essere riutilizzate da altri stack. Ad essere onesti, questa funzionalità è limitata, poiché non sarai in grado di istanziare più di uno stack per regione. Questo perché non puoi esportare due variabili con lo stesso nome e le variabili esportate non hanno spazi dei nomi.

Terraform non offre tali servizi, quindi ti rimangono opzioni meno desiderabili. Terraform ti consente di importare lo stato di un altro stack, ma ciò ti dà accesso a tutte le informazioni in quello stack, inclusi i molti segreti archiviati nello stato. In alternativa, uno stack può esportare alcune variabili sotto forma di un file JSON archiviato in un bucket S3, ma ancora una volta questa opzione è più ingombrante: devi decidere quale bucket S3 utilizzare e dargli le autorizzazioni appropriate e scrivere tutti i programmare tu stesso il codice sia dal lato scrittore che dal lato lettore.

Un vantaggio di Terraform è che ha origini dati. Terraform può quindi interrogare risorse non gestite da Terraform. Tuttavia, in pratica, questo ha poca rilevanza quando si desidera scrivere un modello generico perché in tal caso non si presume nulla sull'account di destinazione. L'equivalente in CloudFormation consiste nell'aggiungere più parametri del modello, che quindi comporta ripetizioni e potenziali errori; tuttavia, nella mia esperienza, questo non è mai stato un problema.

Tornando al problema della gestione delle dipendenze di Terraform, un altro esempio è che ricevi un errore quando provi ad aggiornare le impostazioni per un sistema di bilanciamento del carico e ottieni quanto segue:

 Error: Error deleting Target Group: ResourceInUse: Target group 'arn:aws:elasticloadbalancing:us-east-1:723207552760:targetgroup/strategy-api-default-us-east-1/14a4277881e84797' is currently in use by a listener or a rule status code: 400, request id: 833d8475-f702-4e01-aa3a-d6fa0a141905

Il comportamento previsto sarebbe che Terraform rilevi che il gruppo target è una dipendenza di qualche altra risorsa che non viene eliminata e, di conseguenza, non dovrebbe tentare di eliminarlo, ma non dovrebbe nemmeno generare un errore.

Operazioni

Sebbene Terraform sia uno strumento da riga di comando, è molto chiaro che si aspetta che un essere umano lo esegua, poiché è molto interattivo. È possibile eseguirlo in modalità batch (cioè da uno script), ma ciò richiede alcuni argomenti aggiuntivi della riga di comando. Il fatto che Terraform sia stato sviluppato per essere eseguito dagli umani per impostazione predefinita è piuttosto sconcertante, dato che lo scopo di uno strumento IaC è l'automazione.

È difficile eseguire il debug di Terraform. I messaggi di errore sono spesso molto semplici e non ti permettono di capire cosa sta andando storto, nel qual caso dovrai eseguire Terraform con TF_LOG=debug , che produce un'enorme quantità di output da sfogliare. A complicare ciò, Terraform a volte effettua chiamate API ad AWS che non riescono, ma l'errore non è un problema con Terraform. Al contrario, CloudFormation fornisce messaggi di errore ragionevolmente chiari con dettagli sufficienti per consentirti di capire dove si trova il problema.

Un esempio di messaggio di errore Terraform:

 Error: error reading S3 bucket Public Access Block: NoSuchBucket: The specified bucket does not exist status code: 404, request id: 19AAE641F0B4AC7F, host id: rZkgloKqxP2/a2F6BYrrkcJthba/FQM/DaZnj8EQq/5FactUctdREq8L3Xb6DgJmyKcpImipv4s=

Il messaggio di errore precedente mostra un chiaro messaggio di errore che in realtà non riflette il problema sottostante (che in questo caso era un problema di autorizzazioni).

Questo messaggio di errore mostra anche come Terraform a volte può dipingersi in un angolo. Ad esempio, se crei un bucket S3 e una risorsa aws_s3_bucket_public_access_block su quel bucket, e se per qualche motivo apporti alcune modifiche al codice Terraform che distrugge quel bucket, ad esempio nel capitolo "modifica implica elimina e crea" descritto sopra— Terraform si bloccherà nel tentativo di caricare aws_s3_bucket_public_access_block ma fallendo continuamente con l'errore precedente. Il comportamento corretto di Terraform sarebbe sostituire o eliminare aws_s3_bucket_public_access_block a seconda dei casi.

Infine, non puoi utilizzare gli script di supporto di CloudFormation con Terraform. Questo potrebbe essere un fastidio, soprattutto se speri di utilizzare cfn-signal, che indica a CloudFormation che un'istanza EC2 ha terminato l'inizializzazione ed è pronta a soddisfare le richieste.

Sintassi, community e rollback

Per quanto riguarda la sintassi, Terraform ha un buon vantaggio rispetto a CloudFormation: supporta i loop. Ma secondo la mia esperienza, questa funzione può rivelarsi un po' pericolosa. In genere, un ciclo verrebbe utilizzato per creare un numero di risorse identiche; tuttavia, quando si desidera aggiornare lo stack con un conteggio diverso, potrebbe essere necessario collegare la vecchia e la nuova risorsa (ad esempio, utilizzando zipmap() per combinare i valori di due array che ora sono di dimensioni diverse perché un array ha la dimensione della vecchia dimensione del ciclo e l'altro ha la dimensione della nuova dimensione del ciclo). È vero che un problema del genere può verificarsi senza loop, ma senza loop il problema sarebbe molto più evidente per la persona che scrive la sceneggiatura. L'uso di loop in questo caso offusca il problema.

Se la sintassi di Terraform o la sintassi di CloudFormation è migliore è principalmente una questione di preferenze. Inizialmente CloudFormation supportava solo JSON, ma i modelli JSON sono molto difficili da leggere. Fortunatamente, CloudFormation supporta anche YAML, che è molto più facile da leggere e consente commenti. Tuttavia, la sintassi di CloudFormation tende ad essere piuttosto dettagliata.

La sintassi di Terraform utilizza HCL, che è una specie di derivato JSON ed è piuttosto idiosincratico. Terraform offre più funzioni rispetto a CloudFormation e di solito sono più facili da capire. Quindi si potrebbe sostenere che Terraform ha un leggero vantaggio su questo punto.

Un altro vantaggio di Terraform è il suo set prontamente disponibile di moduli gestiti dalla comunità, e questo semplifica la scrittura di modelli. Un problema potrebbe essere che tali moduli potrebbero non essere sufficientemente sicuri per soddisfare i requisiti di un'organizzazione. Quindi, per le organizzazioni che richiedono un elevato livello di sicurezza, potrebbe essere necessario rivedere questi moduli (così come le versioni successive).

In generale, i moduli Terraform sono molto più flessibili degli stack nidificati di CloudFormation. Uno stack nidificato di CloudFormation tende a nascondere tutto al di sotto di esso. Dallo stack di nidificazione, un'operazione di aggiornamento mostrerebbe che lo stack nidificato verrà aggiornato ma non mostra in dettaglio cosa accadrà all'interno dello stack nidificato.

Un ultimo punto, che potrebbe essere controverso in realtà, è che CloudFormation tenta di ripristinare le distribuzioni non riuscite. Questa è una funzionalità piuttosto interessante, ma purtroppo può essere molto lunga (ad esempio, potrebbero essere necessarie fino a tre ore prima che CloudFormation decida che una distribuzione su Elastic Container Service non è riuscita). Al contrario, in caso di fallimento, Terraform si ferma semplicemente dov'era. Se una funzione di rollback sia una buona cosa o meno è discutibile, ma ho imparato ad apprezzare il fatto che uno stack viene mantenuto in uno stato di lavoro il più possibile quando un'attesa più lunga sembra essere un compromesso accettabile.

In difesa di Terraform contro CloudFormation

Terraform ha vantaggi rispetto a CloudFormation. La cosa più importante, secondo me, è che quando applichi un aggiornamento, Terraform ti mostra tutte le modifiche che stai per apportare, incluso il drill-down di tutti i moduli che sta utilizzando. Al contrario, CloudFormation, quando si utilizzano gli stack nidificati, mostra solo che lo stack nidificato deve essere aggiornato, ma non fornisce un modo per approfondire i dettagli. Questo può essere frustrante, poiché questo tipo di informazioni è abbastanza importante da sapere prima di premere il pulsante "vai".

Sia CloudFormation che Terraform supportano le estensioni. In CloudFormation è possibile gestire le cosiddette "risorse personalizzate" utilizzando una funzione AWS Lambda di propria creazione come back-end. Per Terraform, le estensioni sono molto più facili da scrivere e fanno parte del codice. Quindi c'è un vantaggio per Terraform in questo caso.

Terraform può gestire molti fornitori di cloud. Ciò mette Terraform nella posizione di poter unificare una determinata distribuzione tra più piattaforme cloud. Ad esempio, supponiamo di avere un unico carico di lavoro distribuito tra AWS e Google Cloud Platform (GCP). Normalmente, la parte AWS del carico di lavoro verrebbe distribuita utilizzando CloudFormation e la parte GCP utilizzando Cloud Deployment Manager di GCP. Con Terraform, puoi invece utilizzare un unico script per distribuire e gestire entrambi gli stack nelle rispettive piattaforme cloud. In questo modo, devi distribuire solo uno stack invece di due.

Non argomenti per Terraform e CloudFormation

Ci sono alcune non-argomentazioni che continuano a circolare su Internet. Il più grande lanciato è che, poiché Terraform è multi-cloud, puoi utilizzare uno strumento per distribuire tutti i tuoi progetti, indipendentemente dalla piattaforma cloud in cui vengono eseguiti. Tecnicamente, questo è vero, ma non è il grande vantaggio che potrebbe sembrare, soprattutto quando si gestiscono tipici progetti single-cloud. La realtà è che esiste una corrispondenza quasi uno a uno tra le risorse dichiarate in (ad esempio) CloudFormation e le stesse risorse dichiarate in uno script Terraform. Dal momento che devi conoscere i dettagli delle risorse specifiche del cloud in entrambi i casi, la differenza si riduce alla sintassi, che non è certo il punto dolente più grande nella gestione delle distribuzioni.

Alcuni sostengono che utilizzando Terraform, si può evitare il blocco del fornitore. Questa argomentazione non vale nel senso che usando Terraform sei bloccato da HashiCorp (il creatore di Terraform), proprio come usando CloudFormation sei bloccato da AWS, e così via per altri cloud piattaforme.

Il fatto che i moduli Terraform siano più facili da usare è per me di minore importanza. Prima di tutto, credo che AWS voglia deliberatamente evitare di ospitare un unico repository per i modelli CloudFormation basati sulla comunità a causa della responsabilità percepita per falle di sicurezza create dagli utenti e violazioni di vari programmi di conformità.

A un livello più personale, comprendo appieno i vantaggi dell'utilizzo delle librerie nel caso dello sviluppo di software, poiché tali librerie possono facilmente essere eseguite in decine di migliaia di righe di codice. Nel caso di IaC, tuttavia, la dimensione del codice è generalmente molto inferiore e tali moduli sono generalmente lunghi alcune dozzine di righe. L'uso di copia/incolla in realtà non è una cattiva idea, nel senso che evita problemi con il mantenimento della compatibilità e delegando la tua sicurezza a persone sconosciute.

L'uso di copia/incolla è disapprovato da molti sviluppatori e ingegneri DevOps e ci sono buone ragioni dietro questo. Tuttavia, il mio punto di vista è che l'uso di copia/incolla per frammenti di codice ti consente di adattarlo facilmente alle tue esigenze e non è necessario ricavarne una libreria e dedicare molto tempo a renderlo generico. Il problema di mantenere quei frammenti di codice è generalmente molto basso, a meno che il codice non venga duplicato, ad esempio, in una dozzina o più modelli. In tal caso, appropriarsi del codice e usarlo come stack nidificati ha senso e i vantaggi di non ripetersi sono probabilmente maggiori del fastidio di non essere in grado di vedere cosa verrà aggiornato all'interno dello stack nidificato quando si esegue un aggiornamento operazione.

CloudFormation vs. Conclusione Terraform

Con CloudFormation, AWS vuole fornire ai propri clienti uno strumento solido come una roccia che funzioni sempre come previsto. Anche il team di Terraform lo fa, ovviamente, ma sembra che un aspetto cruciale dei loro strumenti, la gestione delle dipendenze, sfortunatamente non sia una priorità.

Terraform potrebbe avere un posto nel tuo progetto, soprattutto se hai un'architettura multi-cloud, nel qual caso gli script Terraform sono un modo per unificare la gestione delle risorse tra i vari fornitori di cloud che stai utilizzando. Ma in questo caso potresti comunque evitare gli svantaggi di Terraform utilizzando Terraform solo per gestire gli stack già implementati utilizzando i rispettivi strumenti IaC specifici del cloud.

La sensazione generale di Terraform rispetto a CloudFormation è che CloudFormation, sebbene imperfetto, è più professionale e affidabile e lo consiglierei sicuramente per qualsiasi progetto che non sia specificamente multi-cloud.