Blog

Why AI-generated Terraform breaks in production

A green plan only means the text is valid. It says nothing about whether your team can change region next quarter without breaking the rest of the file.

· 7 min read

The failure isn't in the plan output

Ask a general-purpose AI assistant for a VPC, a database, and three application servers, and it will paste back Terraform that plans cleanly and applies without error. That's the wrong test. `terraform plan` checks that HCL is syntactically valid and that the described changes are computable — it says nothing about whether the file is something a team can safely maintain six months later.

The gap between "the plan is green" and "this is production-ready infrastructure" is where most AI-generated Terraform actually fails. Not at apply time — at the second change, the on-call page at 2am, or the security review that asks why encryption was never turned on.

Five patterns, observed directly

These aren't hypothetical. They're the five failure patterns we built NebulaStack's generation pipeline to specifically avoid, and each one shows up reliably in chat-pasted Terraform:

The same settings copied everywhere. Tags, backup windows, network references — repeated verbatim across every resource block instead of declared once and reused. Change one value, and now you're grepping the file for every other place it was copied.

Numbers frozen into the code. Region, instance size, memory, port numbers: hardcoded as literals instead of parameterized. Moving to a new region means editing every occurrence by hand — and missing one is not a hypothetical, it's the median outcome.

No separation between concerns. Network, compute, database, and DNS resources sitting in one undifferentiated file. Nothing wrong with it passing `terraform validate` — everything wrong with trying to reason about it, or reuse a piece of it, later.

Everything named the same thing. Five distinct services becoming five resources all named `app` or `main`. It plans fine. It's unreadable in the AWS console and worse in a 2am incident, when you're trying to match a CloudWatch alarm to a specific resource by name.

Hard questions silently get the cheap default. Database standby, backup retention, encryption at rest, deletion protection — an assistant optimizing for a green plan has every incentive to leave these off, because turning them on requires information the assistant doesn't have and won't ask for. The plan stays green either way. Only one of those two states is safe to run in production.

What actually prevents each one

The fix isn't a better prompt. A model that's better at writing Terraform is still a model deciding, token by token, what the infrastructure should look like — with no persistent request to regenerate from, and no schema to check the output against before it reaches you.

The architecture that avoids this separates three things a chat-based approach collapses into one: what you asked for, what the deterministic engine fills in when you didn't specify it, and the files themselves. NebulaStack's assistant extracts a typed, structured intent from what you describe in plain language — resource type, provider, key attributes — and that's what gets stored. The language model never writes HCL directly. A separate, deterministic engine generates the files from the structured intent, against each provider's real Terraform schema, every time, the same way.

That ordering is what closes each of the five gaps above. Shared settings get written once because they're modeled once, not copy-pasted N times by a model with no persistent state. Numbers stop being frozen because they live in the request, not the file — change the request and the files regenerate. Resources get separated by concern because the generation pipeline is organized around a schema, not around whatever ordering a chat completion happened to produce. Names come from what you actually typed, because the generator has access to the structured intent, not just a blob of prior conversation. And on a Production environment, database standby, extended backups, deletion protection, and encrypted state get turned on as a rule the pipeline enforces — not a box the assistant hopes you'll think to ask about.

What this doesn't fix — on purpose

Three limits are worth stating plainly, because a vendor that only tells you what a system does and never what it doesn't is not a source you should trust.

Not every resource type has a dedicated form yet. Where the multi-provider wizard doesn't cover a resource, the assistant can still write Terraform for it directly — and at that point, all that's checked is that the file is valid, the same green-plan-isn't-enough gap this piece opened with. For networks, compute, and databases specifically — the resources covered above — use the form.

The generated project is the deliverable, not a starting point to hand-edit. NebulaStack is the source of truth: when something needs to change, the change goes into the request and the files regenerate. Copy the output and start patching it by hand, and every problem this piece describes comes back, just one layer removed.

And some expensive choices are made for you, deliberately, not silently. A Production database gets a standby replica even though that costs more — that's a rule, not a suggestion you can miss. Machine size, network, operating system, and region are confirmed with you explicitly rather than defaulted. But not every reliability trade-off is intercepted yet. That's a real, current limit, not a hedge.

Describe. Review. Apply.

Free to start, no credit card.