Forgotten CDK labs keep billing after you stop caring about them. cdk-cost-killer is a small CDK app that finds those stacks and tears them down — nightly for hygiene, and hourly only while you are already over budget.
It does not create a budget. It reads the account budget named Monthly budget (default limit $30; the live AWS Budgets number wins). The whole design is one Lambda, two schedules, and a few hard skip rules.
The loop
EventBridge Scheduler
│
├─ 9:00 PM America/New_York → action=kill, reason=nightly
└─ every hour → action=kill, reason=hourly
│
▼
cdk-cost-killer Lambda
│
┌─────────────────┴─────────────────┐
│ read Monthly budget (actual vs cap)│
└─────────────────┬─────────────────┘
│
hourly + under budget? ─┤── yes → return (no-op)
│
▼
scan enabled regions
group nested stacks by root
stop EC2 first, then DeleteStack
A third path is optional: subscribe the stack output BudgetAlertTopicArn to the budget’s 100% actual alert. SNS then invokes the same Lambda with reason=budget-notification instead of waiting for the next hourly check.
What is expensive enough to kill
A stack family is torn down only when it holds a resource that keeps costing money while it exists: EC2 instances, Elastic IPs, NAT gateways, VPC endpoints, load balancers, Auto Scaling groups, RDS, Redshift, ElastiCache, OpenSearch, EKS, ECS services.
Default scope is CDK only (AWS::CDK::Metadata or a CDK description). Set targetScope to all-cfn if you want any CloudFormation stack with those resources.
Always skipped:
- this
CdkCostKillerstack CDKToolkitbootstrap stacks- anything tagged
CostKillerProtect=true
EC2 in a doomed family is stopped first so compute charges drop while CloudFormation delete finishes. Elastic IPs and load balancers only stop costing money after the stack is gone.
Safety switches
| Knob | Default | Why it exists |
|---|---|---|
dryRun |
true |
Log would-stop / would-delete only. Arm after CloudWatch review. |
enabled |
true |
Redeploy false to DISABLE both schedules and make the Lambda a no-op. |
protectTagKey |
CostKillerProtect |
Tag keepers. Nested stacks inherit the root family’s decision. |
regions |
all enabled | Optional allowlist, e.g. us-east-1,us-east-2. |
npx cdk deploy # dry-run on by default
npx cdk deploy -c dryRun=false # arm after reviewing logs
npx cdk deploy -c enabled=false # off switch, stack stays
Tags.of(stack).add("CostKillerProtect", "true");
No comments:
Post a Comment