Terraform
Advanced 13 min read Terraform

Terraform Remote Backends

One source of truth, guarded

Remote backends centralize state for teams: shared storage, locking, versioning, and audit. S3/GCS/Azure blob + lock table is the de-facto; Terraform/OpenTofu Cloud adds run orchestration, plan artifacts and policy.

remote backendS3 backendGCS backendazure backendlockingrun history
Prerequisites: State & Backends
Before we start

What you will learn

  • Compare S3/GCS/Azure/cloud backends
  • Configure backend + lock + encryption hardening
  • Handle state lifecycle: migrate, clone, rollback
In 60 seconds

Backend = where state lives + locking + history. S3: bucket+key+lock, cheap and standard. Cloud: run engine (plan/apply/approval/states in UI) with real collaboration features. Choose by team maturity and governance needs.

Introduction

What is this?

Backend feature matrix: shared storage (bucket/blob/GCS), locking (DynamoDB/GCS conditions/Az lease or cloud runs), versioning (object versions / run history), workspaces, per-key isolation, concurrency. S3 backend: region, key, bucket, dynamodb_table, encrypt, kms_key, role_arn, profile. GCS: bucket, prefix, impersonation. Azure: resource_group+container+storage_account_name+key. Terraform Cloud/OpenTofu: state per workspace + run queue (VCS integration), Sentinel/OPA policies, notifications, isolation via API/EFF. Migration: init -migrate-state; rolling back a state version = downloading prior object + re-init.

Motivation

Why do you need it?

  • A team can't coordinate against a laptop file; shared+locked state is the baseline.
  • Versioned state means a bad apply is reversible in minutes.
  • Cloud backends add run orchestration (plan comments, approvals, audit) without bespoke CI glue.
  • Per-backend encryption + restricted IAM turns the state bucket into a vault, not a leak.
Think about it

Mental model

Imagine

Company's master ledger kept in a vault with one teller, vs handwritten scraps per desk

Think of

Remote backend vs local state

One vault ledger (locked, audited, versioned) coordinates every teller; desk scraps (local state) guarantee two tellers will balance different truths.

Mechanics

How it works

  • init reads backend block (via -backend-config for secrets), acquires a lock during state ops.
  • Every plan/apply pulls state, writes back with version bump.
  • Cloud runs wrap the same with VCS triggers, approval approval models, and policy evaluation.
  • Object versioning (S3/GCS) preserves history for rollback.
  • Migration moves existing state into the backend cleanly via -migrate-state.

Backend topology.

TV engineers
Backend
Lock
Version
Cloud runs
Hands-on

Examples

Hardened S3 backend

Hardened S3 backend
terraform {
backend "s3" {
bucket = "acme-tfstate"
key = "stack/prod.tfstate"
region = "us-east-1"
dynamodb_table = "tf-locks"
encrypt = true
kms_key_id = "arn:aws:kms:us-east-1:...:key/xyz"
role_arn = "arn:aws:iam::...:role/tf-state"
}
}
# bucket policy: deny delete-without-mfa + deny unencrypted PUT

Encryption, locking, versioning, and least-privilege IAM all roll into the backend definition.

Real world

In production

  • Per-env S3 keys under one hardened bucket (dev can't lock prod state) — narrow IAM.
  • Terraform Cloud run queue: every PR gets plan/apply orchestrated with approvals and audit.
  • Multi-account think: state role per account, backend bucket owned by a platform account.
Guidelines

Best practices

  • Use backend-config files for secrets instead of hardcoding in code.
  • Enable bucket versioning + lock and object encryption (KMS where sensitive).
  • Scope backend IAM per team/env; never a single all-state role spread repo-wide.
  • Document the migration path (init -migrate-state) and run-history retention.
  • Choose cloud backends where process matters more than infra plumbing; S3 for pure-IaC teams.
Watch out

Common mistakes

  • Hardcoding access keys in the backend block / config (leak city).
  • Migrating state by copying files instead of init -migrate-state.
  • Versioning off → a bad apply permanently taps the world (no rollback).
  • One monolithic backend role = every repo can touch every env's state.
When things break

Debugging tips

  • Lock error persists after a crash → inspect the lock row's LeaseID and release via CLI once confirmed.
  • Backend config drift → ensure -backend-config matches across env pipelines (init re-runs hit the old lens).
  • Plan can't read state → verify bucket+key+role per environment (typo in profile/etc.).
  • Cloud run stuck 'queued' → VCS webhook/PRClosed issues; check agent connectivity.
Interviews

Interview checkpoint

Test yourself

Quick quiz

0/2
1

Object versioning on the state bucket enables:

2

The lock table's purpose is:

Go deeper

Official documentation