Retour au cours

infra / terraform

Backend distant : S3 et verrouillage DynamoDB

Leçon 131 exercice

Explication

Ce que vous allez apprendre

  • Créer l'infrastructure du backend lui-même (bucket S3 + table DynamoDB de verrouillage)
  • Comprendre pourquoi la clé de verrouillage DynamoDB s'appelle obligatoirement LockID
  • Résoudre un lock bloqué anormalement avec force-unlock, en toute connaissance des risques
  • Partager les outputs d'un projet Terraform avec un autre via terraform_remote_state
  • Protéger le bucket de state avec versioning, chiffrement et permissions strictes

Dans quel contexte ?

L'équipe a compris depuis la leçon sur le state pourquoi un backend distant est indispensable en équipe. Il faut maintenant construire concrètement cette infrastructure de backend elle-même — un problème un peu particulier, puisque Terraform a besoin d'un state pour exister, mais le backend qui doit héberger ce state n'existe pas encore.

D'abord, il faut résoudre ce paradoxe de démarrage

La solution pratique est simple : la configuration qui CRÉE le bucket S3 et la table DynamoDB s'applique elle-même avec un state LOCAL, une seule fois, au tout début du projet. Une fois ce backend créé, tous les AUTRES projets Terraform de l'organisation peuvent s'y connecter comme backend distant.

Cette étape unique fondatrice explique pourquoi de nombreuses organisations gèrent leur "backend du backend" dans un dépôt à part, appliqué manuellement et rarement modifié ensuite.

Composant AWSRôle
Bucket S3Stocke le fichier terraform.tfstate lui-même
Versioning S3Permet de restaurer une version antérieure en cas de corruption
Table DynamoDB (LockID)Empêche deux apply simultanés de s'exécuter en même temps
Chiffrement (SSE-KMS)Protège le contenu du state au repos, notamment les secrets qu'il contient

Prérequis

Il faut avoir bien compris pourquoi le state local pose problème en équipe (voir la leçon dédiée au state) pour saisir l'enjeu de chacun des composants mis en place dans cette leçon.

Une fois le bucket créé, pourquoi la table DynamoDB a-t-elle une contrainte de nommage précise ?

Terraform exige que la table de verrouillage possède une clé de hachage nommée EXACTEMENT LockID — ce n'est pas une convention arbitraire de cette leçon, mais une exigence technique du backend S3 lui-même. Sans ce nom exact, le mécanisme de verrouillage ne fonctionne tout simplement pas.

Maintenant que le backend est configuré, comment se comporte-t-il en usage réel ?

Le verrouillage devient automatique et transparent : un terraform apply en cours affiche "Acquiring state lock" et bloque toute exécution concurrente jusqu'à sa propre fin, garantissant qu'un seul changement s'applique à la fois sur le même state.

Piège fréquent

Si un processus Terraform est tué brutalement (Ctrl+C mal géré, crash de la CI), le lock peut rester bloqué indéfiniment alors qu'aucun apply n'est réellement en cours. terraform force-unlock <LOCK_ID> débloque cette situation, mais doit être utilisé avec une extrême précaution : forcer un déverrouillage alors qu'un apply est réellement toujours actif ailleurs peut corrompre le state.

Il reste un dernier cas d'usage puissant : partager des données entre projets Terraform séparés

Bonne pratique

Plutôt que de dupliquer manuellement un ID de sous-réseau d'un projet réseau vers un projet applicatif, data "terraform_remote_state" lit directement les outputs d'un AUTRE projet Terraform, à condition que ce projet expose bien les valeurs nécessaires via des output. Cela crée un couplage propre et explicite entre projets indépendants, sans copier-coller de valeurs qui finiraient par diverger.

Le backend distant maintenant solide, la prochaine leçon aborde l'automatisation complète de ce cycle plan/apply dans un pipeline CI/CD.

Commandes & code

Backend distant : S3 et verrouillage DynamoDB

hcl
# Étape 1 : créer l'infrastructure du backend elle-même (bucket S3 + table DynamoDB)
# Attention : cette config initiale est généralement appliquée avec un state LOCAL, une seule fois
resource "aws_s3_bucket" "terraform_state" {
  bucket = "mon-org-terraform-state"

  lifecycle {
    prevent_destroy = true               # protège le bucket de state contre une suppression accidentelle
  }
}

resource "aws_s3_bucket_versioning" "state_versioning" {
  bucket = aws_s3_bucket.terraform_state.id
  versioning_configuration {
    status = "Enabled"                   # permet de revenir à une version antérieure du state en cas de corruption
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "state_encryption" {
  bucket = aws_s3_bucket.terraform_state.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "aws:kms"
    }
  }
}

resource "aws_dynamodb_table" "terraform_locks" {
  name         = "terraform-locks"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"                 # nom de clé EXIGÉ par Terraform pour le verrouillage

  attribute {
    name = "LockID"
    type = "S"
  }
}
hcl
# Étape 2 : dans CHAQUE projet consommateur, configurer le backend
terraform {
  backend "s3" {
    bucket         = "mon-org-terraform-state"
    key            = "projets/api/terraform.tfstate"
    region         = "eu-west-3"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}
bash
terraform init                            # bascule vers le backend distant, migre le state si demandé

# Le verrouillage est AUTOMATIQUE : un "apply" en cours empêche un autre "apply" concurrent
terraform apply
# -> "Acquiring state lock. This may take a few moments..."
# Si un lock est bloqué anormalement (process tué brutalement) :
terraform force-unlock <LOCK_ID>          # à utiliser avec EXTRÊME précaution
hcl
# --- Partager des sorties entre projets Terraform via un state distant (remote state data source) ---
data "terraform_remote_state" "reseau" {
  backend = "s3"
  config = {
    bucket = "mon-org-terraform-state"
    key    = "projets/reseau/terraform.tfstate"
    region = "eu-west-3"
  }
}

resource "aws_instance" "app" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"
  subnet_id     = data.terraform_remote_state.reseau.outputs.subnet_prive_id   # output d'un AUTRE projet
}

Résumé

  • Un backend S3 + verrouillage DynamoDB (LockID en hash_key) est la référence pour le state distant en équipe sur AWS.
  • Le versioning S3 sur le bucket de state permet de restaurer une version antérieure en cas de corruption.
  • terraform_remote_state permet à un projet de consommer les outputs d'un autre projet Terraform indépendant.

Exercices pratiques

1 disponible
1

Mission : sécuriser le backend fondateur de l'organisation

Objectif : Construire l'infrastructure du backend distant elle-même et raisonner sur le paradoxe de démarrage et les risques du verrouillage.

Contexte

L'équipe veut enfin construire son bucket S3 et sa table DynamoDB de verrouillage, la toute première brique dont dépendront ensuite tous les autres projets Terraform de l'organisation. Un pipeline CI plante en plein apply peu après, et le lock DynamoDB reste bloqué alors qu'aucun apply n'est réellement en cours.

Résoudre l’exercice →