Skip to main content

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

FunctionDescription
MappingLinks config resources to real resources
MetadataStores dependencies between resources
PerformanceCache to avoid API requests
SynchronizationSource 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

  1. Configure an S3 backend with DynamoDB
  2. Create workspaces for dev and prod
  3. Import an existing resource
  4. Practice the state mv and rm commands

← Variables and Outputs | Modules →