Terraform State Locking

In my previous post, I mentioned that the next step would be setting up a VPC. But before I do that, I realized I skipped an important foundational piece: Terraform state locking. Terraform generates a state file to keep track of the resources it manages. This file is crucial, and to prevent corruption during simultaneous updates, Terraform can lock it when applying changes. This ensures that your plan is based on the latest state and avoids conflicting modifications — especially important when collaborating or working across environments. For this setup, I’ll be using S3 to store the state. The last time I worked extensively with Terraform, I used DynamoDB for state locking. However, it seems that functionality is being deprecated — and now S3 supports locking on its own, which is a welcome simplification. Here’s the plan: Define the S3 bucket in Terraform Apply the configuration to create the bucket Configure Terraform to use this bucket for remote state storage I created an s3.tf file with the configuration shown below. I'm using paulb- as a prefix to help ensure the bucket name is globally unique. Following Terraform’s best practices, I’ve also enabled versioning on the bucket to better protect state history. Let me know if you'd like help rewriting the code section next, or styling this as a blog post! resource "aws_s3_bucket" "tf_state" { bucket = "paulb-devops-toolkit-terraform-state" } resource "aws_s3_bucket_versioning" "tf_state" { bucket = aws_s3_bucket.tf_state.id versioning_configuration { status = "Enabled" } } I also decided to tag all the AWS resources which I will generate for this project. To do so, I will set the tag in the AWS provider configuration, so I won't have to set it in every resource configuration. provider "aws" { shared_config_files = ["/Users/paulbenetis/.aws/config"] shared_credentials_files = ["/Users/paulbenetis/.aws/credentials"] profile = "default" default_tags { tags = { Project = "devops-toolkit" } } } Now I just need to create a plan which I will apply to create the bucket with versioning enabled. terraform plan And now to create the resources defined in the plan. > terraform apply aws_s3_bucket.tf_state: Creating... aws_s3_bucket.tf_state: Creation complete after 3s [id=paulb-devops-toolkit-terraform-state] aws_s3_bucket_versioning.tf_state: Creating... aws_s3_bucket_versioning.tf_state: Creation complete after 2s [id=paulb-devops-toolkit-terraform-state] Now I can check the status of this new resource. terraform state show aws_s3_bucket.tf_state The bucket is created as expected, and also has the default tag! ... tags_all = { "Project" = "devops-toolkit" } ... Now I just need to assign this bucket as the backend of state storage and locking. I am adding a new block to the terraform block in main.tf. terraform { ... backend "s3" { bucket = "paulb-devops-toolkit-terraform-state" key = "tfstate" region = "us-east-1" use_lockfile = true } ... } Now I will apply this new configuration and start storing the state remotely in s3! > terraform init Initializing the backend... Do you want to copy existing state to the new backend? Pre-existing state was found while migrating the previous "local" backend to the newly configured "s3" backend. No existing state was found in the newly configured "s3" backend. Do you want to copy this state to the new "s3" backend? Enter "yes" to copy and "no" to start with an empty state. Enter a value: yes Successfully configured the backend "s3"! Terraform will automatically use this backend unless the backend configuration changes. Initializing provider plugins... - Reusing previous version of hashicorp/aws from the dependency lock file - Using previously-installed hashicorp/aws v5.95.0 Terraform has been successfully initialized! My Terraform state is now successfully stored in the S3 bucket!

Apr 20, 2025 - 13:56
 0
Terraform State Locking

In my previous post, I mentioned that the next step would be setting up a VPC. But before I do that, I realized I skipped an important foundational piece: Terraform state locking.

Terraform generates a state file to keep track of the resources it manages. This file is crucial, and to prevent corruption during simultaneous updates, Terraform can lock it when applying changes. This ensures that your plan is based on the latest state and avoids conflicting modifications — especially important when collaborating or working across environments.

For this setup, I’ll be using S3 to store the state. The last time I worked extensively with Terraform, I used DynamoDB for state locking. However, it seems that functionality is being deprecated — and now S3 supports locking on its own, which is a welcome simplification.

Here’s the plan:

  • Define the S3 bucket in Terraform
  • Apply the configuration to create the bucket
  • Configure Terraform to use this bucket for remote state storage

I created an s3.tf file with the configuration shown below. I'm using paulb- as a prefix to help ensure the bucket name is globally unique. Following Terraform’s best practices, I’ve also enabled versioning on the bucket to better protect state history.

Let me know if you'd like help rewriting the code section next, or styling this as a blog post!

resource "aws_s3_bucket" "tf_state" {
  bucket = "paulb-devops-toolkit-terraform-state"
}

resource "aws_s3_bucket_versioning" "tf_state" {
  bucket = aws_s3_bucket.tf_state.id
  versioning_configuration {
    status = "Enabled"
  }
}

I also decided to tag all the AWS resources which I will generate for this project. To do so, I will set the tag in the AWS provider configuration, so I won't have to set it in every resource configuration.

provider "aws" {
  shared_config_files      = ["/Users/paulbenetis/.aws/config"]
  shared_credentials_files = ["/Users/paulbenetis/.aws/credentials"]
  profile                  = "default"

  default_tags {
    tags = {
      Project = "devops-toolkit"
    }
  }
}

Now I just need to create a plan which I will apply to create the bucket with versioning enabled.

terraform plan

And now to create the resources defined in the plan.

> terraform apply
aws_s3_bucket.tf_state: Creating...
aws_s3_bucket.tf_state: Creation complete after 3s [id=paulb-devops-toolkit-terraform-state]
aws_s3_bucket_versioning.tf_state: Creating...
aws_s3_bucket_versioning.tf_state: Creation complete after 2s [id=paulb-devops-toolkit-terraform-state]

Now I can check the status of this new resource.

terraform state show aws_s3_bucket.tf_state

The bucket is created as expected, and also has the default tag!

...
tags_all = {
  "Project" = "devops-toolkit"
}
...

Now I just need to assign this bucket as the backend of state storage and locking. I am adding a new block to the terraform block in main.tf.

terraform {
...
  backend "s3" {
    bucket       = "paulb-devops-toolkit-terraform-state"
    key          = "tfstate"
    region       = "us-east-1"
    use_lockfile = true
  }
...
}

Now I will apply this new configuration and start storing the state remotely in s3!

> terraform init
Initializing the backend...
Do you want to copy existing state to the new backend?
  Pre-existing state was found while migrating the previous "local" backend to the
  newly configured "s3" backend. No existing state was found in the newly
  configured "s3" backend. Do you want to copy this state to the new "s3"
  backend? Enter "yes" to copy and "no" to start with an empty state.

  Enter a value: yes


Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using previously-installed hashicorp/aws v5.95.0

Terraform has been successfully initialized!

My Terraform state is now successfully stored in the S3 bucket!