Architecture¶
vpcatlas is a read-only pipeline with four stages. Each stage has one input and one output, so the collectors, the exporters and the dashboard reuse the same data.
collect -> graph -> layout -> export
(boto3) (networkx) (geometry) (drawio, lucid, json, md, html)
Stages¶
- Collect.
collectors/calls the AWS APIs with one collector per service. Every collector implementsCollector.collect(session, region) -> list[resource]. Pagination is always used. A service that returnsAccessDeniedis logged and skipped; it does not stop the run. - Graph. The orchestrator
discover(session, regions, concurrency)runs the collectors in parallel per region and builds aNetworkGraph. The graph is anetworkx.DiGraphwrapped by the model. Nodes are resources; edges are typed (contains,routes_to,attached_to,peered_with,shared_from). External accounts are nodes too. - Layout.
exporters/layout.pycomputes positions and sizes once. The result is a deterministic grid with the nesting cloud > region > VPC > availability zone > subnet. Both diagram exporters consume the same layout. - Export.
exporters/drawio.pywrites mxGraph XML withmxgraph.aws4.*shapes.exporters/lucid.pywrites a Lucid Standard Import package (a zip withdocument.json).inventory/renders the graph to JSON, Markdown and HTML.
Modules¶
| Module | Responsibility |
|---|---|
model/ |
Pydantic v2 models for every resource, ExternalAccount and NetworkGraph. Every resource has arn or id, account_id, region, name, tags and a trimmed raw dict. |
collectors/ |
One module per service family: ec2_vpc, ec2_tgw, ec2_peering, vpn_dx, endpoints, elb, network_firewall, route53_resolver, ram, and the service layer: route53, cloudfront, wafv2, acm, shield, targets, ecs, eks, ec2_instances, lambda_fn, apigw, rds, elasticache, efs, s3, dynamodb, account_services (shared helper _dns_targets). |
inventory/ |
Graph to JSON (versioned schema), Markdown and HTML. Sections follow account > region > VPC > subnet > resource, plus "External accounts and cross-account links". |
exporters/ |
layout.py, drawio.py, lucid.py. |
dashboard/ |
FastAPI app, periodic refresh with an in-memory cache, Mangum handler for Lambda. |
cli.py |
Typer commands: discover, inventory, diagram, serve, iam-policy. |
Node keys and identity¶
Node keys are synthetic: {account}/{region}/{type}/{id}. The ARN is an attribute, filled when the API returns it
or when it can be composed from the account, region and resource id. The account is the owner of the resource, so
a resource shared into your account keeps the owner's account id.
Cross-account detection¶
A resource belongs to an external account when one of these is true:
- its owner id differs from the caller account (
OwnerId, ARN account segment); - a peering connection has
AccepterOwnerIdorRequesterOwnerIddifferent from the caller account; - a Transit Gateway attachment or a Resolver rule is owned by another account;
- a RAM resource share reports a
resourceShareArnowned by another account and makes it visible to you.
Each external account is recorded once, with the id, the way it was seen (for example "TGW attachment via RAM share")
and the resource that links it. The account name is taken from organizations when permitted, otherwise the id is used.
Concurrency and failures¶
- Regions run concurrently, bounded by
concurrency. Collectors inside a region run one after another to keep API throttling low. - boto3 retries are left at the SDK defaults with the adaptive retry mode.
- A failed call is recorded as a warning in the graph metadata and shown in the inventory.