# 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.

```bash
aws s3api create-bucket \
  --bucket tf-state-xxxx \
  --region eu-central-1 \
  --create-bucket-configuration LocationConstraint=eu-central-1
```

Enable bucket versioning.

```bash
aws s3api put-bucket-versioning \
  --bucket tf-state-xxxx \
  --versioning-configuration Status=Enabled
```

Then encryption.

```bash
aws s3api put-bucket-encryption \
  --bucket  tf-state-xxxx\
  --server-side-encryption-configuration '{
    "Rules": [{
      "ApplyServerSideEncryptionByDefault": {
        "SSEAlgorithm": "AES256"
      }
    }]
  }'
```

Create DynamoDB table:

```bash
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.

```json
{
  "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:

```bash
aws iam create-user --user-name projectA-tfstate
```

Attach the policy.

```bash
aws iam put-user-policy \
  --user-name projectA-tfstate \
  --policy-name projectA-tfstate-policy \
  --policy-document file://projectA-policy.json
```

Create access key:

```bash
aws iam create-access-key --user-name projectA-terraform
```

Create an aws cli profile.

```bash
aws sonfigure --profile projectA-terraform
```

# Start using it

Create a Terraform file. For example backend.tf.

```plaintext
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://developer.hashicorp.com/terraform/language/backend/s3)

[https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)

[https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli)

[https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html)
