CloudBudgetMasterCloudBudgetMaster

← All articles

Strategy

Advanced Cloud Cost Optimization Strategy Teams Overlook

September 01, 2026·7 min read·CloudBudgetMaster

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:

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

  1. 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.
  2. Feed the remediation data into your chargeback model – Tag the snapshot resources with RemediatedBy=Automation so cost allocation reports can attribute savings.
  3. Iterate thresholds – Start with a conservative THRESHOLD_HOURS=10. After a month, analyze the false‑positive rate and adjust to 5 or 15 as needed.
  4. Leverage the free AWS waste finder – Run the /tools/aws-waste-finder tool to validate that the automated list aligns with the broader waste landscape.
  5. Onboard new accounts – Use AWS Organizations to attach the CostOptimizationAutomation role 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


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.

Stop guessing where your AWS bill comes from

Upload a CSV, no signup. CloudBudgetMaster finds idle, unused, and overspending AWS resources automatically. GCP and Azure coming soon.

Run a free check