Terraform is great at provisioning cloud infrastructure. Terraform needs to keep the current state somewhere. Local filesystem can be used in many cases, but not ideal if there are multiple people sharing this state file. In case of AWS, we usually wants to keep it in AWS itself.

We can actually use terraform to provision these. I do find a simple copy and paste one-time run bash script sufficient for this case. This way, we do not need to maintain two sets of terraform scripts.

Download and Configure AWS CLI

brew install awscli

Then create a profile with keys

$ aws configure --profile myprofile
AWS Access Key ID [None]: <key>
AWS Secret Access Key [None]: <value>
Default region name [None]: <region or leave none>
Default output format [None]: yaml

Create S3 Bucket

It is recommended to turn on bucket versioning so the history of states is preserved.

$ aws s3 mb s3://<project>-tfstate \
  --profile myprofile \
  --region us-west-2

$ aws s3api put-public-access-block \
  --bucket <project>-tfstate \
  --public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true" \
  --profile myprofile \
  --region us-west-2

$ aws s3api put-bucket-versioning \
  --bucket <project>-tfstate \
  --versioning-configuration "Status=Enabled" \
  --profile myprofile \
  --region us-west-2

Create DynamoDB Table

While terraform uses S3 to store the actual state, it needs some locking mechanism that S3 does not provide. This is done via DynamoDB. The table must have a primary key named LockID as a string.

aws dynamodb create-table \
  --table-name <project>-tfstate \
  --attribute-definitions AttributeName=LockID,AttributeType=S \
  --key-schema AttributeName=LockID,KeyType=HASH \
  --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
  --profile myprofile \
  --region us-west-2

Use the Backend in Terraform

Now we can configure S3 (and DynamoDB) as our backend in main.tf

terraform {
  backend "s3" {
    bucket = "<project>-tfstate"
    key    = "terraform.tfstate"
    dynamodb_table = "<project>-tfstate"
    encrypt = true
    region = "us-west-2"
    profile = "myprofile"
  }
}

References