August 13, 2026

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.

~ min read
~0 min read

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 that was never checked into version control.
  • It accumulates for ordinary reasons, most often an incident hotfix that never gets backfilled into code, not carelessness.
  • Unmanaged resources sit outside drift detection, policy enforcement, and audit logging, which makes them invisible until an incident or a compliance review forces someone to go looking.
  • Bringing brownfield infrastructure under control means discovering what exists against connected state backends, then generating and validating the IaC that governs it.
  • SGCode handles both steps directly, connecting to live cloud accounts, generating Terraform or OpenTofu for discovered resources, and publishing the result as a pull request to an existing Git repository.

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.

What Is Brownfield Infrastructure?

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.

Dimension

Brownfield

Greenfield

Origin

Console, CLI, or ad hoc script

Terraform, OpenTofu, or another IaC tool

Visibility

Invisible to state and drift checks

Tracked from creation

Review

None, created outside any PR process

Reviewed through a pull request

Governance

No policy coverage until codified

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 Actually 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 the risk compounds across four categories:

Risk

What Happens

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 that requires 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 You Have?

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.

How Do You Turn Brownfield Infrastructure Into Managed Code?

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:

  1. Discover: cross-reference the live integration against connected state backends to confirm it has no existing .tf definition
  2. Generate: produce Terraform configuration inferred from the resource's live configuration, matching the module pattern the payments team already uses for every other API Gateway route
  3. Resolve dependencies: the integration references the parent API and a target Lambda; the generated code needs both relationships correctly wired through the module's for_each map or the resulting plan won't apply cleanly
  4. Validate: run the generated configuration through an internal terraform plan, confirming zero diff against the actual resource
  5. Publish: open the validated code as a pull request against the payments team's existing Git repository

The 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.

What Should You Codify First?

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:

  • Production before sandbox: the checkout integration ahead of anything in a dev environment
  • Security-sensitive resources before cosmetic ones: IAM roles, security groups, and anything touching network access ahead of tags or naming conventions
  • Resources with many dependents before leaf resources: a VPC that a dozen other resources reference matters more than a single standalone bucket
  • Recently changed resources before stable ones: a resource edited manually multiple times is more likely to keep drifting than one untouched for 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 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.

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.

Before Codification

After Codification

No drift detection

Automated Drift Check runs on a schedule against the resource

No policy coverage

Tirith or OPA evaluates every planned change before it applies

No audit trail

Every action is logged in Audit Logs, exportable to JSON or CSV

Unknown cost impact

Policy Sets can evaluate the resource against InfraCost data

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.

Conclusion: 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 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.

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 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.

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 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.

Share article