"Mai toccare la console AWS in produzione" suona come una regola estrema. Non lo è. È la disciplina operativa più importante in un team cloud-native, e il costo di violarla si accumula silenziosamente finché non causa un incidente grave.

Questo post spiega perché, e come imporre lo sviluppo IaC-first in un team reale.

Il Problema del State Drift

Terraform (e OpenTofu) mantiene un file di stato che rappresenta l'infrastruttura esistente. Quando fai apply, Terraform confronta il file di stato con la tua configurazione e apporta il minimo insieme di modifiche per allineare la realtà alla configurazione.

Quando clicchi nella console AWS e crei o modifichi risorse, stai cambiando la realtà senza cambiare il file di stato.

Ora: Il prossimo plan mostrerà drift: Terraform vuole distruggere o ricreare risorse che "non dovrebbero esistere" (perché non sono nella configurazione).

O peggio: Lo stato di Terraform dice che la risorsa esiste con la configurazione A, ma la console l'ha cambiata nella configurazione B. Il plan mostra "nessuna modifica" anche se la risorsa è configurata male.

Oppure: qualcuno aggiunge una risorsa nella console, funziona, altre risorse iniziano a dipenderne, e poi un apply la cancella perché non è nello stato.

La console non è la fonte della verità. La tua IaC lo è. La console è una bugia.

Il Workflow di Import: La Risposta Corretta a "Esiste Già"

Quando una risorsa esiste in AWS ma non nella tua configurazione Terraform (creata da qualcuno nella console, creata manualmente via CLI, o migrata da un altro strumento), l'azione corretta è importarla:

Importare una risorsa esistente nello stato Terraform

./deploy.sh --infra --import aws_s3_bucket.service existing-bucket-name
./deploy.sh --infra --import aws_dynamodb_table.service_files serviceFiles
./deploy.sh --infra --import aws_lambda_function.service_api myapp-service-api

Dopo l'import, esegui plan:
./deploy.sh --infra

Il plan mostra la differenza tra ciò che esiste in AWS e ciò che dice la tua configurazione. Correggi la configurazione finché il plan non mostra cambiamenti inaspettati. Poi applica per sincronizzare il file di stato.

Mai cancellare una risorsa dalla console e ricrearla via Terraform. Questo distrugge i dati e rompe le dipendenze. Importa → riconcilia → applica.

L'Unico Uso Legittimo della Console

La console è appropriata per:

  • Esplorazione in sola lettura. Log CloudWatch, metriche CloudWatch, storico invocazioni Lambda, ispezione elementi DynamoDB.
  • Operazioni di emergenza con import immediato. Se la produzione è down e la soluzione richiede una modifica nella console, fai la modifica; ma documentala e importala in Terraform nella stessa sessione di lavoro.

Tutto qui. Tutto ciò che crea, modifica o cancella risorse appartiene alla IaC.

Lo Script di Deploy Come Punto di Ingresso Unico

I comandi raw terraform apply o tofu apply eseguiti direttamente sull'host sono pericolosi perché bypassano il flusso di lavoro di deploy standard del team (validazione, lint, revisione del plan). Crea uno script di deploy che wrappa tutte le operazioni Terraform:

#!/bin/bash
# deploy.sh - l'unico modo per interagire con l'infrastruttura

case “$1” in –infra) case “$2” in –apply) run_apply ;; –plan) run_plan ;; –validate) run_validate ;; –tflint) run_tflint ;; –import) run_import “$3” “$4” ;; *) run_plan ;; # default: solo plan (sicuro) esac ;; esac

Proprietà chiave di un buono script di deploy:

  • Default a plan. Eseguire ./deploy.sh --infra senza secondo argomento dovrebbe mostrare il plan, mai applicare. Applicare richiede un'intenzione esplicita (--apply).
  • Sempre validare prima di applicare. Esegui terraform validate e tflint prima di ogni apply. Rifiuta il deploy se uno dei due fallisce.
  • Sempre mostrare il plan prima di applicare. Anche per --apply, mostra l'output del plan e richiedi conferma in modalità interattiva.
  • Var-file coerente. Includi sempre lo stesso var-file (vars/production.tfvars). Mai applicare senza.

Revisione del Plan Come Gate

Prima che qualsiasi PR infrastrutturale venga mergiata, l'output del plan dovrebbe essere visibile nella PR:
## Terraform Plan

Terraform will perform the following actions:
module.service_api.aws_lambda_function.service_api will be updated in-place
~ resource "aws_lambda_function" "service_api" {
~ timeout = 30 -> 60
}

Plan: 0 to add, 1 to change, 0 to destroy.

module.service_api.aws_lambda_function.service_api will be updated in-place

~ resource "aws_lambda_function" "service_api" {
~ timeout = 30 -> 60
}

Plan: 0 to add, 1 to change, 0 to destroy.

Questo realizza due cose:

  1. Il revisore può vedere esattamente cosa cambierà in produzione prima di approvare.
  2. L'output del plan è un registro storico di ciò che era previsto al momento del merge.

Un plan che mostra distruzioni inaspettate (N to destroy) dovrebbe bloccare la PR finché l'autore non spiega perché.

La Regola del No-Exclude

Un errore comune quando un apply fallisce: escludere la risorsa che fallisce e applicare comunque.

# SBAGLIATO tofu apply -exclude='aws_bedrockagent_knowledge_base.kb'

Questo crea un apply parziale: alcune risorse sono state aggiornate, altre no. Il file di stato ora riflette una configurazione parzialmente applicata. La risorsa esclusa è fuori sincrono con tutto ciò che dipende da essa.

La risposta corretta a una risorsa che fallisce:

  1. Lascia che l'apply finisca. Non interromperlo.
  2. Comprendi perché la risorsa ha fallito.
  3. Correggi la configurazione o importa la risorsa esistente.
  4. Esegui apply di nuovo fino a zero errori.

Se l'errore è "la risorsa esiste già": importala.
Se l'errore è un mismatch di configurazione: correggi la configurazione per adattarla alla risorsa esistente.
Se l'errore è un problema di ordinamento delle dipendenze: correggi depends_on.

Mai: escludere, commentare o altrimenti saltare risorse. La configurazione IaC deve sempre riflettere la realtà.

Il Force-Unlock

Terraform utilizza una tabella di lock (DynamoDB) per prevenire apply concorrenti. Se un lock è bloccato (Lambda in timeout durante l'apply, processo ucciso), vedrai:

Error: Error locking state: Error acquiring the state lock

La soluzione allettante: terraform force-unlock. Questo è pericoloso se un altro apply è effettivamente in esecuzione - il force-unlock di un apply attivo causa corruzione.

La risposta corretta:

  1. Controlla se qualche apply è effettivamente in esecuzione (controlla CloudWatch per la Lambda di deploy, verifica con i colleghi).
  2. Se nessun apply è in esecuzione e sei certo che il lock sia obsoleto, allora force-unlock.
  3. Documenta che hai forzato un lock - è un segnale per investigare perché l'apply precedente non ha fatto pulizia correttamente.

Mai fare force-unlock come prima risposta. Verifica sempre che non ci sia un apply attivo prima.

Rilevamento del Drift

Anche con una stretta disciplina IaC, il drift si accumula. AWS può auto-modificare risorse (es. Cognito che aggiorna un'associazione di trigger Lambda), o un membro del team può fare una modifica di emergenza nella console e dimenticarsi di importarla.

Programma un'esecuzione regolare del plan (settimanalmente o dopo periodi di attività significativa) senza apply:

# In CI/CD: plan settimanale programmato ./deploy.sh --infra --plan 2>&1 | tee plan-output.txt # Avvisa se il plan mostra cambiamenti inaspettati

Se il plan mostra cambiamenti che non dovrebbero esistere, investiga prima che il prossimo apply li distrugga.

Punti Chiave

  • La console AWS è per leggere, non per scrivere. Ogni modifica all'infrastruttura appartiene alla IaC.
  • Quando una risorsa esiste in AWS ma non in Terraform: importala, riconcilia la configurazione, poi applica.
  • Mai usare -exclude per aggirare una risorsa che fallisce. Correggi la causa radice.
  • Wrappa tutte le operazioni Terraform in uno script di deploy. Default a plan; richiedi intenzione esplicita per applicare.
  • Metti l'output del plan in ogni PR infrastrutturale. Le distruzioni inaspettate dovrebbero bloccare la PR.
  • Mai fare force-unlock senza verificare che nessun apply attivo sia in esecuzione.
  • Esegui controlli plan programmati per rilevare il drift tempestivamente.