Setup S3 backend for Terraform

It may happen that you starting working on a Terraform project in a team and local state file doesn’t work for you anymore. Time to a remote backed. It’s not going to be complete documentation. Or better than others. But maybe I’ll manage to hit your needs. If you want more comprehensive documentation - please take a look at “Links” section.
We’re going to use:
S3 bucket with versioning and encryption.
DynamoDB table for locking.
Separate user for backend access.
Separate user backend user for each project.
To follow the the guide you gonna need:
AWS account. Root or enough privileges to manage users and create resources.
aws cli installed.
Terraform installed.
You also need to know:
The project name.
The bucket name. They need to be unique globally. It would be useful to find your favorite name and add a random suffix. Like string generated with the command
openssl rand -hex 3. Or - you can read guide mentioned in links.
Creating AWS resources
First - login to you AWS admin account. You can create your bucket now. Adjust your bucket name and region.
aws s3api create-bucket \
--bucket tf-state-xxxx \
--region eu-central-1 \
--create-bucket-configuration LocationConstraint=eu-central-1
Enable bucket versioning.
aws s3api put-bucket-versioning \
--bucket tf-state-xxxx \
--versioning-configuration Status=Enabled
Then encryption.
aws s3api put-bucket-encryption \
--bucket tf-state-xxxx\
--server-side-encryption-configuration '{
"Rules": [{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "AES256"
}
}]
}'
Create DynamoDB table:
aws dynamodb create-table \
--table-name terraform-locks \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
Create user
Please create a file from following template, replacing bucket name, project name, client id and region. The name used in the example is. projectA-policy.json. Feel free to adjust.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowS3StateAccess",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::<bucket-name>/<project-name>/*"
},
{
"Sid": "AllowS3ListPrefix",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::<bucket-name>",
"Condition": {
"StringLike": {
"s3:prefix": "<project-name>/*"
}
}
},
{
"Sid": "AllowDynamoDBLocking",
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:DeleteItem",
"dynamodb:UpdateItem"
],
"Resource": "arn:aws:dynamodb:eu-central-1:<client-id>:table/terraform-locks"
}
]
}
create the user:
aws iam create-user --user-name projectA-tfstate
Attach the policy.
aws iam put-user-policy \
--user-name projectA-tfstate \
--policy-name projectA-tfstate-policy \
--policy-document file://projectA-policy.json
Create access key:
aws iam create-access-key --user-name projectA-terraform
Create an aws cli profile.
aws sonfigure --profile projectA-terraform
Start using it
Create a Terraform file. For example backend.tf.
terraform {
backend "s3" {
bucket = "tf-state-xxxx"
key = "projectA-terraform/terraform.tfstate"
region = "eu-central-1"
profile = "projectA-terraform"
use_lockfile = true
encrypt = true
}
}
If you’re lucky - you can run terraform init without errors now.
Links
https://developer.hashicorp.com/terraform/language/backend/s3
https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html
https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli
https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html



