Terraform Remote State
Remote state stores terraform.tfstate in a shared, durable location instead of on a local disk. It’s required for team collaboration, enables state locking to prevent corruption, and provides encryption at rest for secrets stored in state.
Why Use Remote State
| Problem with Local State | Remote State Solution |
|---|---|
| Not shared between teammates | Centralized storage everyone reads |
| No locking — concurrent applies corrupt state | Backend-native locking (DynamoDB, GCS, Blob) |
| Accidentally committed to git | State lives outside the repo |
| Lost if disk fails | Durable cloud storage with versioning |
| No audit trail | CloudTrail/access logs on state access |
AWS S3 + DynamoDB Backend
The most widely used backend for AWS teams:
terraform { backend "s3" { bucket = "mycompany-terraform-state" key = "environments/production/terraform.tfstate" region = "us-east-1" encrypt = true kms_key_id = "arn:aws:kms:us-east-1:123456789012:key/mrk-..." dynamodb_table = "terraform-state-lock"
# Optional: enforce server-side encryption check force_path_style = false }}Create the state infrastructure first (bootstrap):
resource "aws_s3_bucket" "state" { bucket = "mycompany-terraform-state"}
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" kms_master_key_id = aws_kms_key.state.arn } }}
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}
resource "aws_dynamodb_table" "state_lock" { name = "terraform-state-lock" billing_mode = "PAY_PER_REQUEST" hash_key = "LockID"
attribute { name = "LockID" type = "S" }}GCP Cloud Storage Backend
terraform { backend "gcs" { bucket = "mycompany-terraform-state" prefix = "environments/production" # Encryption via Cloud KMS is configured on the bucket, not here }}# Create the GCS bucket for stategsutil mb -p my-project -l us-central1 gs://mycompany-terraform-stategsutil versioning set on gs://mycompany-terraform-stategsutil uniformbucketlevelaccess set on gs://mycompany-terraform-stateAzure Blob Storage Backend
terraform { backend "azurerm" { resource_group_name = "terraform-state-rg" storage_account_name = "mycompanytfstate" container_name = "tfstate" key = "production.terraform.tfstate" }}# Create Azure state storage (run once)az group create --name terraform-state-rg --location eastus
az storage account create \ --name mycompanytfstate \ --resource-group terraform-state-rg \ --sku Standard_LRS \ --encryption-services blob
az storage container create \ --name tfstate \ --account-name mycompanytfstateTerraform Cloud / HCP Terraform Backend
terraform { cloud { organization = "mycompany" workspaces { name = "production-aws-infra" } }}Terraform Cloud provides state storage, locking, history, and remote execution for free on small teams. No separate DynamoDB or GCS bucket required.
Partial Backend Configuration
Separate backend location from backend type — useful for supporting multiple environments from one config:
# main.tf — just the backend type, no locationterraform { backend "s3" {}}bucket = "mycompany-terraform-state"key = "production/terraform.tfstate"region = "us-east-1"encrypt = truedynamodb_table = "terraform-state-lock"# Initialize with environment-specific backend configterraform init -backend-config="backends/production.tfbackend"terraform init -backend-config="backends/staging.tfbackend"Reading Another Configuration’s State
Use terraform_remote_state data source to consume outputs from a separate configuration:
# Read outputs from the networking team's Terraform statedata "terraform_remote_state" "networking" { backend = "s3" config = { bucket = "mycompany-terraform-state" key = "shared/networking/terraform.tfstate" region = "us-east-1" }}
# Use networking outputs in this configurationresource "aws_instance" "app" { subnet_id = data.terraform_remote_state.networking.outputs.private_subnet_ids[0] vpc_security_group_ids = [ data.terraform_remote_state.networking.outputs.app_security_group_id ]}Remote state data sources require the state being read to have the relevant outputs defined. This creates a soft dependency between teams — use it for stable, infrequently-changing values like VPC IDs.
State Key Naming Conventions
Organize state files logically — the key is just a path in the bucket:
# By environment + componentproduction/networking/terraform.tfstateproduction/database/terraform.tfstateproduction/application/terraform.tfstate
staging/networking/terraform.tfstatestaging/database/terraform.tfstate
# By team + environmentplatform/production/terraform.tfstatedata-engineering/production/terraform.tfstate
# By account + region + environmentaws-123456789012/us-east-1/production/terraform.tfstateKeep the key consistent and document the convention in your team’s runbook.