👁7views
Reverse Engineering Your AWS Estate into Terraform Using Scripts (Not Click-Ops)

CloudScale AI SEO - Article Summary
  • 1.
    What it is
    A step-by-step scripted approach to reverse engineering an inherited AWS estate into Terraform, using CLI tools like Former2 and Terraformer to generate HCL configuration and state across multiple accounts and regions.
  • 2.
    Why it matters
    Unmanaged AWS infrastructure creates invisible drift, compliance blind spots, and cost waste — getting it into Terraform gives teams a reviewable source of truth and a foundation for incremental cleanup without starting from scratch.
  • 3.
    Key takeaway
    Commit a complete, imperfect baseline first, then refactor incrementally — trying to generate clean Terraform code before anything is checked in is what makes these projects fail.

If you have ever inherited an AWS estate, you know the feeling before you can even describe it. Hundreds of resources spread across regions you didn’t know were enabled. Lambdas with no source repos, pointing at S3 buckets that may or may not still exist. Config rules that predate the current team. IAM roles that look like they were generated by a sleep-deprived octopus at 2am during a compliance audit.

Eventually, someone asks the question: “Can we just put all of this into Terraform?”

You can. But you should know what you are signing up for, and why it is worth it.

Getting your AWS estate into Terraform gives you a source of truth you can actually reason about. It means drift is visible, changes are reviewable, and the next engineer who inherits this mess gets a fighting chance. It also unlocks real compliance and cost visibility — once your infrastructure is codified, you can lint it, audit it, and stop paying for things nobody remembers provisioning. The goal is not a perfect codebase on day one. The goal is a complete, reproducible baseline you can actually clean up over time.

This guide shows you how to get there using scripts, open source CLI tools, and repeatable steps. No dashboards. No manual clicking. No hand-waving.


1. What You Are Actually Doing (And Why It’s Hard)

It is tempting to think of this as “exporting Terraform,” but that framing will set you up for frustration. What you are actually doing is closer to reverse compilation: discovering all resources across accounts and regions, generating Terraform configuration from live infrastructure, reconstructing dependencies and references, capturing state, and then refactoring everything into something a human can maintain.

AWS was never designed to be reverse-compiled into clean IaC. Resources reference each other in ways that tooling will not always catch, and some services simply do not map cleanly to Terraform resources. Accept that going in, and you will be far less surprised by the rough edges.


2. Tooling Strategy (CLI First, No UI Crutches)

The tools you will need are Former2 CLI for generating Terraform HCL from live resources, Terraformer for state generation, the AWS CLI for discovery and credential management, and jq for parsing API responses in scripts.

Install everything before you start:

brew install awscli jq terraform
npm install -g former2
go install github.com/GoogleCloudPlatform/terraformer@latest

Once installed, verify each tool is working correctly:

aws sts get-caller-identity
terraform version
former2 --version
terraformer version

Do not skip the verification step. A broken tool in the middle of a multi-account sweep is a frustrating thing to debug.


3. Multi-Account Discovery

If you are working across an AWS Organization, you will want to sweep every account systematically rather than configuring credentials by hand. The script below iterates over all accounts in your organization, assumes the standard cross-account role in each one, and delegates to a per-account export script:

#!/bin/bash

ACCOUNTS=$(aws organizations list-accounts \
  --query 'Accounts[].Id' \
  --output text)

for ACCOUNT in $ACCOUNTS; do
  echo "Processing account: $ACCOUNT"

  ROLE_ARN="arn:aws:iam::$ACCOUNT:role/OrganizationAccountAccessRole"

  CREDS=$(aws sts assume-role \
    --role-arn $ROLE_ARN \
    --role-session-name terraform-export)

  export AWS_ACCESS_KEY_ID=$(echo $CREDS | jq -r '.Credentials.AccessKeyId')
  export AWS_SECRET_ACCESS_KEY=$(echo $CREDS | jq -r '.Credentials.SecretAccessKey')
  export AWS_SESSION_TOKEN=$(echo $CREDS | jq -r '.Credentials.SessionToken')

  ./export-account.sh $ACCOUNT
done

4. Region Sweep

Within each account, you want to sweep every active region rather than assuming resources only live in your primary one. Inherited estates have a way of surprising you with us-east-2 resources nobody remembers enabling:

#!/bin/bash

ACCOUNT=$1

REGIONS=$(aws ec2 describe-regions \
  --query 'Regions[].RegionName' \
  --output text)

for REGION in $REGIONS; do
  echo "Exporting $ACCOUNT in $REGION"
  export AWS_DEFAULT_REGION=$REGION
  ./export-region.sh $ACCOUNT $REGION
done

5. Core Export with Former2

Former2 does the heavy lifting of generating HCL from your live resources. Point it at a region and tell it to export everything:

#!/bin/bash

ACCOUNT=$1
REGION=$2

OUTDIR="output/$ACCOUNT/$REGION"
mkdir -p $OUTDIR

former2 generate \
  --output terraform \
  --resources '*' \
  --region $REGION \
  --profile default \
  > $OUTDIR/main.tf

The output will be noisy and imperfect. That is expected. The point is to get something complete, not something clean.


6. State Generation with Terraformer

Former2 gives you the configuration, but you also need Terraform state that maps those resources to what actually exists in AWS. Terraformer handles this:

terraformer import aws \
  --resources="*" \
  --regions=$REGION \
  --profile=default \
  --path-output="output/$ACCOUNT/$REGION"

7. Lambda Reality Check

Lambdas deserve special attention. Many inherited functions have no associated source repository, which means the deployment package in AWS is the only copy of the code. Before you do anything else with a Lambda, download its deployment package:

aws lambda get-function \
  --function-name my-function \
  --query 'Code.Location' \
  --output text | xargs curl -o my-function.zip

Do this for every Lambda before you start making changes. You will thank yourself later.


8. AWS Config and Compliance Resources

Config resources require a bit of extra care because they often carry compliance history and recorder state that you do not want to accidentally destroy. A basic recorder resource looks like this, and you will want to verify the role ARN is accurate before letting Terraform manage it:

resource "aws_config_configuration_recorder" "main" {
  name     = "default"
  role_arn = "arn:aws:iam::123:role/config-role"
}

9. Post-Processing the Generated Code

What you have at this point is a working but ugly baseline. The next pass is about making it maintainable. Split resources into domain-specific files, extract hardcoded values into variables, remove hardcoded ARNs where possible, normalize naming conventions, introduce modules for repeated patterns, and configure remote state. None of this needs to happen before you commit — the goal of this phase is to make incremental improvement possible, not to achieve perfection before anything is checked in.


10. The Bootstrap Pattern

The workflow that works best is to generate everything first, verify it is deployable, and commit the whole thing as a baseline-import branch. This gives you a known-good snapshot before any refactoring begins. From there, every improvement is a pull request against something real, rather than a rewrite against a blank slate.

The full pipeline looks like this:

./discover-accounts.sh
  → export-account.sh
    → export-region.sh
      → export-region-resources.sh

11. Hard Truths

Some things will not go smoothly. You will miss resources — usually the ones with the most dependencies. IAM will be messy in ways that are hard to untangle without understanding the original intent. Lambda source code may be genuinely lost. Certain services, particularly older or less common ones, will not produce clean Terraform output no matter what you do.

None of that means the exercise is not worth doing. It means you should budget time for cleanup, set honest expectations with stakeholders, and treat the first commit as a beginning rather than a finish line.


12. Final Thought

Reverse engineering AWS into Terraform is not fundamentally a tooling problem. The tools exist, they work reasonably well, and the scripts above will get you most of the way there. The harder part is the discipline to see it through: to commit the messy baseline, to refactor incrementally rather than waiting for perfect, and to keep the codebase moving toward something your future colleagues can actually understand.

That part is on you.