State Management
Chapter objectives
- Understand the role of the state
- Configure a remote backend
- Manipulate the state
- Manage the state as a team
1 - What is the State?
Definition
The state is a JSON file that stores the current state of your infrastructure.
Role of the state
| Function | Description |
|---|---|
| Mapping | Links config resources to real resources |
| Metadata | Stores dependencies between resources |
| Performance | Cache to avoid API requests |
| Synchronization | Source of truth for the team |
State content
{
"version": 4,
"terraform_version": "1.6.0",
"resources": [
{
"mode": "managed",
"type": "aws_instance",
"name": "web",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 1,
"attributes": {
"id": "i-0123456789abcdef0",
"ami": "ami-12345678",
"instance_type": "t2.micro",
"public_ip": "54.123.45.67"
}
}
]
}
]
}
2 - Local vs remote state
Local state
Problems:
- No team sharing
- Risk of loss
- No locking
- Secrets in plaintext on disk
Remote state (Remote Backend)
Advantages:
- Team sharing
- Locking
- Encryption
- Versioning
- Backup
3 - Configuring backends
S3 Backend (AWS)
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "eu-west-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
Create the backend resources
# backend-setup/main.tf
# Bucket S3 pour le state
resource "aws_s3_bucket" "terraform_state" {
bucket = "my-terraform-state"
}
resource "aws_s3_bucket_versioning" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
resource "aws_s3_bucket_public_access_block" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# DynamoDB pour le locking
resource "aws_dynamodb_table" "terraform_locks" {
name = "terraform-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
}
Azure Backend
terraform {
backend "azurerm" {
resource_group_name = "terraform-state-rg"
storage_account_name = "tfstate12345"
container_name = "tfstate"
key = "prod.terraform.tfstate"
}
}
GCS Backend (Google Cloud)
terraform {
backend "gcs" {
bucket = "my-terraform-state"
prefix = "prod"
}
}
Terraform Cloud Backend
terraform {
cloud {
organization = "my-org"
workspaces {
name = "my-workspace"
}
}
}
4 - State locking
Concept
Force unlock
# En cas de crash, le lock peut rester
terraform force-unlock LOCK_ID
# Le LOCK_ID est affiché dans le message d'erreur
5 - State commands
List resources
# Lister toutes les ressources
terraform state list
# Filtrer par type
terraform state list aws_instance.web
terraform state list 'module.vpc.*'
View details
# Détails d'une ressource
terraform state show aws_instance.web
# Tout le state
terraform show
terraform show -json
Move resources
# Renommer une ressource
terraform state mv aws_instance.web aws_instance.web_server
# Déplacer dans un module
terraform state mv aws_instance.web module.compute.aws_instance.web
# Déplacer depuis un module
terraform state mv module.compute.aws_instance.web aws_instance.web
Remove from the state
# Retirer une ressource du state (sans la détruire dans le cloud)
terraform state rm aws_instance.web
# La ressource existe toujours dans AWS mais Terraform ne la gère plus
Import resources
# Importer une ressource existante
terraform import aws_instance.web i-0123456789abcdef0
# Importer avec module
terraform import module.vpc.aws_vpc.main vpc-12345678
6 - Workspaces
Concept
Workspaces allow you to manage multiple states with the same configuration.
Commands
# Lister les workspaces
terraform workspace list
# Créer un workspace
terraform workspace new staging
terraform workspace new production
# Sélectionner un workspace
terraform workspace select staging
# Afficher le workspace actuel
terraform workspace show
# Supprimer un workspace
terraform workspace delete staging
Usage in the config
# Utiliser le nom du workspace
resource "aws_instance" "web" {
count = terraform.workspace == "production" ? 3 : 1
ami = data.aws_ami.ubuntu.id
instance_type = terraform.workspace == "production" ? "t3.large" : "t3.micro"
tags = {
Name = "web-${terraform.workspace}"
Environment = terraform.workspace
}
}
# Variables par workspace
locals {
env_config = {
default = {
instance_type = "t2.micro"
count = 1
}
staging = {
instance_type = "t2.small"
count = 2
}
production = {
instance_type = "t2.large"
count = 3
}
}
config = local.env_config[terraform.workspace]
}
Backend with workspaces
# Le workspace est ajouté au chemin du state
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "myproject/terraform.tfstate"
region = "eu-west-1"
# State path: myproject/env:/staging/terraform.tfstate
workspace_key_prefix = "env:"
}
}
7 - State Refresh
Synchronize the state
# Refresh le state avec l'état réel
terraform refresh
# Ou via plan
terraform plan -refresh-only
# Appliquer le refresh
terraform apply -refresh-only
Use case
8 - State and security
Sensitive data
The state contains sensitive data:
- Passwords
- API keys
- Private IP addresses
Best practices
# ✅ Chiffrer le state
terraform {
backend "s3" {
bucket = "my-terraform-state"
encrypt = true
}
}
# ✅ Limiter les accès
# Utiliser IAM policies strictes pour le bucket S3
# ✅ Activer le versioning
# Permet de récupérer un state corrompu
# ✅ Ne jamais commiter le state
# .gitignore
*.tfstate
*.tfstate.*
Mask sensitive outputs
output "db_password" {
value = random_password.db.result
sensitive = true
}
9 - Recovery and backup
S3 versioning
resource "aws_s3_bucket_versioning" "state" {
bucket = aws_s3_bucket.terraform_state.id
versioning_configuration {
status = "Enabled"
}
}
Recover a previous version
# Lister les versions
aws s3api list-object-versions \
--bucket my-terraform-state \
--prefix prod/terraform.tfstate
# Télécharger une version
aws s3api get-object \
--bucket my-terraform-state \
--key prod/terraform.tfstate \
--version-id XXXXX \
terraform.tfstate.backup
Pull and push the state
# Télécharger le state
terraform state pull > terraform.tfstate.backup
# Pousser un state (DANGER)
terraform state push terraform.tfstate.fixed
10 - Best practices
State organization
State per environment
terraform-state-bucket/
├── networking/
│ ├── dev/terraform.tfstate
│ ├── staging/terraform.tfstate
│ └── prod/terraform.tfstate
├── compute/
│ ├── dev/terraform.tfstate
│ ├── staging/terraform.tfstate
│ └── prod/terraform.tfstate
└── database/
├── dev/terraform.tfstate
├── staging/terraform.tfstate
└── prod/terraform.tfstate
Security checklist
☐ Backend distant configuré
☐ Chiffrement activé
☐ Versioning activé
☐ Locking configuré (DynamoDB)
☐ Accès restreint (IAM)
☐ State jamais dans Git
☐ Outputs sensibles marqués
Summary
Key points
- The state is the source of truth
- Always use a remote backend in production
- Locking prevents corruption
- Encrypt and version the state
- Split the state for large projects
Practical exercises
- Configure an S3 backend with DynamoDB
- Create workspaces for dev and prod
- Import an existing resource
- Practice the state mv and rm commands