Automate AWS Cost Reporting with Serverless Solutions

A guten voch!

This evening, drawing inspiration from reading about mechanisms that went into building of the Mishkan, I wanted to answer a simple question of what my AWS bill was for the past year.  Earlier in my career, a wise manager taught me to “make easy things easy”. Those of you who know me personally will also know that I like to automate as much as possible. I did not want to whip up a calculator and iterate over 12 separate bills. That would not enable a repeatable process. 

Amazon Q to the res-Q (ha!).

Within ensuing 45 minutes I was able to:

  1. learn that, by default, AWS Billing and Cost Management displays only the past 14 months (excluding January 2025 in my case, since I am right in the middle of March today)
  2. realize how to fix that, by using a new feature that enables multi-year retention (up to 38-months, as of this writing)
  3. create and deploy a serverless AWS Lambda function that runs on a x86 Python runtime
  4. have that function use Cost Explorer APIs to pull historical usage data
  5. authorize this code with appropriate service control policy to allow execution in a dedicated IAM role
  6. rely on AWS EventBridge scheduling (I’ll have to later dive into why the teams did not just name it Cron, at least phonetically)
  7. feed the output to Amazon Simple Email Service, to get an e-mail report with this, which required creation of identities (really fun)
  8. test this end-to-end whilst making minor refinements
  9. write it up and get you to read (so you could be confident to try) it

Figure 1. Selecting multi-year data

Services brought together as part of the overall solution:

  • AWS Lambda – unlocks serverless deployments (there are servers to hug; you don’t have to πŸ˜‰
  • AWS IAM (Identity and Access Management) – fundamental to proper deployments
  • Amazon EventBridge – not your grandfather’s scheduler
  • AWS Cost Explorer – know thy appetite
  • Amazon SES (Simple Email Service) – xbiff and PINE never saw this coming

Here’s a visual diagram. (And if you appreciate good ASCII art history, consider a visit to Roy/SAC)



β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚      AWS Account (my_own_account_nothing_to_see_here_smile_and_wave,b0ys)   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚   EventBridge   β”‚    β”‚                                              β”‚    β”‚
β”‚  β”‚   (Scheduler)   β”‚    β”‚            AWS Lambda Function               β”‚    β”‚
β”‚  β”‚                 │─────    "annual-cost-report-very-original,sir"    β”‚    β”‚
β”‚  β”‚ CRON Expression β”‚    β”‚                                              β”‚    β”‚
β”‚  β”‚ 0 1 1 1 ? *     β”‚    β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚    β”‚
β”‚  β”‚ (Jan 1, 8pm ET) β”‚    β”‚  β”‚           Python 3.12 Code              β”‚ β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚  β”‚                                         β”‚ β”‚    β”‚
β”‚                         β”‚  β”‚  1. Get previous year dates             β”‚ β”‚    β”‚
β”‚                         β”‚  β”‚  2. Call Cost Explorer API              β”‚ β”‚    β”‚
β”‚                         β”‚  β”‚  3. Process cost data                   β”‚ β”‚    β”‚
β”‚                         β”‚  β”‚  4. Format email report                 β”‚ β”‚    β”‚
β”‚                         β”‚  β”‚  5. Send via SES                        β”‚ β”‚    β”‚
β”‚                         β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚    β”‚
β”‚                         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚                                   β”‚                                         β”‚
β”‚                                   β”‚                                         β”‚
β”‚                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                        β”‚
β”‚                  β”‚                β”‚                β”‚                        β”‚
β”‚                  β–Ό                β–Ό                β–Ό                        β”‚
β”‚                                                                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”‚
β”‚  β”‚  Cost Explorer  β”‚    β”‚      IAM        β”‚    β”‚      SES        β”‚          β”‚
β”‚  β”‚      API        β”‚    β”‚   (Permissions) β”‚    β”‚  (Email Service)β”‚          β”‚
β”‚  β”‚                 β”‚    β”‚                 β”‚    β”‚                 β”‚          β”‚
β”‚  β”‚ β€’ GetCost&Usage β”‚    β”‚ Lambda Role:    β”‚    β”‚ β€’ SendEmail     β”‚          β”‚
β”‚  β”‚ β€’ Previous year β”‚    β”‚ β€’ CostExplorer  β”‚    β”‚ β€’ Verified      β”‚          β”‚
β”‚  β”‚   data (2025)   β”‚    β”‚   Access        β”‚    β”‚   Identity      β”‚          β”‚
β”‚  β”‚ β€’ Monthly       β”‚    β”‚ β€’ SES SendEmail β”‚    β”‚                 β”‚          β”‚
β”‚  β”‚   breakdown     β”‚    β”‚   Permission    β”‚    β”‚                 β”‚          β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β”‚
β”‚                                                         β”‚                   β”‚
└────────────────────────────────────────────────────── β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                          β”‚
                                                          β–Ό
                                                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                                  β”‚   Email Report  β”‚
                                                  β”‚                 β”‚
                                                  β”‚ Subject: AWS    β”‚
                                                  β”‚ Annual Cost     β”‚
                                                  β”‚ Report - 2025   β”‚
                                                  β”‚                 β”‚
                                                  β”‚ Total: $my_bil  β”‚
                                                  β”‚ Monthly BBB     β”‚
                                                  β”‚ ...             β”‚
                                                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜




Figure 2. Deploying and testing AWS Lambda

Once the Lambda function is deployed and tested, the appropriate integration with SES takes place.

The data flow sequence can be illustrated in the following 8 steps:

1. EventBridge Timer ──────────────────────────────────────────────────┐
(January 1st, 8pm ET) β”‚
β–Ό
2. Lambda Function Triggered ──────────────────────────────────────────┐
(annual-cost-report) β”‚
β–Ό
3. Lambda calls Cost Explorer API ─────────────────────────────────────┐
GET /GetCostAndUsage β”‚
TimePeriod: 2025-01-01 to 2025-12-31 β”‚
β–Ό
4. Cost Explorer returns data ─────────────────────────────────────────┐
Monthly cost breakdown β”‚
β–Ό
5. Lambda processes and formats ───────────────────────────────────────┐
Creates email content β”‚
β–Ό
6. Lambda calls SES API ───────────────────────────────────────────────┐
SendEmail with report β”‚
β–Ό
7. SES delivers email ─────────────────────────────────────────────────┐
To: <you already know my email> β”‚
β–Ό
8. I receive the annual cost report β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

A word on service relationships? A picture speaks a thousand words.


EventBridge ──triggers──> Lambda ──calls──> Cost Explorer
                            β”‚                     β”‚
                            β”‚                     β–Ό
                            β”‚              Returns cost data
                            β”‚                     β”‚
                            β–Ό                     β”‚
                         Uses IAM β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         permissions
                            β”‚
                            β–Ό
                         Calls SES ──sends──> Email Report

In summary, this is a simple example of a serverless, automated cost reporting system that can help you stay ahead of runaway costs due to ever evolving application and infrastructure deployments or potentially unexpected mechanisms breaking and resulting in bills that could contribute to untimely hair loss. It:

  • Runs once per year automatically
  • Fetches a previous year’s AWS costs
  • Breaks down costs by month
  • Emails a detailed report
  • Requires zero maintenance
  • Costs virtually nothing to operate

Total annual cost:

  • Lambda: ~$0.00 (runs once per year for seconds)
  • EventBridge: ~$0.00 (1 event per year)
  • Cost Explorer: Free
  • SES: ~$0.00 (1 email per year)
  • IAM: Free

To summarize the benefits of this approach:

  • Serverless: No servers to manage
  • Scalable: Handles any amount of cost data
  • Reliable: AWS manages all the infrastructure
  • Costeffective: Pay only for what I use
  • Automated: Set it and forget it

Oh, and in case you missed it earlier β€” this week Amazon Web Services are celebrating our 20th anniversary! Happy anniversary! 

Here’s to experimenting, learning and building better, faster, together — wherever you are!

Leave a comment