git clone --filter=blob:none --sparse https://github.com/calmict/book_labs.git cd book_labs git sparse-checkout set terraform-opentofu/ed1
Prima di imparare la sintassi, devi *sentire* il problema. In questo esercizio costruisci due "server" a mano, come si faceva (e purtroppo si fa ancora) col click-ops: scoprirai che divergono subito. Poi li descrivi come codice: stessa fotografia del risultato per entrambi. Da lì in poi torturi l'infrastruttura — la modifichi a mano di notte, ne cancelli un pezzo, la radi al suolo — e ogni volta un solo comando la riporta esattamente al modello. Alla fine il "server" non è più un animale da compagnia con un nome e una storia: è un capo di bestiame con un'etichetta, sostituibile in ogni momento.
I "server" qui sono semplici file di configurazione sul tuo disco: zero cloud, zero costi, ma i concetti — drift, idempotenza, convergenza, immutabilità dell'identità — sono esattamente gli stessi che incontrerai in produzione.
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap01/solution ./run.sh
Una ricetta elenca i passi: rompi le uova, scalda la padella, versa. Una fotografia mostra il piatto finito. Nel capitolo 1 hai *sentito* il drift; qui metti le mani sulla frattura che lo genera: scrivi davvero uno script imperativo di provisioning, lo guardi esplodere al secondo giro, lo ripari aggiungendo le guardie ("se esiste, salta") — e poi scopri il suo difetto fatale: le guardie rendono lo script ri-eseguibile, ma *cieco*. Un file vandalizzato passa la guardia indisturbato, perché la guardia controlla che il file *esista*, non che sia *giusto*.
Poi fotografi la stessa flotta in un main.tf e la torturi da quattro punti di partenza diversi — cantiere vuoto, mezzo costruito, vandalizzato, già finito — sempre con lo stesso identico comando. I passi non li scrivi più tu: li calcola lo strumento, ogni volta, confrontando la realtà con il modello.
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap02/solution ./run.sh
Davanti a un edificio da cambiare, l'architetto ha due strade: ristrutturare (l'edificio resta in piedi, si cambia un impianto) o demolire e ricostruire (un edificio nuovo prende il posto del vecchio). L'infrastruttura funziona allo stesso modo, e la cosa notevole è che *non decidi tu* quale strada si prende: la conosce il provider, attributo per attributo — e il piano te la annuncia sempre in anticipo, con una segnaletica precisa che in questo esercizio impari a leggere.
Qui il "server" è per la prima volta una cosa viva: un container Docker con dentro nginx. Gli cambi la memoria e lo guardi restare lo stesso oggetto (ristrutturazione, in-place). Poi gli cambi la versione dell'immagine e lo guardi *morire e rinascere* (ricostruzione, replace): dentro quel container nessuno è mai entrato ad aggiornare nginx — questo è, alla lettera, l'immutabilità. Infine prendi in mano il blocco lifecycle e governi la sostituzione: prima inverti l'ordine (costruisci il nuovo, poi demolisci il vecchio), poi metti il fermo di sicurezza che blocca qualunque demolizione — e scopri che blocca anche più di quanto pensassi.
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap03/solution ./run.sh
Nei tre capitoli scorsi una domanda è rimasta aperta: quando le risorse sono tante e collegate, chi decide *in che ordine* costruirle? Non tu — non hai mai scritto un ordine da nessuna parte. Lo decide un capocantiere invisibile: il grafo delle dipendenze, che lo strumento costruisce leggendo il tuo codice.
In questo esercizio lo rendi visibile con lo strumento più onesto che c'è: il cronometro. Costruisci tre piani da 5 secondi l'uno *senza dirgli che sono una torre*: salgono tutti insieme, 5 secondi totali — fisicamente assurdo, ma il modello non sa che un piano poggia sull'altro finché il codice non glielo dice. Poi incateni i piani coi riferimenti e riguardi il cronometro: 15 secondi, uno alla volta. Stesse tre risorse, nessun "ordine" scritto: solo archi nati dai riferimenti. Infine guardi il grafo in faccia con tofu graph, osservi la demolizione procedere all'incontrario, e provi a costruire l'unica cosa che il capocantiere rifiuta: il ciclo — la gallina che nasce dall'uovo che è deposto dalla gallina.
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap04/solution ./run.sh
Nei primi quattro capitoli l'HCL l'hai *letto*, guidato dai commenti. Da questo capitolo lo *scrivi*. Niente più torture all'infrastruttura: qui il lavoro è da architetti a tavolino — compilare la scheda tecnica di un grattacielo usando, uno per uno, tutti i tipi di dato del linguaggio: i primitivi per l'anagrafica, la list per i materiali (dove un duplicato volutamente infilato ti mostrerà la differenza col set), la map per le superfici, l'object per l'indirizzo, la tuple per le coordinate. Poi assembli tutto in un blocco di testo con l'interpolazione: la scheda vera e propria, che l'apply deposita in un file.
Chiude il capitolo l'attrezzo più umile e più usato del mestiere: tofu fmt, messo alla prova su un file scritto da un collega sciatto — valido ma illeggibile. Scoprirai che cosa fmt sistema sempre (la forma) e che cosa non tocca mai (il significato).
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap05/solution ./run.sh
Cinque capitoli di concetti e di esercizi guidati: adesso posi la prima pietra tua. In questo esercizio scrivi da zero — riga per riga, non più a segnaposto — la tua prima configurazione completa: il blocco terraform che dichiara i traduttori, il blocco provider che li configura, le risorse, un output. Il risultato non è un file su disco: è un servizio web vero, raggiungibile col browser, acceso da codice.
E soprattutto vivi il ciclo di vita al rallentatore, guardando dove finora sei passato di corsa: che cosa scarica *davvero* init (andrai a pesare il binario del provider dentro .terraform: sorpresa), che cos'è un piano salvato e perché eseguirlo non chiede conferma, quali domande quotidiane trovano risposta in state list, show e output, e che cosa il destroy demolisce — e che cosa invece lascia in piedi.
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap06/solution ./run.sh
Un cantiere serio ha un capitolato: quali norme si applicano, quali fornitori sono ammessi, e un registro di *esattamente* quali materiali sono stati scelti. Nel progetto, il capitolato è il blocco terraform — e in questo esercizio lo metti alla prova rompendolo e riparandolo: un required_version impossibile ti chiude il cancello in faccia (ed è un bene: scoprirai da che cosa protegge), un pin esatto ti mostra la nascita del lock file, e poi il gioco si fa sottile — allarghi il vincolo con l'operatore ~> e scopri che *non cambia niente*, finché non sei tu a chiederlo con init -upgrade.
È la divisione dei poteri che regge il lavoro in squadra: il *vincolo* nel codice è il recinto (che cosa sarebbe accettabile), il *lock* è la scelta (che cosa usiamo davvero, tutti, oggi). Chiude l'esercizio il collega di luglio: cancella il registro, rilancia init, e ottiene un traduttore diverso dal tuo — stesso codice, mesi dopo, provider diverso. È il drift dei capitoli 1 e 2, risalito dal mondo dei server al mondo degli attrezzi.
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap07/solution ./run.sh
Nel capitolo 6 hai pesato il traduttore: un binario da decine di megabyte dentro .terraform. Ma il binario da solo non basta: bisogna dirgli *con quale mondo* parlare — e questo è il mestiere del blocco provider. Qui lo scopri nel modo più concreto possibile: costruisci un secondo datacenter sulla tua macchina (un Docker dentro Docker: un engine vero, separato, raggiungibile via rete) e configuri *due istanze dello stesso traduttore* — la linea di default verso il cantiere di Milano e una linea con alias verso quello di Francoforte. Poi piazzi lo stesso nginx in entrambi, decidendo la destinazione risorsa per risorsa con una sola riga: provider =.
La seconda metà del capitolo è da leggere, non da eseguire: una galleria di blocchi provider veri — AWS coi ruoli, vSphere con la password nel posto sbagliato e poi in quello giusto — per arrivare alla regola d'oro: il codice dice *dove* e *come* connettersi, mai *chi sei*: i segreti vivono fuori dal codice, sempre.
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap08/solution ./run.sh
La risorsa è il mattone di tutto, e questo capitolo la mette sul tavolo operatorio. La prima scoperta è che ha due facce: gli *argomenti* — ciò che scrivi tu, l'ingresso — e gli *attributi* — ciò che la risorsa ti restituisce una volta nata, l'uscita: l'id, l'indirizzo IP che Docker le ha assegnato, i valori che nessuno conosceva prima dell'apply. Costruisci un dossier che consuma proprio quelle uscite, e nel piano vedi la scia che lasciano: (known after apply), l'ignoto dichiarato che viaggia lungo il grafo.
La seconda scoperta completa il lifecycle del capitolo 3 col suo pezzo più sottile: ignore_changes. La squadra notturna cambia un'impostazione del tuo container a mano; il piano — fedele ai capitoli 1 e 2 — vuole riconvergerla. Ma stavolta il cambiamento è *legittimo*: quella manopola appartiene a un altro processo. Firmerai il contratto dell'occhio chiuso, e imparerai quando è saggezza e quando è solo una pezza.
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap09/solution ./run.sh
Nessun cantiere parte su un terreno vergine: c'è la rete del quartiere, l'acquedotto, il catasto che registra ciò che esiste. Finora ogni cosa nel tuo modello la creavi tu; in questo capitolo impari a *consultare* — leggere ciò che esiste, appartiene ad altri, e non è affar tuo gestire.
La squadra piattaforma ha creato una rete Docker (a mano: Fase 0, sei tu a interpretarla). Tu la leggi con un blocco data — le spie annunciate nella galleria del capitolo 9 — e ci appoggi il tuo container: costruisci *su* ciò che non possiedi. Lungo la strada scopri il rovescio del capitolo 9: gli attributi del data source sono noti *già al plan* — l'esistente si consulta subito, non c'è nulla da aspettare — tranne quando il data dipende da una risorsa che deve ancora nascere: allora la lettura slitta all'apply, e l'ignoto torna. Vedrai i due casi fianco a fianco, nello stesso piano. E al destroy, la prova che chiude il capitolo: ciò che leggi non è tuo — la rete della piattaforma sopravvive intatta.
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap10/solution ./run.sh
Da dieci capitoli usi plan e apply, e c'è un personaggio che lavora nell'ombra a ogni comando: il taccuino dove lo strumento annota che cosa ha costruito e come si chiamava nel codice. In questo capitolo lo apri e lo leggi: terraform.tfstate, la mappatura tra gli indirizzi del modello e gli oggetti reali — con dentro perfino gli archi del grafo.
Poi tre scoperte in crescendo. La prima scotta: crei una password marcata sensitive, l'output te la nasconde — e il taccuino la custodisce *in chiaro*: chi legge lo state legge ogni segreto. La seconda è il gioco delle tre fonti di verità: codice, memoria, realtà — cancelli un container alle spalle del modello e impari il comando che sincronizza *solo la memoria* (plan e apply -refresh-only), separando "aggiornare il taccuino" da "toccare il mondo". La terza è il finale che prepara il capitolo 12: un collega clona il tuo codice ma non la tua memoria — il suo piano vuole ricostruire tutto, il suo apply si schianta sulla realtà che già esiste, e il suo taccuino resta a metà. Stesso codice, due memorie, una sola realtà: è il problema che solo lo stato *condiviso* risolve.
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap11/solution ./run.sh
Il capitolo 11 si è chiuso su un incidente: due colleghi, due taccuini, una realtà contesa. La soluzione ha un nome — backend — ed è la risposta alla domanda "dove vive lo stato?". In questo esercizio la risposta la costruisci: la squadra piattaforma (sempre tu, col casco) accende la bacheca di cantiere — un Consul in container, un backend remoto vero — e tu *trasferisci* il tuo taccuino lì dentro con la manovra ufficiale: il blocco backend più init -migrate-state. Andrai a verificare di persona che il file locale si è svuotato e che lo stato ora abita nel backend (con dentro, sempre in chiaro, quello che sai dal capitolo 11: la casa è cambiata, le regole di custodia no).
Poi il collega torna in scena — e stavolta la storia è diversa: si aggancia allo stesso backend e il suo primo plan dice No changes: *vede le tue risorse*, perché legge la tua stessa memoria. E il gran finale: mentre un tuo apply è in corso, lui prova a lavorare — e il lucchetto glielo impedisce, per nome e cognome: Error acquiring the state lock, con scritto chi lo tiene e per quale operazione. Il caos del capitolo 11 è diventato una coda ordinata.
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap12/solution ./run.sh
Un palazzo senza porte tagliafuoco brucia tutto insieme. Un progetto con un solo stato, anche: in questo esercizio costruisci prima il *monolite* — rete e applicazione nello stesso taccuino — e misuri il raggio dell'esplosione: la squadra rete rinomina la propria rete, e il piano mostra l'incendio che si propaga fino al container dell'app; nel frattempo, un solo lucchetto mette in coda chiunque, qualunque cosa stia facendo.
Poi installi le porte tagliafuoco: due configurazioni, due taccuini (nel Consul che conosci dal capitolo 12), e il canale ufficiale per farli parlare — terraform_remote_state, il data source che legge gli *output* di un altro stato. Chiudi con le due prove del contenimento: il comando più distruttivo che esista, lanciato nella stanza dell'app, non vede nemmeno la rete; e un apply lento dell'app non blocca più il plan della rete — due code, due lucchetti, due squadre che lavorano davvero in parallelo.
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap13/solution ./run.sh
Finora hai scritto configurazioni con i valori *cablati* dentro: il nome del container, la porta, tutto fisso nel file. Funziona per un palazzo solo. Ma lo stesso progetto deve servire dev, staging e prod — e riscrivere il file per ognuno è la fotocopia che il capitolo 1 ci ha insegnato a temere.
Questo capitolo installa tre porte nella configurazione. La porta d'ingresso — le *variabili* — lascia entrare i valori dall'esterno: chi usa il modulo decide environment e external_port senza toccare il codice. La porta di servizio — gli *output* — mostra all'esterno solo ciò che promette: qui, l'URL dove risponde il servizio. E la cucina interna — i *locals* — non ha porta sul mondo: è dove il nome del container si *deriva* una volta sola (cap14-web-${var.environment}) e si riusa ovunque.
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap14/solution ./run.sh
Finora ogni risorsa era un pezzo unico, scritto a mano. Ma un quartiere ha cento case uguali, e nessuno le scrive una per una. Questo capitolo ti dà i due modi per *moltiplicare* una risorsa — e ti mostra perché la scelta tra i due è una delle più importanti che farai.
Il primo modo conta per numero: count. Dai un numero, e ottieni quella quantità di copie, indicizzate [0], [1], [2]. Comodo, immediato — e con una trappola nascosta. L'identità di ogni copia è la sua *posizione*: la casa numero 2. Togli una casa in mezzo alla fila, e tutte quelle dopo *scalano di numero*: la 3 diventa la 2, la 4 diventa la 3. Terraform, che lega l'identità alla posizione, crede che tu abbia rinominato mezza flotta — e la ricostruisce. Lo vedrai col tuo piano: una sola rimozione, e l'incendio si propaga alla coda.
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap15/solution ./run.sh
Il capitolo scorso si chiudeva con una promessa: le collezioni che dai a count e for_each vanno spesso *preparate* prima — pulite, trasformate, filtrate. Questo capitolo ti dà gli attrezzi per prepararle, e un banco su cui provarli prima di montarli.
Gli attrezzi sono le funzioni: HCL ne ha un centinaio, già pronte (non puoi scriverne di tue nel modo classico — prendi quelle che ci sono). Trasformano una stringa (lower, trimspace, split), contano e combinano collezioni (length, merge, keys), le convertono (toset, tolist, jsonencode). Una funzione prende input tra parentesi e restituisce un valore: nient'altro, nessun effetto collaterale.
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap16/solution ./run.sh
Da sedici capitoli scrivi lo stesso schema: variabili in cima, risorse in mezzo, output in fondo. E ogni volta lo riscrivi da capo, cartella dopo cartella. L'architetto vero non ridisegna la stessa palazzina per ogni lotto: la progetta una volta come prefabbricato, e poi la cala nella città quante volte serve, ognuna con le sue rifiniture.
Il prefabbricato, qui, è il modulo: una cartella con dei file .tf, ma vista come una *scatola con delle porte*. Le porte d'ingresso sono le sue variabili (nome, ambiente, porta); il macchinario dentro sono le risorse (immagine e container); le porte d'uscita sono i suoi output (l'URL dove risponde). Chi usa la scatola non guarda dentro: passa gli input alle porte d'ingresso, e legge i risultati dalle porte d'uscita. Sono esattamente le variabili e gli output del capitolo 14 — ma promossi a *interfaccia* di un componente riusabile.
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap17/solution ./run.sh
Il capitolo 17 si è chiuso con un tranello: incapsulando le risorse in un modulo ne hai cambiato l'*indirizzo*, e il capitolo 15 ci aveva avvertiti che l'indirizzo *è* l'identità. Rinomina una risorsa nel codice, e Terraform non vede un cambio di targa: vede una risorsa sparita e una nuova nata — demolisce e ricostruisce. Per un container è un fastidio; per un database in produzione è un disastro.
Ma il taccuino del capitolo 11 — lo stato — è solo una *mappa* tra indirizzi nel codice e oggetti reali. E una mappa si può correggere senza toccare il territorio. Questo capitolo ti dà quattro modi per cambiare le carte senza toccare i palazzi:
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap18/solution ./run.sh
Dev, staging, prod: lo stesso progetto, tre città diverse. Copiare e incollare la configurazione tre volte è la fotocopia che il capitolo 1 ci ha insegnato a temere — ma un ambiente non è solo "lo stesso codice con un nome diverso". Un ambiente è una *copia isolata* della stessa infrastruttura, con le sue impostazioni e — soprattutto — il suo stato. E il capitolo 13 ce lo ha già gridato: gli ambienti non devono mai condividere il raggio dell'esplosione. Un errore in dev non deve poter mettere in coda, corrompere o distruggere la prod.
Questo capitolo mette a confronto le due strategie principali, e le fa toccare con mano.
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap19/solution ./run.sh
Per diciannove capitoli abbiamo detto "un linguaggio, due binari": ogni comando tofu ha il suo gemello terraform. Questo capitolo mantiene la promessa e le mette il confine. Perché i due binari *sono* gemelli — nati dallo stesso codice — ma da un certo punto in poi hanno preso strade diverse.
La storia in breve (20.1): nel 2023 HashiCorp cambiò la licenza di Terraform, da open source a una licenza restrittiva (la BSL). La comunità reagì con un *fork* — una copia del codice che riparte per conto suo — messo sotto la Linux Foundation e ribattezzato OpenTofu, con licenza aperta (MPL). Da lì, due binari gemelli che condividono quasi tutto e divergono su poco.
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap20/solution ./run.sh
Un architetto non consegna un progetto perché "sembra giusto": lo fa passare per una scala di collaudi, dai più economici e frequenti ai più costosi e rari. È la piramide della validazione, e ha quattro piani.
Alla *base*, larga e istantanea, ci sono due controlli che lanci di continuo: fmt verifica che il disegno sia leggibile (la forma), validate che sia internamente coerente (i riferimenti tornano, i tipi combaciano). Costano millisecondi, li fai a ogni salvataggio.
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap21/solution ./run.sh
Per ventuno capitoli hai lanciato tu i comandi: plan, apply, test. Questo capitolo — l'ultimo — li toglie dalle tue mani e li mette su un nastro trasportatore. Da un lato entra un commit; dall'altro esce infrastruttura in produzione. E lungo il nastro, in automatico, scatta la piramide del capitolo 21: forma, coerenza, sicurezza, comportamento. Nessuno applica a mano; nessuno dimentica un controllo.
Il nastro ha due tratti. Il primo è la CI (Continuous Integration): a ogni *proposta* di modifica — una pull request — il nastro esegue i controlli e produce un *plan*, la diffusione di ciò che cambierebbe. È il cancello: se un controllo fallisce, la porta resta chiusa, e nessuno discute con la macchina. Il secondo è la CD (Continuous Delivery/Deployment): quando la modifica viene *approvata e unita* al ramo principale, il nastro fa l'apply. Proporre e consegnare diventano due gesti separati e automatici — plan sulla PR, apply sul merge.
Completa i TODO nei file di partenza, poi lancia il test della soluzione:
cd terraform-opentofu/ed1/cap22/solution ./run.sh