All guides
Practice now
D6 · Terraform State

Remote backends: S3, Azure, GCS, and HCP Terraform

Remote are essential for teamwork: they let you share , enable locking, and keep secrets off the local disk. This guide covers the most commonly used backends and their key differences.

Local backend (default)

If you don't configure a backend, Terraform uses the local backend by default. State is stored in terraform.tfstate in the working directory.

✅ Advantages of the local backend

  • • No configuration needed — works right away
  • • Ideal for learning and experimenting
  • • Perfect for solo personal projects

⚠️ Limitations of the local backend

  • • Locking only on the same machine — not suitable for teams
  • • State on local disk — not shared
  • • Secrets in plain text on the file system
  • • No version history of the state
# Explicit local backend (equivalent to the default behavior)
terraform {
  backend "local" {
    path = "terraform.tfstate"    # path of the state file
  }
}

# Or simply don't define any backend — local is the default

S3 backend (AWS)

The S3 backend is the most widely used in AWS environments. It stores state in an S3 bucket and, since Terraform 1.11, locks natively with a .tflock file in the same bucket (use_lockfile = true). DynamoDB locking (dynamodb_table) is deprecated.

terraform {
  backend "s3" {
    bucket         = "my-company-terraform-state"  # S3 bucket name
    key            = "prod/vpc/terraform.tfstate"  # path of the file inside the bucket
    region         = "us-east-1"                   # bucket region

    # Native S3 locking (Terraform 1.11+) — creates <key>.tflock in the bucket
    use_lockfile   = true                          # without this there is no locking

    # Encryption at rest
    encrypt        = true                          # SSE-S3 or SSE-KMS
    kms_key_id     = "arn:aws:kms:..."             # optional: use a specific KMS key

    # AWS profile (alternative to environment variables)
    profile        = "my-aws-profile"              # optional
  }
}

# Legacy (deprecated): dynamodb_table = "terraform-state-lock"
# with a DynamoDB table whose primary key is LockID (String)
ArgumentRequiredDescription
bucket✅ YesName of the S3 bucket where state is stored
key✅ YesPath of the object inside the bucket (e.g.: prod/tfstate)
region✅ YesAWS region of the bucket
use_lockfile❌ No** Without this there is no locking. Native S3 lock (1.11+); replaces dynamodb_table (deprecated).
encrypt❌ NoIf true, enables SSE encryption of the state file
kms_key_id❌ NoARN of the KMS key for SSE-KMS (more secure than SSE-S3)

Azure Storage backend

Azure Storage provides native locking via Blob Leases, without needing an additional resource (just like S3 with use_lockfile).

terraform {
  backend "azurerm" {
    resource_group_name  = "rg-terraform-state"
    storage_account_name = "samycompanytfstate"     # globally unique name
    container_name       = "tfstate"                # blob container
    key                  = "prod.terraform.tfstate" # blob name

    # Authentication (several options):
    # 1. ARM_* environment variables (recommended in CI/CD)
    # 2. Azure CLI (az login) for local use
    # 3. Managed Identity on VMs/AKS
  }
}

# Advantage over S3: locking is automatic via Blob Lease
# You don't need an additional resource for locking

GCS backend (Google Cloud Storage)

GCS also provides native locking, simplifying the setup compared to S3.

terraform {
  backend "gcs" {
    bucket  = "my-company-terraform-state"    # GCS bucket name
    prefix  = "terraform/prod"               # prefix for the state objects

    # Authentication: uses Application Default Credentials (gcloud auth)
    # Or: credentials = "/path/to/service-account.json" (not recommended to hardcode)
  }
}

# GCS supports native locking via object locking
# It doesn't need an additional resource (same as Azure, unlike S3)

HCP Terraform / cloud backend

has a deeper, special integration beyond traditional storage backends: it doesn't just store the state, it also runs plans and applies remotely.

# Option 1: cloud block (recommended for HCP Terraform)
terraform {
  cloud {
    organization = "my-company"

    workspaces {
      name = "prod-vpc"          # specific workspace
      # Or by tags:
      # tags = ["prod", "networking"]
    }
  }
}

# Option 2: "remote" backend (older form, still supported)
terraform {
  backend "remote" {
    organization = "my-company"

    workspaces {
      name = "prod-vpc"
    }
  }
}

# Authentication: terraform login
# Or: environment variable TF_TOKEN_app_terraform_io

🎯 cloud vs remote backend

The cloud block is the modern way to connect to HCP Terraform (introduced in Terraform 1.1). The remote backend is the older method that still works. For new configurations, always use the cloud block.

Partial configuration and migration

Partial backend configuration lets you avoid hardcoding sensitive information (credentials, bucket names) directly in the .tf files.

# PARTIAL CONFIGURATION — don't hardcode credentials in the backend block
terraform {
  backend "s3" {
    # Only the static (non-sensitive) configuration:
    region  = "us-east-1"
    encrypt = true
    # bucket and key are passed at init
  }
}

# Pass the remaining config in terraform init:
terraform init   -backend-config="bucket=my-company-tfstate"   -backend-config="key=prod/vpc/terraform.tfstate"   -backend-config="use_lockfile=true"

# Or from an HCL file (without the terraform {} block):
# backend.hcl:
#   bucket         = "my-company-tfstate"
#   key            = "prod/vpc/terraform.tfstate"
#   use_lockfile   = true

terraform init -backend-config=backend.hcl

# STATE MIGRATION between backends:
# 1. Change the backend block in the .tf
# 2. Run:
terraform init -migrate-state
# Terraform will ask for confirmation to move the state
BackendLockingNative encryptionExtra config for locking
Local✅ Yes❌ NoNone (file lock, same machine only)
S3✅ Yes✅ Yesuse_lockfile = true (legacy DynamoDB, deprecated)
Azure Storage✅ Yes✅ YesNone (automatic Blob Lease)
GCS✅ Yes✅ YesNone (native)
HCP Terraform✅ Yes✅ YesNone (automatic)

Did you get this topic?

Test what you just learned

A team wants to use the S3 backend for Terraform state but needs to keep credentials and the bucket name out of the .tf files in the repository. What is the correct solution?

Ready to test "Remote Backends: S3, Azure, GCS and HCP Terraform"?

Put what you learned to the test with original Terraform Associate exam questions.

Start Terraform Associate mock exam

Unlimited mock exams with the Pro plan

Log in to track your progress.