Cloud  /  Terraform

IaC Terraform 50 guides · updated 2026

Infrastructure as code done right — providers, state, reusable modules, and the workflow patterns that keep multi-cloud deployments sane in 2026.

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 StateRemote State Solution
Not shared between teammatesCentralized storage everyone reads
No locking — concurrent applies corrupt stateBackend-native locking (DynamoDB, GCS, Blob)
Accidentally committed to gitState lives outside the repo
Lost if disk failsDurable cloud storage with versioning
No audit trailCloudTrail/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):

state-bootstrap/main.tf
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
}
}
Terminal window
# Create the GCS bucket for state
gsutil mb -p my-project -l us-central1 gs://mycompany-terraform-state
gsutil versioning set on gs://mycompany-terraform-state
gsutil uniformbucketlevelaccess set on gs://mycompany-terraform-state

Azure Blob Storage Backend

terraform {
backend "azurerm" {
resource_group_name = "terraform-state-rg"
storage_account_name = "mycompanytfstate"
container_name = "tfstate"
key = "production.terraform.tfstate"
}
}
Terminal window
# 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 mycompanytfstate

Terraform 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 location
terraform {
backend "s3" {}
}
backends/production.tfbackend
bucket = "mycompany-terraform-state"
key = "production/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-state-lock"
Terminal window
# Initialize with environment-specific backend config
terraform 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 state
data "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 configuration
resource "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:

Terminal window
# By environment + component
production/networking/terraform.tfstate
production/database/terraform.tfstate
production/application/terraform.tfstate
staging/networking/terraform.tfstate
staging/database/terraform.tfstate
# By team + environment
platform/production/terraform.tfstate
data-engineering/production/terraform.tfstate
# By account + region + environment
aws-123456789012/us-east-1/production/terraform.tfstate

Keep the key consistent and document the convention in your team’s runbook.