The VPC and network topology decision record: why the subnet segmentation model you chose determines your lateral movement blast radius and your peering compatibility ceiling

Published 2026-07-22 · WhyChose

VPC and network topology decisions are made when an engineer creates the first cloud account for a new project, when a second service requires its own networking tier and the team adds subnets, and when a compliance review or security audit flags that the current security group configuration does not satisfy the auditor's network segmentation requirement. The AI session that handles the initial VPC setup is infrastructure-bootstrapping-oriented and terminates at the working instance: it creates the VPC with an available CIDR block (often the suggested default), creates public and private subnets across availability zones, configures an internet gateway for the public subnets and a NAT gateway for the private subnets, creates a security group that allows inbound SSH from the engineer's current IP, deploys an EC2 instance into the private subnet, verifies that SSH works through the bastion or SSM agent, and closes. The network is operational. The session succeeds. The session closes.

What the AI session does not produce is the second half of the VPC decision. The session answers "how do I create a working VPC with public and private subnets?" and delivers a functional network with an operational instance. It does not ask: what is the rationale for the selected CIDR block and what is the maximum number of IP addresses per subnet before the space is exhausted, given the expected ENI density from ECS Fargate tasks and Lambda functions with VPC attachment; which of the three subnet tiers — public, private, isolated — should each future service be placed in, and is the placement decision documented as a convention that the next engineer who deploys a service will follow rather than re-derive; what is the security group model for service-to-service traffic — does the platform convention use specific source security group references (which allow only instances associated with the named group) or VPC CIDR ranges (which allow all instances in the entire CIDR regardless of their function); whether the CIDR block overlaps with any existing corporate network, common organizational address range, or default VPC CIDR that might need to be peered in the future; what are the NAT gateway availability requirements, and is the single NAT gateway in one availability zone a single point of failure for outbound internet access from private subnets in the other AZs; and what the egress control model is for the most sensitive tiers — whether the database subnet has a route to 0.0.0.0/0 through the NAT gateway (which would allow a compromised database instance to initiate arbitrary outbound connections) or whether the isolated subnet has no default route at all (allowing only VPC-internal traffic and explicit VPC endpoint routes). Each of these questions has an answer that is not derivable from "an EC2 instance in the private subnet can reach the internet through the NAT gateway and accept SSH connections from the bastion" as the session completion criterion. Each answer determines whether a future VPC peering attempt with an enterprise customer is blocked by overlapping CIDRs and requires a two-week VPC replacement, whether a compromised application service can attempt direct database connections because the security group allows the VPC CIDR as the source rather than only the specific application security group, whether an outage in one availability zone takes down outbound internet access for private subnet services in all availability zones because there is only one NAT gateway, and whether a penetration tester's finding "database is reachable from the public subnet" is a consequence of the security group model or a misconfiguration. The answers are in the AI sessions. They are almost never written down.

Two ways VPC design decisions produce the wrong outcome in production

The default CIDR block that blocked an enterprise partnership for two months

A developer tools startup launches on AWS in month one. The founding engineer creates an AWS account, runs through the VPC wizard in the console — selects "VPC with Public and Private Subnets" from the template, accepts the default CIDR block of 172.31.0.0/16 (the same CIDR that AWS assigns to the default VPC in every new account), creates two public subnets and two private subnets across two availability zones, accepts the generated NAT gateway, and deploys the application's first EC2 instance. The network topology is functional. The engineer writes an ARCHITECTURE.md file noting "VPC in us-east-1, public and private subnets across 2 AZs, EC2 instances in private subnets behind an ALB in the public subnet." The CIDR block is not mentioned in the document because it does not feel like an architectural decision — it is simply the value that was pre-populated in the wizard form.

Twenty-two months later, the startup closes a strategic partnership agreement with an enterprise software company. The partnership requires a data integration: the enterprise's analytics platform needs to read from the startup's internal API using a private network connection, avoiding the public internet for compliance reasons. The integration team schedules a call to set up VPC peering between the enterprise's AWS account and the startup's VPC. The enterprise engineer looks up their VPC CIDR: 172.31.0.0/16. The startup engineer looks up their VPC CIDR: 172.31.0.0/16. The CIDRs are identical. AWS VPC peering requires non-overlapping CIDR blocks — two VPCs with the same address space cannot be peered because routing between them would be ambiguous. The VPC peering connection attempt fails immediately with InvalidVpcPeerId.Overlapping.

The startup's infrastructure engineer investigates the options. Adding a secondary CIDR block to the startup's VPC would not resolve the peering conflict because secondary CIDRs supplement the primary CIDR rather than replacing it, and the primary CIDR still overlaps with the enterprise VPC. The only resolution is to create a new VPC with a non-overlapping CIDR (the startup chooses 10.42.0.0/16 from a range that does not overlap with the enterprise's address space), recreate all networking infrastructure in the new VPC (subnets, security groups, route tables, internet gateway, NAT gateway, VPC endpoints), migrate all EC2 instances to the new VPC (requiring instance replacement since EC2 instances cannot be moved between VPCs while running), migrate the RDS database to the new VPC (requiring a snapshot restore into a new subnet group in the new VPC — the shortest maintenance window they can achieve with their snapshot-based migration is 47 minutes), update all Terraform configurations and application environment variables that reference VPC IDs, subnet IDs, and internal IP addresses, validate all service connectivity in the new VPC, and decommission the old VPC. The migration takes seven weeks: three weeks of planning and preparation, one week of staging migration, a weekend of production migration with a maintenance window, and two weeks of validation and cleanup. The enterprise partnership data integration is delayed by eight weeks from the date the CIDR conflict was discovered.

The root cause was not a poor architectural choice — the VPC the startup created was functional, correctly configured, and served the business well for twenty-two months. The root cause was the absence of documentation: the CIDR block was accepted from a wizard default without a rationale recorded, and no engineer on the team knew that the value 172.31.0.0/16 is the default VPC CIDR assigned to every AWS account. If the founding session had produced a CIDR allocation note — "we selected 172.31.0.0/16, which is the AWS default VPC CIDR; if we need to peer with partners or acquisitions, this CIDR may conflict with their own AWS default VPCs; consider migrating to a non-default range before any peering requirement arises" — the startup would have had seven weeks of warning before the first peering requirement appeared rather than discovering the conflict when the peering attempt failed.

The CIDR-based security group rule that a penetration tester reached the database through

A B2B SaaS company builds a three-tier architecture: an application load balancer in the public subnets forwarding to API service instances in the private subnets, with a PostgreSQL RDS instance in the private subnets accessible to the API service. The founding infrastructure session creates three security groups: alb-sg (inbound TCP 443 from 0.0.0.0/0), api-sg (inbound TCP 8080 from the VPC CIDR 10.0.0.0/16, outbound all), and db-sg (inbound TCP 5432 from the VPC CIDR 10.0.0.0/16). The rationale for using the VPC CIDR range rather than security group references is operational simplicity: referencing the VPC CIDR means the engineer only needs to know the VPC's address space to write the rule, whereas referencing a security group ID requires looking up the ID, verifying it is the correct group, and updating the rule if the source group is replaced. The convention of "allow from the VPC CIDR" is adopted implicitly as the platform practice because it is the pattern that worked in the first infrastructure session, and subsequent sessions follow it without questioning it.

Fifteen months after launch, the company engages a penetration testing firm for a SOC 2 Type II audit preparation review. The penetration testers are given access to a staging environment that mirrors the production security group configuration. The test identifies a finding classified as High: the database security group (db-sg) allows inbound TCP 5432 from 10.0.0.0/16, which is the entire VPC CIDR. The application load balancer is in a public subnet with an IP in the 10.0.1.0/24 range, which is within the 10.0.0.0/16 VPC CIDR. In a scenario where an attacker exploits a vulnerability in the application to gain code execution on an ALB instance (or on any other instance in the VPC — the company also has an analytics service, a background worker, and an admin tool all with IPs in the 10.0.0.0/16 CIDR), the attacker can attempt direct database connections because the db-sg rule allows TCP 5432 from any IP in the 10.0.0.0/16 range, including the ALB and every other service in the VPC. The penetration tester demonstrates the finding by launching a PostgreSQL connection attempt from a test instance in the VPC's analytics service subnet — a service that has no legitimate reason to connect to the database — and confirming that the connection reaches the database's TCP listener. The actual database authentication would still require credentials, but the finding is that the network allows the connection attempt, which expands the blast radius of any authentication vulnerability or credential theft from the API service tier to any compromised service in the VPC.

The remediation requires replacing every CIDR-based internal security group rule with a security group reference rule. The infrastructure engineer audits all 31 security groups in the production VPC and finds 47 inbound rules that use the VPC CIDR as the source for internal service-to-service traffic. Each rule must be analyzed to determine which specific security group should be the source — a process that requires reading the application architecture documentation (which is incomplete) and in several cases interviewing the engineer who originally created the rule to understand the intended traffic pattern. The replacement rules are applied to the staging environment first and tested over two weeks by running the application's full integration test suite and manually verifying that each service can reach the services it legitimately needs to reach and cannot reach the services it should not reach. The production migration is applied over a weekend maintenance window. Total engineering time: six weeks. The root cause was not the initial CIDR-based security group convention — it was functional and the services operated correctly with it. The root cause was the absence of a documented security group model: if the founding session had produced a security group convention noting "internal service-to-service rules use security group references as the source, not VPC CIDR ranges; the database security group allows inbound 5432 only from api-sg, not from the VPC CIDR; this is the required pattern for all new security group rules for internal traffic," every subsequent engineer who added a security group rule would have followed the reference model rather than the CIDR model, and the penetration test would have produced a different finding at a fraction of the remediation cost.

Three structural properties that VPC design decisions determine

The CIDR allocation model and the peering compatibility ceiling

The CIDR allocation model specifies the primary address block for the VPC, the per-subnet sizing within that block, the allocation strategy for secondary CIDRs, the rationale for the chosen values, and the known address ranges that the chosen CIDR must not overlap with. Without a documented CIDR allocation model, the VPC's address space is recorded only in the AWS console and in Terraform state, with no explanation of why the specific CIDR was chosen, no inventory of address ranges the VPC must coexist with, and no record of the ENI density assumptions that informed the per-subnet sizing — which means that when a future peering requirement appears, the engineer evaluating whether the peering is possible must discover through attempted connection whether overlap exists rather than consulting a documented allocation. The CIDR allocation model must specify six properties. First, the primary VPC CIDR and the selection rationale: the three RFC 1918 private address spaces are 10.0.0.0/8 (16,777,216 addresses), 172.16.0.0/12 (1,048,576 addresses), and 192.168.0.0/16 (65,536 addresses); a production VPC typically uses a /16 block (65,536 addresses) from the 10.0.0.0/8 space, which provides enough per-subnet capacity for dense ENI workloads while leaving room for additional CIDRs in secondary blocks; the specific /16 chosen within the 10.0.0.0/8 space must be documented alongside the known conflicts it avoids — the corporate VPN range (commonly 10.0.0.0/8 with /16 or /24 subnets assigned to office networks), the other environment VPCs in the same account (production should not overlap with staging or development), and common default ranges (172.31.0.0/16 for the AWS default VPC, 10.0.0.0/16 for the most common non-default VPC tutorial configuration). Second, the per-subnet sizing: a /16 VPC split into /20 subnets provides 16 subnets with 4,096 addresses each (4,091 usable after AWS reserves 5 per subnet); this is the appropriate sizing for most workloads, but a service that uses ECS Fargate tasks with VPC networking — where each Fargate task requires one ENI in the task's subnet — can consume the /20 address space if autoscaling produces thousands of concurrent tasks; the per-subnet sizing must document the expected ENI density ceiling and whether the subnet size supports the maximum expected concurrent resource count. Third, the availability zone distribution: each subnet is in one availability zone; the VPC design must specify the number of AZs covered (two-AZ is the minimum for high availability; three-AZ is the standard for production workloads where the failure of one AZ should not reduce capacity below the minimum required to serve traffic), the rationale for the AZ selection (us-east-1 has six AZs; the decision record must specify which three are used and why the others were excluded — typically because specific instance families or managed services have capacity limitations in certain AZs), and the subnet layout per AZ (which subnet types are created in each AZ). Fourth, the NAT gateway availability model: a single NAT gateway in one AZ serves outbound internet traffic for private subnets in all AZs; if that AZ experiences an outage, NAT gateway traffic from private subnets in the surviving AZs is disrupted because the route table entry for 0.0.0.0/0 points to the failed NAT gateway; one NAT gateway per AZ eliminates the single-AZ dependency at the cost of additional NAT gateway hours and per-AZ Elastic IP charges; the decision must specify whether the additional availability justifies the cost for each environment. Fifth, the secondary CIDR strategy: AWS allows up to four secondary CIDR blocks per VPC (subject to range restrictions based on the primary CIDR); secondary CIDRs are useful when the primary CIDR is exhausted and additional subnets are needed; the secondary CIDR strategy must specify under what conditions secondary CIDRs are added, which ranges are reserved for potential secondary use, and whether any planned secondary CIDRs might conflict with planned peering targets; secondary CIDRs from the same RFC 1918 space as the primary CIDR can extend the address space, but secondary CIDRs from a range that overlaps with a peering target will block that peering in the same way as the primary CIDR. Sixth, the peering compatibility inventory: the CIDR allocation document must record the known address ranges that the VPC must not overlap with — current corporate VPN ranges, address ranges assigned to other environment VPCs, address ranges used by partner organizations with known connectivity requirements, and ranges reserved for potential future use — so that future peering requirements can be evaluated against the documented inventory rather than discovered through collision. The infrastructure decision record specifies the cloud account structure and the resource organizational model; the CIDR allocation model is the network-layer complement to the infrastructure decision record, specifying the address space structure that underlies the resource placement model and determining whether environment isolation at the VPC level is achievable with the chosen CIDR allocation or whether overlapping address spaces prevent the isolation required by the compliance framework.

The subnet segmentation model and the lateral movement blast radius

The subnet segmentation model specifies the three-tier layout, the assignment convention for each tier, the security group model for inter-tier traffic, the network ACL rules at each subnet boundary, and the egress model for each tier. Without a documented subnet segmentation model, the placement of new services is a local decision made by the engineer who deploys each service — some services land in public subnets because the engineer needed to connect to them during development and did not configure the bastion or SSM access required for private subnet connectivity; some services in the private subnet have security group rules with the VPC CIDR as the source because the engineer copied the pattern from an existing security group rather than a documented convention; the isolated subnet that was created for the database is occasionally used for other workloads that needed "a subnet without internet access" without consideration of whether those workloads should have access to the database. The subnet segmentation model must specify six properties. First, the three tiers and their internet access model: the public tier contains internet-facing load balancers (ALB, NLB) and NAT gateways — resources that either accept inbound traffic from the public internet or provide outbound internet access for the private tier; public subnet route tables have a route for 0.0.0.0/0 pointing to the internet gateway; EC2 instances and container tasks should not be placed in the public subnet even if they need internet access, because the private subnet with NAT gateway access provides equivalent outbound connectivity without exposing instances to direct inbound internet traffic; the private tier contains application services, background workers, and caches — resources that need outbound internet access (for package downloads, external API calls, managed service endpoints) but should not accept inbound connections directly from the internet; private subnet route tables have a route for 0.0.0.0/0 pointing to the NAT gateway; the isolated tier contains databases, internal secret stores, and audit log sinks — resources that should have no outbound internet access at all and that should only be accessible from specific services in the private tier; isolated subnet route tables have no default route (no 0.0.0.0/0 entry), only routes for VPC CIDR traffic and explicit VPC endpoint routes for the specific AWS services the isolated-tier resources need to call. Second, the service placement convention: the decision record must specify which service types belong in each tier, not as a taxonomy of current services but as a decision rule for future services — internet-facing ALBs in public subnets, application API services in private subnets, database RDS instances in isolated subnets, ElastiCache clusters in isolated subnets, ECS tasks for background workers in private subnets, ECS tasks for data processing that must call external APIs in private subnets, ECS tasks for data processing that only need database access in isolated subnets; the convention must be applied consistently rather than re-derived for each service deployment. Third, the security group model for inter-tier traffic: the platform convention must specify whether inbound security group rules for service-to-service traffic use security group references as the source (api-sg as the source on db-sg's inbound 5432 rule) or VPC CIDR ranges (10.0.0.0/16 as the source); the difference is the lateral movement blast radius if any resource in the CIDR is compromised — SG references constrain access to instances associated with the named group, while CIDR rules allow any instance in the CIDR; the convention must be written into onboarding documentation and infrastructure code templates so that a new engineer writing their first security group rule follows the reference model without needing to discover it through code review feedback. Fourth, the network ACL rules for each subnet tier: network ACLs are stateless subnet-boundary filters that operate independently of security groups; they allow or deny traffic at the subnet level based on CIDR, protocol, and port range rules, with explicit rules required for both inbound and outbound traffic including return traffic (which security groups handle automatically through connection tracking); network ACLs provide a defense-in-depth layer that catches misconfigurations in security group rules — a security group rule that inadvertently allows traffic that should be blocked by the subnet-level policy will be caught by the NACL; the standard NACL configuration for the isolated tier blocks all inbound traffic from the public subnet CIDR (preventing any internet-facing resource from directly accessing the isolated tier even if the isolated-tier security group is misconfigured), blocks all outbound traffic to 0.0.0.0/0 (preventing isolated-tier resources from initiating internet-bound connections), and allows traffic on specific ports (5432 for PostgreSQL, 6379 for Redis) only from the private subnet CIDR. Fifth, the egress model for the isolated tier: isolated subnets with no route to 0.0.0.0/0 cannot make outbound internet connections, which eliminates the exfiltration risk for database-tier resources but creates a constraint on the AWS services those resources can call; a database instance in the isolated subnet cannot reach the AWS Secrets Manager public endpoint through NAT (because there is no NAT route) and cannot call the RDS API or the CloudWatch Logs API for log delivery without VPC Interface Endpoints for those services; the isolated subnet egress model must enumerate the AWS services that isolated-tier resources need to call and confirm that VPC endpoints are configured for each service. Sixth, the NAT availability model: if the private subnet route table points to a single NAT gateway in one AZ, an outage in that AZ disrupts outbound internet access for private subnet resources in all AZs; the availability model must specify whether one NAT gateway per AZ is required for the environment and document the cost trade-off (approximately $32/month per NAT gateway in addition to per-GB processing charges). The zero-trust network access decision record specifies the service-to-service authentication model; the subnet segmentation model is the network-layer foundation that zero-trust authentication builds on — zero-trust requires that service identity is verified cryptographically at every connection rather than inferred from network position, but the network segmentation model determines the blast radius when zero-trust verification is bypassed or when a service is compromised, and a well-segmented network constrains lateral movement even when zero-trust controls are circumvented.

The security group model and the access control blast radius

The security group model specifies the platform convention for inbound and outbound rules, the per-service security group allocation, the default outbound policy, the review process for new rule additions, and the handling of the default VPC security group. Without a documented security group model, security groups accumulate rules through organic additions — each engineer who needs a new traffic path adds an inbound rule with the source that is most convenient to specify (often the VPC CIDR, because it requires no lookup and always allows the traffic through), and the security group inventory grows into a collection of overlapping and redundant rules that no engineer has a complete mental model of. The security group model must specify six properties. First, the source convention for internal service-to-service traffic: the platform convention must explicitly require security group references rather than VPC CIDR ranges as the source for all internal traffic rules; the convention must include the reasoning — not just "use SG references" but "CIDR rules allow all instances in the CIDR, including compromised instances in other tiers; SG references allow only instances explicitly associated with the permitted group, constraining the blast radius of a compromised service to the traffic patterns it legitimately requires" — because engineers who understand the reason for the convention will apply it consistently in edge cases, while engineers who know only the rule will violate it when the rule feels burdensome. Second, the per-service security group allocation: each service should have its own security group (api-sg, worker-sg, analytics-sg, admin-sg) rather than sharing a common VPC-wide security group; a shared security group requires that the security group's rules permit the union of all services' traffic patterns, which produces overly permissive rules that allow services to communicate with resources they do not need access to; per-service security groups allow the inbound rules on each resource to specify exactly which services are permitted, and the blast radius of a compromised service is limited to the resources that allow that service's security group as a source. Third, the default security group policy: every VPC has a default security group that allows all inbound traffic from other resources associated with the same security group and all outbound traffic to any destination; new resources launched without an explicit security group assignment are added to the default security group; the decision record must specify that the default security group is not used for any application resources, that all application security groups are created explicitly with the minimum required rules, and that the default security group is modified to have no inbound rules and a single outbound rule (all traffic allowed) as the default posture for ungrouped resources. Fourth, the outbound rule convention: security group outbound rules are often left at the default "all traffic allowed" because outbound restrictions create operational complexity without clear security benefit in most service architectures; however, services in the isolated tier (databases, caches) should have explicit outbound rules that only allow traffic to specific destinations (VPC endpoints for the AWS services they call, and no other destinations), because a compromised isolated-tier resource that cannot initiate arbitrary outbound connections is substantially constrained in what it can do with that compromise — it cannot download attacker tools, cannot exfiltrate data to arbitrary destinations, and cannot beacon to a command-and-control server; the outbound rule convention must specify for each tier whether the default "all outbound allowed" rule is acceptable or whether explicit outbound restrictions are required. Fifth, the security group rule review process: new security group rules should be reviewed against the documented convention before being applied — specifically, inbound rules from the VPC CIDR should require an explicit justification for why a security group reference is not appropriate, and inbound rules with 0.0.0.0/0 as the source should be restricted to ALB and NLB security groups in public subnets where internet-facing inbound traffic is expected; the review process can be automated with AWS Config rules (for example, a rule that flags any security group with an inbound rule allowing TCP traffic from 0.0.0.0/0 to a port other than 443/80 on a non-public-subnet resource) or with infrastructure-as-code linting that rejects security group rules with CIDR sources on non-load-balancer resources. Sixth, the security group lifecycle: security groups accumulate over time as services are added and replaced, and old security groups attached to decommissioned services may contain inbound rules that are still valid as sources in other security groups — the old security group's rule allows traffic from a source group that no longer exists, but the permission remains in the rule; the security group lifecycle must specify a review cadence (quarterly is typical) that identifies security groups with no attached network interfaces, removes them, and audits surviving security groups for rules that reference deleted security groups or that allow traffic patterns that are no longer consistent with the current service architecture. The access control model decision record specifies the IAM permission model; security groups are the network-layer equivalent of IAM policies — they specify what traffic is permitted between resources in the same way that IAM policies specify what API actions are permitted between principals — and the principle of least privilege that governs IAM policy design applies equally to security group design: the minimum set of ports, protocols, and sources required for legitimate service operation, not the broadest set that prevents any service from failing due to a missing rule.

Three AI session types that embed VPC decisions without documenting them

The initial cloud setup session is where the CIDR allocation, subnet segmentation, and security group model are established without being named as decisions. The session is infrastructure-bootstrapping-oriented and terminates at the working resource: create the VPC, create subnets (accepting the suggested CIDR blocks), configure the internet gateway and NAT gateway, create a security group with the most permissive rules that allow the immediate task (often inbound from the VPC CIDR for all internal traffic, to avoid the constraint of specifying exact security group references that do not yet exist for services that have not yet been deployed), launch the first instance, verify access, and close. The CIDR block is accepted from the console wizard or copied from a tutorial without recording why that value was chosen or what it must not overlap with. The security group convention is established by the first rule written — and "allow from the VPC CIDR" is substantially easier to write in an initial session than "allow from the security group that will be assigned to the service that will eventually send this traffic" because the source security group does not exist yet at initial setup time. The subnet assignment is made by placing the first service in the private subnet because that is what the tutorial prescribes, but the convention — that application services go in private subnets, databases go in isolated subnets, and no application service goes in the public subnet — is not recorded anywhere. The initial setup session result is "VPC created, instances running, internet access confirmed" — not "VPC CIDR is 10.42.0.0/16, chosen to avoid overlap with our corporate VPN (10.0.0.0/8 range, specific /16s documented in network-topology.md) and the common AWS default (172.31.0.0/16); security group convention is SG-reference rules for internal traffic to limit lateral movement blast radius; three-tier subnet model assigns load balancers to public subnets, application services to private subnets, and databases to isolated subnets with no NAT gateway route; NAT gateway is deployed in us-east-1a with a fallback NAT gateway in us-east-1b for AZ independence." The second result requires treating each configuration choice as a constraint on future network topology decisions rather than as a technical setup step with a binary functional completion criterion. The new CTO onboarding problem with VPC design is that the incoming technical leader can view the VPC topology in the AWS console — subnets, route tables, security groups, NAT gateways — and understand the current configuration without accessing any documentation, but cannot determine from the console view whether the CIDR was chosen intentionally to avoid overlap with known partner ranges or accepted from a wizard default, whether the security group convention is CIDR-based because that was a deliberate choice or because that was the pattern the first engineer used and subsequent engineers copied, or whether the isolated subnet has no NAT gateway route as a deliberate security control or as an oversight that is incidentally providing security benefits.

The service proliferation session embeds subnet assignment and security group convention decisions as per-service configurations that accumulate without a coherent architecture. The session is triggered by a new service that needs to be deployed: the engineer decides where to put the new service (often by checking where similar existing services are deployed, which inherits whatever decisions were made for those services, which may themselves have been placed by copying from earlier services), creates a security group for the new service (often by copying the security group configuration of the most similar existing service, which imports the source conventions of that service including whether they use CIDR ranges or SG references), configures the service's outbound traffic requirements (often by leaving the default "all outbound allowed"), and verifies that the service can communicate with the resources it needs. The session does not check whether the new service is in the correct subnet tier for its security requirements, whether the security group rules created are consistent with the platform convention (if one has been established), or whether the new service's security group is being added as a source in other security groups (which would require the other security groups to be updated if the new service's SG ID changes). The peering connection is added in a service proliferation session when a new integration partner or shared service requires private connectivity: the engineer creates the peering connection, adds route table entries to the subnets that need connectivity, and verifies that the integration works — without recording the CIDR range of the peer VPC, whether the peering connection is transitive with any other existing peering connections (which it cannot be, because VPC peering is non-transitive), or whether the peer VPC's CIDR overlaps with any VPCs that this VPC might need to peer with in the future. The infrastructure decision record captures the organizational model for cloud resources; the VPC peering and subnet additions made in service proliferation sessions change the network topology without necessarily being captured in the infrastructure decision record, creating a gap between the documented architecture and the deployed architecture that grows with each proliferation session.

The security hardening or compliance preparation session embeds security group changes as remediation actions that are not grounded in a model. The session is triggered by a compliance audit finding, a penetration test report, or a security review: the engineer reviews the auditor's findings, identifies the affected security group rules, replaces the flagged rules with the auditor's recommended replacement, marks the compliance finding as remediated, and closes. The replacement may correctly address the finding (replacing a CIDR-based source with an SG reference) without establishing the forward-going model (documenting that all future internal security group rules must use SG references, not CIDR ranges). The security group rule changes are made in the staging environment first, tested to confirm the services still work, and applied to production. The compliance finding is closed. The session result is "security group misconfiguration remediated" — not "security group convention updated to require SG references as sources for all internal service-to-service rules, with documentation updated to make the convention discoverable to future engineers." The same session may also enable VPC flow logs in response to an audit requirement for network traffic visibility: flow logs are enabled to CloudWatch Logs with a log group name and a retention period accepted from the console default (180 days), without specifying an analysis workflow (what queries will be run against the flow logs to identify anomalous traffic), an alerting model (what conditions trigger alerts from the flow log data), or a cost impact assessment (flow log volume is proportional to traffic volume, and a high-traffic VPC with flow logs enabled can generate significant CloudWatch Logs costs). The decisions never written down in a security hardening session are the model that should govern future security group additions — the session produces rule changes but not the model that makes those changes systematic — and the analysis workflow and cost model for the VPC flow logs that were enabled as a compliance checkbox.

The five sections of a VPC and network topology decision record

The first section documents the CIDR block selection and address space model. The primary CIDR selection must specify the RFC 1918 range chosen (10.0.0.0/8, 172.16.0.0/12, or 192.168.0.0/16), the specific /16 (or other prefix length) assigned to each VPC, the rationale for the selection (not just "we chose 10.42.0.0/16" but "we chose 10.42.0.0/16 because our corporate VPN uses 10.0.0.0/8 with specific assignments in the 10.0.0.0/16 through 10.10.0.0/16 range, and we reserved the 10.40.0.0/14 supernet for our cloud VPCs to give each environment a /16 with room for additional VPCs"), the known address ranges that the chosen CIDR must not overlap with (documenting corporate VPN ranges, partner organization ranges, and other environment VPCs), and the peering compatibility assessment at the time of writing. The per-subnet CIDR allocation must specify the prefix length for each subnet type (public, private, isolated), the number of AZs covered, and the resulting per-AZ address counts with the ENI density ceiling noted (for example, /20 subnets provide 4,091 usable addresses; an ECS Fargate service scaled to 1,000 tasks in one AZ consumes 1,000 ENIs from the subnet, leaving 3,091 available). The secondary CIDR strategy must specify the conditions under which secondary CIDRs are added (for example, when any subnet in the primary CIDR has less than 500 free IP addresses for a 30-day period), the ranges reserved for secondary use, and the overlap check requirement (secondary CIDRs must be checked against the known address range inventory before assignment). The NAT gateway availability model must specify the number of NAT gateways per environment and the rationale for the choice — one NAT gateway per VPC (single AZ dependency, acceptable for development environments) versus one NAT gateway per AZ (AZ-independent outbound access, recommended for production). The address space document must be updated when new peering connections are added (to record the peer VPC's CIDR in the compatibility inventory), when new corporate VPN ranges are assigned (to verify non-overlap), and when the company completes an acquisition (to compare the acquired company's VPC CIDRs against the documented inventory before attempting peering). The load balancer decision record specifies which load balancer type and placement model is used for each service; load balancers in public subnets consume IP addresses from the public subnet CIDR and scale their ENI count as traffic increases, which affects the subnet sizing required for the public tier in the CIDR allocation model.

The second section documents the subnet segmentation model. The three-tier model must specify the exact name and purpose of each tier as deployed (for example, "public-1a / public-1b / public-1c" for the public tier, "private-1a / private-1b / private-1c" for the private tier, "isolated-1a / isolated-1b / isolated-1c" for the isolated tier), the route table configuration for each tier (public subnets: local route and 0.0.0.0/0 → internet gateway; private subnets: local route, 0.0.0.0/0 → NAT gateway per AZ, and any VPC endpoint routes; isolated subnets: local route and VPC endpoint routes only), and the service placement convention that specifies which resource types belong in each tier. The placement convention must be written as a decision rule, not as a list of current placements: "internet-facing ALBs and NLBs are placed in public subnets; NAT gateways are placed in public subnets with Elastic IPs; EC2 application instances, ECS tasks, and Lambda functions that require outbound internet access are placed in private subnets; RDS instances, ElastiCache clusters, and DocumentDB clusters are placed in isolated subnets; ECS tasks and Lambda functions that only require database access and no outbound internet access are placed in isolated subnets." The network ACL specification must document the NACL rules for each tier, with the explicit rationale for each rule set: isolated subnet NACLs deny all inbound traffic from the public subnet CIDR range (defense-in-depth against load balancer misconfiguration that might inadvertently route internet traffic to the isolated tier), deny all outbound traffic to 0.0.0.0/0 (preventing exfiltration from compromised database instances), and allow inbound and outbound traffic on the database port range only from the private subnet CIDR (allowing application tier traffic while blocking public tier traffic at the subnet boundary regardless of the security group configuration). The VPC endpoint configuration must list which AWS services are accessed through VPC endpoints from each subnet tier, the endpoint type (Gateway for S3 and DynamoDB, Interface for all other services), and the endpoint policy if the default "allow all" policy is restricted. The container orchestration decision record specifies the ECS or EKS configuration; the subnet segmentation model determines which subnets the container runtime places tasks into, and the ECS task definition or EKS node group configuration must reference the correct subnet IDs for each service tier — a misconfigured task definition that places a database migration job in a public subnet rather than an isolated subnet violates the segmentation model and must be caught by the infrastructure code review process before deployment.

The third section documents the security group model. The inbound rule convention must specify the source type for each traffic category: internet-facing inbound (0.0.0.0/0 as the source on ALB security groups for 443 and 80 only), tier-crossing inbound (security group reference as the source on all service-to-service rules, specifying the exact source SG ID rather than the CIDR range), and same-tier inbound (security group reference where applicable, or the tier's subnet CIDR only when there is no specific source group to reference, with the trade-off documented). The outbound rule convention must specify whether each tier uses the default "all outbound allowed" or explicit outbound rules: public and private tiers default to all outbound allowed; isolated tier uses explicit outbound rules listing only the ports and destinations required (5432 outbound to the application SG, VPC endpoint destinations for the AWS services in use, and no 0.0.0.0/0 route to prevent internet exfiltration). The per-service security group allocation must specify the naming convention for security groups (for example, "prod-api-sg", "prod-worker-sg", "prod-db-sg") and the naming convention for security group rules (descriptive names that identify the source and purpose, for example "From api-sg: inbound 5432 for API to database connection"). The default security group handling must specify that the VPC's default security group (which allows all inbound from members of the same group and all outbound) is not used for any application resource, that new instances are always launched with explicitly created security groups, and that the default security group's inbound rules are cleared so that ungrouped resources have no inbound access. The security group review requirement must specify the process for adding new inbound rules: rules with 0.0.0.0/0 as the source require architecture review to verify that the resource is in a public subnet and is a load balancer; rules with the VPC CIDR as the source for non-load-balancer resources require a justification for why an SG reference is not possible; rules with SG references that span subnet tiers (for example, allowing the public-subnet load balancer's SG as a source on a private-subnet application SG, which is expected and correct) are approved automatically. The audit cadence must specify how often security groups are reviewed for unused groups (no attached ENIs), rules that reference deleted security groups, and rules whose source SGs have been retired. The security ADR and threat model decision record specifies the team's threat model; the security group model must be evaluated against the threat model by assessing the blast radius of a compromised resource in each subnet tier under the documented security group convention — a compromised API service with SG-reference rules as the source on the database SG can only connect to the database, not to other resources in the VPC; a compromised API service with the VPC CIDR as the source on the database SG can reach every resource in the VPC whose security group has a CIDR-based inbound rule, including the database, the cache, and the admin tools.

The fourth section documents the VPC connectivity model. The connectivity model specifies how the VPC connects to other VPCs (in the same or different AWS accounts), to on-premises networks, and to AWS services, and the routing model for each connectivity type. VPC peering connections must be documented with the peer VPC's account ID, region, and CIDR block (to update the peering compatibility inventory), the route table entries added in each VPC to enable connectivity through the peering connection (which subnets in VPC A can reach which subnets in VPC B), and the security group rules added to allow traffic from the peer VPC's CIDR range. The non-transitive nature of VPC peering must be noted for each peering connection: VPC A peered with VPC B cannot route traffic from VPC A to VPC C through VPC B, even if VPC B is also peered with VPC C; each pair of VPCs that needs direct connectivity requires its own peering connection. If the team has or anticipates more than four VPCs with full-mesh connectivity requirements, the connectivity model must evaluate whether migrating to AWS Transit Gateway reduces operational complexity enough to justify the hourly attachment cost. Transit Gateway attachments must be documented with the TGW route table assignments (production VPCs in the production route table that does not include development VPC routes, development VPCs in the development route table), the propagation configuration (whether each VPC's CIDR is automatically propagated to the TGW route tables or manually added), and the blackhole routes (routes that terminate traffic to prevent certain VPCs from communicating, even if both are attached to the same TGW). Direct Connect and Site-to-Site VPN connections must document the BGP ASNs, the allowed prefix ranges, and the failover model (active-active versus active-passive). VPC Interface Endpoints must be listed by service name, subnet placement, and endpoint policy. The CDN decision record specifies whether CloudFront is used as the internet boundary for the application; a CloudFront distribution that forwards to an origin ALB in the public subnet adds an internet-facing layer between the public internet and the VPC, and the ALB security group can be configured to restrict inbound 443 to CloudFront's managed prefix list (com.amazonaws.global.cloudfront.origin-facing) rather than 0.0.0.0/0, tightening the public tier security group model without affecting the VPC's internal segmentation.

The fifth section documents the egress control model and internet boundary. The egress model specifies which resources in which subnets can initiate outbound internet connections, through what path, and with what restrictions. The public subnet egress model is direct: resources with public IPs or Elastic IPs communicate directly through the internet gateway; load balancers in public subnets accept inbound connections and forward them to private subnet resources. The private subnet egress model is NAT-mediated: resources in private subnets route 0.0.0.0/0 traffic to the NAT gateway, which translates the private source IP to the NAT gateway's Elastic IP and forwards the connection; the NAT gateway's Elastic IP can be listed as an allowed outbound address at external API providers and firewall rules, but the single IP for all private subnet resources means that the Elastic IP allowlist entry authorizes all private subnet resources equally, not specific services. The isolated subnet egress model is VPC-endpoint-only: no route to 0.0.0.0/0, only explicit routes to VPC endpoints for the AWS services the isolated-tier resources call directly (Secrets Manager, CloudWatch Logs, S3 for database backups via Gateway endpoint); this model prevents isolated-tier resources from initiating internet connections even if compromised, at the cost of requiring VPC endpoints for any AWS service the isolated tier needs to call. The egress filtering model must specify whether the team uses AWS Network Firewall, a NAT gateway with security group outbound rules, or no protocol-level egress filtering; AWS Network Firewall provides domain-based egress filtering (blocking outbound connections to domains not in the allowlist) that can prevent exfiltration to arbitrary internet destinations even from private subnet resources, but adds architectural complexity and per-GB processing cost; the egress filtering decision must be evaluated against the threat model for each environment — development environments may not require domain-level egress filtering, while production environments with strict data handling requirements may justify the cost. The VPC flow log model must specify the traffic capture filter (ALL traffic, ACCEPT-only, or REJECT-only — ALL is required for security analytics but generates substantially higher log volume than REJECT-only), the log destination (CloudWatch Logs with a specified retention period, or S3 with lifecycle policies), the aggregation interval (1-minute for security-relevant environments, 10-minute to reduce log volume and cost), and the analysis workflow (what queries run against the flow logs to detect anomalous outbound connections, port scans, or data exfiltration patterns). The database connection pooling decision record specifies the connection pool configuration; the database tier's isolated subnet placement means that the connection pool must be co-located with the application tier in the private subnet or use a connection proxy (PgBouncer, RDS Proxy) in the private subnet that manages connections to the database in the isolated subnet — a connection pooler placed in the public subnet to reduce the management burden of private subnet administration violates the segmentation model by placing persistent database credential holders in the most exposed tier.

The initial VPC setup session that created the VPC with the AWS default CIDR block without recording that it is a common value that will conflict with future peering partners, the service proliferation sessions that added security group rules with VPC CIDR ranges as sources because each engineer followed the path of least resistance — the CIDR value is visible in the console, the security group ID requires a lookup — without a documented convention specifying that SG references are the platform model for internal traffic, and the security hardening session that replaced flagged security group rules without establishing the model that should govern all future additions — each produced VPC decisions whose operational consequences (a two-month enterprise partnership delay while a two-week VPC migration replaced the default-CIDR VPC; six weeks of security group audit and replacement when a penetration test found database reachability from the public subnet; VPC flow logs enabled to a CloudWatch log group without an analysis workflow, accumulating costs without producing security value) exceed what documenting the CIDR selection rationale with a known-conflicts inventory, establishing the SG-reference convention at initial setup with a clear model that applies to all future security group additions, and specifying the flow log analysis workflow at the time of enabling flow logs would have cost to record. The decisions are in the AI sessions: the CIDR block accepted from the console wizard without recording the selection rationale or the non-overlap requirements that should constrain future peering attempts, the first security group rule with the VPC CIDR as the source that established the pattern subsequent engineers copied without a documented convention to override it, and the isolated subnet created for the database without a documented segmentation model that would have governed the placement of future services and prevented the eventual mixing of application and data tiers. WhyChose's open-source extractor surfaces these initial cloud setup sessions, service proliferation sessions, and security hardening sessions as structured decision records before an enterprise partnership is blocked by a CIDR overlap that was one wizard-default CIDR choice away from not existing, before a penetration tester demonstrates that the database is reachable from the public subnet through a security group rule that followed the pattern of the first rule created in the founding session, and before the outgoing infrastructure engineer who knew the VPC's CIDR rationale, subnet assignment conventions, and security group model retires without those constraints being findable by the engineer who inherits the account. The decisions never written down in a VPC setup are the CIDR allocation rationale with its non-overlap inventory, the subnet segmentation convention that specifies where each class of service belongs, and the security group model that determines whether the network's lateral movement blast radius is bounded to specific service-to-service pairs or extends to every resource in the VPC CIDR.

Further reading