Skip to content

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 of VPCATLAS_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 --strict the exit code is 4 when there is any issue (see exit codes).
  • Inventory. JSON: issues[], each with kind, account_id, region, service, operation, code and message. Markdown and HTML: the section "Issues and coverage gaps", which says that the inventory above may be incomplete. The kind is denied, throttled, unsupported or error.
  • Graph. vpcatlas-graph.json keeps them as gaps[].
  • Dashboard. The tab "Collection gaps" lists them, and the status shows the count (issue_count in /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 never 0.
  • Shield Advanced writes no node when its state cannot be read.
  • When ec2:DescribeRegions is denied, the scan falls back to the regions the installed boto3 knows, and the regions that are not enabled produce region_unavailable gaps.

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 with stub: 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=class to 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 standard mode, up to 8 attempts, with backoff.
  • At most 4 collectors call ec2 in 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 see throttled gaps, or if the account is shared with other tools.
  • Only after the retries are exhausted does a call become a throttled gap, 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 429 with a Retry-After header.
  • On AWS Lambda a response is capped at 6 MB, so a very large .drawio or .zip export 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.