<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Nat Thompson</title>
    <description>The latest articles on Forem by Nat Thompson (@obsidianriver).</description>
    <link>https://forem.com/obsidianriver</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3847648%2F8f26f287-6da5-4c5d-a50f-5b544910296a.png</url>
      <title>Forem: Nat Thompson</title>
      <link>https://forem.com/obsidianriver</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/obsidianriver"/>
    <language>en</language>
    <item>
      <title>I Built an AI-Powered AWS Cost Optimizer — Here's How It Works</title>
      <dc:creator>Nat Thompson</dc:creator>
      <pubDate>Sat, 28 Mar 2026 12:45:10 +0000</pubDate>
      <link>https://forem.com/obsidianriver/i-built-an-ai-powered-aws-cost-optimizer-heres-how-it-works-41d4</link>
      <guid>https://forem.com/obsidianriver/i-built-an-ai-powered-aws-cost-optimizer-heres-how-it-works-41d4</guid>
      <description>&lt;p&gt;I'm an AWS consultant. My clients always ask the same question: &lt;em&gt;"Why is my AWS bill so high?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The answer is always the same: idle resources, oversized instances, and services nobody remembered to turn off. So I built a tool to find them automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;p&gt;Sharktooth connects to a customer's AWS account via a &lt;strong&gt;cross-account IAM role&lt;/strong&gt; — the same pattern used by Datadog, CloudHealth, and AWS's own tools.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Customer AWS Account          Sharktooth AWS Account
┌─────────────────┐          ┌──────────────────────┐
│                 │          │                      │
│  IAM Role ◄──────────────── STS AssumeRole        │
│  (read-only)    │          │   + ExternalId       │
│                 │          │                      │
│  Cost Explorer  │          │  Cost Analysis       │
│  CloudWatch     │────────► │  AI Recommendations  │
│  EC2 Describe   │          │  Dashboard           │
│  RDS Describe   │          │                      │
└─────────────────┘          └──────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1: Connect (5 minutes)
&lt;/h3&gt;

&lt;p&gt;The customer creates a read-only IAM role with this trust policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"AWS"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::SHARKTOOTH_ACCOUNT:root"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"StringEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sts:ExternalId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UNIQUE_PER_CUSTOMER"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;ExternalId&lt;/strong&gt; prevents confused deputy attacks — each customer gets a unique one.&lt;/p&gt;

&lt;p&gt;Permissions requested:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;ce:GetCostAndUsage&lt;/span&gt;
&lt;span class="s"&gt;cloudwatch:GetMetricStatistics&lt;/span&gt;
&lt;span class="s"&gt;ec2:DescribeInstances&lt;/span&gt;
&lt;span class="s"&gt;ec2:DescribeInstanceTypes&lt;/span&gt;
&lt;span class="s"&gt;rds:DescribeDBInstances&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No write permissions. No S3, Lambda, or IAM access.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Pull Cost Data
&lt;/h3&gt;

&lt;p&gt;Using &lt;code&gt;STS AssumeRole&lt;/code&gt;, we get temporary credentials (1-hour expiry) and call the Cost Explorer API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Monthly spend&lt;/strong&gt; by service (last 6 months)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Daily spend&lt;/strong&gt; trend (last 30 days)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service breakdown&lt;/strong&gt; with amounts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3: Detect Idle Resources
&lt;/h3&gt;

&lt;p&gt;For each running EC2 instance, we pull 7 days of CloudWatch &lt;code&gt;CPUUtilization&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Average CPU &amp;lt; 5%&lt;/strong&gt; = flagged as idle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Average CPU 5-20%&lt;/strong&gt; = right-sizing candidate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For RDS, we check &lt;code&gt;DatabaseConnections&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;0 connections over 7 days&lt;/strong&gt; = flagged as idle&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4: AI Analysis
&lt;/h3&gt;

&lt;p&gt;This is where it gets interesting. We feed everything into &lt;strong&gt;Claude&lt;/strong&gt; (via AWS Bedrock):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monthly spend total and service breakdown&lt;/li&gt;
&lt;li&gt;30-day daily trend&lt;/li&gt;
&lt;li&gt;List of idle resources&lt;/li&gt;
&lt;li&gt;Right-sizing candidates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI returns structured JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Right-size or consolidate Lightsail instances"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lightsail represents 42.9% of total spend..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"estimatedMonthlySavings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;45.36&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"effort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"medium"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cost per AI analysis: &lt;strong&gt;~$0.005&lt;/strong&gt; (half a cent) using Haiku.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;On my own account ($352/month), it found &lt;strong&gt;$135/month in savings&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consolidate Lightsail instances ($45/mo)&lt;/li&gt;
&lt;li&gt;Remove unused ELB ($20/mo)&lt;/li&gt;
&lt;li&gt;Audit WorkMail seats ($22/mo)&lt;/li&gt;
&lt;li&gt;Clean up VPC resources ($18/mo)&lt;/li&gt;
&lt;li&gt;Plus 6 more recommendations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's 38% waste. On an AWS consultant's own account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;.NET 10&lt;/strong&gt; — Razor Pages + Minimal API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQLite&lt;/strong&gt; — EF Core, lightweight and self-contained&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Bedrock&lt;/strong&gt; — Claude Haiku 4.5 for AI analysis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chart.js&lt;/strong&gt; — Cost trend visualizations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stripe&lt;/strong&gt; — Billing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The whole thing runs on a single Lightsail instance alongside 7 other .NET apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;Free tier available at &lt;a href="https://sharktoothproject.com" rel="noopener noreferrer"&gt;sharktoothproject.com&lt;/a&gt;. Pro ($19/mo) adds idle detection, right-sizing, and AI recommendations.&lt;/p&gt;

&lt;p&gt;If you manage AWS for clients, the Team tier ($39/mo) covers unlimited accounts.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by &lt;a href="https://natthompson.com" rel="noopener noreferrer"&gt;Nat Thompson&lt;/a&gt; at Obsidian River. Questions? Find me on LinkedIn or drop a comment below.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>ai</category>
      <category>dotnet</category>
    </item>
  </channel>
</rss>
