Aller au contenu

TP 22 : Gérer un state distant et son verrouillage

TP 22 : Gérer un state distant et son verrouillage

Section intitulée « TP 22 : Gérer un state distant et son verrouillage »

À l’issue de ce TP, vous serez capable de :

  • expliquer pourquoi le state local est inadapté au travail en équipe ;
  • provisionner un bucket S3 dédié au stockage du state (chiffrement, versioning, blocage public) ;
  • migrer un state local existant vers un backend distant sans perte ;
  • activer le verrouillage natif S3 (use_lockfile) et observer un conflit de verrou ;
  • lever un verrou bloqué avec terraform force-unlock.
  • Terraform >= 1.11 (le verrouillage natif S3 est GA depuis la 1.11 ; il était expérimental en 1.10).
  • Un compte AWS et la CLI aws configurée (aws sts get-caller-identity doit répondre).
  • Deux terminaux ouverts pour simuler la concurrence.

Vérifiez votre version :

Fenêtre de terminal
terraform version

L’équipe plateforme d’InfraBank gère jusqu’ici son infrastructure avec un terraform.tfstate posé sur le poste d’un ingénieur. Deux incidents récents (un state écrasé, un fichier perdu au reformatage d’un laptop) imposent de passer à un state distant partagé, chiffré et verrouillé.

Créez un dossier de travail et une configuration minimale qui génère du state sans facturer de ressource réelle.

versions.tf
terraform {
required_version = ">= 1.11"
required_providers {
random = {
source = "hashicorp/random"
version = "~> 3.6"
}
time = {
source = "hashicorp/time"
version = "~> 0.12"
}
}
}
main.tf
resource "random_pet" "server" {
length = 2
}
output "server_name" {
value = random_pet.server.id
}
Fenêtre de terminal
terraform init
terraform apply -auto-approve
ls -l terraform.tfstate

Le state est bien un fichier local. C’est ce que nous allons déplacer.

Le bucket qui héberge le state ne doit pas se gérer lui-même. On le crée donc dans un dossier séparé (bootstrap/) avec son propre state local.

bootstrap/main.tf
terraform {
required_version = ">= 1.11"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.60"
}
}
}
provider "aws" {
region = "eu-west-3"
}
resource "aws_s3_bucket" "state" {
bucket = "infrabank-tfstate-${random_id.suffix.hex}"
}
resource "random_id" "suffix" {
byte_length = 4
}
resource "aws_s3_bucket_versioning" "state" {
bucket = aws_s3_bucket.state.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "state" {
bucket = aws_s3_bucket.state.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
}
}
}
resource "aws_s3_bucket_public_access_block" "state" {
bucket = aws_s3_bucket.state.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
output "bucket_name" {
value = aws_s3_bucket.state.bucket
}
Fenêtre de terminal
cd bootstrap
terraform init
terraform apply -auto-approve
terraform output bucket_name # notez ce nom
cd ..

Le versioning est indispensable : il permet de récupérer une version antérieure du state en cas de corruption, et il est requis pour un usage sûr du verrou natif.

Ajoutez le bloc backend dans la configuration principale. Remplacez le nom du bucket par celui obtenu à l’étape 2.

backend.tf
terraform {
backend "s3" {
bucket = "infrabank-tfstate-XXXXXXXX"
key = "plateforme/terraform.tfstate"
region = "eu-west-3"
encrypt = true
use_lockfile = true
}
}

Points importants :

  • le bloc backend n’accepte ni variable ni interpolation : il est lu très tôt, avant l’évaluation des variables. Toutes les valeurs sont littérales.
  • use_lockfile = true active le verrouillage natif S3. Terraform crée un objet plateforme/terraform.tfstate.tflock par écriture conditionnelle (If-None-Match). Plus besoin de table DynamoDB.
  • encrypt = true demande le chiffrement de l’objet state écrit par Terraform.
Fenêtre de terminal
terraform init -migrate-state

Terraform détecte le changement de backend et propose de copier le state local vers S3. Répondez yes. Vérifiez :

Fenêtre de terminal
aws s3 ls s3://infrabank-tfstate-XXXXXXXX/plateforme/
terraform state list

Le terraform.tfstate local est désormais vide (ou remplacé par un pointeur). La source de vérité est dans S3.

Pour observer le verrou, il faut une opération qui dure. Ajoutez une temporisation :

concurrence.tf
resource "time_sleep" "hold_lock" {
create_duration = "90s"
}

Terminal A :

Fenêtre de terminal
terraform apply -auto-approve
# reste 90 s à créer time_sleep.hold_lock ; le verrou est détenu

Terminal B (pendant que A tourne) :

Fenêtre de terminal
terraform plan

Terminal B échoue avec une erreur de type :

Error: Error acquiring the state lock
StatusCode: 412 ... PreconditionFailed
Lock Info:
ID: ...
Operation: OperationTypeApply
Who: ...@poste-b

Le code HTTP 412 PreconditionFailed est l’écriture conditionnelle refusée : l’objet .tflock existe déjà. Ce n’est pas un bug, c’est le verrou qui fait son travail. Quand A termine, l’objet .tflock est supprimé et B peut s’exécuter.

Si un processus meurt en cours de route (Ctrl-C brutal, coupure réseau), le verrou peut rester. Récupérez l’ID affiché dans l’erreur, puis :

Fenêtre de terminal
terraform force-unlock <LOCK_ID>

N’utilisez force-unlock que si vous êtes certain qu’aucune autre exécution n’est en cours : forcer le mauvais verrou ouvre la porte à un écrasement de state.

  • terraform state list renvoie les ressources depuis S3.
  • Un objet .tflock apparaît puis disparaît pendant un apply.
  • Le terminal B est bien bloqué tant que A détient le verrou.
Fenêtre de terminal
# Détruire l'infra de démonstration
terraform destroy -auto-approve
# Vider puis détruire le bucket (dans bootstrap/)
aws s3 rm s3://infrabank-tfstate-XXXXXXXX --recursive
cd bootstrap
terraform destroy -auto-approve