Terraform Import: A Founder's Guide (and Why You Probably Aren't There Yet)
What terraform import actually does, the old command vs. the 1.5+ import block, the gotchas — and an honest take on why most bootstrapped founders don't need Terraform at all yet.
Table of contents8 sections
- 01The Day My Infrastructure Outgrew My Memory
- 02What terraform import Actually Does
- 03The Old Way: the terraform import Command
- 04The New Way: the import {} Block (Terraform 1.5+)
- 05The Gotchas Nobody Puts in the Tutorial
- 06Terraform or OpenTofu? The 2026 Fork in the Road
- 07Why You Probably Don’t Need Any of This Yet
- 08The Bottom Line
The Day My Infrastructure Outgrew My Memory
It was late 2024. I was cleaning up the wreckage of a project I’d wildly over-engineered — the same one where I convinced myself I needed a Kubernetes cluster to run what was, functionally, a glorified redirect service. (If you want the full confession, it’s in the Kubernetes post.)
By the time I came up for air, I had a mess. A managed Kubernetes cluster I’d clicked together in a cloud console. A load balancer I half-remembered creating. Three object-storage buckets, two of which I couldn’t explain. A DNS zone with records pointing at things that no longer existed. Security groups I was scared to touch. All of it built by hand, at 1 AM, across a dozen browser tabs.
Then the real fear hit me: I have no idea how to recreate any of this. If I nuked it by accident, I couldn’t rebuild it. If I got hit by a bus, nobody could. My infrastructure lived in my head and in a UI I’d forgotten the shape of.
That is the exact moment people discover terraform import. And that is exactly the moment I want to talk you out of, if you’re not really there yet. So let me teach you the thing honestly — what it does, how to use it, where it bites — and then tell you why you probably don’t need it.
What terraform import Actually Does
Quick level-set, because half the confusion here is vocabulary.
Terraform, from HashiCorp, is an Infrastructure as Code tool. Instead of clicking around a cloud console, you write down what your infrastructure should look like in files (HashiCorp’s own config language, HCL), and Terraform makes reality match those files. (Terraform provisions the servers; if you then need to configure what runs on them, that’s a different tool — Ansible, and its Galaxy hub — that you probably also don’t need yet.) That’s the dream: your servers, buckets, and DNS records described in text you can version-control, review, and rebuild from scratch.
The catch is a thing called state. Terraform keeps a state file — a JSON ledger mapping “the resource in my config called aws_s3_bucket.uploads” to “the actual bucket my-app-uploads-prod living in the cloud.” When you run terraform apply, it diffs your config against that state against reality, and figures out what to create, change, or destroy.
Here’s the problem import solves: the resource already exists, but Terraform’s state doesn’t know about it. That bucket I made by hand in the console? Terraform has never heard of it. If I just write a matching config block and run apply, Terraform sees no state entry and cheerfully tries to create a brand-new bucket — which either collides with the real one or gives me a duplicate. Not what I want.
terraform import is the bridge. It says: “this real resource over here, with this ID — start tracking it as this address in my config.” It doesn’t create anything. It doesn’t change your infrastructure. It just teaches the state file that a thing you built by hand is now yours to manage as code.
That’s the whole idea. Adoption papers for infrastructure that already exists.
The Old Way: the terraform import Command
For years, importing was a two-step chore, and it went like this.
Step one: you write the resource block yourself, by hand, empty-ish:
resource "aws_s3_bucket" "uploads" {
# you'll fill this in...
}
Step two: you run the command, pointing an address (aws_s3_bucket.uploads) at the real resource’s ID (my-app-uploads-prod):
terraform import aws_s3_bucket.uploads my-app-uploads-prod
Terraform reaches out, finds the bucket, and writes it into state under that address. Done — the resource is now tracked.
Except it’s not really done, and this is where people get burned. The command only fixes state. It does not write your config for you. So now you have a resource in state with an almost-empty config block, and the next terraform plan screams at you about a hundred differences between your bare block and the fully-configured real bucket. Your job is to hand-write every argument — versioning, encryption, lifecycle rules, tags, the works — until plan shows “no changes.” For one bucket, annoying. For fifty resources, it’s a soul-leaving-body afternoon of squinting at console settings and copying them into HCL.
The command still exists in 2026 and still works. But nobody sane wants to do it fifty times.
The New Way: the import {} Block (Terraform 1.5+)
In June 2023, Terraform 1.5 shipped the thing that actually made import bearable: a config-driven import block. Instead of a one-off command, you declare your intent right in your .tf files:
import {
id = "my-app-uploads-prod"
to = aws_s3_bucket.uploads
}
Two lines. id is the real resource’s identifier; to is the address you want it to live at. Because it’s part of your config, it shows up in terraform plan — it’s reviewable, it’s version-controlled, and you can stack up a whole file of import blocks and run them in one shot instead of typing the command over and over.
But the genuinely great part is what you pair it with. Add your import blocks, then run:
terraform plan -generate-config-out=generated.tf
Terraform inspects each real resource and writes the HCL config for you into generated.tf. That soul-crushing step two from the old workflow — hand-copying every argument — Terraform now does a best-effort first draft of it automatically. You review the generated file, clean it up, move it into your real config, drop the import blocks, and you’re managing infrastructure you built by hand months ago.
One honest caveat, because I promised honesty: config generation is still marked experimental even now, in 2026 (latest stable is the 1.15 line). The generated HCL is a rough draft, not gospel. It’s a huge head start, not a finished product.
The Gotchas Nobody Puts in the Tutorial
I learned these the hard way, so you don’t have to.
- The generated config lies a little.
-generate-config-outguesses arguments from the live resource. It’ll sometimes emit defaults you don’t want, miss relationships between resources, or spit out something that won’t even validate. Always read it before trusting it. - State is now sacred. The moment real infrastructure lives in a Terraform state file, that file is the most dangerous object you own. Lose it, corrupt it, or let two people run
applyat once, and you can orphan or destroy live resources. Put it in remote state with locking before you import anything real. Don’t keep prod state on your laptop. - Import doesn’t do dependencies. Importing a server doesn’t magically import its security group, its disk, its network. You import each resource, and you’re responsible for wiring the references between them afterward. A “simple” VM can be six imports.
- Secrets leak into state. Database passwords, API keys, connection strings — a lot of them get pulled into the state file in plaintext during import. That’s another reason state belongs somewhere encrypted and locked down, not in a Git repo.
for_eachand modules are fiddly. Importing into a resource that usesfor_eachor lives inside a module means getting the address exactly right (module.db.aws_db_instance.main["primary"]), and it’s easy to get wrong. (Those modules mostly come from the Terraform Registry — the npm-for-infrastructure hub worth knowing if you’re this deep.)
None of this is a reason to avoid Terraform. It’s a reason to respect it. It’s a chainsaw, not a butter knife.
Terraform or OpenTofu? The 2026 Fork in the Road
You can’t write about this in 2026 and skip the elephant. In August 2023, HashiCorp changed Terraform’s license from the open-source MPL to the Business Source License (BSL 1.1) — source-available, but with restrictions, and not OSI-approved open source. The community wasn’t thrilled. So a fork was born: OpenTofu, which took the last open version and kept going under the MPL, governed by the Linux Foundation. It’s been a CNCF project since 2025 and has real momentum.
Then the plot twist: IBM acquired HashiCorp for $6.4 billion, closing in February 2025. Terraform is now an IBM product, being wired into their broader enterprise stack. The license didn’t revert.
For you, a bootstrapper, the practical version is simple. OpenTofu is a near drop-in replacement — the commands are the same, you just type tofu import instead of terraform import, and the import block works identically. If you care about staying on genuinely open-source tooling, OpenTofu is the safer long-term bet. If you want the biggest ecosystem and don’t mind the BSL, Terraform is still fine for a solo project (the license restrictions are aimed at companies building competing products, not at you managing your own infra). Either way, the concepts in this post are identical. And if you’re allergic to HCL entirely, Pulumi lets you define infra in real programming languages — but that’s a different rabbit hole.
Pick knowingly. That’s all I ask.
Why You Probably Don’t Need Any of This Yet
Here’s the part the Terraform tutorials will never tell you, because they want you inside Terraform.
Most solo founders do not need Terraform. At all. Not yet.
If your stack is Vercel for the frontend, Railway or Fly.io for a backend, and Neon for Postgres — congratulations, you have almost no infrastructure to codify. Those platforms are your infrastructure layer. You connect a Git repo, you set a few environment variables, you click deploy. There’s no fleet of servers, no VPC, no tangle of IAM policies and load balancers to describe in a thousand lines of HCL. The whole surface area of your “infra” is a handful of dashboards you could rebuild from memory in an afternoon.
Terraform earns its keep when you have sprawl — dozens or hundreds of cloud resources, a team that needs to review infra changes in pull requests, multiple environments (dev/staging/prod) that must stay identical, and a genuine fear of clicking the wrong button in a console. That’s an AWS / Google Cloud / Azure problem. It is a good problem — it usually means you have real usage and real revenue. It is not a $0 MRR problem.
I reached for Terraform in that 2024 cleanup because I’d manufactured sprawl by over-engineering a URL shortener that had zero users. I gave myself an enterprise problem so I could go learn the enterprise tool. Classic Broken Engineer move. The learning was genuinely fun and I don’t regret it. But if I’d just run that thing on Railway and Neon like a sane person, there’d have been nothing to import, because there’d have been nothing built by hand in the first place.
So here’s the honest decision tree. Are you fighting to keep a dozen hand-clicked cloud resources from drifting out of sync, on a team, with real traffic? Then yes — learn terraform import, do it carefully, lock your state, and reap the benefits. Are you a solo founder with a Vercel deploy and a Neon database and a dream? Then close this tab, go build the product, and bookmark this for the day the sprawl finds you.
The Bottom Line
terraform import is a genuinely elegant tool for a genuinely painful problem: infrastructure that exists in the world but not in your code. The new import {} block plus -generate-config-out turned it from an afternoon of hand-copying into a mostly-automated review job. Learn the old command, prefer the new block, respect the state file, and pick your side of the Terraform/OpenTofu fork on purpose.
But the deepest lesson isn’t about the command. It’s about when. The best infrastructure code is the code you didn’t have to write because your platform handled it for you. Terraform is what you graduate to when you’ve outgrown the magic boxes — not the thing you start with to feel like a real engineer. I started with it to feel like a real engineer. Don’t be like me.
This is the Broken Engineer Guide. I over-engineer everything, fail at business, and share the scars so you can skip a few. Go build something that doesn’t need a state file yet.
