Oliver Cion
Clarusway
Published in
9 min readJan 16, 2021

--

How to use s3 backend with a locking feature in Terraform to collaborate more efficiently? (easily and visually explained)

This article aims to create an S3 backend using the locking feature in Terraform to collaborate more efficiently as a team. First, I will mention why we store our state in remote storage and its advantages and then use the locking feature. Moreover, I’ll explain how we use the AWS S3 bucket as a backend with an excellent hands-on visually step by step and how to lock our state file with the Amazon DynamoDB table.

Usually, terraform creates a state file locally called as terraform.tfstate. This file stores the state machine of the infrastructure. When it comes to working as a team, team members can’t access this file concurrently; thus, they can’t collaborate as a team. That is why Terraform provides a capability to access this file from the cloud to support teamwork. So, the state files of the infrastructure held in a remote repository.

Some of the advantages of using remote state are as follows:

· If you are working with a team, keeping your state in a remote repository allows users to share their infrastructure data easily.

· Terraform users should both use the final state file before starting to work on their infrastructure and ensure that no other work is being done at that time. In such situations, using Terraform state locally becomes complicated.

· If you encounter an unrecoverable problem with your local files or your computer crashes, etc., keeping your state file in remote storage prevents data loss.

· You can also version the state files of your infrastructure if your remote repo supports it. For example, the AWS S3 bucket supports versioning.

· Sensitive data such as passwords may be exposed in our state files. For this reason, you can encrypt your files in such cases if your remote storage supports it. Thanks to the AWS S3 bucket, it also supports encryption.

· We can always have the original text of our infrastructure at our disposal. For example, we may have created our infrastructure with Terraform, but we may have changed our resources via the console. For example, we may have created 50 EC2 instances on AWS with Terraform, but then we may have increased the number of machines to 100 via the AWS console. However, I can always reach my first state from my S3 bucket with Terraform.

In this study, we will also show the locking feature. This feature can also be used depending on the support of your backend. As known, Terraform creates its execution plan by looking at the state file. When we make infrastructure changes, Terraform compares the state file with the real situation and creates an execution plan accordingly. So, state files are vital for terraform. For this reason, when two or more users work on the infrastructure simultaneously, problems may arise in the creation of the resources, as there will be a situation such as executing another process before the state is finalized. Therefore, Terraform can lock your state to prevent other users from breaking our infrastructure using the same state file at the same time. However, not every backend supports this feature. But AWS S3 bucket supports this feature with the Amazon DynamoDB table.

So, we can use the locking feature using AWS S3 and DynamoDB table. In other words, while a user is working on the infrastructure with Terraform, another user cannot work on the same state file simultaneously. In our example, to simulate this situation, we will connect to the same directory from two different terminals and run the terraform simultaneously. Likewise, we will simulate two different users working on the same file simultaneously, and we will see the result.

Let’s start our showcase.

Consider a company that has infrastructure on AWS. Now this company is provisioning its resources with terraform. In our example, we create a simple Amazon EC2 instance with Terraform. Imagine working with the DevOps team to keep the state in a remote state to store the state files they build their infrastructure on more securely. For this purpose, they first create an S3 bucket in AWS. They are also evaluating that they can use the S3 bucket’s features to both version and encrypt the state files. Then, while working as a team, they want to lock the state with the Amazon DynamoDB table to prevent the state file from being corrupted with overlapping entries.

1. In the example, I will use an Amazon Linux 2 EC2 instance and install Terraform in it. So I choose Amazon Linux 2 AMI and t2.micro type instance. Next, I opened 22 port for SSH connection and selected the other features default. Then I connected my EC2 instance with Visual Studio Code Remote-SSH extension. Next, I will install the Terraform.

2. Now, let’s create a folder named Remote_State under the /home/ec2-user folder. Then enter the folder and create two folder names, Create_AWS_EC2 and S3_Backend_with_Locking. Next, enter the Create_AWS_EC2 and create a file name main.tf and create a file name backend.tf in the S3_Backend_with_Locking folder.

$ mkdir Remote_State
$ cd Remote_State
$ mkdir Create_AWS_EC2
$ cd Create_AWS_EC2
$ touch main.tf
$ cd ..
$ mkdir S3_Backend_with_Locking
$ cd S3_Backend_with_Locking
$ touch backend.tf

3. I work in the AWS console, and I don’t want to use my credentials in my Terraform config file. So I go to the IAM service in AWS and create a role. We assign roles (AmazonEC2FullAccess, AmazonS3FullAccess, AmazonDynamoDBFullAccess) to our EC2 instance to access these 3 services, EC2, S3 bucket, and DynamoDB table. (If you want, you can use your access_key and secret_key in your provider block, or if you use your local and have AWS CLI, you can use AWS credentials in CLI.)

4. If you want to use S3 as a backend in Terraform, first, you must create an S3 bucket and then specify that bucket in your config file as backend. Now we create our S3 bucket for remote state and Amazon DynamoDB table for Locking state. Go to the backend.tf file and add the following.

$ vim backend.tf
#backend.tf
provider "aws" {
region = "us-east-1"
}
resource "aws_s3_bucket" "tf_remote_state" {
bucket = "terraform-s3-backend-with-locking"
lifecycle {
prevent_destroy = true
}
versioning {
enabled = true
}
server_side_encryption_configuration {
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
}
#locking part

resource "aws_dynamodb_table" "tf_remote_state_locking" {
hash_key = "LockID"
name = "terraform-s3-backend-locking"
attribute {
name = "LockID"
type = "S"
}
billing_mode = "PAY_PER_REQUEST"
}

provider: Our infrastructure is in AWS, so we use AWS provider, and I work in North Virginia (us-east-1) region. If you want, you can change it.

bucket: It must be the name of the bucket. Our bucket name must be unique. So you can add some words or letters to make it unique.

lifecycle (prevent_destroy): This argument prevents us from accidental or wrongly deletion of the bucket.

versioning: This allows us to keep all versions of our objects.

server_side_encryption_configuration: You can use encryption for stored data in the S3 bucket.

DynamoDB table (Locking)

If we are using the DynamoDB table for locking, the primary key must be LockID (type: string). Otherwise, the locking feature will not work. So, we use the arguments hash_key and attribute as above.

5. Now, in the S3_Backend_with_Locking folder, run the commands belove. First, initialize Terraform, then create an S3 bucket and DynamoDB table.

$ terraform init
$ terraform apply

6. Go to the AWS console and check the S3 bucket and DynamoDB service.

7. Now, it’s time to create our infrastructure and make the state in the S3 backend and lock. Go to the Create_AWS_EC2 folder and add the following to the main.tf.

$ cd create_AWS_EC2
$ vim main.tf
#main.tf
provider "aws" {
region = "us-east-1"
}

terraform {
backend "s3" {
bucket = "terraform-s3-backend-with-locking"
key = "terraform/backend/terraform_aws.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-s3-backend-locking"
encrypt = true
}
}

resource "aws_instance" "terraform_aws_ec2" {
ami = "ami-0be2609ba883822ec"
instance_type = "t2.micro"
}

bucket: Name of S3 bucket we just created.

key: Path of our Terraform state in S3 bucket

dynamodb table: Name of the DynamoDB table we just created for locking.

encrypt: Enable encryption

ami: Amazon Linux 2 AMI in North Virginia.

8. Next, run the commands belove in the Create_AWS_EC2 folder. Initialize Terraform and create an AWS EC2 instance using the S3 backend and lock.

$ terraform init
$ terraform apply

9. Go to the AWS console and check the S3 bucket.

As you can see, our state is now started to be stored in the S3 bucket.

10. It’s time to try the locking. What we will do at this point; I will simulate two different users working on the same state file simultaneously. Then see if the state lock.

11. First, go to the main.tf and add the following. It is just an example of making any change on the config file.

output "public_ip" {
value = aws_instance.terraform_aws_ec2.public_ip
}

11. Normally, I (User 1) connect to my EC2 from VS Code (Remote-SSH). Think that there is another user (User 2). And User 2 uses another instance. Here, I simulate it by connecting from another terminal (Ubuntu 18.04 Windows subsystem for Linux). I come to the same folder in both terminals.

12. Next, I enter the same commands in both terminals.

13. I will run the command as USER 1 first and then immediately run the command as USER 2. Let’s check the result.

$ terraform apply

As you can see, because the USER 1 ran command first, the command worked, and it succeeded. However, USER 2 was not allowed to run because the state was locked. Now, rerun the command as USER 2. You will see that the command is running.

14. We saw the locking state. Now, go to the AWS console and check the S3 bucket and DynamoDB table.

As you can see, the S3 bucket started to take versions of the state.

I hope this article was understandable and helpful for you. If you made this study with me, don’t forget to run the terraform destroy command. Also, as we use the prevent destroy argument while creating the S3 bucket, the S3 bucket and DynamoDB table will not be deleted. So you can delete them from the AWS console.

References:

https://www.terraform.io/docs/index.html

https://docs.aws.amazon.com/

--

--