Advanced Cloud Cost Optimization Strategy You’re Missing
How a tag‑driven decommissioning strategy can cut hidden AWS spend
Most engineering and platform teams focus on rightsizing instances, buying Savings Plans, or cleaning up obvious orphaned assets. The resources that slip through the cracks are those that are still attached to a project tag but have been idle for weeks or months. By combining strict tag policies with an automated decommissioning pipeline, you can continuously prune idle resources, keep Savings Plans intact, and see the dollar impact in real time.
Build a reliable tagging foundation
A clean tag hierarchy is the backbone of any automated cost‑control workflow. Without it, you cannot reliably query for idle assets or guarantee that a decommissioned resource will not break a production workload.
Define a minimal tag set
- Environment –
dev,staging,prod - Owner – email or IAM role identifier
- Project – short code that matches your internal project tracker
- Expiration – ISO‑8601 date when the resource should be reviewed
Enforce tags at creation time
- Use Service Control Policies (SCP) in AWS Organizations to require the presence of the four tags on every supported service.
- Example SCP snippet:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"StringNotLikeIfExists": {
"aws:TagKeys": ["Environment","Owner","Project","Expiration"]
}
}
}]
}
- Apply the policy to the OU that contains all development accounts.
Tag propagation for legacy resources
Run a one‑off script that adds missing tags based on naming conventions or CloudTrail logs:
aws resourcegroupstaggingapi get-resources \
--resource-type-filters ec2:instance rds:db \
--query 'ResourceTagMappingList[?Tags[?Key==`Name`]]' \
--output json > missing-tags.json
# Process JSON and call tag-resource for each ARN
Store the output in a version‑controlled repo so you can audit changes.
Identify idle resources with automated queries
Once tags are in place, you can use AWS Cost Explorer, AWS Config, and Amazon Athena to surface idle assets.
Use Cost Explorer for usage‑based filters
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"]}}' \
--metrics "UnblendedCost"
Look for resources with zero usage over the last 30 days.
Leverage AWS Config rules
Enable the managed rule ec2-instance-no-public-ip and rds-instance-deletion-protection-enabled to flag mis‑configurations that often accompany idle assets. Add a custom rule that checks the Expiration tag against the current date.
Query with Athena for cross‑service visibility
Create a unified cost‑and‑usage table:
CREATE EXTERNAL TABLE IF NOT EXISTS cloud_costs (
line_item_usage_start string,
line_item_resource_id string,
line_item_usage_type string,
line_item_unblended_cost double,
resource_tags map<string,string>
)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
LOCATION 's3://my-cost-and-usage-bucket/cur/';
Then run:
SELECT resource_tags['Project'] AS project,
line_item_resource_id AS arn,
SUM(line_item_unblended_cost) AS cost,
MAX(line_item_usage_start) AS last_used
FROM cloud_costs
WHERE line_item_usage_type NOT LIKE '%DataTransfer%'
GROUP BY project, line_item_resource_id
HAVING MAX(line_item_usage_start) < date_add('day', -30, current_date);
The result set is a precise list of resources that have not been used in the past month.
Create automated decommissioning pipelines
Manual ticket creation for each idle resource is error‑prone and slow. A serverless pipeline can close the loop from detection to termination.
Architecture overview
- EventBridge rule triggers daily and runs an Athena query (via
StartQueryExecution). - Step Functions orchestrates the workflow: fetch query results, evaluate the Expiration tag, and decide between stop, terminate, or notify.
- Lambda functions perform the actual API calls (
aws ec2 stop-instances,aws rds delete-db-instance, etc.). - SNS sends a summary email to the resource Owner with an undo link that invokes a Lambda to cancel the termination.
Sample Step Functions definition (YAML)
StartAt: RunAthenaQuery
States:
RunAthenaQuery:
Type: Task
Resource: arn:aws:states:::athena:startQueryExecution.sync
Parameters:
QueryString: "SELECT ..." # same query from the previous section
QueryExecutionContext:
Database: default
ResultConfiguration:
OutputLocation: s3://my-athena-results/
Next: ProcessResults
ProcessResults:
Type: Map
ItemsPath: "$.ResultSet.Rows"
Iterator:
StartAt: EvaluateResource
States:
EvaluateResource:
Type: Choice
Choices:
- Variable: "$.Expiration"
StringLessThan: "{{now}}"
Next: DeleteResource
- Variable: "$.UsageHours"
NumericLessThan: 1
Next: StopResource
Default: NotifyOwner
StopResource:
Type: Task
Resource: arn:aws:states:::lambda:invoke
Parameters:
FunctionName: stopResourceFunction
Payload:
arn.$: "$.Arn"
End: true
DeleteResource:
Type: Task
Resource: arn:aws:states:::lambda:invoke
Parameters:
FunctionName: deleteResourceFunction
Payload:
arn.$: "$.Arn"
End: true
NotifyOwner:
Type: Task
Resource: arn:aws:states:::lambda:invoke
Parameters:
FunctionName: notifyOwnerFunction
Payload:
arn.$: "$.Arn"
End: true
Deploy the state machine with the AWS CLI:
aws stepfunctions create-state-machine \
--name TagDrivenDecommissioner \
--definition file://state-machine.yaml \
--role-arn arn:aws:iam::123456789012:role/StepFunctionsExecutionRole
Safety nets
- Grace period – add a 48‑hour wait before termination; the SNS message includes a signed URL that calls
aws lambda invoketo cancel. - Cost impact check – before terminating a Reserved Instance‑eligible instance, compare the projected loss of RI coverage against the expected savings.
Align decommissioning with Savings Plans and Reserved Instances
A common pitfall is to delete an idle instance that is covered by a Savings Plan, inadvertently reducing the plan’s utilization.
Identify covered resources
aws savingsplans describe-savings-plans \
--query 'SavingsPlans[?State==`active`].{Id:SavingsPlanId,Region:Region,InstanceFamily:InstanceFamily}'
Cross‑reference the list with the idle resources from Athena. If an idle instance matches a Savings Plan, prefer stop over terminate.
Adjust commitments dynamically
If a Savings Plan is under‑utilized for more than 30 days, consider the Modify Savings Plan API (currently in preview) or purchase a new plan that better matches your actual workload mix. Document the decision in a shared spreadsheet to keep finance and engineering aligned.
Manual vs automated tag‑driven decommissioning
| Aspect | Manual ticket‑based cleanup | Automated tag‑driven pipeline |
|---|---|---|
| Frequency | Weekly or ad‑hoc | Daily scheduled run |
| Human effort | High – each resource requires review | Low – only exception notifications need attention |
| Error rate | Prone to missed resources or accidental deletion | Guardrails (Expiration tag, SNS undo) reduce errors |
| Visibility | Spreadsheet or ticket system | Real‑time dashboard via CloudWatch metrics |
| Cost impact reporting | Manual aggregation | Immediate dollar impact shown in Cost Explorer or CloudBudgetMaster |
Integrate the workflow with CloudBudgetMaster
The free AWS waste finder gives you a quick snapshot of idle resources before you commit to a full pipeline. Run it from the Tools menu, copy the list of ARNs, and feed them into the Step Functions state machine.
Once the pipeline is live, create a free account to let CloudBudgetMaster continuously monitor the dollar impact of each termination. The platform pulls the same Cost Explorer data you use in the Athena query, so you get a single source of truth for budgeting and forecasting.
Frequently asked questions
How often should the decommissioning pipeline run?
A daily schedule balances freshness with API rate limits. For very large environments you can stagger runs by account or region to stay within service quotas.
What if a stopped instance is needed again?
The pipeline sends an SNS message with a signed URL that re‑starts the instance. The URL is valid for 48 hours, giving the owner time to react.
Does this approach work for serverless services?
Serverless resources such as Lambda functions do not have a stop state, but you can use the aws lambda delete-function API for functions that have not been invoked in the analysis window. Add a separate Step Functions branch for serverless.
Will terminating idle resources affect my compliance reports?
Because every resource carries an Owner and Project tag, you retain full auditability. The termination event is logged in CloudTrail, and the SNS notification is archived in an S3 bucket for later review.
Key takeaways
- Enforce a minimal, organization‑wide tag set with SCPs to guarantee data quality.
- Use Cost Explorer, Config, and Athena to surface resources with zero usage for 30+ days.
- Deploy a serverless Step Functions pipeline that stops, terminates, or notifies based on tag values.
- Cross‑check idle resources against active Savings Plans to avoid unnecessary loss of discounts.
- Automate reporting with CloudBudgetMaster’s free AWS waste finder and keep finance aligned.
CloudBudgetMaster automates this entire workflow. It scans AWS in read‑only mode today, identifies idle and wasted resources, and reports the dollar impact of each recommendation. Support for GCP, Azure, and Snowflake is coming soon.
CloudBudgetMaster