This repository contains an AWS Lambda function that checks live sites on a schedule and via API Gateway. It validates TLS certificates (for final HTTPS destinations), confirms the sites return 2xx responses after redirects, emails alerts via SES, and writes the latest results to S3.
This code builds an AWS Lambda function. It runs on a cron schedule that is configured in .env. It can also be triggered via the script _ping_endpoint. When it runs, it looks though the contents of .json files at urls specified within .env. It reads the content of that json file, which tells the function which sites to ping with http(s) requests and, if there is any problem, which emails to notify.
If you want to add an email to receive notifications then you need to go to the group's AWS Console, navigate to SES, and 'validate' that email address.
This template was built and tested with:
OpenTofu v1.10.6
on darwin_arm64
+ provider registry.opentofu.org/hashicorp/aws v6.18.0
If you upgrade tofu/terraform in the future and try to use the state files from this template, you may run into issues. In that case, re-initialize the project with tofu init -upgrade after ensuring your version is compatible.
After cp .env-template .env and editing .env to your liking, and running tofu init, the essential workflow is:
./_docker
./_tf plan
./_tf apply
- API Gateway:
GET /check - CloudWatch schedule: controlled by
TF_VAR_CRON_EXPR
Manual API Gateway invocations require the shared secret from
TF_VAR_API_TRIGGER_SECRET in the x-sbn-check-secret request header. The
scheduled CloudWatch trigger does not need this header.
When creating a new project based on this template, ALL such repos MUST have the following:
- docker/Dockerfile
- docker/build.sh
- .env-template
- tf_main.tf
- tf_variables.tf
- tf_outputs.tf
- src/index.ts
- package.json
- tsconfig.json
- _docker
- _tf
- AGENTS.md
All other scripts and files are on a per-project basis.
- Lambda function written in TypeScript and packaged in an AWS Docker container
- API Gateway endpoint for on-demand checks
- CloudWatch schedule for periodic checks
- SES email alerts for failures
- S3 bucket for storing
latest-results.jsonand private per-run history snapshots - CloudFront distribution in front of
latest-results.json
- The primary sender receives a single summary email with all down sites.
- Each email listed in a site list source receives one digest email containing the down sites it is associated with.
- We only use terraform/tofu with AWS.
- We use a docker container so that npm modules are installed/built on the target linux platform.
- Wrapper scripts are intentionally thin;
_dockerprints the commands it executes, and_tfsources.envbefore running tofu commands. _tfprints a guide of commands that I use often.
Everything related to state is kept in the root directory; placing files elsewhere complicates terminal commands and makes the user dependent on wrapper scripts. See _tf for details.
These are supplied via .env (use TF_VAR_ prefixes where noted).
AWS_ACCESS_KEY_ID: AWS access key for terraform and tooling.AWS_SECRET_ACCESS_KEY: AWS secret key for terraform and tooling.DOCKER_IMAGE_NAME: Docker image name for the build container.DOCKER_IMAGE_TAG: Docker image tag for the build container.TF_VAR_PROJECT_PREFIX: Prefix applied to resource names.TF_VAR_LAMBDA_FUNCTION_NAME: Lambda function name suffix.TF_VAR_S3_BUCKET_NAME: Bucket forlatest-results.jsonand private run history snapshots.TF_VAR_LAMBDA_RUNTIME: Lambda runtime identifier.TF_VAR_AWS_REGION: AWS region for resources.TF_VAR_LAMBDA_FUNCTION_DESC: Lambda description string.TF_VAR_LAMBDA_ARCHITECTURE: Lambda architecture (e.g., x86_64).TF_VAR_LAMBDA_MAX_RUNTIME_SECONDS: Lambda timeout in seconds.TF_VAR_LAMBDA_DEPLOYMENT: Deployment label (also API Gateway stage).TF_VAR_LAMBDA_LOGS_TO_CLOUDWATCH: Whether to create a CloudWatch log group.TF_VAR_LAMBDA_LOGS_RETENTION_DAYS: Log retention in days.TF_VAR_API_GATEWAY_DATA_TRACE_ENABLED: Whether API Gateway should log full request/response bodies. Keep false unless debugging.TF_VAR_S3_PUBLIC_READ: Toggle public read forlatest-results.jsononly.TF_VAR_S3_FILE_PREFIX: Currently unused by the Lambda (reserved).TF_VAR_S3_RUN_HISTORY_RETENTION_DAYS: Number of days to retain private per-run snapshots underruns/.TF_VAR_CLOUDFRONT_PRICE_CLASS: CloudFront price class.TF_VAR_CLOUDFRONT_DESCRIPTION: CloudFront distribution description.TF_VAR_CRON_EXPR: CloudWatch schedule expression.TF_VAR_STATIC_PRIMARY_SENDER_EMAIL: Primary sender email for alerts (SES-verified).TF_VAR_API_TRIGGER_SECRET: Shared secret required for manualGET /checkcalls.TF_VAR_CHECK_CONCURRENCY_LIMIT: Maximum number of site checks to run concurrently.TF_VAR_EMAIL_CONCURRENCY_LIMIT: Maximum number of alert emails to send concurrently.TF_VAR_SITE_LIST_SOURCES: JSON array of URLs pointing to JSON site lists.TF_VAR_EXTRA_SITES: JSON array of strings or{ "url": "...", "emails": ["..."] }(emails are ignored; only the primary sender is notified).TF_VAR_GITHUB_ACCESS_TOKEN: Optional token for private GitHub raw content access.
The Lambda writes the current result to latest-results.json and also writes a
private per-run snapshot under runs/YYYY/MM/DD/<timestamp>.json. Terraform
configures an S3 lifecycle rule that expires objects under runs/ after
TF_VAR_S3_RUN_HISTORY_RETENTION_DAYS days. The lifecycle rule does not expire
latest-results.json.
The result object shape is:
{
"time": "2025-01-01T00:00:00.000Z",
"results": [ ... ]
}
Each site result includes failure metadata when relevant, including
failureKind values such as dns, timeout, tls, http_status,
network, invalid_url, or unknown.
The Lambda response includes both s3LatestObjectKey and
s3RunHistoryObjectKey so each invocation can be matched to its private S3
snapshot.
Run the local TypeScript build and focused unit tests with:
npm test
Run only the compiler with:
npm run build:local
- This repo is set for Node 22. If/when AWS Lambda supports Node 24, update
.env-templateanddocker/Dockerfile.