Terraform Doesn't Know About the Manually-Made DB. Just Let It Recreate It?
Scenario
Months ago, someone stood up a production RDS instance by hand in the console — probably under deadline pressure — and it has been quietly serving live customer traffic ever since. Now the team is codifying everything in Terraform. An engineer writes the aws_db_instance resource block, runs terraform plan, and Terraform announces it will create a database — because as far as its state file knows, none exists. Depending on naming collisions and how the apply plays out, the path from here runs through creating a duplicate or destroying and rebuilding the real one. Either way, Terraform has no idea the most important database in the company is standing right where it wants to build.
The Quick Fix on the Table
A colleague shrugs: just run the apply. Terraform will build a matching database from the code, everything ends up consistent and codified, and the manual-infrastructure problem is gone. The end state is the goal, so why fuss over how we get there?
The quick fix is on the table and the room is waiting for your call. Would you sign off on it? Take a position and justify it — out loud or on paper — before revealing the analysis.
Why the Quick Fix Fails
- A recreated database is an empty database. Terraform builds resources from configuration, not from data. A fresh RDS instance matching the code has the right instance class and parameter group — and zero rows of the customer data that months of production traffic wrote into the real one.
- "Consistent and codified" is about management, not molecules. The goal of IaC is that Terraform tracks the resource, not that Terraform gave birth to the resource. Conflating the two turns a bookkeeping task into a data-loss event.
- The transition is an outage by design. Whether it's a destroy-and-recreate or a cutover to a duplicate, applications lose their database or their data mid-flight. There is no window in which this is invisible to customers.
- Backups are a mitigation, not a plan. "We'll restore from a snapshot after" means accepting data loss back to the last snapshot, an unplanned restore under pressure, and downtime — all to avoid running one import command.
- It normalizes a catastrophic reflex. If the team's answer to "Terraform doesn't know about X" is "let Terraform rebuild X," the next unimported resource — a DNS zone, a KMS key, an S3 bucket full of customer files — gets the same treatment.
The interviewer nods: “Fine, the quick fix is off the table. So what exactly would you do — step by step?” Sketch your plan before revealing the approach.
The Right Approach
- Stop and protect the resource first. Enable deletion protection on the instance (
aws rds modify-db-instance --deletion-protection) and take a manual snapshot. Now no sequence of Terraform mistakes can silently destroy it. - Write the resource block to mirror reality. Pull the live configuration with
aws rds describe-db-instancesand encode it: engine version, instance class, storage, parameter and subnet groups, tags. The code should describe the database that exists, not the one you wish existed. - Import it into state. Use an
importblock withterraform plan -generate-config-outto bootstrap, or the classicterraform import aws_db_instance.main <db-identifier>. This tells Terraform: this resource already exists — manage it, don't make it. - Iterate until the plan is a no-op. Run
terraform planand fix every diff by adjusting the code (or consciously accepting a real change). Watch for attributes that force replacement — the plan output flags them — and do not apply while any destructive change remains. - Add guardrails in code. Set
deletion_protection = trueand alifecycle { prevent_destroy = true }block on the resource so a future refactor can't schedule the database for demolition. - Apply and verify. A clean apply should change nothing at AWS. Confirm the app is healthy and the instance ARN in state matches the live one.
- Sweep for other orphans. This database won't be the only hand-made resource. Compare the account inventory against state (drift-detection tooling, or scripted
describecalls) and schedule imports for the rest before someone else's plan proposes recreating them.
Final pushback: “Your plan costs more time and money than the quick fix. Convince me.” How do you defend your position under pressure?
How to Defend It
- "Terraform can recreate the database's configuration, but not its contents. The plan says 'create'; for our customers it reads 'delete everything since the last snapshot.'"
- "The end state we actually want is 'this exact database, under management.' Import gets us there with zero downtime; recreate gets us a lookalike with none of the data."
- "Import is one command plus a code-tuning loop until the plan is empty. The recreate path is an outage, a restore, and an incident review — that's not the faster option."
- "Before we touch anything I'm turning on deletion protection and taking a snapshot, so even a mistake in this process can't cost us data."
- "If this database was made by hand, others were too. Let's use this as the template: inventory, import, protect — instead of finding the next one the hard way."