Advanced Cloud Cost Optimization Strategy Teams Overlook
Why an Event‑Driven Cleanup Strategy Beats Manual Audits
Most engineering and platform teams still rely on quarterly spreadsheets or ad‑hoc scripts to hunt down idle EC2 instances, under‑utilized RDS databases, or forgotten Elastic IPs. Those methods miss short‑lived waste, generate alert fatigue, and require constant human oversight. An event‑driven cleanup strategy ties together CloudWatch Contributor Insights, Cost Explorer, EventBridge, and Lambda so that low‑utilization resources are identified, notified, and remediated automatically. The result is a continuous feedback loop that reduces waste in near‑real time while preserving the safety nets that teams need.
Prerequisites: Permissions, Tagging, and Baseline Metrics
Before you build the automation, make sure the following are in place:
- IAM Role for Automation – Create a role named
CostOptimizationAutomationwith the policiesAmazonEC2FullAccess,AmazonRDSFullAccess,AWSLambdaFullAccess,CloudWatchReadOnlyAccess,AWSBudgetsReadOnlyAccess, andAWSBillingReadOnlyAccess. Attach the role to the Lambda function. - Consistent Tagging – Enforce a
CostCentertag on every provisioned resource. Use AWS Config rulerequired-tagsto block creation of untagged resources. - Baseline Utilization Data – Enable CloudWatch Detailed Monitoring for EC2 (
--monitoring Enabled) and RDS (--enable-performance-insights). This provides the granularity needed for Contributor Insights. - AWS CLI v2 – All commands below assume the latest CLI version.
Step 1: Enable CloudWatch Contributor Insights for the Services You Want to Track
Contributor Insights aggregates high‑cardinality metrics and surfaces the top talkers in a service. For idle‑resource detection, focus on CPU, network, and storage I/O.
# Enable for EC2
aws cloudwatch put-contributor-insights-rule \
--rule-name EC2IdleRule \
--rule-state ENABLED \
--contributor-insights-configuration '{"Metrics":[{"Namespace":"AWS/EC2","MetricName":"CPUUtilization"}]}'
# Enable for RDS
aws cloudwatch put-contributor-insights-rule \
--rule-name RDSIdleRule \
--rule-state ENABLED \
--contributor-insights-configuration '{"Metrics":[{"Namespace":"AWS/RDS","MetricName":"CPUUtilization"}]}'
After a few hours, open the CloudWatch Console → Contributor Insights to verify that the top‑10 instances with the lowest CPU appear.
Step 2: Build a Cost Explorer Query That Pulls Low‑Utilization Resources
Cost Explorer can filter by usage amount. Combine it with the UsageQuantity metric to surface resources that have consumed less than a threshold in the past 30 days.
aws ce get-cost-and-usage \
--time-period Start=$(date -d '-30 days' +%Y-%m-%d),End=$(date +%Y-%m-%d) \
--granularity DAILY \
--filter '{"Dimensions":{"Key":"USAGE_TYPE","Values":["BoxUsage:t2.micro","Usage:Hours"]}}' \
--metrics "UsageQuantity" \
--group-by Type=DIMENSION,Key=RESOURCE_ID \
--query 'ResultsByTime[?Total[?Amount<`10`]]'
Replace the USAGE_TYPE values with the services you monitor. The query returns a JSON list of resource IDs that have used less than 10 hours in the last month – a typical idle threshold for development workloads.
Step 3: Write the Lambda Remediation Function
The Lambda function receives the list of idle IDs, validates safety constraints (e.g., Environment=prod tag must be absent), and then either stops (for EC2) or creates a snapshot and deletes (for RDS). Below is a minimal Python example.
import boto3, os, json
ec2 = boto3.client('ec2')
rds = boto3.client('rds')
sns = boto3.client('sns')
THRESHOLD_HOURS = int(os.getenv('THRESHOLD_HOURS', '10'))
SNS_TOPIC = os.getenv('SNS_TOPIC_ARN')
def lambda_handler(event, context):
idle_ids = event.get('idle_ids', [])
actions = []
for rid in idle_ids:
# Determine resource type by ARN prefix
if rid.startswith('arn:aws:ec2'):
instance = ec2.describe_instances(InstanceIds=[rid.split('/')[-1]])['Reservations'][0]['Instances'][0]
tags = {t['Key']: t['Value'] for t in instance.get('Tags', [])}
if tags.get('Environment') == 'prod':
continue # skip production
ec2.stop_instances(InstanceIds=[instance['InstanceId']])
actions.append(f"Stopped EC2 {instance['InstanceId']}")
elif rid.startswith('arn:aws:rds'):
db_id = rid.split('/')[-1]
db = rds.describe_db_instances(DBInstanceIdentifier=db_id)['DBInstances'][0]
tags = {t['Key']: t['Value'] for t in rds.list_tags_for_resource(ResourceName=rid)['TagList']}
if tags.get('Environment') == 'prod':
continue
# Snapshot before deletion
snap_id = f"{db_id}-auto-snap-{int(context.aws_request_id[:8],16)}"
rds.create_db_snapshot(DBSnapshotIdentifier=snap_id, DBInstanceIdentifier=db_id)
rds.delete_db_instance(DBInstanceIdentifier=db_id, SkipFinalSnapshot=True)
actions.append(f"Deleted RDS {db_id} after snapshot {snap_id}")
if actions:
sns.publish(TopicArn=SNS_TOPIC, Message='\n'.join(actions))
return {'status': 'complete', 'actions': actions}
Deploy the function with the following CLI command:
aws lambda create-function \
--function-name IdleResourceRemediator \
--runtime python3.11 \
--role arn:aws:iam::123456789012:role/CostOptimizationAutomation \
--handler lambda_function.lambda_handler \
--zip-file fileb://remediator.zip \
--environment Variables={THRESHOLD_HOURS=10,SNS_TOPIC_ARN=arn:aws:sns:us-east-1:123456789012:CostAlerts}
Step 4: Schedule the Workflow with EventBridge and Wire Up Notifications
Create an EventBridge rule that runs daily, calls the Cost Explorer query, and invokes the Lambda with the result.
aws events put-rule \
--name DailyIdleCheck \
--schedule-expression "rate(24 hours)" \
--state ENABLED
aws events put-targets \
--rule DailyIdleCheck \
--targets '[{"Id":"1","Arn":"arn:aws:lambda:us-east-1:123456789012:function:IdleResourceRemediator"}]'
Add the necessary permission for EventBridge to invoke the function:
aws lambda add-permission \
--function-name IdleResourceRemediator \
--principal events.amazonaws.com \
--statement-id EventBridgeInvoke \
--action 'lambda:InvokeFunction' \
--source-arn arn:aws:events:us-east-1:123456789012:rule/DailyIdleCheck
Finally, set up an SNS topic for alerts and subscribe your Slack webhook or email address. The Lambda function already publishes a concise list of actions to this topic.
Manual vs. Automated Idle‑Resource Remediation
| Aspect | Manual Quarterly Review | Event‑Driven Automated Workflow |
|---|---|---|
| Detection latency | Up to 90 days (quarterly) | Minutes to hours after idle period |
| Human effort | Hours of console navigation, spreadsheet updates | One‑time setup; < 5 minutes per run for monitoring |
| Risk of accidental termination | Low (human checks) but prone to oversight | Controlled by tag filters; automated safety checks |
| Cost savings visibility | Post‑mortem, hard to attribute | Real‑time Cost Explorer data, actionable alerts |
| Scalability | Limited to a few accounts | Works across dozens of accounts via cross‑account role |
The table shows why most teams that still rely on manual audits miss a large portion of waste.
Integrating the Strategy with Existing FinOps Processes
- Add the idle‑resource KPI to your monthly dashboard – Pull the Lambda‑generated SNS log into CloudWatch Logs Insights and surface the count of stopped/terminated resources.
- Feed the remediation data into your chargeback model – Tag the snapshot resources with
RemediatedBy=Automationso cost allocation reports can attribute savings. - Iterate thresholds – Start with a conservative
THRESHOLD_HOURS=10. After a month, analyze the false‑positive rate and adjust to5or15as needed. - Leverage the free AWS waste finder – Run the
/tools/aws-waste-findertool to validate that the automated list aligns with the broader waste landscape. - Onboard new accounts – Use AWS Organizations to attach the
CostOptimizationAutomationrole as a trusted entity in each member account.
Frequently asked questions
How do I prevent the Lambda from stopping critical production instances?
The function checks for a Environment=prod tag before taking any action. You can also add a DoNotTerminate=true tag for an extra safeguard. Updating the tag list in the Lambda code is a single‑line change.
Will creating snapshots before deleting RDS instances increase my bill?
Snapshots incur storage costs proportional to the data size. Because the automation only runs on resources that have been idle for weeks, the snapshot size is usually small. Monitor snapshot growth in the Backup console and set a lifecycle policy to delete snapshots older than 30 days.
Can this workflow be extended to other services like Elastic Load Balancers or NAT Gateways?
Yes. Contributor Insights supports any CloudWatch metric namespace. Add a new rule for AWS/ELB or AWS/NATGateway, adjust the Lambda logic to call elbv2.delete_load_balancer or ec2.delete_natgateway, and update the safety‑tag checks accordingly.
How do I test the automation without affecting live resources?
Create a separate AWS account or use a sandbox OU in AWS Organizations. Deploy the same IAM role and Lambda, but set the environment variable DRY_RUN=true. The function will log intended actions to CloudWatch Logs instead of invoking stop/delete APIs.
Key takeaways
- An event‑driven workflow closes the detection‑remediation gap that manual audits leave open.
- CloudWatch Contributor Insights provides low‑latency utilization signals without extra agents.
- Cost Explorer filters isolate resources that have consumed negligible hours over a configurable window.
- A single Lambda, triggered daily by EventBridge, can stop EC2, snapshot and delete RDS, and notify stakeholders via SNS.
- Tag‑based safety checks (
Environment,DoNotTerminate) keep the automation from touching production workloads. - Integrating the strategy with existing FinOps dashboards turns raw remediation data into measurable savings.
CloudBudgetMaster automates this entire pipeline for AWS today: it scans your accounts read‑only, identifies idle and wasted resources, and reports the dollar impact of each finding. Support for GCP, Azure, and Snowflake is coming soon. To try the automation, create a free account or explore the free AWS waste finder.
CloudBudgetMaster