TL;DR
Brownfield infrastructure is any cloud resource running in production with no corresponding Infrastructure as Code, created through a console click, a CLI command, or a script nobody checked into version control. It accumulates for ordinary reasons, most often an incident hotfix that never gets backfilled into code, and it sits outside drift detection, policy enforcement, and audit logging until someone goes looking for it. This guide covers why it happens, what it actually costs to leave alone, and how discovery and codification bring it back under management.
Every cloud environment beyond a certain age has some brownfield infrastructure. This isn't a sign that a team did something wrong. It's the normal byproduct of how infrastructure actually gets built under deadline pressure, incident response, and years of accumulated changes that outlast whoever made them.
Take a payments team at a mid-sized fintech company running its checkout service on AWS. A traffic spike during a product launch triggers timeouts. An on-call engineer traces it to the API Gateway, which needs a new integration route pointing checkout traffic to a backup Lambda, and wires it up directly through the AWS CLI at 11 pm, because waiting for a PR review during an active incident isn't realistic. The fix works. The incident closes. Nobody circles back to add that integration to the Terraform module managing every other route. Eighteen months later, that manually created integration is still sitting there, undocumented, unreviewed, and invisible to every tool supposedly watching the account.
This guide follows that same resource through its whole lifecycle, why it happened, what it costs to leave alone, and what it takes to bring it back under management.
Getting Started: Understanding Brownfield Infrastructure
Brownfield infrastructure is any cloud resource that exists and runs in production without a corresponding code definition. The API Gateway integration from the checkout incident is a clean example: it works, it's actively routing traffic, and it has no .tf file, no state entry, and no mechanism for any platform to know it exists.
Greenfield infrastructure is the opposite case. Resources get defined in code from the moment they're created, reviewed through a pull request, and tracked in state from day one. Most teams start there. Few stay there, and the gap shows up across four dimensions. Origin is the first split: brownfield resources come from a console click, a CLI command, or an ad hoc script, while greenfield resources come from Terraform, OpenTofu, or another IaC tool from the start. Visibility follows directly from that: brownfield resources are invisible to state and drift checks, while greenfield resources are tracked from creation. Review works the same way: brownfield resources are created outside any pull request process, while greenfield resources are reviewed before they exist. And governance depends on all three: brownfield resources carry no policy coverage until someone codifies them, while greenfield resources are policy-checked before every apply.
Both categories coexist in the same account, often in the same VPC. The payments team's networking layer is fully codified. The integration sitting next to it isn't.
Why Does Brownfield Infrastructure Keep Accumulating?
The checkout service incident isn't a one-off. It's the most common pattern behind brownfield accumulation: an engineer makes the right call under pressure, and the code never catches up. The same pattern shows up in a few recurring forms:
- Incident hotfixes: the checkout example above, where a manual integration resolves the outage but the module update meant to follow it never happens
- PR review bottlenecks: a fix that could go through code review takes 48 hours to approve, so someone ships it manually and means to backfill later
- Pre-adoption legacy resources: infrastructure provisioned before a team adopted Terraform at all, left untouched because nobody wants to risk breaking something that currently works
- Shadow IT: a team spins up resources outside the platform team's approved workflow, often without realizing there was a workflow to follow
- Mergers and acquisitions: an acquired company's AWS account arrives with years of infrastructure history and zero documentation
Each of these is individually reasonable. The problem is cumulative, not any single decision.
What Risk Does Unmanaged Infrastructure Create?
Eighteen months after the checkout incident, nothing has changed about that integration except that nobody remembers which backend it was supposed to be temporary. It carries risk precisely because nothing is watching it, and that risk compounds across four categories:
- No drift detection: the target Lambda or timeout could be changed further tomorrow and nothing would flag it, since there's no baseline to compare against
- No policy enforcement: a policy requiring every integration to use a validated timeout and payload format never evaluates this route, because it was never part of a governed workflow
- No audit trail: there's no record of who created the integration, when, or why, which becomes a real problem the moment a security review asks
- Cost blind spots: if the same incident had also spun up an oversized instance, nothing would catch it until the invoice arrived
A single forgotten integration looks like a minor gap. An account with hundreds of unmanaged resources like it, alongside stale IAM roles nobody remembers granting, looks like an incident that hasn't happened yet.
How Do You Find Out How Much Brownfield Infrastructure Exists?
The payments team doesn't know that integration exists until someone runs a discovery scan against the account. Guessing based on memory consistently underestimates the real number, since the resources that come to mind are the recent ones, not the ones sitting quietly from eighteen months ago.
Measuring starts with a cloud inventory scan cross-referenced against connected state backends. The scan discovers every resource actually running in the account, and the cross-reference against state determines which resources have a corresponding .tf definition and which don't. For the payments team, that scan surfaces the exact API Gateway integration from the incident, along with whatever else has accumulated since.
How Do You Turn Brownfield Infrastructure Into Managed Code?
Codification generates the missing IaC for an existing resource without destroying and recreating it. For the payments team's integration, the route keeps sending checkout traffic to the backup Lambda throughout the process. What changes is whether a platform can see and govern it afterward. The technical sequence runs in five steps: discover the live integration against connected state backends to confirm it has no existing .tf definition, generate Terraform configuration inferred from the resource's live setup, resolve its dependencies on the parent API and target Lambda, validate the output through an internal terraform plan confirming zero diff, and publish the result as a pull request against the team's existing repository.
The payments team already manages its API Gateway through a reusable module, so generated code slots a new entry into the existing for_each map rather than writing a standalone resource block:
module "apigatewayv2_integration" {
source = "./modules/apigatewayv2_integration"
for_each = var.apigatewayv2_integrations
api_id = module.apigatewayv2_api[each.value.api_key].id
integration_type = each.value.integration_type
integration_method = each.value.integration_method
integration_uri = each.value.integration_uri
payload_format_version = each.value.payload_format_version
timeout_milliseconds = each.value.timeout_milliseconds
connection_type = each.value.connection_type
}
Running a plan after adding the missing entry confirms the generated configuration actually matches what's live:
$ terraform plan
module.apigatewayv2_integration["checkout_backup"]: Refreshing state...
No changes. Your infrastructure matches the configuration.
A zero-diff plan is what makes the pull request safe to merge. Skipping this validation step is the most common mistake teams make when codifying manually. Generated code that looks correct but doesn't quite match the live resource's actual configuration produces an unexpected diff the first time anyone runs a plan against it, often more disruptive than leaving the resource unmanaged in the first place.
Not every unmanaged resource carries the same urgency once a scan surfaces fifty of them. Production resources come before sandbox ones; security-sensitive resources like IAM roles and security groups come before cosmetic tagging fixes; resources with many dependents come before standalone leaf resources; and recently changed resources come before stable ones that haven't been touched in years. Tracking IaC coverage as a measurable percentage, rather than a one-time audit, turns this into an ongoing target instead of a project that stalls after the first sprint.
How Does StackGuardian Handle Brownfield Codification?
SGCode was built specifically for this problem. It connects to AWS, Azure, and GCP accounts through its Cloud Inventory, discovers every resource across the environment, and classifies coverage against connected state backends, whether the gap is three days old or three years old, like the checkout API Gateway integration.
Discovered infrastructure gets organized into Infra Projects, a structured way to group related resources rather than working through a flat, undifferentiated list. From there, selecting the integration for codification triggers AI-powered generation: SGCode produces Terraform configuration that matches the payments team's existing module pattern, resolves API and Lambda target dependencies, and validates the output through an internal plan run before proposing anything.

The generated code publishes directly as a pull request to the payments team's connected repository. Once that PR merges, workflows created from SGCode automatically update to point to the target branch, so there's no manual reconfiguration step to remember afterward. State Backends management handles where the newly codified resource's state actually lives, connecting to the team's existing backend rather than requiring a separate setup process.
Once codified, the integration becomes eligible for governance through SGOrchestrator. A Tirith policy scoped to the payments team's Workflow Group can now evaluate it directly, something that was impossible while the resource sat outside state:
{
"meta": {
"required_provider": "stackguardian/terraform_plan",
"version": "v1"
},
"evaluators": [
{
"id": "require-integration-timeout",
"description": "Require an explicit timeout on every API Gateway integration",
"provider_args": {
"operation_type": "attribute",
"terraform_resource_type": "aws_apigatewayv2_integration",
"terraform_resource_attribute": "timeout_milliseconds"
},
"condition": {
"type": "IsNotEmpty",
"error_tolerance": 1
}
}
],
"eval_expression": "require-integration-timeout"
}
That policy now runs against the checkout integration on every future change, something Sentinel-style, plan-time enforcement could never do while the resource had no state entry to evaluate.
What Changes Once Brownfield Infrastructure Is Codified?
For the payments team, the shift is concrete. The checkout integration goes from a resource nobody was watching to one covered by every governance mechanism at once: Automated Drift Check now runs on a schedule against it where none existed before, Tirith or OPA evaluates every planned change before it applies where no policy coverage existed before, every action gets logged in Audit Logs and is exportable to JSON or CSV where no audit trail existed before, and Policy Sets can evaluate the resource against InfraCost data where its cost impact was previously unknown.
None of this required touching the resource itself. The integration kept routing checkout traffic to the backup Lambda exactly as it did before. What changed is whether anything is watching it, checking it, and recording what happens to it going forward.
Getting Brownfield Infrastructure Under Control
Brownfield infrastructure isn't solved by a one-time audit. The payments team can codify every resource currently sitting unmanaged and still have a new batch within a year, produced by the same operational pressure that created the checkout integration in the first place. What actually works is treating IaC coverage as a metric to track continuously, the same way uptime or error rate gets tracked, rather than a project with a defined end date.
The starting point is the same, no matter how large the gap is. Run a discovery scan, see the real number, and prioritize from there using production impact, security sensitivity, and dependency count as the ranking criteria. Explore StackGuardian's SGCode to see how discovery and codification work against a live cloud account, or read how the same platform handles ongoing policy enforcement once resources are brought under management.
FAQs
1. What's the difference between brownfield and greenfield infrastructure?
Greenfield infrastructure is defined in code from the moment it's created, reviewed through a pull request, and tracked in state from day one. Brownfield infrastructure exists and runs in production without any of that, created through a console click, a CLI command, or a script that was never checked into version control.
2. How do you find unmanaged cloud resources?
Run a cloud inventory scan against the live account and cross-reference the results against connected Terraform or OpenTofu state backends. Resources that show up in the live scan but have no corresponding state entry are unmanaged. Tools like SGCode automate this cross-reference directly.
3. Can you generate Terraform automatically from existing infrastructure?
Yes. AI-powered codification tools generate Terraform or OpenTofu configuration for a discovered resource, resolve its dependencies on other resources, and validate the output through an internal plan run before publishing it as a pull request, rather than requiring someone to write the resource block by hand.
4. What's the risk of leaving infrastructure uncodified?
Uncodified resources sit outside drift detection, policy enforcement, and audit logging. Changes go unnoticed indefinitely, security and compliance checks never evaluate the resource, and there's no record of who created it or why, which becomes a real problem the moment a security review or an incident forces someone to explain it.
5. Does codifying brownfield infrastructure require downtime?
No. Codification generates the missing IaC definition and imports the resource into state; it doesn't destroy or recreate anything. The resource keeps running exactly as before, and validating the generated configuration through an internal plan run confirms the code matches the resource without changing the live infrastructure.
6. How much brownfield infrastructure does the average cloud account actually have?
There's no universal figure, since it depends heavily on how long an account has existed and how consistently a team enforced IaC-only provisioning. What's consistent across teams that run a discovery scan for the first time is that the real number tends to be significantly higher than what anyone estimated from memory beforehand.
7. Is brownfield infrastructure the same thing as infrastructure drift?
No, though they're related. Drift describes a resource already tracked in code that diverges from its declared configuration. Brownfield infrastructure describes a resource that was never tracked in code, so it has no declared configuration to drift from in the first place.


Top comments (0)