Dan L
Posted on February 26, 2024
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
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
}
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"
And now we can destroy all the infra registered in the .tfstate file.
terraform destroy
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
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
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) }'
That's it, just run it and wait for Terraform to do the rest.
Posted on February 26, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.