<?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>DEV Community: Sreedan S Kumar</title>
    <description>The latest articles on DEV Community by Sreedan S Kumar (@sreedan_skumar_5b2eac68a).</description>
    <link>https://dev.to/sreedan_skumar_5b2eac68a</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4050675%2F75a5d56d-4728-4802-947b-247842e5444a.JPG</url>
      <title>DEV Community: Sreedan S Kumar</title>
      <link>https://dev.to/sreedan_skumar_5b2eac68a</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sreedan_skumar_5b2eac68a"/>
    <language>en</language>
    <item>
      <title>💸 The "Zero-Bill" Cloud: Routing AWS Cost Alerts to Discord with Lambda and SNS</title>
      <dc:creator>Sreedan S Kumar</dc:creator>
      <pubDate>Wed, 29 Jul 2026 12:24:57 +0000</pubDate>
      <link>https://dev.to/sreedan_skumar_5b2eac68a/the-zero-bill-cloud-routing-aws-cost-alerts-to-discord-with-lambda-and-sns-3072</link>
      <guid>https://dev.to/sreedan_skumar_5b2eac68a/the-zero-bill-cloud-routing-aws-cost-alerts-to-discord-with-lambda-and-sns-3072</guid>
      <description>&lt;p&gt;🚨 &lt;strong&gt;The Problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every cloud computing enthusiast shares the exact same fear: accidentally leaving a resource running and waking up to a massive, unexpected credit card bill.&lt;/p&gt;

&lt;p&gt;When collaborating with teammates on new MVPs or spinning up hackathon backends, standard email budget alerts just aren't enough. They easily get buried in spam or ignored in a cluttered inbox. I see beginners hesitate to learn AWS purely out of financial fear. If a cloud bill is spiking, you need to know immediately, right where you already hang out.&lt;/p&gt;

&lt;p&gt;This post demonstrates how to build a "Zero-Bill" alert system that monitors your AWS account and instantly fires a notification directly to a Discord server the moment your projected spend crosses $1.00.&lt;/p&gt;

&lt;p&gt;🏗️ &lt;strong&gt;Architecture Overview&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6j1y8ce4d4jjt3inid8u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6j1y8ce4d4jjt3inid8u.png" alt="Architecture" width="618" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is how the data flows:&lt;br&gt;
1.AWS Budgets: Monitors your account spend in real-time.&lt;br&gt;
2.Amazon SNS (Simple Notification Service): Acts as the pub/sub messenger between Budgets and Lambda.&lt;br&gt;
3.AWS Lambda: A lightweight Python function that formats the alert and sends it out.&lt;br&gt;
4.Discord Webhook: The endpoint that receives the message and posts it to your server.&lt;br&gt;
🛠️ Prerequisites and IAM&lt;br&gt;
Before building, ensure you have:&lt;/p&gt;

&lt;p&gt;• An active AWS Account.&lt;br&gt;
• A Discord Server where you have permission to create Webhooks.&lt;br&gt;
• The IAM Policy: Your SNS topic must explicitly allow AWS Budgets to publish to it. When editing your default SNS access policy, you must append this exact statement to the Statement array:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;JSON&lt;/em&gt;&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;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AllowBudgetsToPublish"&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;"Service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"budgets.amazonaws.com"&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;"SNS:Publish"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&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:sns:YOUR_REGION:YOUR_ACCOUNT_ID:Zero-Bill-Alerts"&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;(Remember to swap in your actual Region and Account ID!)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Create the Discord Webhook&lt;/p&gt;

&lt;p&gt;First, we need a destination for the alerts.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Open your Discord Server and navigate to Settings &amp;gt; Integrations &amp;gt; Webhooks.&lt;/li&gt;
&lt;li&gt; Click New Webhook, name it AWS Billing Bot, and select your private monitoring channel.&lt;/li&gt;
&lt;li&gt; Click Copy Webhook URL and save this securely.
⚠️ Security Warning: Never commit this URL to a public GitHub repository! If leaked, anyone can spam your Discord server.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F26gn5c8p7k8re8zc79gc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F26gn5c8p7k8re8zc79gc.png" alt="Interface of webhook" width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;: Set up the SNS Topic&lt;/p&gt;

&lt;p&gt;Amazon SNS acts as the bridge.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Go to the AWS SNS Console and create a Standard topic named Zero-Bill-Alerts.&lt;/li&gt;
&lt;li&gt; Edit the Topic's Access Policy. Ensure you append the Budgets permission JSON (from the prerequisites) to the existing default policy list, rather than overwriting it entirely.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Write the Lambda Function&lt;/p&gt;

&lt;p&gt;We need a tiny Python script to catch the SNS message and forward it to Discord.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Go to the AWS Lambda Console and create a new Python 3.12 (or 3.13) function.&lt;/li&gt;
&lt;li&gt; Under Configuration -&amp;gt; Environment variables, add a key called DISCORD_WEBHOOK_URL and paste your copied URL as the value.&lt;/li&gt;
&lt;li&gt; Paste the following code into the lambda_function.py file:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Python&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;webhook_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DISCORD_WEBHOOK_URL&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Extract the message from the SNS event
&lt;/span&gt;    &lt;span class="n"&gt;sns_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Records&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Sns&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Format the message for Discord
&lt;/span&gt;    &lt;span class="n"&gt;discord_payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS Billing Bot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;avatar_url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://a0.awsstatic.com/libra-css/images/logos/aws_logo_smile_1200x630.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🚨 **AWS BUDGET ALERT** 🚨&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;```
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;endraw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sns_message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
```&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Send the request
&lt;/span&gt;    &lt;span class="n"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PoolManager&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;webhook_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;discord_payload&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Message sent to Discord&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt; Click Deploy. Then, click Add Trigger, select SNS, and choose the Zero-Bill-Alerts topic.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpdl71gm3ta9ne272ar40.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpdl71gm3ta9ne272ar40.png" alt="Lambda Diagram Flow" width="764" height="236"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; Create the AWS Budget&lt;br&gt;
Now, we wire it all together by creating the actual financial tripwire.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Navigate to the AWS Billing Dashboard and select Budgets.&lt;/li&gt;
&lt;li&gt; Create a Cost budget and set the budgeted amount to $1.00.&lt;/li&gt;
&lt;li&gt; In the alert configuration, set it to trigger when Forecasted costs reach 100% of the budget.&lt;/li&gt;
&lt;li&gt; Under the notification settings, enter the ARN (Amazon Resource Name) of your Zero-Bill-Alerts SNS topic.&lt;/li&gt;
&lt;li&gt; Test it by, using the push notification, provide a message and scroll down to click the push notification button, go back to your discord server and verify whether the application is working.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6kit93fvjfzzxzrqc7gd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6kit93fvjfzzxzrqc7gd.png" alt="Output Image" width="800" height="126"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🧱 The "Gotchas" (Lessons Learned)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While building this, I hit a few roadblocks that aren't explicitly covered in the standard AWS documentation.&lt;/p&gt;

&lt;p&gt;•&lt;strong&gt;&lt;em&gt;The SNS InvalidParameter Error&lt;/em&gt;&lt;/strong&gt;: When attaching the IAM policy to the SNS topic, you might get this error: InvalidParameter: Policy Error: null. This happens if your JSON syntax is missing its wrappers or if you overwrite the default SNS policy completely. You must add the Budget permissions to the existing default Statement array, separating them with a comma.&lt;/p&gt;

&lt;p&gt;•&lt;strong&gt;&lt;em&gt;The urllib3 vs. requests Trap&lt;/em&gt;&lt;/strong&gt;: Many tutorials tell you to use the requests library in Python. However, requests is not built into the standard AWS Lambda Python runtime. If you use it, your code will crash unless you manually upload a custom Lambda Layer. By using urllib3, which is included natively, the script runs instantly with zero extra configuration.&lt;/p&gt;

&lt;p&gt;🧹 &lt;strong&gt;Resource Cleanup (FinOps)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This architecture is entirely serverless and easily fits within the AWS Free Tier. However, to maintain good cloud hygiene and ensure you don't leave orphaned resources behind, I highly recommend deleting the Budget, Lambda function, and SNS topic once you verify the architecture works.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>finops</category>
      <category>serverless</category>
      <category>python</category>
    </item>
  </channel>
</rss>
