What Is Brownfield Infrastructure? A Guide to Codifying What Already Exists
Brownfield infrastructure sits outside drift detection and policy checks until it's codified. See how discovery, generation, and validation actually work.
Brownfield infrastructure sits outside drift detection and policy checks until it's codified. See how discovery, generation, and validation actually work.


Every cloud environment beyond a certain age has some brownfield infrastructure in it. 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 that manages every other route. Eighteen months later, that one manually created integration is still sitting there, undocumented, unreviewed, and invisible to every tool that's supposed to be watching the account. This post follows that resource through the whole lifecycle: why it happened, what it costs to leave alone, and what it takes to bring it back under management.
Brownfield infrastructure is any cloud resource that exists and runs in production without a corresponding definition in code. 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 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. Very few stay there.
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.
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:
Each of these is individually reasonable. The problem is cumulative, not any single decision.

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 the risk compounds across four categories:
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.
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, because 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; 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.
Codification generates the missing IaC for a resource that already exists, 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:
.tf definitionterraform plan, confirming zero diff against the actual resourceThe payments team already manages its API Gateway through a reusable module, so the generated code slots a new entry into the existing for_each map rather than writing a standalone resource block:
module "apigatewayv2_api" {
source = "./modules/apigatewayv2_api"
for_each = var.apigatewayv2_apis
name = each.value.name
protocol_type = each.value.protocol_type
description = each.value.description
route_selection_expression = each.value.route_selection_expression
api_key_selection_expression = each.value.api_key_selection_expression
cors_configuration = each.value.cors_configuration
tags = each.value.tags
}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 to var.apigatewayv2_integrations 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, which is often more disruptive than leaving the resource unmanaged in the first place.

Not every unmanaged resource carries the same urgency. If the payments team's discovery scan surfaces fifty unmanaged resources, the checkout API Gateway integration, sitting in production and routing live payment traffic, matters more than an unused S3 bucket in a sandbox account that nobody's opened in a year.
A reasonable prioritization order:
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.
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 the Terraform configuration matching the payments team's existing module pattern, resolves the API and Lambda target dependencies, and validates the output through an internal plan run before proposing anything.
The generated code is published directly as a pull request to the payments team's connected repository. Once that PR merges, workflows created from SGCode update automatically to point at 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.
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.
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.

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 regardless of how large the gap turns out to be: run a discovery scan, see the real number, and prioritize from there. Guessing at the size of the problem before measuring it usually means underestimating it by a wide margin.
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.
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.
Yes. AI-powered codification tools like SGCode 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.
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.
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 it did before. Validating the generated configuration through an internal plan run before publishing is what confirms the code matches the resource without requiring any change to the live infrastructure.