It’s a configuration where Terraform will store its state.

There are a number of available backends. The most important for me are:

local is the default backend option.

Remote backends solve:

  • Manual error. All team members and CI’s will have the latest state of the infrastructure.
  • Locking. Avoids race conditions.
  • Secrets. Most remote backends support encryption in transit and encryption at rest. local backend will store unencrypted sensitive values in plain text.
  • Versioning. s3 backend has this support.

Configuration

backend block in Terraform does not support variables.

Partial configuration with local file

Create a separate file, e.g. backend.hcl:

# backend.hcl
bucket         = "terraform-up-and-running-state"
region         = "us-east-2"
dynamodb_table = "terraform-up-and-running-locks"
encrypt        = true

In the Terraform code provide remaining parameters:

# Partial configuration. The other settings (e.g., bucket, region) will be passed in from a file via -backend-config arguments to 'terraform init'
terraform {
  backend "s3" {
    key = "example/terraform.tfstate"
  }
}

To put this all together, run:

terraform init -backend-config=backend.hcl

Partial configuration with CLI parameters

Similar to the approach above. I configured it this way.

Makefile recipe:

-include .env
 
init:
	cd infra && terraform init -reconfigure \
		-backend-config="bucket=${TF_BACKEND_BUCKET}" \
		-backend-config="region=${AWS_REGION}" \
		-backend-config="dynamodb_table=${TF_BACKEND_TABLE}"
# .env
TF_BACKEND_BUCKET="tetradeployer-test-bucket-name-12345"
TF_BACKEND_TABLE="tetradeployer-test-table-name-12345"
AWS_REGION="us-east-1"
terraform {
  backend "s3" {
    key = "terraform.tfstate"
    encrypt = true
  }
}

Terragrunt

Can parameterize backend block. But for what it’s worth, I would avoid it, as it adds additional layer of complexity and one more tool to manage.