Tolling Infrastructure · Foundation

One governed cloud foundation under 40+ accounts

A four-layer landing zone, one hub-and-spoke network, and a CI/CD pipeline where an unintended apply is structurally impossible.

Project
Tolling Infrastructure
Capability
Foundation & governance
Stack
Terraform · AWS Organizations · Transit Gateway · GitHub Actions
Tags
governance · networking · terraform · cicd · multi-account
Reading time
4 min
Published

Key results

40+
AWS accounts under one consistent governance and security model
Weeks to hours
Time to provision a fully governed new environment
Zero
Accidental production applies since the pipeline redesign
The problem

Every account was its own island

Before this work, each tolling project's AWS environment had been built independently (its own network design, its own security posture, its own way of provisioning a new environment. That meant no consistent audit trail across the platform, security tooling rolled out unevenly, and a new environment taking weeks of manual configuration to stand up. For teams operating tolling infrastructure, that inconsistency is itself a risk.

The brief was to design a single governance model that every account) present and future: would inherit automatically, without slowing engineering teams down or requiring them to understand the underlying plumbing.

Architecture

A four-layer landing zone, deployed bottom-up

The entire platform was organized into four dependency-ordered Terraform layers, each one only allowed to depend on the layer beneath it. That constraint is what keeps the blast radius of any single change small and the deployment order unambiguous.

03

Applications

API Gateway + Lambda Aurora PostgreSQL S3 & EFS storage SQS / SNS messaging QuickSight BI
↑ depends on
02

Shared Services

Self-hosted HA identity provider CI/CD controller Bastion hosts DNS & certificates EDR + vulnerability scanning
↑ depends on
01

Core Network

Transit Gateway hub Spoke VPCs per account Site-to-Site VPN Centralized VPC endpoints
↑ depends on
00

Foundation

AWS Organizations & SCPs Centralized logging & GuardDuty Terraform remote state OIDC federation for CI/CD
Why layers, not one big Terraform config: state is decoupled between layers via remote-state lookups, so a change to an application never risks touching the network hub, and a new account only ever needs to run the layers it's missing: not rebuild the platform from scratch.
Networking

Hub-and-spoke, not point-to-point

Every account's VPC attaches to one central Transit Gateway rather than maintaining its own peering relationships and its own path to on-premises systems. A mirrored hub in a secondary region handles disaster recovery, so failover is a routing decision, not a rebuild.

How a request actually moves through the hub

Public entry (edge + DNS) Internal, hub-routed path
GOVERNANCE & COMPLIANCE WORKLOAD ACCOUNTS CloudFront + WAF → Route 53 Internet End Users Transit Gateway Networking Hub Security Account Archive & Migration Shared Services Route 53 · Identity Production Accounts Dev / Staging Accounts DR Region Standby Hub
Click Show traffic flow to follow a public request in through the edge, and see how it's then distributed like any other hub-routed traffic.

Security and Archive & Migration are kept as a lightweight governance and compliance stack (audit, guardrails, long-term retention) structurally separate from the accounts that actually run workloads. Every account in either group still connects to the same single hub.

To be precise about the path: a public request hits CloudFront first, with a WAF in front of it, then resolves through Route 53 (both the public zone for the domain and the private zones used for internal service discovery) hosted in the Shared Services account. From there it reaches the Transit Gateway in the Networking account and gets distributed to whichever account actually owns the workload, Production included. It's one entry point, not a separate ingress path per account.

Rollout

A strict, dependency-ordered build sequence

Because every layer depends only on the one below it, the entire platform (or a single new account joining it) follows the same fixed sequence. In practice this turned a bespoke, multi-week onboarding into a repeatable checklist:

Organization & guardrails

Stand up the AWS Organization, service control policies, and centralized logging before anything else exists.

Primary network hub

Deploy the Transit Gateway and attach the first wave of spoke VPCs.

DR network hub

Stand up the mirrored hub in the secondary region so failover has somewhere to go.

Shared services

Bastion access, DNS, certificates, identity, and security tooling: the layer every application depends on.

Applications, per account

Databases, storage, messaging, and the workload stack itself, brought up once its dependencies are confirmed live.

CI/CD

Designing so a mistake literally cannot ship

Terraform changes are safe to review but dangerous to run carelessly against 40+ live accounts, so the deployment pipeline was designed to make an unintended apply structurally impossible rather than relying on manual vigilance. Every module inherits one shared pipeline engine instead of copy-pasted YAML, and every run is confined to exactly one module and one environment.

Layer Guardrail
1 Pull requests can only ever trigger a read-only plan: the automated PR job has no apply step to run, full stop.
2 That plan job assumes an IAM role scoped to AWS-managed read-only access (even a compromised or misconfigured workflow is hard-rejected by AWS itself if it attempts a write.
3 A manual deploy defaults to a dry-run plan. Actually changing infrastructure requires explicitly unchecking a "plan only" flag.
4 Applying still requires typing the exact target environment name as a confirmation string) a typo or a copy-paste from the wrong run aborts the pipeline before Terraform runs at all.
5 The apply step itself is conditionally skipped in the workflow definition unless every prior check passed: it's not a policy, it's how the job graph is built.

The result: onboarding a brand-new module into this pipeline takes under two minutes of YAML, but no engineer (however new) can accidentally run terraform apply against production by clicking the wrong button.

Outcome

What changed

40+
AWS accounts under one consistent governance and security model
Weeks → Hours
Time to provision a fully governed new environment
Zero
Accidental production applies since the pipeline redesign

Beyond the numbers, the platform stopped depending on any one person's tribal knowledge. A new engineer can read the layer they're touching, trust that everything beneath it is already governed, and ship a change with a pipeline that makes the unsafe path harder than the safe one.

Questions about this case study?

Happy to go deeper on any part of this: the architecture, the trade-offs, or the decisions behind it.

anupojuprudhvi@gmail.com