Limitations¶
What a vpcatlas run does not see, and how it tells you. The rule behind all of it: a call that fails never
stops the scan and never reads as an empty answer. It is recorded, listed, and where a number would have been, the
page says not read.
One run reads one account, unless you repeat the options¶
A run reads the account of the credentials it finds. To read more, repeat --profile or --role-arn (one account
each, merged into one graph); see Cross-account. Things to know:
- There is no discovery of the accounts of an AWS Organization yet. You list them.
- The accounts of a multi-account run are scanned one after the other. The regions of each are scanned in parallel.
- Everything that is not scanned stays outside: other accounts appear as external accounts with stubs for the resources your accounts refer to, nothing more.
- A run is a snapshot. The files describe the network at the moment of the scan. The dashboard scans again every
VPCATLAS_REFRESH_SECONDS(never more often than every 300 seconds, the value ofVPCATLAS_MIN_REFRESH_SECONDS) and keeps the history.
Gaps when a call is denied¶
Every collector wraps its AWS calls. When one fails, the collector records a gap, keeps what it had read, and continues with its next call. A collector that fails as a whole also becomes a gap, not an error.
A gap holds the service, the operation, a code, the message, the region and the account. The code is the
tool's own classification of the failure, and the message begins with the AWS error code.
| Code | Meaning | Usual cause |
|---|---|---|
denied |
The call was refused: AccessDenied, UnauthorizedOperation, AccessDeniedException and the like, or a message that says "not authorized" or "explicit deny". |
The role lacks the action, or a service control policy, permissions boundary or resource policy blocks it. |
throttled |
The API said slow down and the retries ran out. | See throttling. |
region_unavailable |
The region rejected the credentials (AuthFailure, OptInRequired, UnrecognizedClientException, InvalidClientTokenId). |
The region is not enabled for the account. |
unsupported_region |
The endpoint of that service could not be reached in that region. | The service does not exist in the region, or the region name is wrong. |
unsupported_client |
The installed boto3 does not know the operation or rejects its parameters. | An old boto3 next to a newer vpcatlas, for example in a custom install. |
partial |
A deliberate note that a read is incomplete. | CloudTrail is read in the home region only, so single-region trails of other regions are not listed. |
error |
Anything else. | The message tells. |
"Not found" answers of child lookups (a resource deleted between the list and the describe) are dropped silently and are not gaps.
Where gaps are shown¶
- CLI. The summary table after every run has an "Issues" count, and the first ten issues are printed in yellow
with account, region, service, operation and message. With
--strictthe exit code is 4 when there is any issue (see exit codes). - Inventory. JSON:
issues[], each withkind,account_id,region,service,operation,codeandmessage. Markdown and HTML: the section "Issues and coverage gaps", which says that the inventory above may be incomplete. Thekindisdenied,throttled,unsupportedorerror. - Graph.
vpcatlas-graph.jsonkeeps them asgaps[]. - Dashboard. The tab "Collection gaps" lists them, and the status shows the count (
issue_countin/api/status).
What an incomplete answer looks like¶
- A denied list call (for example
ec2:DescribeTransitGateways) means that resource type is missing from the diagram. The gap is the only evidence that it was not read, so read the gaps before you trust a missing box. - The account services (S3, DynamoDB, CloudTrail, GuardDuty, Config, CloudWatch, KMS, ECR) are drawn as one summary
node each. When the call that counts them is denied, the number is written
not read. It is never0. - Shield Advanced writes no node when its state cannot be read.
- When
ec2:DescribeRegionsis denied, the scan falls back to the regions the installed boto3 knows, and the regions that are not enabled produceregion_unavailablegaps.
Unverified resources¶
Some things in the output are real but not confirmed by a read of the object itself.
- Stubs. A resource that was referenced but never described: the Transit Gateway of a hub you cannot read, the
peer VPC of another account. It is drawn dashed, keyed
external:<id>in the graph withstub: true, and is not counted in the resource totals. Scanning the owning account replaces the stub with the real resource. - Lucid shape classes. A few kinds (the directory service and security group icons, for example) have a Lucid
shape class that has not been checked against Lucid's catalog. By default they are drawn as plain rectangles. Set
VPCATLAS_LUCID_UNVERIFIED=classto use the class anyway.
Regions that are disabled¶
Without --regions, the scan covers the regions that are enabled for the account (describe_regions without
the disabled opt-in regions). A region you opted out of is never visited.
If you name a disabled region in --regions, the first call there is refused (AuthFailure or OptInRequired),
the tool records a region_unavailable gap, skips the remaining collectors for that region and removes it from the
account's regions. The names of such regions are in the graph metadata as unavailable_regions. A typo that passes
the format check (eu-west-9) shows as unsupported_region gaps.
Global services (Route 53, CloudFront, the CloudFront scope of WAF, Direct Connect gateways, IAM and Organizations
names) are read once, in the home region: us-east-1 in the standard partition, or the first region scanned when
us-east-1 is not scanned. Their nodes carry the region global.
Throttling¶
AWS rate limits are per account, per API and per region. The tool is polite by design:
- Every client retries with the botocore
standardmode, up to 8 attempts, with backoff. - At most 4 collectors call
ec2in the same region at once, and at most 2 for any other service. --concurrency(1 to 32, default 8) bounds the regions scanned in parallel. Lower it if you seethrottledgaps, or if the account is shared with other tools.- Only after the retries are exhausted does a call become a
throttledgap, and the scan carries on.
A scan of an account with 17 enabled regions makes on the order of a thousand list calls. Narrowing with
--regions is the most effective way to shorten it.
What is out of scope¶
By design, and listed in Coverage:
- Anything that changes state. No create, update, delete, tag or attach call is made.
- Contents. No S3 objects, DynamoDB items, secrets, parameters, log events, metrics, KMS key policies, API keys, client certificates or database credentials. VPN pre-shared keys and Direct Connect keys are removed from anything the tool keeps.
- Rule and policy bodies. Network Firewall rules, WAF rule statements, IAM policies and bucket policies.
- Traffic. No flow logs, reachability analysis or route simulation. The numbered flows on a diagram are derived from the kind of each edge, not from observed traffic, so they show how the network is built, not what is carried.
- Network services without a collector in 0.1.0: Global Accelerator (a Route 53 alias to one is recognised, the accelerator is not read), VPC Lattice, IPAM, Network Manager and Cloud WAN, Client VPN, traffic mirroring, Verified Access, Outposts local gateways, carrier gateways and VPC route servers.
- Services that attach to subnets but have no collector: MSK, OpenSearch, App Runner, Route 53 Profiles and Cloud Map. Their network interfaces are seen and counted as workloads in the subnet.
- Other clouds. AWS only.
- Hand-made layout. The diagrams are generated and editable, but a regenerated file replaces your edits. Keep edited copies under another name.
Practical limits of the dashboard¶
- Every scan runs inside the process; there is one scan at a time. A manual refresh before the minimum interval is
answered
429with aRetry-Afterheader. - On AWS Lambda a response is capped at 6 MB, so a very large
.drawioor.zipexport may not fit; use the CLI for those. - The dashboard shows the topology of your network. Put it behind a private network or set
VPCATLAS_AUTH_PASSWORD(HTTP Basic); then only/healthz,/static/and the API description stay open.