AWS Diagram Guide

AWS VPC diagram: subnets, availability zones, and routes that read correctly

By the engineer who builds Datadef, from client work on data platforms · Reviewed August 21, 2026

A VPC diagram is the one AWS drawing where placement is not decoration. Which container a resource sits in tells the reader what network path traffic takes, what fails when a zone fails, and whether anything is reachable from the internet. Get the nesting right and the diagram answers questions on its own. Get it wrong and it actively misleads.

7 min readFor engineers documenting a VPC for onboarding, audit, or a network review

See it as a diagram

Everything below, as a diagram you can edit. Describe yours and see it in seconds.

266/20003 credits left
Try:

No account needed · Editable canvas, not a picture

The nesting order, and the labels each level carries

Region contains VPC contains availability zone contains subnet contains resource. Every level carries one piece of text a reader will look for. The VPC carries its CIDR block. Each availability zone carries its real name, eu-west-1a rather than AZ 1. Each subnet carries its tier (public, private, isolated), its CIDR, and its default route target.

Three tiers is the shape most production VPCs converge on: public subnets for anything with a route to an internet gateway, private subnets for compute that egresses through NAT, and isolated data subnets with no default route at all. Showing that a database subnet has no route to 0.0.0.0/0 is a stronger security statement than any padlock icon.

| Subnet | AZ | CIDR | Default route |
| --- | --- | --- | --- |
| public-a | eu-west-1a | 10.0.0.0/24 | internet gateway |
| private-app-a | eu-west-1a | 10.0.16.0/20 | nat-a |
| data-a | eu-west-1a | 10.0.64.0/22 | none |
| public-b | eu-west-1b | 10.0.1.0/24 | internet gateway |
| private-app-b | eu-west-1b | 10.0.32.0/20 | nat-b |
| data-b | eu-west-1b | 10.0.68.0/22 | none |

Where each network component actually sits

The internet gateway attaches to the VPC, so draw it on the VPC edge, not inside a subnet. A NAT gateway does live in a subnet, specifically a public one, and production designs usually run one per availability zone so that a zone failure does not take egress with it. Drawing a single shared NAT is fine when that is the truth, and worth annotating, because it is both a cost decision and a failure domain.

Load balancers span zones. An ALB or NLB registers subnets in several availability zones, so it belongs at the VPC level or drawn across the zone containers. VPC endpoints split by type: gateway endpoints for S3 and DynamoDB attach to route tables and are best shown as a route entry, while interface endpoints are elastic network interfaces that really do sit inside a subnet and deserve a node when they matter.

Transit Gateway and peering connections attach at the VPC edge. If the VPC is a spoke on a Transit Gateway owned by a network account, draw the attachment and let the hub live in the multi-account view rather than duplicating the whole hub here.

Routes and rules without adding boxes

Route tables are configuration, not topology. Put the default route on the subnet container as a label (0.0.0.0/0 to nat-a) and only promote a route table to its own node when the routing itself is the subject of the diagram, for example an inspection VPC where traffic is deliberately hair-pinned through a firewall.

Security group rules work the same way: write the rule on the edge it permits. An arrow labelled tcp/5432 from the app security group carries the rule, the direction, and the reason. Network ACLs are worth drawing only when they carry a rule that is not implied by the security groups, which is rare and therefore notable.

Generating the VPC picture from Terraform

Almost every VPC in production was written as code, usually as a module with for_each over availability zones. Datadef parses the .tf and .tfvars files of a connected repository directly, so the subnet plan in the diagram comes from the declaration rather than from someone reading the console. No terraform init, no state file, no AWS credentials, read-only repository access.

Because the parse understands count and for_each, a subnet created once per zone stays one node carrying its real multiplicity instead of exploding into six identical boxes. A literal for_each map is counted directly and the node reads ×3. A for_each over a variable is followed to the call site and then into the tfvars, so it reads "per subnets (2)" when every environment agrees and "per subnets (DEV 2, PROD 3)" when they do not.

Nesting is derived rather than guessed. The catalog records, per resource type, which attribute points at the container the resource lives in: vpc_id for aws_subnet and aws_security_group, subnet_id for aws_nat_gateway and aws_instance. A reference through one of those attributes becomes containment, drawn as nesting and never as an arrow, which is what stops a VPC diagram turning into a mesh of id references. aws_vpc and aws_subnet also carry cidr_block onto the node, so the table above comes out of the .tf files rather than out of the console.

Registry modules such as terraform-aws-modules/vpc/aws cannot be expanded from your repository alone. The call becomes a single node labelled with its source and its pinned version, standing for everything it provisions and wired to whatever references its outputs. In the module reference table its Resources cell reads "external", which is the honest answer rather than a count nobody could compute.

Keep the picture attached to the code

A VPC diagram drawn by hand is wrong the first time someone adds a third availability zone. Connecting the repository means the daily sync redraws it, and the README embed follows. See repository sync.

FAQ

Should a load balancer be drawn inside an availability zone?

No. ALBs and NLBs are regional resources that span every subnet registered with them, so they belong at the VPC level or spanning the zone containers. Only the target compute, and the NAT gateways, are genuinely per zone.

How do I show public versus private subnets clearly?

Label the subnet with its tier and its default route target. A public subnet routes 0.0.0.0/0 to an internet gateway, a private subnet routes it to a NAT gateway, and an isolated data subnet has no default route. Those three labels tell the reader the reachability story without any extra icons.

Do VPC endpoints belong in the diagram?

Interface endpoints are elastic network interfaces inside a subnet, so they can be nodes when they carry the traffic in question. Gateway endpoints for S3 and DynamoDB attach to route tables and read better as a route entry on the subnet than as a box.

Should CIDR blocks appear on the diagram?

Yes on the VPC and on each subnet. CIDRs are what people need when they add a subnet, open a peering connection, or debug a route, and they are the fastest way for a reviewer to spot an overlap with another VPC or an on-premises range.

Can a VPC diagram be generated from Terraform code?

Yes. Datadef parses the .tf and .tfvars files of a connected repository, including count and for_each, and draws subnets, gateways, and modules with their real names and counts. References through container attributes such as vpc_id and subnet_id are rendered as nesting rather than as arrows. It reads the source only, with no init, no state file, and no cloud credentials.