"Versionamento? Oh, io prendo sempre l’ultima versione - cosa mai potrebbe andare storto?"
Proprio questo atteggiamento è uno dei motivi per cui alcuni ingegneri vengono svegliati alle 3 del mattino. E anche il motivo per cui la mattina successiva, durante il Daily Stand-up, tutti fissano imbarazzati le proprie scarpe.
Parliamone brevemente nell’ultima parte della nostra serie "Terraform @ Scale", prima che capiti anche a voi.
Dopo aver parlato nei precedenti capitoli di questa serie di dipendenze, Blast Radius e testing, oggi ci dedichiamo a un tema spesso sottovalutato: il versionamento professionale dei moduli Terraform. Perché un modulo senza un versionamento pulito è come un’auto senza revisione annuale: può funzionare per un po’, ma prima o poi causerà grossi problemi.
Perché il versionamento dei moduli è essenziale
Immaginate questo scenario: un modulo di rete centrale viene utilizzato da 42 diversi progetti. Lo sviluppatore originale ha lasciato il team, la sua documentazione consisteva in "vedi codice" e ora il modulo deve essere urgentemente modificato. Senza un versionamento chiaro, non saprete:
- Quali progetti utilizzano quale versione del modulo
- Se un aggiornamento comporta Breaking Changes per i deployment esistenti
- Come eseguire un rollback in caso di problemi
- Quando il modulo è stato modificato l’ultima volta e per quale motivo
Il risultato? La famosa escalation delle 3 di notte, perché nessuno sa più dove esistono quali dipendenze. E allora inizia il grande gioco del "chi indovina?", mentre l’infrastruttura è in fiamme.
Il versionamento professionale dei moduli non è un “Nice-to-Have” per perfezionisti. È la base che permette alla vostra Infrastructure-as-Code di rimanere gestibile anche con più di 100 progetti, senza che dobbiate intraprendere studi di archeologia per capire cosa è stato fatto, quando e perché.
Semantic Versioning - La Lingua Franca del versionamento
Il Semantic Versioning (SemVer) è lo standard de facto per il versionamento del software e quindi anche per i moduli Terraform l’unica scelta sensata. Lo schema è semplice: MAJOR.MINOR.PATCH (ad esempio v1.3.7).
![]()
Le tre componenti nel dettaglio
Versione MAJOR (1.x.x) - La grande mazzata:
Viene incrementata in presenza di Breaking Changes. Si tratta di modifiche che rendono inevitabilmente incompatibile il codice esistente. Esempi:
- Una variabile obbligatoria viene rinominata o rimossa
- Viene eliminato un output da cui altri moduli dipendono
- La struttura delle risorse cambia al punto da richiedere un terraform state mv
- I requisiti del provider cambiano in modo incompatibile (ad esempio da AWS Provider 4.x a 5.x)
Regola pratica: se gli utenti del modulo DEVONO modificare la loro configurazione Terraform, si tratta di un aggiornamento major.
Versione MINOR (x.1.x) - L’estensione amichevole:
Viene incrementata quando vengono introdotte nuove funzionalità retrocompatibili. Esempi:
- Nuove variabili opzionali con valori di default sensati
- Output aggiuntivi che finora nessuno aveva sentito la mancanza
- Nuovi sub-moduli o risorse opzionali
- Miglioramenti delle prestazioni senza modifiche funzionali
Regola pratica: il codice esistente continua a funzionare, ma ci sono nuove possibilità. L’aggiornamento è facoltativo, ma consigliato.
Versione PATCH (x.x.1) - Il bugfix:
Viene incrementata per correzioni di bug che non modificano la funzionalità. Esempi:
- Correzione di errori di battitura negli output o nelle descrizioni delle variabili
- Risoluzione di race condition o problemi di timing
- Aggiornamenti della documentazione
- Correzione di valori di default evidentemente errati
Regola pratica: pura correzione di errori. Gli aggiornamenti dovrebbero essere sempre sicuri.
Pre-Release e Build-Metadata
SemVer consente di aggiungere etichette supplementari per versioni beta o informazioni di build:
v1.2.3-beta.1 # Versione Beta v1.2.3-rc.2 # Release Candidate v1.2.3+build.42 # Build-Metadata (viene ignorato durante il confronto delle versioni) v2.0.0-alpha.1 # Versione Alpha di un aggiornamento major
Queste versioni Pre-Release sono pensate per lo sviluppo e il testing. In ambienti di produzione non dovrebbero mai essere utilizzate - anche se alcuni team la pensano diversamente e ne pagano le conseguenze.
Scenari di aggiornamento dei moduli e le loro insidie
La teoria è bella e interessante. La pratica, come al solito, è più complicata. Vediamo cosa succede quando si tenta di aggiornare un modulo dalla versione v1.0.0 a varie versioni successive.

L’aggiornamento soft di tipo Patch (v1.0.0 → v1.0.1)
Dovrebbe essere l’esercizio più semplice di tutti. Un bugfix, nessun Breaking Change, nulla di drammatico. In teoria basta eseguire un terraform init -upgrade e il gioco è fatto.
Nella pratica, però, potreste imbattervi in:
- Provider-Lock-Files: Il vostro .terraform.lock.hcl potrebbe contenere hash bloccati incompatibili con il bugfix
- State-Drift: Il bugfix corregge un errore già presente nello state - ora Terraform vuole modificare le risorse
- Dipendenze a valle (Downstream): Altri moduli che utilizzano il vostro modulo hanno i propri file di lock
Best Practice: Anche per gli aggiornamenti di tipo Patch è consigliabile eseguire un plan-run in un ambiente di test. Non per paranoia, ma per esperienza.
L’aggiornamento amichevole di tipo Minor (v1.0.0 → v1.1.0)
Nuove funzionalità, retrocompatibili. Sembra tutto perfetto. Ma "retrocompatibile" non significa "senza effetti collaterali". Le nuove variabili opzionali hanno valori di default, e questi default vengono applicati, che lo vogliate o no.
Scenario di esempio:
# v1.0.0 - Original Version
resource "oci_core_vcn" "this" {
compartment_id = var.compartment_id
cidr_block = var.cidr_block
display_name = var.display_name
}
# v1.1.0 - New optional Features
resource "oci_core_vcn" "this" {
compartment_id = var.compartment_id
cidr_block = var.cidr_block
display_name = var.display_name
# NEW: Optional DNS Configuration
dns_label = var.dns_label # Default: null
# NEW: Optional IPv6 Support
is_ipv6enabled = var.enable_ipv6 # Default: false
}
Cosa succede durante l’aggiornamento? Terraform rileva nuovi argomenti nella risorsa. Anche se i valori di default sono "null" o "false", Terraform deve comunque valutare se qualcosa cambia. A seconda del provider e della risorsa, questo può causare un aggiornamento in-place o addirittura una sostituzione completa.
Particolarità dei provider: In alcuni provider, un nuovo valore di default impostato su "false" può comunque generare una modifica nel piano se il provider tratta lo stato "unset" in modo diverso da "false" esplicito. Ciò è particolarmente rilevante per gli attributi booleani aggiunti successivamente a risorse esistenti.
Best Practice: Testare ampiamente gli aggiornamenti Minor negli ambienti DEV. Verificare l’output del piano per identificare modifiche inattese. E sì, significa davvero LEGGERE il piano, non solo scorrerlo velocemente.
L’aggiornamento Major temuto (v1.5.0 → v2.0.0)
Qui le cose si fanno serie. I Breaking Changes significano che il vostro codice esistente SMETTERÀ di funzionare. La domanda è solo dove esattamente e quanto gravemente.
Scenari tipici negli aggiornamenti Major:
- Una variabile è stata rinominata: subnet_ids ora si chiama subnet_id_list
- La struttura dell’output è cambiata: ciò che prima era una lista ora è una mappa
- Gli indirizzi delle risorse sono cambiati: necessaria una migrazione dello state
- I requisiti del provider sono aumentati: OCI Provider 5.x → 6.x
Il simbolo con il punto interrogativo rosso nell’immagine sopra non è un caso. Con gli aggiornamenti Major può accadere che un upgrade diretto da v1.5.0 a v2.0.0 semplicemente non funzioni. Il motivo: troppi Breaking Changes tutti insieme.
Best Practice: Leggete le Release Notes. Tutte. Completamente. Create una checklist di migrazione. Testate in ambienti isolati. Pianificate strategie di rollback. E aspettatevi l’inaspettato.
Version Pinning - L’arte del caos controllato
Ora passiamo alla pratica. Come fissare (pinnare) i moduli a versioni specifiche? Terraform offre diversi meccanismi, che variano a seconda della fonte del modulo.
Moduli basati su Git con parametro ref
Per i moduli in repository Git, il parametro ?ref= è il metodo standard:
# Via Git-Tag (minimum for production environments)
module "vpc" {
source = "git::https://github.com/your-org/terraform-modules.git//vpc?ref=v1.2.3"
compartment_id = var.compartment_id
cidr_block = "10.0.0.0/16"
}
# Via Branch (only fpr development!)
module "vpc_dev" {
source = "git::https://github.com/your-org/terraform-modules.git//vpc?ref=develop"
compartment_id = var.dev_compartment_id
cidr_block = "172.16.0.0/16"
}
# Via Commit Hash (for maximum security, recommended)
module "vpc_immutable" {
source = "git::https://github.com/your-org/terraform-modules.git//vpc?ref=a1b2c3d4"
compartment_id = var.compartment_id
cidr_block = "192.168.0.0/16"
}
Importante: Il ?ref= va DOPO il percorso del modulo (//vpc), non prima. Questo errore è sorprendentemente comune e provoca messaggi di errore criptici.
Nota sui Commit-Hash: Quando si utilizzano Commit-Hash, è necessario impiegare l’hash completo. Gli hash abbreviati a 7 caratteri potrebbero non essere risolvibili a seconda della configurazione del server Git e causare errori durante terraform init.
Terraform Registry con vincoli di versione (Version Constraints)
Per i moduli provenienti da Registry pubbliche o private di Terraform si utilizza il parametro version:
# Exact Version (maximum pinning)
module "vpc" {
source = "oracle-terraform-modules/vcn/oci"
version = "3.5.4"
compartment_id = var.compartment_id
vcn_cidr = "10.0.0.0/16"
}
# Pessimistic Constraint Operator (recommended)
module "vpc_safe" {
source = "oracle-terraform-modules/vcn/oci"
version = "~> 3.5.0" # Allows 3.5.x, but not 3.6.0
compartment_id = var.compartment_id
vcn_cidr = "10.0.0.0/16"
}
# Range Constraints (allows more flexibility)
module "vpc_range" {
source = "oracle-terraform-modules/vcn/oci"
version = ">= 3.5.0, < 4.0.0" # All 3.x Versions, starting with 3.5.0
compartment_id = var.compartment_id
vcn_cidr = "10.0.0.0/16"
}
Operatori di vincolo di versione - Le note in piccolo
Terraform supporta diversi operatori per i vincoli di versione:
- = o nessuna indicazione: versione esatta (es. = 1.2.3 o 1.2.3)
- !=: tutte le versioni tranne quella indicata (es. != 1.2.3)
- >, >=, <, <=: operatori di confronto (es. >= 1.2.0)
- ~>: vincolo pessimista - consente solo incrementi della componente di versione più a destra specificata (es. ~> 1.2.0 consente >= 1.2.0 e < 1.3.0)
L’operatore ~> merita particolare attenzione. Consente solo incrementi della componente più a destra specificata:
~> 1.2 # Consente >= 1.2.0 e < 2.0.0 (tutte le versioni minor e patch del Major 1) ~> 1.2.0 # Consente >= 1.2.0 e < 1.3.0 (tutte le patch della minor 1.2) ~> 1.2.3 # Consente >= 1.2.3 e < 1.3.0 (tutte le patch a partire da 1.2.3 nella minor 1.2)
Per ambienti di produzione si consiglia: versioni esatte o ~> con specifica di Patch (ad es. ~> 3.5.0), per consentire solo i rilasci di patch all’interno di una stessa Minor. Se volete consentire anche aggiornamenti Minor automatici, usate ~> 3.5 - ma questo permetterà tutte le versioni fino a < 4.0.0. Tutto il resto porterà, prima o poi, a spiacevoli sorprese.
Version Pinning in ambienti di grandi dimensioni - La dura realtà
In piccoli setup è possibile fissare manualmente ogni modulo a una versione specifica. Ma questo porta a un micromanagement continuo e non è scalabile.
Con 5 progetti è ancora gestibile.
Con 50 progetti diventa faticoso.
Con 500 progetti è impossibile.
Il minimo indispensabile con Git-URL-Pinning
In Terraform Open Source, per i moduli basati su Git avete solo il parametro ?ref=. È la soluzione minimale assoluta. Funziona, ma dovete manualmente:
- Aggiungere un ?ref= a ogni chiamata di modulo
- Trovare e modificare in tutto il codice ogni riferimento interessato durante gli aggiornamenti
- Sperare che nessuno dimentichi di aggiungere il ?ref=
Per ambienti seri non è una soluzione sostenibile. È come piantare viti nel muro con un martello - tecnicamente possibile, in casi eccezionali forse tollerabile, ma come prassi generale sarebbe semplicemente insensato. A partire da una certa dimensione della codebase IaC servono soluzioni più professionali.
La soluzione Registry con Version Constraints
Migliore, ma ancora non perfetta: una Private Terraform Registry con vincoli di versione (Version Constraints). Potete controllare in modo mirato quali versioni sono consentite:
# In versions.tf
terraform {
required_version = ">= 1.10.0"
required_providers {
oci = {
source = "oracle/oci"
version = "~> 6.0"
}
}
}
# In main.tf
module "vcn" {
source = "your-company.com/modules/oci-vcn"
version = "~> 2.1.0" # Allows Patches, but not minor Upgrades
compartment_id = var.compartment_id
cidr_block = "10.0.0.0/16"
}
Il problema: dovete comunque passare da tutti i repository e aggiornare i vincoli di version ogni volta che volete eseguire un aggiornamento con Breaking Changes. Con più di 100 repository questo diventa un lavoro a tempo pieno.
Version Constraints per moduli - L’approccio Enterprise
In Terraform Enterprise o Terraform Cloud potete definire i vincoli di versione per i moduli a livello di Workspace o addirittura in modo globale. Questa è la soluzione professionale per ambienti di grandi dimensioni:
# Sentinel policy for module versioning
import "tfconfig/v2" as tfconfig
# Define allowed versions per module
allowed_module_versions = {
"oracle-terraform-modules/vcn/oci": {
"min_version": "3.5.0",
"max_version": "3.9.9",
},
"your-company.com/modules/oci-compute": {
"min_version": "2.0.0",
"max_version": "2.9.9",
},
}
# Verify all modul calles
import "versions"
mandatory_version_constraint = rule {
all tfconfig.module_calls as name, module {
# Only for registry sources which we want to manage explicitely
has_key(allowed_module_versions, module.source) implies (
module.version is not null and
func() {
v = allowed_module_versions[module.source]
c = versions.constraint(">= " + v.min_version + ", <= " + v.max_version)
versions.matches(c, module.version)
}()
)
}
}
Con Sentinel potete controllare centralmente quali versioni di moduli sono consentite in quali Workspace. Un aggiornamento a una nuova Major Release viene quindi distribuito tramite una modifica della Sentinel Policy, non modificando ogni singolo repository.
Questo è Enterprise-Grade Version Management. Richiede un investimento economico e di setup iniziale, ma è effettivamente scalabile su centinaia di progetti.
Test dei Module Upgrade con Terraform Testing Framework
Avete aggiornato un modulo. Ora dovete testare che tutto funzioni ancora. Il testing manuale è per dilettanti. I professionisti automatizzano.
Il Terraform Testing Framework (da Terraform 1.6, notevolmente migliorato in 1.10+) offre gli strumenti giusti per farlo. Ecco un esempio pratico per i test di upgrade dei moduli:
Vantaggio aggiuntivo: il Testing Framework può fungere anche da guardrail contro batch troppo grandi - tramite test che limitano il numero di modifiche alle risorse pianificate. Questo attenua indirettamente i problemi di API limit che abbiamo trattato in dettaglio nelle parti precedenti di questa serie.
Scenario di test: upgrade del modulo VCN da v2.5.0 a v3.0.0
# tests/upgrade_v2_to_v3.tftest.hcl
variables {
compartment_id = "ocid1.compartment.oc1..example"
vcn_cidr = "10.0.0.0/16"
vcn_name = "test-upgrade-vcn"
}
# Test 1: Existing v2.5.0 funktionality
run "test_v2_baseline" {
command = plan
module {
source = "oracle-terraform-modules/vcn/oci"
version = "2.5.0"
}
assert {
condition = length(output.vcn_id.value) > 0
error_message = "VCN ID should be generated in v2.5.0"
}
assert {
condition = output.vcn_cidr_block.value == var.vcn_cidr
error_message = "CIDR block mismatch in v2.5.0"
}
}
# Test 2: v3.0.0 breaking changes
run "test_v3_migration" {
command = plan
module {
source = "oracle-terraform-modules/vcn/oci"
version = "3.0.0"
}
# v3.0.0 renamed 'vcn_name' to 'display_name'
variables {
display_name = var.vcn_name # Neuer Parameter-Name
}
assert {
condition = length(output.vcn_id.value) > 0
error_message = "VCN ID should still be generated in v3.0.0"
}
assert {
condition = output.vcn_display_name.value == var.vcn_name
error_message = "Display name not correctly migrated to v3.0.0"
}
}
# Test 3: Backwards Compatibility Check
run "test_output_compatibility" {
command = plan
module {
source = "oracle-terraform-modules/vcn/oci"
version = "3.0.0"
}
variables {
display_name = var.vcn_name
}
# Check if critical outputs still exist
assert {
condition = (
can(output.vcn_id.value) &&
can(output.vcn_cidr_block.value) &&
can(output.default_route_table_id.value)
)
error_message = "Critical outputs missing in v3.0.0 - breaking downstream dependencies!"
}
}
Questi test vengono eseguiti con terraform test e si interrompono se un upgrade introduce Breaking Changes non documentati o non gestiti.
Integrazione con CI/CD
I test sono utili quanto la loro automazione. Ecco un esempio GitLab CI/CD:
# .gitlab-ci.yml
stages:
- test
- plan
- apply
terraform_test:
stage: test
image: hashicorp/terraform:1.10
script:
- terraform init
- terraform test -verbose
only:
- merge_requests
- main
terraform_plan:
stage: plan
image: hashicorp/terraform:1.10
script:
- terraform init
- terraform plan -out=tfplan
dependencies:
- terraform_test
only:
- main
artifacts:
paths:
- tfplan
terraform_apply:
stage: apply
image: hashicorp/terraform:1.10
script:
- terraform init
- terraform apply tfplan
dependencies:
- terraform_plan
only:
- main
when: manual
Senza test superati niente plan. Senza plan superato niente apply. Questa è la pipeline che volete.
Suggerimento aggiuntivo: oltre ai test, dovreste implementare anche plan-scan automatizzati per rilevare modifiche distruttive (delete/destroy), come già trattato nelle parti precedenti di questa serie. Questi sistemi di allerta precoce integrano i guardrail del testing e intercettano potenziali problemi di Blast Radius prima che raggiungano la Production.
Sentinel Policies per l’enforcement delle versioni
I test sono utili. Le policy sono migliori. Perché i test si possono aggirare, se davvero lo si vuole (o per pigrizia). Le Sentinel Policies in Terraform Enterprise non si possono aggirare - a meno di essere amministratori, e allora è tutta un’altra storia.
Policy: imponi il versionamento dei moduli
import "tfconfig/v2" as tfconfig
import "strings"
# Helper function: is the source a VCS (git)?
is_vcs = func(src) {
strings.has_prefix(src, "git::") or strings.contains(src, "://")
}
# Helper function: Extract ref parameter from URL
ref_of = func(src) {
idx = strings.index_of(src, "?ref=")
idx >= 0 ? strings.substring(src, idx+5, -1) : ""
}
# Policy: All non-local modules MUST be versioned
mandatory_versioning = rule {
all tfconfig.module_calls as name, module {
# Local modules (./ oder ../) are exempt
not (strings.has_prefix(module.source, "./") or
strings.has_prefix(module.source, "../")) implies (
# For VCS sources: ?ref= must exist
is_vcs(module.source) implies length(ref_of(module.source)) > 0
and
# For registry sources: version must be set
not is_vcs(module.source) implies module.version is not null
)
}
}
# Main policy
main = rule {
mandatory_versioning
}
Questa policy rifiuta ogni terraform plan o apply in cui un modulo non è versionato correttamente. Nessuna eccezione. Nessuna clemenza.
Policy: vieta versioni note come difettose
import "tfconfig/v2" as tfconfig
# Deny-list for modules with known bugs
forbidden_module_versions = {
"oracle-terraform-modules/vcn/oci": ["3.2.0", "3.2.1"], # Bug in DHCP-Options
"your-company.com/modules/compute": ["1.5.0"], # Security Issue
}
# Policy: Disallow known faulty versions
deny_forbidden_versions = rule {
all tfconfig.module_calls as name, module {
has_key(forbidden_module_versions, module.source) implies (
module.version not in forbidden_module_versions[module.source]
)
}
}
main = rule {
deny_forbidden_versions
}
Ora avete un meccanismo per bloccare globalmente le versioni critiche dei moduli. Se la versione 3.2.0 del modulo VCN ha un bug grave, la bloccate nella policy. Fine. Nessuno potrà più usarla, nemmeno “per errore”.
Workflow pratici per gli aggiornamenti dei moduli
La teoria è utile. La pratica è meglio. Ecco workflow collaudati per diversi scenari di aggiornamento.
Workflow 1: Patch-Update (ad es. v2.3.1 → v2.3.2)
- Comunicazione: annuncio nel team chat, anche se è “solo” una patch
- Test in DEV: aggiornamento in ambiente Development, terraform init -upgrade, verifica del plan
- Test automatici: eseguire terraform test
- Staging-Deployment: rollout in Staging, monitoraggio per 24h
- Production-Rollout: rollout in Produzione durante l’orario lavorativo (non il venerdì alle 16:00)
- Documentazione: aggiornare CHANGELOG e Confluence/Wiki
Durata: 1-3 giorni, in base alla dimensione dell’ambiente
Nota: le durate indicate sono basate sull’esperienza e possono variare sensibilmente in base ai requisiti di compliance (SOX, ISO, GDPR) e ai processi di Change Management.
Workflow 2: Minor-Update (ad es. v2.3.2 → v2.4.0)
- Revisione delle Release Notes: analisi completa di tutte le nuove feature e modifiche
- Analisi dell’impatto: quali nuovi default diventano attivi? Ci sono effetti collaterali inattesi?
- Piano di test: scenari di test documentati per le nuove feature
- DEV-Testing: testing approfondito in Development (almeno 1 settimana)
- Canary-Deployment: rollout su un sottoinsieme limitato della Produzione
- Fase di monitoraggio: 1 settimana di monitoraggio del Canary-Deployment
- Full Rollout: rollout graduale nell’arco di più giorni/settimane
- Postmortem-Review: documentare le Lessons Learned
Durata: 2-4 settimane, in base alla valutazione del rischio
Workflow 3: Major-Update (ad es. v2.4.0 → v3.0.0)
- Inventario dei Breaking Changes: elenco completo di tutti i Breaking Changes
- Migrations-Guide: guida passo-passo per ciascun Breaking Change
- Analisi delle dipendenze: quali moduli downstream sono coinvolti?
- Allestimento ambiente di test: ambiente dedicato per il Migration-Testing
- Test di State-Migration: Terraform State Surgery se necessario
- Strategia di rollback: piano documentato per il Worst Case
- DEV-Migration: migrazione completa in Development con test
- Staging-Migration: migrazione in Staging con 2 settimane di monitoraggio
- Go/No-Go Meeting: decisione formale basata sui risultati dei test
- Production-Migration: finestra di manutenzione pianificata, tutti operativi
- Post-Migration Monitoring: monitoraggio intensivo per 2 settimane
- Aggiornamento documentazione: aggiornare tutta la documentazione alla nuova versione
Durata: 1-3 mesi, a seconda di complessità e dimensioni
Checklist: versionamento professionale dei moduli
✅ Basi
[ ] Il Semantic Versioning è applicato in modo coerente a tutti i moduli
[ ] I Git-Tag vengono creati per ogni versione (non solo per i Major-Release)
[ ] Il CHANGELOG.md viene aggiornato a ogni release
[ ] Le Release Notes sono chiare e complete (non solo “minor fixes”)
[ ] I Breaking Changes sono esplicitamente contrassegnati e documentati
✅ Version Pinning
[ ] Tutte le chiamate ai moduli usano un pinning esplicito della versione (non “latest”)
[ ] I moduli Git usano ?ref= con tag concreti, non i branch
[ ] I moduli da Registry usano version-Constraints (idealmente ~>)
[ ] Gli ambienti Development/Test possono essere pinnati in modo più permissivo rispetto alla Produzione
[ ] I Lock-File (.terraform.lock.hcl) sono versionati in Git
✅ Testing & Validation
[ ] Il Terraform Testing Framework è implementato per tutti i moduli critici
[ ] Esistono test di upgrade per Major e Minor update
[ ] I test vengono eseguiti automaticamente nella pipeline CI/CD
[ ] La Backwards-Compatibility viene verificata prima di ogni release
[ ] Sono presenti test di regressione per bug noti
✅ Governance
[ ] Le Sentinel Policies impongono il versionamento dei moduli (Terraform Enterprise)
[ ] È implementata una deny-list per le versioni di moduli difettose
[ ] Il processo di aggiornamento delle versioni è documentato e adottato
[ ] Le modifiche ai moduli passano attraverso un processo di review
[ ] I Major-Update richiedono un Go/No-Go Meeting formale
✅ Documentazione
[ ] Il README.md spiega la strategia di versionamento del modulo
[ ] Il CHANGELOG.md è curato e segue il formato Keep-a-Changelog
[ ] Esistono Migration-Guide per tutti i Major-Update
[ ] I Known Issues sono documentati
[ ] Le Deprecation-Notice vengono comunicate almeno 2 minor version in anticipo
✅ Monitoring & Incident Response
[ ] Le versioni dei moduli nei deployment sono tracciabili (log, tag)
[ ] È attivo l’alerting in caso di aggiornamenti di modulo inattesi
[ ] Le procedure di rollback sono documentate e testate
[ ] Esiste un Incident-Response Playbook per i problemi di modulo
[ ] È stabilito un processo di postmortem dopo gli update falliti
Pensieri conclusivi
Il versionamento dei moduli non è “sexy”. Non è l’argomento di cui si parla alle conferenze. Non ci sono grafici appariscenti o demo spettacolari. Ma è la differenza tra un’infrastruttura che rimane gestibile anche dopo tre anni e un caos ingestibile che nessuno vuole più toccare.
Se da questo articolo doveste portare a casa una sola cosa, che sia questa: investite ora in un versionamento pulito, oppure pagherete in seguito dieci volte tanto sotto forma di incident, rollback e interventi nel weekend.
Le escalation alle 3 di notte forse non spariranno del tutto. Ma diventeranno molto più rare. E già questo non è poco.
Perché l’unica versione davvero utile è quella che potete controllare.




