If you’ve been managing your Terraform state in AWS S3, you’ve probably been using DynamoDB to enable state locking. Well, here’s some great news: Terraform 1.10 lets you ditch DynamoDB and handle state locking directly in S3! No extra tables, no extra costs, and no more unnecessary complexity.
So, how does it work? Let me break it down step by step.
What Changed in Terraform 1.10?
Previously, when using an S3 backend for Terraform state, you needed DynamoDB to prevent multiple users or processes from making simultaneous changes. This worked, but setting up and maintaining a DynamoDB table just for state locking felt like overkill.
With Terraform 1.10, HashiCorp introduced native S3 locking. By simply adding a new parameter “use_lockfile = true”, Terraform will automatically create a lock file in your S3 bucket whenever a state-changing operation is in progress. Once the process is done, the lock is released. Isn’t it simple, right?
Creating an S3 backend for storing the Terraform state file
Note: You can create an S3 backend and enable versioning through the AWS Console, but I’m doing it using the AWS CLI here.
- Create an S3 Bucket If you haven’t already created an S3 bucket for storing your Terraform state:
aws s3api create-bucket –bucket your-bucket-name –region your-region –create-bucket-configuration LocationConstraint=your-region
- Enable versioning to keep track of state file changes:
aws s3api put-bucket-versioning –bucket your-bucket-name –versioning-configuration Status=Enabled
Setting Up S3 State Locking Without DynamoDB
Alright, let’s get this up and running. Follow these steps to configure your Terraform backend to use S3 locking:
- Ensure You’re Using Terraform 1.10 or Later First things first, make sure your Terraform version is up to date. Run:
terraform –version
If you’re not on 1.10 or later, upgrade using:
terraform init -upgrade
- Modify Your S3 Backend Configuration Now, open your Terraform configuration file (usually backend.tf or inside main.tf) and update the backend settings like this:
terraform {
backend “s3” {
bucket = “your-bucket-name”
key = “path/to/your/statefile”
region = “your-region”
use_lockfile = true # This enables native S3 state locking
}
}
My example Terraform code with backend settings:
terraform {backend “s3” {bucket = “cj-tf-state” # This is the name of the S3 bucket which I createdkey = “RDS_SG/terraform.tfstate” # This is the name of the state file in the bucket. You can name it whatever you want.region = “ap-south-1” # This is the region where the bucket is locateduse_lockfile = true # This enables native S3 state locking}}
resource “aws_security_group” “existing_sg” {name = “RDSSecurityGroup”description = “This security group was created manually but now we are managing it with Terraform.”vpc_id = var.vpcid # The VPC ID is parameterized for security reasons. In your code, either replace it here with your actual VPC ID or provide it in a *.tfvars file, as I have done.ingress {from_port = 3306to_port = 3306protocol = “tcp”cidr_blocks = [var.cidr_block] # Referencing the parameterized CIDR block due to security reasons. Replace it with your actual CIDR block or provide it in a *.tfvars file.}}
3. Initialize Terraform Again After making these changes, reinitialize your configuration to apply the backend changes:
terraform init
Note: If you want to migrate your local Terraform state file to an S3 backend like I did, you’ll need to run a few additional commands as shown below. However, be sure to back up your state file first.
- Run the following command to initialize Terraform and migrate the local state file to S3:
terraform init -migrate-state
- After successful migration, confirm the state file is stored in S3:
terraform state list
- Additionally, you can check the S3 bucket to ensure the state file has been uploaded.
- Remove Local State File (It is an Optional)
Once you’ve confirmed that the migration is successful, you can remove the local state file for security:
rm terraform.tfstate terraform.tfstate.backup
That’s it! Now, whenever Terraform runs, it will automatically create a lock file in your S3 bucket. If another process tries to make changes while a lock is active, Terraform will block it until the lock is released.
In the example below, I’m attempting to run the previously mentioned AWS RDS service Security Group provisioning Terraform code from two different Windows and Linux machines. However, Terraform has successfully detected the state file locking in S3, preventing any overwrites of the backend state file:
Screenshot: terraform apply command executed in Windows machine
Screenshot: terraform apply command executed in Linux machine
Why Use Native S3 Locking?
- No More DynamoDB – One less AWS service to configure, manage, and pay for.
- Simpler Setup – Just one setting in your backend config and you’re done.
- Still Secure – S3 provides strong consistency, ensuring your state remains protected.
- Cost-Effective – Since you’re not using DynamoDB, you avoid additional costs.
Any Downsides?
Right now, this feature is still new, so it’s a good idea to test it in a lower environment before rolling it out to production. Also, if you have existing state files that rely on DynamoDB, you’ll need to plan your migration carefully.
Example Terraform code related to this topic
You can check my Below GitHub link in which you can see how I have written code as per real-world examples:
https://github.com/cjcheema/terraform_remote_state_locking_example
Wrap Up !
Terraform’s new native S3 locking is a game changer. It simplifies infrastructure management, reduces AWS costs, and keeps your state secure without extra overhead. If you’re still using DynamoDB for locking, it’s time to move on.
So, go ahead, update your Terraform configs, and enjoy a more streamlined workflow. If you like my article please comment and share it in your network. Happy automating!
- No More DynamoDB! Use Native S3 locking for Terraform State - February 7, 2025
- How to Bring and Manage Manually Created AWS Resources Under Terraform Management - January 31, 2025
- Iterating Cloud Resource Provisioning Using Terraform Count and For_Each Meta-Arguments - January 27, 2025