| name | cost-optimization |
| description | Reduce cloud infrastructure costs through right-sizing, reserved capacity, spot instances, autoscaling, and waste elimination. Outputs cost analysis reports, rightsizing recommendations, and automated cleanup pipelines. |
| argument-hint | ["cloud provider","current monthly spend","main cost drivers","optimization targets"] |
| allowed-tools | Read, Write, Bash |
Cloud Cost Optimization
Cloud costs grow faster than usage because resources are over-provisioned, idle resources accumulate, and nobody owns the bill. Cost optimization is not a one-time project โ it is a continuous engineering practice.
Cost Reduction Hierarchy
- Eliminate waste โ delete unused resources (highest ROI, zero tradeoffs)
- Right-size โ match resource size to actual utilization
- Autoscale โ dynamically match capacity to demand
- Commit to reserved capacity โ 1-3 year commitments for stable workloads
- Use spot/preemptible instances โ for fault-tolerant, interruptible workloads
- Optimize data transfer โ reduce cross-region and egress costs
Process
- Baseline current spend โ cost by service, team, environment, and resource type.
- Find waste โ idle EC2/RDS, unattached EBS, old snapshots, unused load balancers.
- Right-size compute โ analyze CPU/memory utilization; resize over-provisioned instances.
- Review storage classes โ move cold data to cheaper tiers automatically.
- Implement autoscaling โ eliminate weekend/night idle capacity.
- Purchase commitments โ savings plans or reserved instances for stable workloads.
- Set budgets and alerts โ automated alerts before budgets are exceeded.
- Implement tagging โ enforce cost allocation by team/service/environment.
Output Format
Cost Analysis Script (AWS)
import boto3
from datetime import datetime, timedelta
from dataclasses import dataclass
from collections import defaultdict
@dataclass
class CostItem:
service: str
amount_usd: float
change_pct: float
top_resources: list
class AWSCostAnalyzer:
def __init__(self):
self.ce = boto3.client("ce")
self.ec2 = boto3.client("ec2")
self.cloudwatch = boto3.client("cloudwatch")
def get_cost_by_service(self, days: int = 30) -> list[CostItem]:
end = datetime.today()
start = end - timedelta(days=days)
prior_start = start - timedelta(days=days)
current = self.ce.get_cost_and_usage(
TimePeriod={"Start": start.strftime("%Y-%m-%d"), "End": end.strftime("%Y-%m-%d")},
Granularity="MONTHLY",
Metrics=["UnblendedCost"],
GroupBy=[{"Type": "DIMENSION", "Key": }]
)
prior = .ce.get_cost_and_usage(
TimePeriod={: prior_start.strftime(), : start.strftime()},
Granularity=,
Metrics=[],
GroupBy=[{: , : }]
)
current_costs = {
g[][]: (g[][][])
result current[]
g result[]
}
prior_costs = {
g[][]: (g[][][])
result prior[]
g result[]
}
items = []
service, amount (current_costs.items(), key= x: -x[]):
prior_amount = prior_costs.get(service, )
change_pct = ((amount - prior_amount) / prior_amount * ) prior_amount >
items.append(CostItem(
service=service,
amount_usd=(amount, ),
change_pct=(change_pct, ),
top_resources=[]
))
items
() -> []:
instances = .ec2.describe_instances(
Filters=[{: , : []}]
)
idle = []
end = datetime.utcnow()
start = end - timedelta(days=)
reservation instances[]:
instance reservation[]:
instance_id = instance[]
instance_type = instance[]
metrics = .cloudwatch.get_metric_statistics(
Namespace=,
MetricName=,
Dimensions=[{: , : instance_id}],
StartTime=start,
EndTime=end,
Period=,
Statistics=[]
)
metrics[]:
avg_cpu = (d[] d metrics[]) / (metrics[])
avg_cpu < :
name = (
(t[] t instance.get(, []) t[] == ),
)
idle.append({
: instance_id,
: instance_type,
: name,
: (avg_cpu, ),
: INSTANCE_PRICES.get(instance_type, ) * ,
})
(idle, key= x: -x[])
() -> []:
volumes = .ec2.describe_volumes(
Filters=[{: , : []}]
)
[
{
: v[],
: v[],
: v[],
: ._ebs_monthly_cost(v[], v[]),
: v[].isoformat(),
: ((t[] t v.get(, []) t[] == ), ),
}
v volumes[]
]
() -> :
prices = {: , : , : , : , : }
(prices.get(volume_type, ) * size_gb, )
S3 Intelligent Tiering
import boto3
def apply_cost_lifecycle_rules(bucket_name: str):
s3 = boto3.client("s3")
s3.put_bucket_lifecycle_configuration(
Bucket=bucket_name,
LifecycleConfiguration={
"Rules": [
{
"ID": "intelligent-tiering-all",
"Status": "Enabled",
"Filter": {"Prefix": ""},
"Transitions": [
{"Days": 30, "StorageClass": "INTELLIGENT_TIERING"},
],
},
{
"ID": "archive-logs",
"Status": "Enabled",
"Filter": {"Prefix": "logs/"},
"Transitions": [
{"Days": 90, "StorageClass": "GLACIER_IR"},
{"Days": 365, "StorageClass": "DEEP_ARCHIVE"},
],
"Expiration": {"Days": 2555},
},
{
"ID": "delete-incomplete-multipart",
"Status": "Enabled",
: {: },
: {: },
},
]
}
)
()
Automated Waste Cleanup
import boto3
class WasteEliminator:
"""Automated cleanup of unambiguous waste. Always dry-run first."""
def __init__(self, dry_run: bool = True):
self.dry_run = dry_run
self.ec2 = boto3.client("ec2")
def delete_old_snapshots(self, retention_days: int = 30) -> list[str]:
"""Delete EBS snapshots older than retention_days with no tags."""
cutoff = datetime.utcnow() - timedelta(days=retention_days)
snapshots = self.ec2.describe_snapshots(OwnerIds=["self"])["Snapshots"]
to_delete = [
s for s in snapshots
if s["StartTime"].replace(tzinfo=None) < cutoff
and not s.get("Tags")
]
deleted = []
for snap in to_delete:
if not self.dry_run:
self.ec2.delete_snapshot(SnapshotId=snap["SnapshotId"])
deleted.append(snap["SnapshotId"])
print(f"{ self.dry_run }Deleted snapshot ")
deleted
() -> []:
addresses = .ec2.describe_addresses()[]
unassociated = [a a addresses a]
released = []
addr unassociated:
.dry_run:
.ec2.release_address(AllocationId=addr[])
released.append(addr.get())
released
Cost Budgets & Alerts
import boto3
def create_cost_budget(name: str, monthly_limit_usd: float, alert_pct: float = 80):
budgets = boto3.client("budgets")
account_id = boto3.client("sts").get_caller_identity()["Account"]
budgets.create_budget(
AccountId=account_id,
Budget={
"BudgetName": name,
"BudgetLimit": {"Amount": str(monthly_limit_usd), "Unit": "USD"},
"TimeUnit": "MONTHLY",
"BudgetType": "COST",
},
NotificationsWithSubscribers=[
{
"Notification": {
"NotificationType": "ACTUAL",
"ComparisonOperator": "GREATER_THAN",
"Threshold": alert_pct,
"ThresholdType": "PERCENTAGE",
},
"Subscribers": [{"SubscriptionType": "EMAIL", "Address": "infra@example.com"}]
},
{
"Notification": {
"NotificationType": "FORECASTED",
"ComparisonOperator": "GREATER_THAN",
"Threshold": 100,
"ThresholdType": ,
},
: [{: , : }]
}
]
)
Rules
- Waste elimination before optimization โ deleting an idle RDS instance saves 100% of its cost; right-sizing saves 30%.
- Tag everything from day one โ untagged resources cannot be attributed; enforce tagging in CI and IAM policies.
- Reserved instances for predictable workloads only โ committing to reserved capacity for bursty workloads wastes money.
- Spot instances require fault-tolerant architectures โ design for interruption first, then use spot.
- Right-sizing requires 2+ weeks of utilization data โ never right-size based on peak or a single day.
- Set budgets before you need them โ discovering overspend at month-end is too late; alert at 80% of budget.
- Autoscaling to zero on evenings/weekends โ dev/staging environments do not need to run 24/7.
- Compress and deduplicate before storing โ storage is cheap per GB, but logging uncompressed petabytes is not.
- Cost optimization is a team sport โ engineers who write the code should see the cost it generates.
- Measure savings, not just spending โ track cost per request, cost per user, and cost per transaction.
Worked Example and Anti-Patterns
Anti-Patterns to Avoid
| Anti-pattern | Problem | Fix |
|---|
| No runbook | On-call engineer has no guidance during incident | Write runbook before going to production |
| Single point of failure | One component down takes everything with it | Design for redundancy at every layer |
| No monitoring | Problems discovered by users, not engineers | Instrument before launch |
| Manual toil | Repeated manual steps slow down and introduce errors | Automate anything done more than twice |
| Undocumented decisions | Next engineer repeats the same mistakes | Use Architecture Decision Records (ADRs) |
Rules
- Start with the simplest thing that works -- complexity should be earned, not assumed.
- Make it observable before making it complex -- logs, metrics, and traces first.
- Automate toil -- anything done manually more than twice should be scripted.
- Document decisions -- use ADRs; future engineers will thank you.
- Test failure modes -- chaos engineering starts small; break one thing at a time.
- Prefer reversible decisions -- irreversible architecture decisions need the most careful thought.
- Own your runbooks -- every service needs a runbook before it goes to production.
- Measure before optimizing -- do not optimize what you have not profiled.
- Design for the 99th percentile user -- the average case is not the hard case.
- Keep it boring -- stable, predictable, well-understood technology over cutting-edge.