<?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: Bijay Singh deo</title>
    <description>The latest articles on Forem by Bijay Singh deo (@bijay_raj_singh_deo).</description>
    <link>https://forem.com/bijay_raj_singh_deo</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%2F3504187%2Fded9bcd1-5a83-4017-bb53-c412d3253c1e.png</url>
      <title>Forem: Bijay Singh deo</title>
      <link>https://forem.com/bijay_raj_singh_deo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/bijay_raj_singh_deo"/>
    <language>en</language>
    <item>
      <title>Taming S3 Costs: Automated Reports with Lambda, Python, Athena &amp; SES (because weekends are for coffee, not cost reports ☕)</title>
      <dc:creator>Bijay Singh deo</dc:creator>
      <pubDate>Tue, 16 Sep 2025 17:54:57 +0000</pubDate>
      <link>https://forem.com/bijay_raj_singh_deo/taming-s3-costs-automated-reports-with-lambda-python-athena-ses-because-weekends-are-for-5476</link>
      <guid>https://forem.com/bijay_raj_singh_deo/taming-s3-costs-automated-reports-with-lambda-python-athena-ses-because-weekends-are-for-5476</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Let’s be honest—S3 is cheap.... until it isn’t.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One fine day, I was staring at my AWS bill and realized S3 storage had quietly been eating away a chunk of it. With buckets spread across teams, projects, and experiments that nobody remembered, I had a problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;How do I figure out which buckets are costing me the most?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;And how do I make sure I get a report every Monday without manually pulling Athena queries (and pretending to be productive)?&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, like any lazy engineer who doesn’t want to do repetitive work, I decided to automate it.😎&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Inventory, Inventory, Inventory 📦
&lt;/h2&gt;

&lt;p&gt;First, I enabled S3 Inventory reports in Parquet format (JSON would’ve been too chatty). These reports contain all the details about objects, storage classes, and sizes. Perfect for Athena queries.&lt;/p&gt;

&lt;p&gt;Once enabled, the reports started landing in a dedicated S3 bucket. From there, I built multiple Athena tables pointing to these Parquet files—because SQL &amp;gt; endless scrolling through S3 console.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Writing Athena Queries Like a Detective 🕵️
&lt;/h2&gt;

&lt;p&gt;The idea was simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For each table (inventory per bucket/prefix), calculate storage size.&lt;/li&gt;
&lt;li&gt;Estimate cost based on storage class pricing.&lt;/li&gt;
&lt;li&gt;Find out the top 'N' offenders (buckets hogging most of the bill).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a flavour of one Athena query I used in my code to get cost, size (per storage class) of a bucket/table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH filtered_data AS (
    SELECT 
        storage_class, 
        intelligent_tiering_access_tier,
        SUM(size) AS total_size
    FROM 
        my_bucket_1
    WHERE 
        dt = '2025-08-19-01-00'
    GROUP BY 
        storage_class, intelligent_tiering_access_tier
)
SELECT 
    storage_class,
    intelligent_tiering_access_tier,
    total_size,
    CASE 
        WHEN storage_class = 'STANDARD' THEN total_size * 0.0210 / (1024 * 1024 * 1024)
        WHEN storage_class = 'GLACIER' THEN total_size * 0.0036 / (1024 * 1024 * 1024)
        WHEN storage_class = 'DEEP_ARCHIVE' THEN total_size * 0.00099 / (1024 * 1024 * 1024)
        ELSE 0
    END AS estimated_cost_usd
FROM 
    filtered_data;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically, I’m asking Athena:&lt;br&gt;
💡 “Hey buddy, tell me which storage class is silently burning my credits.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Lambda + Boto3 = Automation ❤️
&lt;/h2&gt;

&lt;p&gt;Now came the fun part—wrapping it up in a Lambda function so I don’t have to run queries by hand.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the Lambda does:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;For each S3 Inventory Athena table it runs a single SQL that returns per-prefix and per-storage-class (and intelligent-tier) metrics: object_count, total_size (bytes) and an estimated_cost_usd (I hard-coded the per-GB prices for STANDARD, GLACIER, DEEP_ARCHIVE and the intelligent-tier access tiers right in the query).&lt;/li&gt;
&lt;li&gt;It polls Athena (the classic get_query_execution loop with time.sleep(5)) until each query finishes, then fetches the CSV result written to S3.&lt;/li&gt;
&lt;li&gt;It aggregates the query rows into a Python structure keyed by (table, prefix) — summing object counts, bytes and the estimated cost, and keeping a breakdown list for each storage class / tier.&lt;/li&gt;
&lt;li&gt;Once all tables are processed the Lambda computes total cost per (table,prefix) and sorts descending to pick the top N (configurable, I used 15).&lt;/li&gt;
&lt;li&gt;It then builds a detailed CSV report with a parent summary row per ranked prefix (total cost, total size, total objects) followed by the breakdown rows for each storage class/tier (object_count, size_GB, cost_USD).&lt;/li&gt;
&lt;li&gt;The CSV is uploaded to S3 (so the report is versioned, shareable, and easy to inspect).&lt;/li&gt;
&lt;li&gt;Finally the Lambda sends a short SES email summary (top lines + sizes/costs) and includes the S3 path to the CSV — so every Monday morning (via EventBridge schedule) I get an actionable report I can forward to teams or use to trigger cleanups.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Check the full python code in my &lt;a href="https://github.com/cranky420/s3-cost-inspector" rel="noopener noreferrer"&gt;github repo&lt;/a&gt;, give it a shot.💪&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, instead of me digging into Athena on Mondays, I just sip coffee and open my inbox ☕📧.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Scheduling with EventBridge (aka my weekend butler)
&lt;/h2&gt;

&lt;p&gt;To make this truly hands-off, I scheduled the Lambda to run every Sunday midnight via EventBridge. That way, when I walk into Monday standup, I already know which buckets are the villains of the week.&lt;/p&gt;

&lt;p&gt;(And yes, this occasionally makes me look more prepared than I actually am. 😎)&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Report Look 📊
&lt;/h2&gt;

&lt;p&gt;The email I get looks something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Table: my_bucket_1 | Bucket: my-bucket-1 | Size: 12.3 TB | Cost: $257.23&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Table: my_bucket_2 | Bucket: my.bucket.2 | Size: 9.1 TB | Cost: $198.72&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;… and so on.&lt;/li&gt;
&lt;li&gt;Neat, simple, actionable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Visibility: S3 is easy to ignore, but costs add up fast.&lt;/li&gt;
&lt;li&gt;Automation: No manual queries, no “oops I forgot.”&lt;/li&gt;
&lt;li&gt;Cost Savings: Spot old projects, test buckets, or misconfigured storage classes early.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What started as a weekend experiment has now become my weekly cost sanity check.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;It’s not rocket science, but it saves $$$ and brain cycles.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you’re also haunted by “mystery buckets” on your AWS bill, give this approach a shot. You’ll thank yourself every Monday morning.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Have you tried something similar to tame your S3 costs? Or maybe you’ve found an even better way? Drop a comment—I’d love to learn (and maybe steal your idea 😅).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>serverless</category>
      <category>python</category>
    </item>
    <item>
      <title>Masking Sensitive Data in CloudWatch Logs for APIs (and keeping your secrets safe!)</title>
      <dc:creator>Bijay Singh deo</dc:creator>
      <pubDate>Tue, 16 Sep 2025 13:15:19 +0000</pubDate>
      <link>https://forem.com/bijay_raj_singh_deo/masking-sensitive-data-in-cloudwatch-logs-for-apis-and-keeping-your-secrets-safe-49dk</link>
      <guid>https://forem.com/bijay_raj_singh_deo/masking-sensitive-data-in-cloudwatch-logs-for-apis-and-keeping-your-secrets-safe-49dk</guid>
      <description>&lt;h2&gt;
  
  
  😬 The Problem
&lt;/h2&gt;

&lt;p&gt;So, picture this: you’ve built a shiny API that people love. Life is good, until one day you peek into your CloudWatch Logs and — BAM 💥 — staring right back at you are… user credentials. Passwords, tokens, maybe even PAN numbers. Not exactly the kind of surprise you want in your logs, especially when regulators (and your security team) are watching.&lt;/p&gt;

&lt;p&gt;As much as I love CloudWatch for debugging and monitoring, nobody wants to see their production logs looking like an open diary of sensitive user data. In fintech, that’s a big no-no (think PCI, GDPR, SOC2 nightmares).&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 The Mission
&lt;/h2&gt;

&lt;p&gt;My mission was clear: 👉 &lt;strong&gt;Mask sensitive data&lt;/strong&gt; (like passwords, tokens, card details) in CloudWatch Logs — while still keeping logs useful for troubleshooting.&lt;br&gt;
Bonus challenge: make the solution scalable, beginner-friendly, and fun.&lt;/p&gt;
&lt;h2&gt;
  
  
  🛠️ The Fix
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. First, a refresher
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/WhatIsCloudWatchLogs.html" rel="noopener noreferrer"&gt;What are CloudWatch Logs?&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
CloudWatch Logs is like your app’s black box recorder — every API call, error, and debug line can land here. - You can stream logs from API Gateway, Lambda, ECS, EC2, you name it. - You can search them in CloudWatch Insights or ship them to OpenSearch for SQL-style queries. - You can even set alarms when things go sideways.&lt;br&gt;
But — and it’s a big BUT — by default, CloudWatch logs whatever you give it. If you send passwords, it happily stores passwords. 🙈&lt;/p&gt;
&lt;h3&gt;
  
  
  2. My “Aha!” moment
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/cloudwatch-logs-data-protection-policies.html#what-are-data-protection-policies" rel="noopener noreferrer"&gt;CloudWatch Data Protection Policies&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Instead of writing messy regex scripts or re-engineering logging middleware, AWS now gives us Data Protection Policies for log groups.&lt;/p&gt;

&lt;p&gt;Think of it like a secret filter on your logs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You tell CloudWatch which sensitive data patterns to look for.&lt;/li&gt;
&lt;li&gt;It uses managed data identifiers (e.g., credit cards, AWS keys, emails, etc.).&lt;/li&gt;
&lt;li&gt;When a match is found → it’s automatically masked (like ****).&lt;/li&gt;
&lt;li&gt;Only users with the special logs:Unmask permission can see raw values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you create a data protection policy in the AWS Console, you’ll go through a few options:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Managed Data Identifiers&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS provides a long list of preconfigured data types (AWS keys, financial numbers, PII, PHI, etc.) you can select via checkboxes.&lt;/li&gt;
&lt;li&gt;You can also define custom data identifiers using regex if you need something AWS doesn’t provide.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Audit vs Mask (Deidentify)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Audit: Detect and record sensitive data findings without altering the logs. Great for discovery — you can see where secrets show up before deciding to mask.&lt;/li&gt;
&lt;li&gt;Deidentify (Mask): Actually redacts those values, so they show up as **** everywhere logs are consumed. This ensures secrets never persist unmasked in CloudWatch.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Findings Destination&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you choose Audit, you can direct audit findings to another log group, S3 bucket, or Kinesis Firehose. Useful for compliance reports.&lt;/li&gt;
&lt;li&gt;If you don’t specify, it can be left empty ({}), which means no special destination.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Apply Policy&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After you save the policy, only new logs ingested are scanned and masked. Old logs are not retroactively changed.&lt;/li&gt;
&lt;li&gt;Masking only works on Standard log class groups.&lt;/li&gt;
&lt;li&gt;Also, the DataIdentifier arrays in Audit and Deidentify statements must match exactly; otherwise AWS rejects the policy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a sample policy combining both audit and mask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`{
"Name": "data-protection-policy",
"Description": "",
"Version": "2021-06-01",
"Statement": [
{
"Sid": "audit-policy",
"DataIdentifier": [
"arn:aws:dataprotection::aws:data-identifier/AwsSecretKey",
"arn:aws:dataprotection::aws:data-identifier/BankAccountNumber-FR"
],
"Operation": {
"Audit": {
"FindingsDestination": {}
}
}
},
{
"Sid": "redact-policy",
"DataIdentifier": [
"arn:aws:dataprotection::aws:data-identifier/AwsSecretKey",
"arn:aws:dataprotection::aws:data-identifier/BankAccountNumber-FR"
],
"Operation": {
"Deidentify": {
"MaskConfig": {}
}
}
}
]
}`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In practice, I like to start with Audit only so I can measure where sensitive data actually shows up. Once I’m confident, I enable Deidentify to make sure those values are masked everywhere.&lt;/p&gt;

&lt;p&gt;Boom 💥 — no more plaintext credentials in logs!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using the logs after masking
Once logs are masked, you can still consume them as usual: - CloudWatch Logs Insights → run queries
like:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fields @timestamp, @message
  | filter @message like /ERROR/
  | sort @timestamp desc
  | limit 20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;OpenSearch SQL → even fancier! You can connect your log group to OpenSearch and query like:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT
    requestId,
    httpMethod,
    status,
    message
  FROM apigateway_logs
  WHERE status &amp;gt;= 500
  ORDER BY timestamp DESC
  LIMIT 10;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Masked fields still show up as **** , which means you keep debugging power without exposing secrets.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✅ The Outcome
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No more sensitive data (passwords, tokens, PAN) leaking into logs.&lt;/li&gt;
&lt;li&gt;Security and compliance team = happy campers.&lt;/li&gt;
&lt;li&gt;Developers still get useful logs for troubleshooting.&lt;/li&gt;
&lt;li&gt;And me? I sleep better at night knowing I won’t wake up to a compliance ticket.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  😂 Fun Takeaway
&lt;/h2&gt;

&lt;p&gt;Think of CloudWatch Data Protection like an automatic censor beep on live TV. Your logs may still be dramatic, but at least they’re PG-13 instead of Rated R for Regulatory Nightmares.&lt;/p&gt;

&lt;p&gt;So if you’re running a fintech API (or really any API), do yourself a favor — let CloudWatch keep the logs, but mask the secrets. After all, what happens in production logs should stay in production logs... safely masked.🥺&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What do you think? Have you tried CloudWatch Data Protection yet, or do you still rely on custom masking? I’d love to hear how you’re handling log hygiene in your stack!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudwatch</category>
      <category>devops</category>
      <category>security</category>
    </item>
  </channel>
</rss>
