Cost management: Budgets, Cost Explorer, Savings Plans
-
Phase 0: Before you start
-
Phase 1: The foundation
-
Phase 2: Operations and security
-
Phase 3: SQL Server on AWS
-
Phase 4: Troubleshooting step by step
-
Phase 5: Final project and career
The cloud bill is the one report every manager reads. AWS cost optimization is a routine, not a one-off project: see where the money goes, get warned before a budget is blown, remove waste, and only then commit to discounts like Savings Plans.
You’ll break down your month-to-date cost by service and by the course tag, create a tag-based budget with email alerts, check what Savings Plans would suggest, and write Find-Waste.ps1, a script that finds the leftovers that quietly cost money. Then you’ll fix a date bug that breaks cost scripts on the 1st of every month.
| Time | You need | By the end you can |
|---|---|---|
| 45 minutes | Lesson 4 done (the course tag activated for cost allocation), your laptop signed in as saat-admin, an email address for budget alerts. Cost: about USD 0.10 (Cost Explorer API calls are USD 0.01 each, plus a few minutes of an idle Elastic IP). | Query Cost Explorer by service and tag, create budgets on actual and forecasted spend, read Savings Plans recommendations, find idle resources with a script, and avoid common cost-reporting mistakes. |
What it is, in one line
AWS cost optimization means continuously matching what you pay for with what you actually use, using visibility (Cost Explorer), alerts (Budgets), cleanup and commitment discounts.
In Simple Words
The household electricity bill
When the electricity bill jumps, a family first looks at what used the power: the AC, the geyser, the old fridge. That is Cost Explorer.
Then they set a rule: “if we reach 80 percent of our usual bill by the 20th, we talk about it.” That is a budget alert.
Then they switch off fans in empty rooms and replace the old fridge. That is removing waste and rightsizing. Only after that does a yearly plan with the electricity company make sense, which is what a Savings Plan is.
Engineer's View
How AWS cost tools really work
Cost Explorer shows cost and usage by service, account, Region, usage type and cost allocation tag, with up to 13 months of history and forecasts. Its API (ce) charges USD 0.01 per request. Tags only appear in billing after you activate them as cost allocation tags, and only for costs incurred after activation.
Time periods use an inclusive start and exclusive end date.
AWS Budgets tracks cost, usage, Savings Plans or Reserved Instance coverage against a limit, with alerts on actual or forecasted values to email, SNS or chat, and optional actions (apply an IAM policy or SCP, stop instances). Cost Anomaly Detection uses machine learning to flag unusual spend and is enabled with a default monitor for many accounts.
Savings Plans trade a one- or three-year commitment in USD per hour for up to about 66 percent off (Compute) or more (EC2 Instance). Rightsize before committing, and cover only the steady base. Compute Optimizer recommends instance sizes from CloudWatch data.
Common waste: unassociated Elastic IPs (all public IPv4 is charged), unattached volumes, old snapshots, idle NAT gateways and load balancers, and log groups without retention.
How it works, step by step
Here is the routine you’ll practise:
1See it
Group cost by service and by tag. “Which team or project spent this?” only works if resources are tagged and the tag is activated.
2Get warned
Budgets on forecasted spend warn you mid-month, while you can still act; budgets on actual spend confirm what happened.
3Remove waste, then commit
Most first reviews find the same leaks:
Committing to Savings Plans before cleaning up locks you into paying for waste for a year or three. Clean up and rightsize first.
✅ Checkpoint: You can explain cost allocation tags, actual versus forecasted budget alerts, why Savings Plans come last, and the common sources of waste.
Hands-on lab: AWS cost optimization from the CLI
Sign in and set your alert email:
cd C:\aws-lab
aws login --profile saat-admin
$env:AWS_PROFILE = 'saat-admin'
$acct = aws sts get-caller-identity --query Account --output text
$email = 'you@example.com'1See month-to-date cost by service and tag
Paste this whole block (two Cost Explorer calls, USD 0.02):
$today = Get-Date
if ($today.Day -eq 1) { $start = $today.AddMonths(-1).ToString('yyyy-MM-01') } else { $start = $today.ToString('yyyy-MM-01') }
$end = $today.ToString('yyyy-MM-dd')
$r = aws ce get-cost-and-usage --time-period "Start=$start,End=$end" --granularity MONTHLY --metrics UnblendedCost --group-by Type=DIMENSION,Key=SERVICE --output json | ConvertFrom-Json
$byService = $r.ResultsByTime[0].Groups | ForEach-Object { [pscustomobject]@{ Service = $_.Keys[0]; USD = [math]::Round([double]$_.Metrics.UnblendedCost.Amount, 2) } } | Sort-Object USD -Descending
$byService | Select-Object -First 4 | Format-Table -AutoSize
$t = aws ce get-cost-and-usage --time-period "Start=$start,End=$end" --granularity MONTHLY --metrics UnblendedCost --group-by Type=TAG,Key=course --output json | ConvertFrom-Json
$t.ResultsByTime[0].Groups | ForEach-Object { "{0,-28} {1:N2}" -f $_.Keys[0], [double]$_.Metrics.UnblendedCost.Amount }You should see something like this (your numbers will differ):
Service USD
------- ---
Amazon Elastic Compute Cloud - Compute 1.84
Amazon Virtual Private Cloud 1.12
Amazon Simple Storage Service 0.21
AWS Key Management Service 0.08
course$ 0.31
course$saatvedha-aws 3.42course$ is untagged spend: things like tax lines, some data transfer, and resources created without the tag. In a real company, a large untagged share is the first thing to fix, because nobody can own it. VPC cost is mostly the interface endpoints you create in labs.

2Budget the course tag
Paste this whole block. Confirm nothing; budget emails need no subscription:
@'
{
"BudgetName": "saat-course-tag",
"BudgetLimit": { "Amount": "5", "Unit": "USD" },
"TimeUnit": "MONTHLY",
"BudgetType": "COST",
"CostFilters": { "TagKeyValue": [ "user:course$saatvedha-aws" ] }
}
'@ | Set-Content budget.json
@'
[
{ "Notification": { "NotificationType": "ACTUAL", "ComparisonOperator": "GREATER_THAN", "Threshold": 80, "ThresholdType": "PERCENTAGE" },
"Subscribers": [ { "SubscriptionType": "EMAIL", "Address": "EMAIL_ADDRESS" } ] },
{ "Notification": { "NotificationType": "FORECASTED", "ComparisonOperator": "GREATER_THAN", "Threshold": 100, "ThresholdType": "PERCENTAGE" },
"Subscribers": [ { "SubscriptionType": "EMAIL", "Address": "EMAIL_ADDRESS" } ] }
]
'@ -replace 'EMAIL_ADDRESS', $email | Set-Content budget-notifications.json
aws budgets create-budget --account-id $acct --budget file://budget.json --notifications-with-subscribers file://budget-notifications.json
Start-Sleep 30
aws budgets describe-budget --account-id $acct --budget-name saat-course-tag --query "Budget.[BudgetLimit.Amount,CalculatedSpend.ActualSpend.Amount]" --output textYou should see something like this (spend can take a few hours to appear for a new budget):
5.0 3.42You’ll get an email when course-tagged spend passes 80 percent of USD 5, and earlier if AWS forecasts you’ll pass USD 5 by month end. Note the filter format: user:course$saatvedha-aws, with the user: prefix for your own tags.
3Ask about Savings Plans
Paste this whole block:
aws ce get-savings-plans-purchase-recommendation --savings-plans-type COMPUTE_SP --term-in-years ONE_YEAR --payment-option NO_UPFRONT --lookback-period-in-days THIRTY_DAYS --query 'length(SavingsPlansPurchaseRecommendation.SavingsPlansPurchaseRecommendationDetails || `[]`)'You should see this in a lab account:
0No recommendation, because nothing in a lab account runs steadily. In a company running 40 servers all month, this call suggests an hourly commitment and the expected saving. Buy only what covers the floor of your usage, after rightsizing.
4Write a waste finder
Paste this whole block. It saves Find-Waste.ps1 and runs it:
@'
param([string]$Region = 'ap-south-1')
# Rough Mumbai list prices; check the pricing pages for your Region.
$gp3PerGb = 0.0912; $snapPerGb = 0.05; $ipv4PerMonth = 3.65
$rows = @()
foreach ($l in @(aws ec2 describe-volumes --region $Region --filters Name=status,Values=available --query "Volumes[].[VolumeId,Size,VolumeType,AvailabilityZone]" --output text)) {
if (-not $l) { continue }
$id, $size, $type, $az = $l -split "`t"
$rows += [pscustomobject]@{ Type = 'EBSVolume'; Id = $id; Detail = "$size GiB $type, $az"; EstUsdPerMonth = [math]::Round([double]$size * $gp3PerGb, 2) }
}
foreach ($l in @(aws ec2 describe-addresses --region $Region --query "Addresses[?AssociationId==null].[AllocationId,PublicIp]" --output text)) {
if (-not $l) { continue }
$id, $ip = $l -split "`t"
$rows += [pscustomobject]@{ Type = 'ElasticIP'; Id = $id; Detail = $ip; EstUsdPerMonth = $ipv4PerMonth }
}
foreach ($l in @(aws ec2 describe-snapshots --region $Region --owner-ids self --query "Snapshots[].[SnapshotId,VolumeSize,StartTime]" --output text)) {
if (-not $l) { continue }
$id, $size, $start = $l -split "`t"
if ([datetime]$start -lt (Get-Date).AddDays(-90)) { $rows += [pscustomobject]@{ Type = 'OldSnapshot'; Id = $id; Detail = "$size GiB, $start"; EstUsdPerMonth = [math]::Round([double]$size * $snapPerGb, 2) } }
}
foreach ($l in @(aws ec2 describe-instances --region $Region --filters Name=instance-state-name,Values=stopped --query "Reservations[].Instances[].[InstanceId,InstanceType]" --output text)) {
if (-not $l) { continue }
$id, $type = $l -split "`t"
$rows += [pscustomobject]@{ Type = 'StoppedEC2'; Id = $id; Detail = "$type (volumes still billed)"; EstUsdPerMonth = $null }
}
if ($rows) { $rows | Format-Table -AutoSize } else { "No waste found in $Region" }
'@ | Set-Content C:\aws-lab\Find-Waste.ps1
.\Find-Waste.ps1In a clean lab account you should see this:
No waste found in ap-south-1Run it in each Region you use, monthly. It looks for unattached volumes, Elastic IPs not attached to anything, snapshots older than 90 days and stopped instances, whose volumes are still billed. Cost Explorer and AWS Trusted Advisor have similar checks, but a script you own can run anywhere and feed a report.

✅ Checkpoint: You broke down this month’s cost by service and tag, created the saat-course-tag budget with actual and forecast alerts, saw the Savings Plans recommendation, and Find-Waste.ps1 reports a clean account.
Break It, Then Fix It
A script that fails once a month, and the leftovers that cost money every hour.
1Break it: run a cost report on the 1st of the month
Simulate the 1st, when “start of month to today” means the same date twice:
$today = (Get-Date).ToString('yyyy-MM-dd')
aws ce get-cost-and-usage --time-period "Start=$today,End=$today" --granularity MONTHLY --metrics UnblendedCostYou’ll get this error:
An error occurred (ValidationException) when calling the GetCostAndUsage operation: Start date (and hour) should be before end date (and hour)The end date is exclusive, so an empty range is invalid. Scheduled cost reports that work all month fail every 1st, which is exactly when finance wants last month’s numbers. Fix: handle the 1st explicitly, as step 1 does: report the previous month, or use an end date of tomorrow for “today so far”.
2Break it: leave an Elastic IP and a volume behind
Create the two most common leftovers, then run the waste finder:
$eip = aws ec2 allocate-address --domain vpc --tag-specifications 'ResourceType=elastic-ip,Tags=[{Key=course,Value=saatvedha-aws},{Key=lesson,Value=36}]' --query AllocationId --output text
$vol = aws ec2 create-volume --availability-zone ap-south-1a --size 1 --volume-type gp3 --tag-specifications 'ResourceType=volume,Tags=[{Key=course,Value=saatvedha-aws},{Key=lesson,Value=36}]' --query VolumeId --output text
aws ec2 wait volume-available --volume-ids $vol
.\Find-Waste.ps1You should see something like this:
Type Id Detail EstUsdPerMonth
---- -- ------ --------------
EBSVolume vol-0a1b2c3d4e5f60082 1 GiB gp3, ap-south-1a 0.09
ElasticIP eipalloc-0a1b2c3d4e5f60081 13.234.56.78 3.65
An Elastic IP not attached to anything costs about USD 3.65 a month, more than this small volume. In big accounts there are often hundreds of each, left behind by deleted servers, NAT gateways and tests. Fix: release and delete what nobody owns (snapshot volumes first if anyone might need the data), and schedule the script:
aws ec2 release-address --allocation-id $eip
aws ec2 delete-volume --volume-id $vol
aws budgets delete-budget --account-id $acct --budget-name saat-course-tag
Remove-Item budget.json, budget-notifications.json
.\Find-Waste.ps1You should see this:
No waste found in ap-south-1The budgets from Lesson 2 stay. Keep Find-Waste.ps1 and run it after every lab, next to Find-LabResources.ps1.
✅ Checkpoint: You fixed the 1st-of-month date bug and found and removed an idle Elastic IP and an unattached volume. The extra budget is deleted and Find-Waste.ps1 is saved.
This happened at work
The NAT gateways nobody remembered
A media company’s AWS bill rose 30 percent over six months with no new products. Cost Explorer by service showed EC2-Other as the biggest growth, which tells you almost nothing on its own.
Grouping by usage type found it: NAT gateway hours and NAT data processing. Each new test VPC created by a template got three NAT gateways, one per zone, and test VPCs were rarely deleted. There were 41 of them.
Worse, servers pulled container images and S3 data through NAT instead of VPC endpoints, paying per GB.
They deleted idle VPCs, added S3 and ECR endpoints, used one NAT gateway in test VPCs, tagged everything with an owner, and set budgets per team with forecast alerts. The bill dropped below where it had started, and a Compute Savings Plan on the remaining steady base cut another fifth.
Common mistakes and troubleshooting
| Mistake | What happens | What to do |
|---|---|---|
| Buying Savings Plans before cleanup | Paying for waste for years | Clean up and rightsize first; cover the steady base |
| Tags not activated for cost allocation | Tag reports show nothing | Activate tags in Billing; they apply from activation onwards |
| Budgets on actual spend only | You learn after the money is spent | Add forecasted alerts |
| Cost scripts with start equal to end | Fail on the 1st of every month | End is exclusive; handle the 1st |
| Idle Elastic IPs and unattached volumes | Charges every hour, forever | Run a waste finder monthly in every Region |
| Traffic through NAT that could use endpoints | Per-GB processing charges | Gateway endpoints for S3 and DynamoDB, interface endpoints where volume justifies |
Interview questions
Click a question to open it. Each one is written the way an interviewer would ask it. Under it you will see why they ask it and one way you could answer, in your own words.
Fresher level
1. Your manager says this month’s AWS bill is higher than usual and asks you to find out why. Where would you start?
Why they ask this: They want to know you can use the basic cost tools on your own. Every admin gets asked about the bill sooner or later.
How you might answer: I’d open Cost Explorer and group by service first, then compare this month with last month to see which service grew the most. Then I’d break that service down by usage type, account, Region and cost allocation tags.
That usually points me to the exact resources and the team that owns them.
2. What’s the difference between an actual budget alert and a forecasted budget alert?
Why they ask this: They are checking that you know how to get warned early, not only after the money is spent. Forecasted alerts give you time to act.
How you might answer: An actual alert fires when spend has already reached the threshold. A forecasted alert fires when AWS predicts you’ll go over by the end of the period.
I like to set both, because the forecasted one gives me time to fix things before the month closes.
3. If you had one hour to look for wasted money in an AWS account, what would you check first?
Why they ask this: They want to see practical experience with common waste. These are the quick wins every cost review finds.
How you might answer: I’d look for unattached EBS volumes, unassociated Elastic IPs and old snapshots first, because they’re easy to find and safe to clean up. Then I’d check for idle NAT gateways and load balancers, over-sized instances, and log groups without retention.
2–5 years experience
4. Finance asks if you should buy Savings Plans. How would you decide, and which type would you pick?
Why they ask this: They want to see that you think before committing money for a long time. Buying commitments too early locks in waste.
How you might answer: I’d only buy Savings Plans for steady, predictable usage, and only after rightsizing and cleanup, so I’m not committing to waste. Compute Savings Plans are flexible across instance families, Regions, Fargate and Lambda.
EC2 Instance Savings Plans give a bigger discount, but only for a fixed instance family and Region, so I’d pick them only where that usage won’t change.
5. You built a cost report by the Team tag, but last month shows nothing. Why might that be?
Why they ask this: They are checking that you understand how cost allocation tags work. This catches out a lot of people the first time they report by tag.
How you might answer: Either the tag wasn’t activated for cost allocation at that time, or the resources weren’t tagged. Activating a tag only applies to costs from that point on, so it won’t fill in last month.
That’s why I activate the important tags early, as soon as a new account is set up.
6. The “EC2-Other” line on your bill doubled this month. How would you investigate it?
Why they ask this: They want to see that you know what hides inside this confusing cost line. It’s a very common question in real cost reviews.
How you might answer: EC2-Other isn’t instances. It includes EBS volumes and snapshots, NAT gateway hours and data, data transfer and Elastic IPs.
So I’d group by usage type in Cost Explorer to see which one grew, then find the resources and owners behind it.
Senior level
7. A company spending USD 200,000 a month on AWS has no cost control at all. How would you set up FinOps practices for them?
Why they ask this: They want to see cost governance at scale: ownership, visibility, alerts and regular review. They are checking that you can make cost everyone’s job, not only finance’s.
How you might answer: I’d make owner and cost-centre tags mandatory, enforced by tag policies and SCPs, and give each team its own account. Each account would get budgets with forecasts, plus anomaly detection.
Every month we’d review Cost Explorer and the Cost and Usage Report in Athena, rightsize with Compute Optimizer, and shut down non-prod on a schedule. I’d review the Savings Plans strategy every quarter.
8. Developers have sandbox accounts, and one of them accidentally left something huge running. How would you stop a runaway cost like that automatically?
Why they ask this: They want to see that you build guardrails, not only reports. They are checking that you can limit damage without someone watching all the time.
How you might answer: I’d set a budget with a budget action that kicks in when actual spend passes a threshold. The action can apply a restrictive SCP or IAM policy, or stop the instances.
I’d also send anomaly alerts to the account owner, so they know what happened and why.
Quick quiz
Q1. Why should Savings Plans come after cleanup?
- They are cheaper later
- Otherwise you commit to paying for waste
- They need tags
- They only cover S3
Show answer
B — Rightsize and clean up, then commit to the steady base.
Q2. What does course$ mean in a tag cost report?
- A broken tag
- Cost with no value for the course tag
- Tax only
- Credits only
Show answer
B — It is untagged spend for that tag key.
Q3. Why did the cost query fail with Start equal to End?
- Wrong Region
- End date is exclusive, so the range was empty
- No permission
- Cost Explorer was off
Show answer
B — Handle the 1st of the month explicitly.
Q4. Which budget alert warns you before money is spent?
- Actual
- Forecasted
- Both equally
- Neither
Show answer
B — Forecasted alerts give time to act.
Q5. What does an unassociated Elastic IP cost?
- Nothing
- About USD 3.65 a month
- USD 100 a month
- Only data transfer
Show answer
B — All public IPv4 addresses are charged hourly.
Quick recap
- See cost by service, usage type and activated tags in Cost Explorer.
- Budgets with forecasted alerts warn you in time.
- Find and remove idle Elastic IPs, unattached volumes and old snapshots regularly.
- Rightsize and clean up before buying Savings Plans.
- Cost Explorer end dates are exclusive; handle the 1st of the month.
Next lesson
Lesson 37: Site-to-Site VPN and Direct Connect → You’ll connect a simulated office network to saat-vpc with a Site-to-Site VPN and learn when Direct Connect is worth it.
Further reading: AWS cost optimization
Want to go deeper into AWS cost optimization? Read Analyzing your costs with AWS Cost Explorer and Managing your costs with AWS Budgets. You can also see the full plan for this course on the AWS Cloud Administration course page.
