AWS Lambda¶
The dashboard runs as a Lambda container image. deploy/Dockerfile.lambda builds it
on the AWS Lambda Python base image and sets the handler vpcatlas.dashboard.lambda_handler.handler: the FastAPI
application behind Mangum. deploy/lambda/README.md has the short version of this
page.
How it behaves in Lambda¶
Lambda freezes the container between invocations, so there is no background refresh loop. The dashboard runs in
lazy mode, chosen automatically when AWS_LAMBDA_FUNCTION_NAME is set (override with VPCATLAS_REFRESH_MODE).
The handler accepts two kinds of events:
- HTTP events from a Function URL, API Gateway or an Application Load Balancer are served by the dashboard.
- A scan event:
{"atlas_action": "scan"}, or an EventBridge scheduled event (sourceaws.events, ordetail-typeScheduled Event). It runs a discovery and stores the snapshot inVPCATLAS_SNAPSHOT_URI. If that variable is not set the event is skipped (the result would be lost), and the invocation returns{"status": "skipped", ...}. On success it returns the snapshot id, the time, the number of pages and the number of collection issues.
Recommended setup: a scheduled scan plus an S3 snapshot. A schedule invokes the function with the scan event, the
snapshot is stored in S3, and page views only read it, so nobody waits for a scan. Set
VPCATLAS_SCAN_ON_REQUEST=false so a request never starts a scan.
Without S3, the first request after the snapshot expires scans inline (VPCATLAS_REFRESH_SECONDS is the snapshot's
lifetime; 0 keeps it for the life of the container). A Function URL can wait for that; API Gateway cannot, because
its integration timeout is far shorter than a scan.
A scan is a full read of the account, roughly a thousand list calls for 17 Regions. Keep it inside the function
timeout (15 minutes at most) by listing the Regions you need in VPCATLAS_REGIONS. Responses are limited to 6 MB, so a
very large .drawio or .zip export may not fit; use the CLI for those.
1. Build and push the image¶
Lambda pulls images from Amazon ECR, in the Region of the function. Build for the architecture of the function:
REGION=eu-west-1
ACCOUNT=123456789012
REPO=vpcatlas-lambda
REGISTRY="$ACCOUNT.dkr.ecr.$REGION.amazonaws.com"
aws ecr create-repository --repository-name "$REPO" --region "$REGION"
aws ecr get-login-password --region "$REGION" | docker login --username AWS --password-stdin "$REGISTRY"
docker build --platform linux/arm64 -f deploy/Dockerfile.lambda -t "$REGISTRY/$REPO:0.1.0" .
docker push "$REGISTRY/$REPO:0.1.0"
Tagged releases also publish vpcatlas/vpcatlas-lambda:<version>-amd64 and -arm64 on Docker Hub. Lambda cannot pull
from Docker Hub, so copy the one you need into your ECR repository instead of building it.
2. Roles¶
The execution role of the function needs:
- the basic Lambda logging policy (
AWSLambdaBasicExecutionRole); - the auditor policy
deploy/iam/auditor-policy.json(also printed byvpcatlas iam-policy), orReadOnlyAccess; orsts:AssumeRoleon the auditor roles of other accounts when you setVPCATLAS_ROLE_ARN(andVPCATLAS_EXTERNAL_IDwhen their trust policies have one; see iam.md); - when you use
VPCATLAS_SNAPSHOT_URI:s3:GetObject,s3:PutObjectands3:DeleteObjectonarn:aws:s3:::my-bucket/atlas/*(the history lives under that prefix and the oldest snapshots are deleted), pluss3:ListBucketonarn:aws:s3:::my-bucketwith the conditions3:prefix=atlas/*. Withouts3:ListBucket, S3 answersAccessDeniedinstead ofNoSuchKeyfor an object that is not there yet, and the store reports it as a read error.
The function reads AWS with the credentials of its execution role: no access keys are configured. The keys written in the bucket are listed in the dashboard page.
3. Create the function¶
aws lambda create-function \
--function-name vpcatlas \
--package-type Image \
--code ImageUri="$REGISTRY/$REPO:0.1.0" \
--architectures arm64 \
--role "arn:aws:iam::$ACCOUNT:role/vpcatlas-lambda" \
--memory-size 2048 \
--timeout 900 \
--environment '{"Variables":{"VPCATLAS_REGIONS":"eu-west-1,eu-central-1","VPCATLAS_SNAPSHOT_URI":"s3://my-bucket/atlas","VPCATLAS_REFRESH_SECONDS":"1800","VPCATLAS_SCAN_ON_REQUEST":"false"}}' \
--region "$REGION"
The architecture of the function must match the image (--platform linux/arm64 above and --architectures arm64).
The image sets no port: Lambda does not use one. The settings are the same VPCATLAS_* variables as everywhere else
(the dashboard page).
4. Schedule the scan¶
aws scheduler create-schedule \
--name vpcatlas-scan \
--schedule-expression "rate(30 minutes)" \
--flexible-time-window Mode=OFF \
--target '{"Arn":"arn:aws:lambda:eu-west-1:123456789012:function:vpcatlas","RoleArn":"arn:aws:iam::123456789012:role/vpcatlas-scheduler","Input":"{\"atlas_action\":\"scan\"}"}' \
--region "$REGION"
The scheduler role needs lambda:InvokeFunction on the function. Invoke the same event once by hand to create the
first snapshot:
aws lambda invoke --function-name vpcatlas --cli-binary-format raw-in-base64-out \
--payload '{"atlas_action":"scan"}' out.json
Until a snapshot exists, the data routes answer 503. Keep the schedule interval at or below
VPCATLAS_REFRESH_SECONDS so the page never shows a snapshot that the dashboard already considers stale.
5. Put something in front of it¶
The dashboard shows your network topology, so decide who may open it:
| Option | When |
|---|---|
Function URL with AWS_IAM auth |
Automation that signs its requests (for example curl --aws-sigv4). Browsers cannot sign. |
Function URL with NONE auth and VPCATLAS_AUTH_PASSWORD |
A small team over HTTPS. The URL is public and only HTTP Basic protects it: use a long password. |
| API Gateway HTTP API with a JWT authorizer (Cognito or any OIDC provider) | Single sign-on. Set VPCATLAS_BASE_PATH when the API has a stage prefix. |
| CloudFront in front of either | Custom domain, WAF, caching of /static/. |
/healthz is not covered by the password, and neither are /static/ and /api/docs; see
authentication. Binary downloads (.lucid, .zip) are returned base64-encoded;
Function URLs and HTTP APIs decode them. For a REST API, add application/zip and application/vnd.jgraph.mxfile to
its binary media types.
Alternative: AWS Lambda Web Adapter¶
To run the unmodified web image instead, add the Lambda Web Adapter extension to deploy/Dockerfile
(COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:<version> /lambda-adapter /opt/extensions/lambda-adapter,
pinning a released version) and set AWS_LWA_PORT=8080. The Mangum handler needs no extension and also supports the
scheduled scan event, so it is the default.