DevOps Elastic Hayway
Document

SUBSCRIBE TO GET FULL ACCESS TO THE E-BOOKS FOR FREE 🎁SUBSCRIBE NOW

Professional Dropdown with Icon

SUBSCRIBE NOW TO GET FREE ACCESS TO EBOOKS

TerraformLesson 7 / 2410 min readUpdated September 13, 2026

State Terraform distant : S3 + verrouillage

À la fin : pourquoi le state local échoue en équipe/CI ; backend s3 (ca-central-1, key, encrypt) ; use_lockfile (TF ≥ 1.10) vs DynamoDB legacy ; bootstrap bucket ; init -migrate-state ; naming keys, IAM least-privilege, flags CI, recovery versioning — sans casser la prod.

Niveau : Débutant → Intermédiaire · Temps : 45–60 min · Versions : Terraform 1.16.2 (≥ 1.9 ; lock ≥ 1.10), AWS provider ~> 5.0 · Vérifié : 2026-09-10

Slug live / canonical : terraform-state-file-on-s3 (WP #1630) · Alias draft : terraform-state-backend-s3 · Série : Terraform

Prérequis

  • Workflow : init → plan → apply → destroy
  • Providers et Variables / outputs
  • Terraform ≥ 1.9 (idéalement ≥ 1.10 pour use_lockfile), AWS CLI v2, profil lab (aws sts get-caller-identity OK)

Coût : bucket S3 quasi nul en lab + quelques API. Supprimez le bucket de lab en fin. Aucun secret dans HCL, tfvars commités ni logs CI.

Auth sans secrets. AWS_PROFILE / SSO uniquement. Jamais d’access_key / secret_key dans provider ou backend. State sensible : encrypt + IAM restreint.

Ce que nous allons construire

Lab backend S3 (ca-central-1)
  ├── bootstrap (hors backend) → versioning + encrypt + BPA
  ├── backend.tf               → s3 + use_lockfile = true
  ├── versions / providers     → TF ≥ 1.9 (≥ 1.10 lock), aws ~> 5.0
  ├── .gitignore               → *.tfstate*, .terraform/
  └── init -migrate-state → plan → state list/show → nettoyage

(Schéma QA : 09-state-s3.png — alt : « Backend Terraform S3 : state distant, chiffrement et use_lockfile ».)

Enrichit le post live #1630 (/terraform-state-file-on-s3/) : state distant S3 + verrouillage moderne 2026.

Étape 1 — Pourquoi un state distant (équipe, CI, pas de .tfstate en Git)

terraform.tfstate est la source de vérité (IDs, ARNs, attributs). En local, chaque poste/job a sa copie → désync et écrasements.

Contexte Risque state local
Équipe Deux apply divergents → orphelins / destructions
CI/CD Runner sans state → Terraform veut tout (re)créer
Git Committer .tfstate = fuites + conflits de merge

Le backend S3 partage un state. Versionnez le HCL, jamais le state. Les workspaces segmentent ensuite env/stacks.

Étape 2 — Contenu sensible du state

Même avec sensitive = true, le state reste en clair sans chiffrement objet : mots de passe, tokens, chaînes de connexion, ARNs, IPs.

Conséquences : encrypt = true + SSE-S3/KMS ; IAM sur le préfixe key ; BPA ; versioning ; jamais de state pull dans un ticket. sensitive masque la CLI — il ne chiffre pas le state.

Étape 3 — Backend s3 : bucket, key, region, encrypt

Le bloc backend est dans terraform { } (souvent backend.tf). Pas d’interpolation de variables : littéraux, ou partiel + -backend-config.

# backend.tf — moderne (TF ≥ 1.10)
terraform {
  required_version = ">= 1.10.0"
  required_providers {
    aws = { source = "hashicorp/aws", version = "~> 5.0" }
  }
  backend "s3" {
    bucket       = "deh-tfstate-LAB-SUFFIXE-UNIQUE"
    key          = "labs/terraform-state-backend-s3/terraform.tfstate"
    region       = "ca-central-1"
    encrypt      = true
    use_lockfile = true
  }
}
Argument Rôle
bucket Bucket state (nom globalement unique)
key Chemin objet (1 stack/env = 1 key)
region ca-central-1
encrypt Chiffre state (+ lockfile) à l’upload
use_lockfile Verrouillage natif S3 (étape 4)

Provider séparé : region = "ca-central-1", aucune clé. Backend via AWS_PROFILE / rôle.

Convention de naming des keys

Hiérarchie stable : <component>/<env>/terraform.tfstate — ex. network/prod, platform-eks/staging, app-billing/dev. Une key par stack limite le blast radius et accélère les plans. Évitez une key monolithique pour tout le compte.

Étape 4 — Verrouillage : use_lockfile vs DynamoDB (legacy)

Sans verrou, deux apply simultanés corrompent le state. Historiquement : DynamoDB (dynamodb_table). Depuis TF 1.10, HashiCorp recommande le lock natif S3 : use_lockfile = true crée un .tflock (conditional writes). Plus de DynamoDB pour les nouveaux projets.

Approche Versions Statut 2026
use_lockfile = true ≥ 1.10 (≥ 1.11 idéal) Moderne / recommandé
dynamodb_table Ancien standard Legacy — encore vu en prod

Migration douce : (1) upgrade équipe+CI ≥ 1.10 ; (2) ajoutez use_lockfile = true en gardant dynamodb_table, puis init -reconfigure (double lock) ; (3) après apply OK, retirez dynamodb_table, init -reconfigure, supprimez la table si plus de références. Encore en 1.9 ? Gardez DynamoDB (LockID String, on-demand).

# Legacy — à migrer vers use_lockfile
backend "s3" {
  bucket         = "deh-tfstate-exemple"
  key            = "prod/app/terraform.tfstate"
  region         = "ca-central-1"
  encrypt        = true
  dynamodb_table = "deh-tfstate-locks"
}

Lab : TF ≥ 1.10 et use_lockfile seul.

Étape 5 — Bootstrap du bucket (chicken-egg)

Le backend ne crée pas le bucket. Créez-le hors backend (CLI ou mini projet state local jetable).

export AWS_PROFILE=lab AWS_REGION=ca-central-1
BUCKET="deh-tfstate-lab-$(whoami)-$(date +%Y%m%d)"

aws s3api create-bucket --bucket "$BUCKET" --region ca-central-1 
  --create-bucket-configuration LocationConstraint=ca-central-1
aws s3api put-public-access-block --bucket "$BUCKET" 
  --public-access-block-configuration 
  BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
aws s3api put-bucket-versioning --bucket "$BUCKET" 
  --versioning-configuration Status=Enabled
aws s3api put-bucket-encryption --bucket "$BUCKET" 
  --server-side-encryption-configuration 
  '{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"}}]}'
echo "Bucket : $BUCKET"

Checklist : BPA, versioning, chiffrement défaut, nom unique → backend.tf (sans credentials). Optionnel : lifecycle 90 j sur versions non courantes.

Étape 6 — terraform init -migrate-state

mkdir -p ~/terraform-state-s3-lab && cd ~/terraform-state-s3-lab
# HCL + backend.tf (bucket réel, use_lockfile = true)
export AWS_PROFILE=lab AWS_REGION=ca-central-1
terraform init -migrate-state

Confirmez bucket/key. Projet neuf : init ; sinon -migrate-state ou -reconfigure. Vérifiez S3, puis plan (aucun changement attendu).

Étape 7 — Aperçu terraform state list / show

terraform state list
terraform state show aws_s3_bucket.lab_example
# state pull → JSON sensible : local contrôlé uniquement
Commande Usage
state list Inventaire des adresses
state show ADDR Détail d’une ressource
state pull JSON complet — ne pas logger

mv / rm / import ailleurs ; ici : lire avant de toucher.

Étape 8 — .gitignore tfstate

*.tfstate
*.tfstate.*
.terraform/
crash.log
crash.*.log
override.tf
*_override.tf
*.tfvars
!example.tfvars

Committez le HCL — jamais .tfstate. Secrets via SSO / variables CI.

Étape 9 — IAM least-privilege (state + .tflock)

CI et ingénieurs : ListBucket sur le bucket ; GetObject/PutObject/DeleteObject seulement sur la key state et le .tflock adjacent.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::BUCKET"
    },
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
      "Resource": [
        "arn:aws:s3:::BUCKET/network/prod/terraform.tfstate",
        "arn:aws:s3:::BUCKET/network/prod/terraform.tfstate.tflock"
      ]
    }
  ]
}

Runners via OIDC (rôle court), jamais de clés longues en CI.

Étape 10 — CI/CD : -input=false, -lock-timeout, plan artifact

Pas d’invite clavier ; tolérez une contention de lock brève :

terraform init -input=false -backend-config=env/prod.backend.hcl
terraform plan -input=false -lock-timeout=5m -out=tfplan
terraform apply -input=false -lock-timeout=5m tfplan

Gardez tfplan en artifact : l’apply déploie exactement le plan reviewé. Voir workflow.

Étape 11 — Recovery versioning et partage d’outputs

Rollback : listez les versions S3, téléchargez une version antérieure, puis terraform state push — prudence (écrase le state courant ; aucun apply en cours ; testez en lab).

aws s3api list-object-versions --bucket "$BUCKET" --prefix network/prod/terraform.tfstate
aws s3api get-object --bucket "$BUCKET" --key network/prod/terraform.tfstate 
  --version-id VERSION_ID previous.tfstate
# terraform state push previous.tfstate  # après revue seulement

Partage inter-stacks : terraform_remote_state lit les outputs d’une autre key — pratique, mais expose le state entier. Plus strict : publiez peu de valeurs en SSM et lisez via aws_ssm_parameter (data sources).

Étape 12 — Checklist

  1. State S3 ca-central-1 ; encrypt = true.
  2. Lock : use_lockfile = true (TF ≥ 1.10) ; DynamoDB = legacy.
  3. Bucket bootstrapé avant backend (versioning, BPA, encrypt).
  4. Key component/env/terraform.tfstate ; IAM = state + .tflock.
  5. init -migrate-state → S3 → plan.
  6. .gitignore : .tfstate, .terraform/.
  7. CI : -input=false, -lock-timeout, artifact tfplan ; pas de clés HCL.
  8. Lab ≠ bucket prod ; recovery versioning avec prudence.

Nettoyage

Ne détruisez jamais un bucket state prod sans procédure (backup versioning, freeze CI).

cd ~/terraform-state-s3-lab
terraform destroy -auto-approve
aws s3 rm "s3://${BUCKET}" --recursive
aws s3api delete-bucket --bucket "$BUCKET" --region ca-central-1
rm -rf ~/terraform-state-s3-lab

Avec versioning : videz les versions avant delete-bucket. Lock orphelin (.tflock) : confirmez qu’aucun apply n’est en cours.

Erreurs fréquentes

Symptôme Cause Correction
Error acquiring the state lock Apply/CI concurrent ou lock orphelin Attendez ; force-unlock seulement si certitude
Accès S3 / workspaces IAM, region, bucket absent Profil, ca-central-1, nom bucket
use_lockfile inconnu TF &lt; 1.10 Upgrade ≥ 1.10
Chicken-egg Bucket absent Bootstrap CLI / state local (étape 5)
State encore local Pas de -migrate-state init -migrate-state
Fuite Git *.tfstate commités .gitignore (+ purge historique)

Quiz (3 questions)

1. Pourquoi éviter de committer terraform.tfstate ?

  • A. Fichier trop volumineux pour Git
  • B. Données sensibles + conflits / désync en équipe
  • C. Terraform refuse d’appliquer si le state est versionné

2. Quelle option active le verrouillage natif S3 (TF 1.10+) ?

  • A. dynamodb_table = "locks"
  • B. use_lockfile = true
  • C. locking = "s3"

3. Comment résoudre le chicken-egg du bucket de state ?

  • A. Laisser le backend créer le bucket dès le premier init
  • B. Créer le bucket hors backend (CLI / bootstrap local), puis init
  • C. Désactiver encrypt jusqu’au second apply

Réponses : 1‑B · 2‑B · 3‑B

Pourquoi / quand passer un state distant S3

Le state local convient à un lab solo. Dès que deux personnes ou une CI appliquent le même root, vous avez besoin d’un state partagé, chiffré, versionné, avec verrouillage pour éviter deux apply concurrents. Ce tuto pose le backend s3 en Region ca-central-1, compatible Terraform ≥ 1.9 (use_lockfile moderne vs DynamoDB legacy).

Contenu sensible — rappel opérationnel

Le state peut contenir des IDs, des sorties, parfois des secrets si une resource mal conçue les y a placés. Conséquences :

  • Ne jamais committer .tfstate ni *.tfstate.backup.
  • Chiffrer le bucket (encrypt = true), bloquer l’accès public, restreindre l’IAM au least privilege.
  • Éviter de stocker des mots de passe en clair dans des resources ; préférez Secrets Manager / SSM et références.

Pièges backend S3

  • Chicken-egg : créer le bucket de backend avant de migrer (bootstrap manuel ou script one-shot).
  • Mauvaise key (chemin d’objet) partagée entre projets : collision de state.
  • Oublier le lock : deux pipelines apply = state corrompu possible.
  • Migrer avec init -migrate-state sans backup ni versioning bucket.
  • Laisser des droits s3:* sur tout le compte « pour que ça marche ».

Quand rester en state local

Ateliers courts, machines jetables, zéro partage. Passez distant dès le premier dépôt d’équipe.

FAQ state & backend

use_lockfile remplace-t-il DynamoDB ? Pour les versions récentes du backend S3, le lockfile natif est la voie recommandée ; DynamoDB reste documenté en legacy selon votre version.

Puis-je lire le state à la main ? terraform state list / show ; évitez d’éditer le JSON brut.

Que faire après un lock orphelin ? Diagnostiquer le process/CI bloqué ; ne forcez le unlock qu’en connaissance de cause.

Versioning S3 utile ? Oui : récupération après un apply catastrophique ou un overwrite.

Outputs et remote state ? terraform_remote_state data source pour partager des sorties entre roots — avec discipline (couplage).

Secrets dans le backend block ? Pas de clés en clair ; credentials via profil/instance role, comme pour le provider AWS.

Pour aller plus loin

Maillage série Terraform (P2)

← Précédent Variables / Locals / Outputs
→ Suivant Data sources
Aussi Workflow · Providers · Workspaces · Overview
Carte Overview · Install · Workflow · Blocks · Providers · Variables · State S3 · Data sources · Loops · Conditionals · Dynamic · Modules · Workspaces · labs AWS

Cas réel — deux laptops, un state local, un apply qui écrase

Alice applique en local, Bob aussi : le dernier terraform.tfstate gagne, les IDs divergent, le compte AWS a des orphelins. Le backend S3 + lock (natif use_lockfile ou Dynamo selon votre version) existe pour ça. Activez le chiffrement bucket, bloquez le public access, et ne commitez jamais *.tfstate.

Piège : migrer le backend au milieu d’un lab sans terraform init -migrate-state. Terraform croit à un nouveau state vide et propose de tout recréer. Stop. Relisez le message d’init. En ca-central-1, le bucket de state n’est pas le bucket métier du tuto workflow — séparez-les dès le nommage.

À retenir : le state est plus sensible que le HCL. Traitez-le comme un secret d’infrastructure.

Retour parcours Terraform — hub de la série et leçons sœurs.

Share your love

Leave a Reply

Your email address will not be published. Required fields are marked *