💡 Automatically tears down publicly accessible AWS resources (e.g., S3 buckets) when an AWS Budget threshold is exceeded.
This project uses a Makefile-driven flow, a build script and CloudFormation to:
- Build and package each service (Lambda) with dependencies.
- Upload artifacts under unique keys to a premade S3 bucket.
- Deploy/update a single CloudFormation stack that wires AWS Budget → SNS → Lambda for teardown actions.
graph TD
Budget[AWS Budget] -->|Alert via SNS| Topic[SNS Topic]
Topic -->|Invoke| Lambda[S3 Teardown Lambda]
Lambda -->|S3 APIs| S3[S3 Buckets]
- AWS Budget: Monitors monthly cost; when usage exceeds the threshold, it publishes to an SNS Topic.
- SNS Topic: Receives budget alerts and invokes one or more Lambda functions.
- S3 Teardown Lambda: Parses the SNS event, examines S3 buckets tagged
access=public
, and for each, blocks public access and removes website configuration. - CloudFormation: Defines IAM role, SNS topic, Budget with SNS subscriber, Lambda functions, SNS subscriptions, and applies tags.
-
AWS CLI configured with credentials and default region.
-
Deployment S3 Bucket (e.g.,
organization-deployment-artifacts
) exists. Purges artifacts after the stack updates. -
Local tools:
uv
for Python dependency management (pyproject.toml-based).bash
,zip
,python3
, andpip
available for build scripts.
-
GNU Make to run provided targets.
.
├── infrastructure/
│ └── budget-teardown-service.yaml # CloudFormation template
├── scripts/
│ └── build.sh # Packages a service into build/<service>.zip
├── services/
│ └── s3_teardown_lambda/
│ ├── pyproject.toml # Lambda package metadata & dependencies
│ └── s3_teardown_lambda/ # Python module folder
│ ├── __init__.py
│ └── main.py # Handler: lambda_handler(event, context)
├── build/ # Generated by build script (not checked in)
│ └── s3_teardown_lambda.zip # Example built artifact
├── Makefile # Orchestrates sync, test, build, upload, deploy
├── pyproject.toml # Root project dependencies (if any)
└── README.md # This file
-
Configure AWS CLI:
aws configure # Ensure default region matches or export AWS_REGION in your shell.
-
Ensure deployment bucket exists and configure lifecycle rules (or versioning + lifecycle) to expire old artifacts:
-
Make build script executable:
chmod +x scripts/build.sh
-
Install project dev dependencies (if any):
make sync make install
Set or export variables, or adjust defaults at top of Makefile:
AWS_BUCKET
– your S3 bucket for artifacts.AWS_STACK
– CloudFormation stack name (default:budget-teardown-service
).AWS_REGION
– AWS region (default:us-east-1
).BUDGET_LIMIT
,BUDGET_THRESHOLD
– budget amount (USD) and threshold (%) for alerts.- (Optionally)
ProjectTag
,AccessTag
if you override tag values.
From repo root:
make deploy
This runs:
- package-all: For each directory under
services/
, runsscripts/build.sh <service>
, producingbuild/<service>.zip
. - upload-all: For each service, uploads
build/<service>.zip
to S3 under a unique key (e.g.,<service>-20250620T123456.zip
) and collects CloudFormation parameter overrides. - do-deploy: Invokes
aws cloudformation deploy
with:DeploymentArtifactsBucket=$(AWS_BUCKET)
- per-Lambda
S3Key
parameters set to the new artifact key BudgetLimit
,BudgetThresholdPercentage
,ProjectTag
,AccessTag
CloudFormation then creates or updates the stack. Because the S3Key differs, Lambda functions’ code is updated.
Run unit tests locally (with Moto mocks). Ensure you execute within the uv .venv:
make test
Whenever code changes:
make deploy
This packages new code, uploads under a new key, and updates the stack.
- Parameters:
DeploymentArtifactsBucket
(String): S3 bucket name.- For each Lambda service, a parameter
<ServiceCamelCase>S3Key
(String): the artifact key uploaded by Makefile. BudgetLimit
,BudgetThresholdPercentage
,ProjectTag
,AccessTag
.
- Resources:
- IAM Role (
LambdaExecutionRole
) with CloudWatch Logs and S3 permissions. - SNS Topic (
BudgetSNSTopic
). - AWS Budgets resource, notifying the SNS Topic when threshold exceeded.
- Lambda function resources referencing
Code: S3Bucket
and parameterizedS3Key
. - SNS subscription and Lambda permission to allow SNS to invoke the Lambda.
- IAM Role (
- Tags: All resources tagged with
project
,access
, andawsApplication
.
- Add additional folders under
services/
, each with its own pyproject.toml and module code. - In CloudFormation template, add parameters for each service’s S3Key (e.g.,
AnotherServiceS3Key
) and reference them in its Lambda resource. - The Makefile loop auto-detects all directories under
services/
and uploads each zip under a git hashed key, adding parameter overrides for each.
This project provides:
- A Makefile-driven workflow for building, uploading, and deploying multiple Lambda services.
- A
build.sh
that packages each service (with dependencies) into a ZIP for Lambda. - CloudFormation template parameterized to accept S3 keys for each Lambda, so deploying new code is as simple as uploading under a new key and passing it in.
- AWS Budget → SNS → Lambda wiring to automatically tear down publicly accessible resources when budget thresholds are exceeded.