DEV Community

Dan L
Dan L

Posted on

How to destroy an infra deployed with Terraform by .tfstate

Sometimes it's necessary to remove previously deployed infrastructure using Terraform, but in some cases it can be challenging to do so.

This could be due to old pipelines where destroying cannot be started due to breaking changes in configuration (e.g. environment variables no longer exists) or manually created infrastructure during development.

Today I want to show you a quick and easy way using only the .tfstate files where Terrafrom stores the current state (assuming you follow best practices and store them in one place like an S3 bucket).

Initial configuration

In my examples, I'll use AWS, but you can easily adapt this to any other supported provider.

You'll need the correct version of Terraform (the version of Terraform used for apply can be found in terraform_version at the beginning of the .tfstate file), to switch between versions I recommend tfenv.

First, create an .env where we'll specify all the variables. For backend configuration we'll pass it as command-line arguments because Terraform still doesn't support variables in the backend section.

TF_VAR_account_id=000000000000
TF_VAR_region=us-east-1
TF_VAR_access_key=XXXXXXXXXXXXXXXXXXXX
TF_VAR_secret_key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
TF_BACKEND_BUCKET=your-bucket-name
TF_BACKEND_KEY=path/to/terraform.tfstate
TF_BACKEND_ENCRYPT=true
Enter fullscreen mode Exit fullscreen mode

Next, create main.tf specifying the provider, its version, and the used backend. Use the optional allowed_account_ids parameter to ensure it runs in the correct AWS account.

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "~> 5.9"
    }
  }

  backend "s3" {}
}

provider "aws" {
  allowed_account_ids = [var.account_id]
  region = var.region
  access_key = var.access_key
  secret_key = var.secret_key
}
Enter fullscreen mode Exit fullscreen mode

Now let's run terraform init, (Note: we use the env $(cat .env | xargs) to read variables from the .env file).

env $(cat .env | xargs) terraform init \
  -backend-config="region=$TF_VAR_region" \
  -backend-config="access_key=$TF_VAR_access_key" \
  -backend-config="secret_key=$TF_VAR_secret_key" \
  -backend-config="bucket=$TF_BACKEND_BUCKET" \
  -backend-config="key=$TF_BACKEND_KEY" \
  -backend-config="encrypt=$TF_BACKEND_ENCRYPT"
Enter fullscreen mode Exit fullscreen mode

And now we can destroy all the infra registered in the .tfstate file.

terraform destroy
Enter fullscreen mode Exit fullscreen mode

Creating a utility

Usually if you start doing this you'll have to execute multiple times which is not very convenient, let's create a run.sh entrypoint where we can pass the path to the .tfstate file as argument.

#!/bin/bash

if [ -z $1 ]; then
  echo "Path to terraform.tfstate is required"
  exit 1
fi

export $(cat .env | xargs)
export TF_BACKEND_KEY=$1

terraform init -reconfigure \
  -backend-config="region=$TF_VAR_region" \
  -backend-config="access_key=$TF_VAR_access_key" \
  -backend-config="secret_key=$TF_VAR_secret_key" \
  -backend-config="bucket=$TF_BACKEND_BUCKET" \
  -backend-config="key=$TF_BACKEND_KEY" \
  -backend-config="encrypt=$TF_BACKEND_ENCRYPT"

terraform destroy -auto-approve
Enter fullscreen mode Exit fullscreen mode

We added the -reconfigure to init to reuse cached plugins and modules (don't delete the .terraform folder between calls, it'll significantly speed up the process). To avoid confirming destroy each time, we added the -auto-approve flag.

How to use it

Now, you can run it (remember to make it executable with chmod +x).

./run.sh path/to/terraform.tfstate
Enter fullscreen mode Exit fullscreen mode

To batch run, we need to find all matching .tfstate files and call our script for each of them using awk. Please double check that you have filtered only what you need to destroy before running it.

For AWS with the S3 backend run this (if you are using another backend, you'll need to adapt this command).

aws s3 ls s3://your-bucket-name --recursive |
  grep terraform.tfstate |
  awk '{ system("./run.sh" $4) }'
Enter fullscreen mode Exit fullscreen mode

That's it, just run it and wait for Terraform to do the rest.

Top comments (1)

Collapse
 
rollinsjw profile image
rollinsjw

Thanks, this helped us delete a lot of unused infra created during our iteration process