Terraform State Locking on AWS: Hands-On with S3 and DynamoDB
Learn how to secure Terraform state and avoid conflicts with AWS S3 and DynamoDB integration.
Prerequisite: To gain a better understanding, it is advisable to read this blog first: Click here before continuing with this one.
State Management Hands-on:
I am writing this blog to enhance my practical experience with the concepts discussed in my previous blog on Terraform State Management. Let's begin.
- Create a separate folder in VS Code with name terraform-backends. Here you will create a backend Infrastructure
You just need to create Dynamo DB table and S3 bucket in Infrastructure.
You can copy the
terraform.tf
file from theterraform-practice
folder and add it directly to ourterraform.tf
file intoterraform-backends
folder. This setup is intended for different backend infrastructure using S3 and Dynamo DB.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
}
- Create a
variables.tf
file and add an entry foraws_region
:
variable "aws_region" {
description = "AWS Region"
default = "eu-west-1"
}
Let's create an AWS S3 Bucket and DynamoDB in the
backend_infra.tf
file:What is DynamoDB? It is a NoSQL database, which stands for Not Only SQL. This means you can perform queries without needing to create tables first. In MySQL, you must define a schema for the table, including the table name and column names, as it is a relational database. However, in NoSQL databases, there is no predefined schema.
resource "aws_s3_bucket" "my_bucket" {
bucket = var.aws_s3_bucket
tags = {
Name = var.aws_s3_bucket
}
}
resource "aws_dynamodb_table" "my_dynamo_table" {
name = var.aws_dynamodb_table
#billing_mode = "PROVISIONED" #It is for constant billing
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
tags = {
Name = var.aws_dynamodb_table
}
}
Refer Terraform Documentation to create Dynamo DB.
We will add variables in out
variable.tf
file:
variable "aws_region" {
description = "AWS Region"
default = "eu-west-1"
}
variable "aws_s3_bucket" {
description = "AWS Backend Bucket name"
default = "state-locking"
}
variable "aws_dynamodb_table" {
description = "AWS Backend Dynamo Table name"
default = "acrobat-state-table"
}
Explanation of Dynamo DB code:
resource "aws_dynamodb_table" "my_dynamo_table" {
Declares a DynamoDB table resource.name = var.aws_dynamodb_table
Sets the table name dynamically using a variable.billing_mode = "PAY_PER_REQUEST"
Enables billing only for what you use.hash_key = "LockID"
Sets "LockID" as the table's primary key.attribute {
Defines the attributes for the table. It is like a column name.name = "LockID"
Declares "LockID" as the attribute name.type = "S"
Specifies the attribute type as a string.
Now we will execute our code.
terraform init
terraform plan
terraform apply -auto-approve #It is used to approve without saying yes
- Now you can see our S3 bucket & Dynamo DB Table has been created on AWS.
- If you navigate to the Explore item in DynamoDB, you will notice it is empty. This is because we have not yet utilized the acrobat-state-table and state-locking.
Now we will go to our
terraform-practice
folder and openterraform.tfstate
file.To manage how the Terraform state file is maintained, we need to modify the "
terraform.tf
" file to operate at the Terraform level. This is necessary to attach a remote backend, ensuring that your tfstate file is stored in a remote S3 bucket.Add the following code to the
terraform.tf
file in ourterraform-practice
folder:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "state-locking" #bucket name
key = "terraform.tfstate" #State file name
region = "eu-west-1"
dynamodb_table = "acrobat-state-table"
}
}
provider "aws" {
region = var.aws_region
}
Explanation of backend code:
backend "s3"
Configures S3 as the Terraform backend.bucket = "state-locking" #bucket name
Specifies the S3 bucket to store the state.key = "terraform.tfstate" #State file name
Sets the file name for storing Terraform state.region = "eu-west-1"
Defines the AWS region for the bucket.dynamodb_table = "acrobat-state-table"
Uses DynamoDB for state locking to prevent conflicts.
Now open the terminal and go to
terraform-practice
folder.Whenever you add a provider or make changes at the Terraform level, it is important to run
terraform init
first.
- As shown in the image below, our S3 bucket is currently empty.
- Once we confirm, the
terraform.tfstate
file will be backed up in the S3 bucket, as shown in the image below.
- If you delete
terraform.tfstate
andterraform.tfstate.backup
and then runterraform state list
, you will notice that the state is no longer available locally. However, the state still appears in the terminal because it is stored in the remote backend (S3 Bucket).
- Now lets increase our
instance count = 2
& apply the changes and then see how state locking works.
You will observe the message "Acquiring state lock". Next, navigate to the acrobat-state-table
, click on "view tables details", and then refresh the page. You should see that two items are returned.
- You can click on "edit" to view detailed information, such as the user's identity and other relevant details.
- Now, open another terminal tab while keeping the original tab with the
terraform apply
command open. In the new tab, increase the instance count to 10 and runterraform apply
. Observe the results:
You can observe that it prevents multiple users from accessing the
terraform.tfstate
file simultaneously.Now you can see state lock has been removed and only digest will be there.
- Now, we can modify the instance state of the two servers we created using either the AWS console (start, stop) or Terraform. Let's see how to achieve this using Terraform. Refer documentation.
resource "aws_ec2_instance_state" "test" {
instance_id = aws_instance.test.id
state = "stopped"
}
This is how you can use Terraform State Locking.
Happy Learning :)
Chetan Mohod ✨
For more DevOps updates, you can follow me on LinkedIn.