<?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: Abhijeet Yadav</title>
    <description>The latest articles on DEV Community by Abhijeet Yadav (@shree_abhijeet).</description>
    <link>https://dev.to/shree_abhijeet</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%2F3023460%2F398a8ec3-84c7-44c4-9025-99bf2c19e698.jpg</url>
      <title>DEV Community: Abhijeet Yadav</title>
      <link>https://dev.to/shree_abhijeet</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shree_abhijeet"/>
    <language>en</language>
    <item>
      <title>Infrasheet Automation</title>
      <dc:creator>Abhijeet Yadav</dc:creator>
      <pubDate>Thu, 29 Jan 2026 11:08:22 +0000</pubDate>
      <link>https://dev.to/shree_abhijeet/infrasheet-automation-4edc</link>
      <guid>https://dev.to/shree_abhijeet/infrasheet-automation-4edc</guid>
      <description>&lt;h2&gt;
  
  
  I Stopped Copy-Pasting AWS Console Data and Let Lambda Do It for Me
&lt;/h2&gt;

&lt;p&gt;It did not start as an automation project.&lt;/p&gt;

&lt;p&gt;It started with one simple request.&lt;br&gt;
“Can you share the VPC and subnet details?”&lt;/p&gt;

&lt;p&gt;I opened the AWS console. Clicked into VPCs. Checked subnets. Copied a few values. Dropped them into a spreadsheet. Done.&lt;/p&gt;

&lt;p&gt;No complaints. No resistance.&lt;/p&gt;

&lt;p&gt;Then the same request came again.&lt;br&gt;
Then it came with a deadline.&lt;br&gt;
Then it came when I was already busy with something else.&lt;/p&gt;

&lt;p&gt;That is usually how these things begin.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Moment Manual Work Starts Feeling Heavy
&lt;/h2&gt;

&lt;p&gt;At first, the AWS console feels friendly. Everything is visual. Filters work. Lists make sense.&lt;/p&gt;

&lt;p&gt;But as the environment grows, the console starts telling a different story.&lt;/p&gt;

&lt;p&gt;More VPCs.&lt;br&gt;
More subnets.&lt;br&gt;
More availability zones.&lt;br&gt;
More scrolling.&lt;/p&gt;

&lt;p&gt;You stop trusting your eyes.&lt;br&gt;
You double check values.&lt;br&gt;
You still miss things.&lt;/p&gt;

&lt;p&gt;That is when a simple task turns into fragile work.&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.amazonaws.com%2Fuploads%2Farticles%2Fh296vf5jyjn71w4qbj5l.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.amazonaws.com%2Fuploads%2Farticles%2Fh296vf5jyjn71w4qbj5l.png" alt=" " width="800" height="211"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, everything still looks manageable.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Repetition Is the Real Problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The problem was not difficulty.&lt;br&gt;
The problem was repetition.&lt;/p&gt;

&lt;p&gt;Every report followed the same pattern.&lt;br&gt;
Open console.&lt;br&gt;
Navigate.&lt;br&gt;
Filter.&lt;br&gt;
Copy.&lt;br&gt;
Paste.&lt;br&gt;
Format.&lt;/p&gt;

&lt;p&gt;The output depended entirely on how careful someone was that day.&lt;/p&gt;

&lt;p&gt;That is not a system. That is luck.&lt;/p&gt;

&lt;p&gt;And engineers do not like relying on luck.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Choosing a Small Entry Point&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I did not try to automate everything.&lt;/p&gt;

&lt;p&gt;No EC2.&lt;br&gt;
No IAM.&lt;br&gt;
No billing.&lt;/p&gt;

&lt;p&gt;Just VPCs and Subnets.&lt;/p&gt;

&lt;p&gt;They are foundational. If networking data is wrong, everything above it becomes questionable. It was the safest place to start.&lt;/p&gt;

&lt;p&gt;The goal became clear.&lt;/p&gt;

&lt;p&gt;Stop asking humans.&lt;br&gt;
Ask AWS directly.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Before Code, the Flow Was Already Clear&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Even before writing the function, the flow made sense in my head.&lt;/p&gt;

&lt;p&gt;Lambda would run the logic.&lt;br&gt;
Python would talk to AWS.&lt;br&gt;
The data would be structured properly.&lt;br&gt;
Excel would be the output because everyone understands Excel.&lt;br&gt;
S3 would store the result so it is always available.&lt;/p&gt;

&lt;p&gt;Simple. Linear. Predictable.&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.amazonaws.com%2Fuploads%2Farticles%2Fnuyvehjhau0ay6g2jnct.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.amazonaws.com%2Fuploads%2Farticles%2Fnuyvehjhau0ay6g2jnct.png" alt=" " width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Permissions Decide Everything&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before the function could do anything useful, IAM had to be right.&lt;/p&gt;

&lt;p&gt;Lambda needed to describe VPCs.&lt;br&gt;
It needed to describe subnets.&lt;br&gt;
It needed to upload a file to S3.&lt;/p&gt;

&lt;p&gt;Nothing more.&lt;/p&gt;

&lt;p&gt;Keeping permissions minimal saved time later and avoided unnecessary debugging.&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.amazonaws.com%2Fuploads%2Farticles%2Fg2qgmtzo2f0v9a33rqow.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.amazonaws.com%2Fuploads%2Farticles%2Fg2qgmtzo2f0v9a33rqow.png" alt=" " width="800" height="293"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This shows the exact permissions that make the automation work.&lt;/p&gt;

&lt;p&gt;The First Real Friction Point&lt;/p&gt;

&lt;p&gt;Then came Pandas.&lt;/p&gt;

&lt;p&gt;Lambda does not ship with Pandas or OpenPyXL. That meant building a custom Lambda Layer.&lt;/p&gt;

&lt;p&gt;I built it locally with only what I needed. No extra libraries. No bloated packages. Just Pandas and OpenPyXL, aligned with Python 3.11.&lt;/p&gt;

&lt;p&gt;This step mattered more than expected.&lt;/p&gt;

&lt;p&gt;A clean layer meant fewer surprises later.&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.amazonaws.com%2Fuploads%2Farticles%2F5rquoizf5ylbjya6cm5v.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.amazonaws.com%2Fuploads%2Farticles%2F5rquoizf5ylbjya6cm5v.png" alt=" " width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When Defaults Quietly Fail You&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The first execution failed.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Not because of bad logic.&lt;br&gt;
Because of default limits.&lt;br&gt;
Pandas needs memory.&lt;/p&gt;

&lt;p&gt;Excel generation needs time.&lt;/p&gt;

&lt;p&gt;Once memory was increased to 512 MB and timeout was adjusted, the function behaved perfectly.&lt;/p&gt;

&lt;p&gt;Serverless does not mean resource-less.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;Lambda Function Setup&lt;/u&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RuntimePython 3.11&lt;/li&gt;
&lt;li&gt;Memory512 MB or higher&lt;/li&gt;
&lt;li&gt;Timeout 1 5 mins 0 seconds&lt;/li&gt;
&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2Ftpb4bwe5kjrzreoqsm3w.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.amazonaws.com%2Fuploads%2Farticles%2Ftpb4bwe5kjrzreoqsm3w.png" alt=" " width="800" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Boto3 fetched VPCs.&lt;br&gt;
Boto3 fetched subnets.&lt;br&gt;
Pandas shaped the data into rows and columns.&lt;br&gt;
OpenPyXL created two sheets in a single Excel file.&lt;/p&gt;

&lt;p&gt;Everything stayed in memory. No temp files. No disk usage.&lt;/p&gt;

&lt;p&gt;The function did one thing and did it cleanly.&lt;/p&gt;

&lt;p&gt;import boto3&lt;br&gt;
import pandas as pd&lt;br&gt;
from io import BytesIO&lt;br&gt;
import time&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.amazonaws.com%2Fuploads%2Farticles%2Fcx3t28xcixg7nsmtv2vh.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.amazonaws.com%2Fuploads%2Farticles%2Fcx3t28xcixg7nsmtv2vh.png" alt=" " width="432" height="131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; boto3 connects to AWS APIs. &lt;/li&gt;
&lt;li&gt; pandas structures tabular data. &lt;/li&gt;
&lt;li&gt; BytesIO handles Excel creation in-memory. &lt;/li&gt;
&lt;li&gt; time is used for tracking execution duration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;def lambda_handler(event, context):&lt;br&gt;
start = time.time()&lt;br&gt;
print("Lambda started")&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.amazonaws.com%2Fuploads%2Farticles%2F6wqsfmks20tk5wz55w6i.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.amazonaws.com%2Fuploads%2Farticles%2F6wqsfmks20tk5wz55w6i.png" alt=" " width="448" height="86"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Logs the start of execution and records time.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ec2 = boto3.client('ec2')&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.amazonaws.com%2Fuploads%2Farticles%2Fhlammw3usktp85cqj97t.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.amazonaws.com%2Fuploads%2Farticles%2Fhlammw3usktp85cqj97t.png" alt=" " width="392" height="46"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Establishes a client to interact with EC2 services.&lt;/p&gt;

&lt;p&gt;vpcs = ec2.describe_vpcs()['Vpcs']&lt;br&gt;
print(f"Fetched {len(vpcs)} VPCs")&lt;/p&gt;

&lt;p&gt;Retrieves all VPC metadata from the AWS account.&lt;/p&gt;

&lt;p&gt;Vpc_data = [{&lt;br&gt;
'VPC ID': vpc['VpcId'],&lt;br&gt;
'CIDR Block': vpc['CidrBlock'],&lt;br&gt;
'State': vpc['State'],&lt;br&gt;
'IsDefault': vpc.get('IsDefault', False)&lt;br&gt;
} for vpc in vpcs]&lt;/p&gt;

&lt;p&gt;Extracts required information from the VPCs into a list of dictionaries.&lt;/p&gt;

&lt;p&gt;subnets = ec2.describe_subnets()['Subnets']&lt;br&gt;
print(f"Fetched {len(subnets)} Subnets")&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Retrieves all subnet data.&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.amazonaws.com%2Fuploads%2Farticles%2Fb0em68ufkbwl4u7qn7h9.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.amazonaws.com%2Fuploads%2Farticles%2Fb0em68ufkbwl4u7qn7h9.png" alt=" " width="555" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;subnet_data = [{&lt;br&gt;
'Subnet ID': subnet['SubnetId'],&lt;br&gt;
'VPC ID': subnet['VpcId'],&lt;br&gt;
'CIDR Block': subnet['CidrBlock'],&lt;br&gt;
'Availability Zone': subnet['AvailabilityZone'],&lt;br&gt;
'State': subnet['State']&lt;br&gt;
} for subnet in subnets]&lt;/p&gt;

&lt;p&gt;Gathers details like subnet ID, VPC association, AZ, etc.&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.amazonaws.com%2Fuploads%2Farticles%2Fg2qm5er5b0ol7lo98v4l.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.amazonaws.com%2Fuploads%2Farticles%2Fg2qm5er5b0ol7lo98v4l.png" alt=" " width="551" height="231"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Creates Excel sheets.&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.amazonaws.com%2Fuploads%2Farticles%2Fc1s0y094n2py8po98y5e.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.amazonaws.com%2Fuploads%2Farticles%2Fc1s0y094n2py8po98y5e.png" alt=" " width="768" height="155"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finalizes and prepares Excel file for upload.&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.amazonaws.com%2Fuploads%2Farticles%2Fdvgewkoqqhlbqx9gr2tl.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.amazonaws.com%2Fuploads%2Farticles%2Fdvgewkoqqhlbqx9gr2tl.png" alt=" " width="550" height="146"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Uploads the Excel to S3.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;duration = round(time.time() - start, 2)&lt;br&gt;
print(f"Total Execution Time: {duration} seconds")&lt;br&gt;
return {&lt;br&gt;
'statusCode': 200,&lt;br&gt;
'body': f'Report uploaded to s3://{bucket_name}/{file_name} in {duration}&lt;br&gt;
seconds.' }&lt;/p&gt;

&lt;p&gt;The Classic AWS Reminder&lt;/p&gt;

&lt;p&gt;The next failure was quick.&lt;/p&gt;

&lt;p&gt;Access denied.&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.amazonaws.com%2Fuploads%2Farticles%2F4c4zoqejvezkd6xbujze.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.amazonaws.com%2Fuploads%2Farticles%2F4c4zoqejvezkd6xbujze.png" alt=" " width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The IAM role was missing permission to upload objects to S3. One small update fixed it immediately.&lt;/p&gt;

&lt;p&gt;AWS errors are rarely mysterious. They are usually precise.&lt;/p&gt;

&lt;p&gt;The First Successful Run&lt;/p&gt;

&lt;p&gt;The execution finally completed.&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.amazonaws.com%2Fuploads%2Farticles%2F3skrw1u08jgjazjy48ei.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.amazonaws.com%2Fuploads%2Farticles%2F3skrw1u08jgjazjy48ei.png" alt=" " width="800" height="162"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Status showed “Succeeded”.&lt;br&gt;
Logs confirmed the runtime.&lt;br&gt;
S3 showed a new Excel file.&lt;/p&gt;

&lt;p&gt;No console clicking.&lt;br&gt;
No copy paste.&lt;br&gt;
No manual formatting.&lt;/p&gt;

&lt;p&gt;The system worked.&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.amazonaws.com%2Fuploads%2Farticles%2F20u93sknpvw1zi6bg6og.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.amazonaws.com%2Fuploads%2Farticles%2F20u93sknpvw1zi6bg6og.png" alt=" " width="800" height="362"&gt;&lt;/a&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.amazonaws.com%2Fuploads%2Farticles%2Fr79fafea6ow4wbcnc1cj.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.amazonaws.com%2Fuploads%2Farticles%2Fr79fafea6ow4wbcnc1cj.png" alt=" " width="800" height="210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Opening the Report Felt Different&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Excel file had Different Services sheets showing all the resources.&lt;/p&gt;

&lt;p&gt;VPCs, Subnets,EC2-servers, RDS, Volumes and all whichever was mentioned over Lambda Function.&lt;/p&gt;

&lt;p&gt;Clean columns.&lt;br&gt;
Accurate data.&lt;br&gt;
Live infrastructure information.&lt;/p&gt;

&lt;p&gt;This was no longer a document created by a human. It was a report generated by the system itself.&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.amazonaws.com%2Fuploads%2Farticles%2F9pf58rgnlz0q3govuh0u.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.amazonaws.com%2Fuploads%2Farticles%2F9pf58rgnlz0q3govuh0u.png" alt=" " width="800" height="285"&gt;&lt;/a&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.amazonaws.com%2Fuploads%2Farticles%2F1nbfgmnqdtgg9m7f92v2.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.amazonaws.com%2Fuploads%2Farticles%2F1nbfgmnqdtgg9m7f92v2.png" alt=" " width="800" height="444"&gt;&lt;/a&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.amazonaws.com%2Fuploads%2Farticles%2F6gm1hmdcpqzhq9th325q.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.amazonaws.com%2Fuploads%2Farticles%2F6gm1hmdcpqzhq9th325q.png" alt=" " width="800" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This shows the real value of the automation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What This Quietly Became&lt;/p&gt;

&lt;p&gt;This started as a small fix for a repeating task.&lt;/p&gt;

&lt;p&gt;It turned into a reusable reporting foundation.&lt;/p&gt;

&lt;p&gt;Today, the same pattern can be extended easily. EC2 inventories. IAM users. Security groups. Cost summaries.&lt;/p&gt;

&lt;p&gt;Once the pipeline exists, expansion feels natural.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AWS already knows everything about your infrastructure.&lt;/p&gt;

&lt;p&gt;The real work is asking the right questions and shaping the answers into something humans can trust.&lt;/p&gt;

&lt;p&gt;This automation does exactly that.&lt;/p&gt;

&lt;p&gt;Quietly. Reliably. Every time it runs.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If needed Full Lambda Code, just drop a comment i will share the IAM role (JSON format) and the github link for automation Code.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>automation</category>
      <category>python</category>
    </item>
    <item>
      <title>Incident Memory System Building a Self-Learning, Self-Healing AWS Operations Engine</title>
      <dc:creator>Abhijeet Yadav</dc:creator>
      <pubDate>Fri, 16 Jan 2026 11:51:49 +0000</pubDate>
      <link>https://dev.to/shree_abhijeet/incident-memory-system-building-a-self-learning-self-healing-aws-operations-engine-4b44</link>
      <guid>https://dev.to/shree_abhijeet/incident-memory-system-building-a-self-learning-self-healing-aws-operations-engine-4b44</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;How We Built a Self-Learning, Self-Healing AWS Operations Engine from Real Production Failures&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Many cloud environments today are thoroughly monitored.&lt;/p&gt;

&lt;p&gt;Alarms trigger. Dashboards display warnings. Notifications pop up in Slack.&lt;/p&gt;

&lt;p&gt;Yet, when an issue arises at 3:17 AM, engineers still find themselves asking:&lt;/p&gt;

&lt;p&gt;“Has this happened before… and how did we resolve it previously?”&lt;/p&gt;

&lt;p&gt;The real source of operational distress lies not in insufficient monitoring, but in this gap.&lt;/p&gt;

&lt;p&gt;In this blog, I aim to share how we designed and implemented an Incident Memory System on AWS — not as a theoretical concept, but as a fully operational system, created by intentionally disrupting production-like infrastructure and compelling it to restore itself.&lt;/p&gt;

&lt;p&gt;This is not a tutorial.&lt;/p&gt;

&lt;p&gt;This is not a PoC based on ideal scenarios.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is a real exercise.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Architecture: Designing an Incident Memory System That Can Actually Learn&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before automation or self-healing can exist, responsibilities must be clearly defined.&lt;/p&gt;

&lt;p&gt;We did not start by choosing services.&lt;br&gt;
We started by defining roles.&lt;/p&gt;

&lt;p&gt;At a high level, the system needed to do five things reliably:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Detect a real failure&lt;/li&gt;
&lt;li&gt;Decide whether the failure is significant&lt;/li&gt;
&lt;li&gt;Record the incident in a way the system can remember&lt;/li&gt;
&lt;li&gt;Apply a known recovery action&lt;/li&gt;
&lt;li&gt;Track whether the recovery actually worked&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Only after these responsibilities were clear did we map them to AWS services.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Logical Architecture Breakdown&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The Incident Memory System was designed as a pipeline, not a monolith.&lt;br&gt;
Each stage has a single responsibility.&lt;/p&gt;

&lt;p&gt;Detection Layer&lt;br&gt;
CloudWatch alarms are responsible only for answering one question:&lt;br&gt;
Is something broken right now?&lt;/p&gt;

&lt;p&gt;Event Routing Layer&lt;br&gt;
EventBridge is responsible for routing only meaningful state changes.&lt;br&gt;
It does not execute logic. It only forwards signals.&lt;/p&gt;

&lt;p&gt;Memory Layer&lt;br&gt;
DynamoDB acts as the system’s long-term memory.&lt;br&gt;
Every incident is stored with its symptom, timestamp, and resolution state.&lt;/p&gt;

&lt;p&gt;Execution Layer&lt;br&gt;
Lambda functions execute predefined actions.&lt;br&gt;
They do not diagnose. They do not guess.&lt;/p&gt;

&lt;p&gt;Control Plane Execution&lt;br&gt;
AWS Systems Manager performs the actual recovery on the instance.&lt;/p&gt;

&lt;p&gt;This separation was intentional.&lt;br&gt;
If any layer fails, the system degrades safely instead of silently pretending to heal.&lt;/p&gt;

&lt;p&gt;High-level responsibility flow of the Incident Memory System&lt;/p&gt;
&lt;/blockquote&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.amazonaws.com%2Fuploads%2Farticles%2Ficm53vu3fezyvh46gknd.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.amazonaws.com%2Fuploads%2Farticles%2Ficm53vu3fezyvh46gknd.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This diagram shows how detection, memory, and recovery are decoupled to avoid tight coupling.&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.amazonaws.com%2Fuploads%2Farticles%2F79icvddqgohigrhh7cr0.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.amazonaws.com%2Fuploads%2Farticles%2F79icvddqgohigrhh7cr0.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Launching the Compute Layer
&lt;/h2&gt;

&lt;p&gt;The foundation of this system starts with a single Linux EC2 instance.&lt;/p&gt;

&lt;p&gt;Nothing special was chosen here.&lt;br&gt;
No autoscaling.&lt;br&gt;
No containers.&lt;br&gt;
No managed service.&lt;/p&gt;

&lt;p&gt;The goal was to begin with the most common real-world backend setup: a VM running a web service.&lt;/p&gt;

&lt;p&gt;After launching the instance, I connected to it using EC2 Instance Connect and verified basic OS access.&lt;/p&gt;

&lt;p&gt;At this point, the instance was empty.&lt;br&gt;
No application.&lt;br&gt;
No web server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing and Verifying the Web Service
&lt;/h2&gt;

&lt;p&gt;I chose nginx for one reason only: predictability.&lt;/p&gt;

&lt;p&gt;nginx is simple, stable, and widely used in production environments.&lt;br&gt;
If something breaks here, it reflects a real operational failure, not a framework issue.&lt;/p&gt;

&lt;p&gt;The installation was done directly from the package manager.&lt;/p&gt;

&lt;p&gt;Once installed, I verified:&lt;/p&gt;

&lt;p&gt;nginx binaries were present&lt;/p&gt;

&lt;p&gt;the service could start&lt;/p&gt;

&lt;p&gt;the default page rendered correctly&lt;/p&gt;

&lt;p&gt;This confirmed the backend was operational before introducing any AWS-level complexity.&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.amazonaws.com%2Fuploads%2Farticles%2F7et3eqn9p8kxmamlm3pa.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.amazonaws.com%2Fuploads%2Farticles%2F7et3eqn9p8kxmamlm3pa.png" alt=" " width="400" height="147"&gt;&lt;/a&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.amazonaws.com%2Fuploads%2Farticles%2Fp1b4gkl731ki8590upcv.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.amazonaws.com%2Fuploads%2Farticles%2Fp1b4gkl731ki8590upcv.png" alt=" " width="800" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Placing an Application Load Balancer in Front
&lt;/h2&gt;

&lt;p&gt;With a working backend, the next step was to expose it properly.&lt;/p&gt;

&lt;p&gt;An Application Load Balancer was created with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An HTTP listener on port 80&lt;/li&gt;
&lt;li&gt;A target group pointing to the EC2 instance&lt;/li&gt;
&lt;li&gt;Health checks configured on the root path&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This step is critical.&lt;/p&gt;

&lt;p&gt;Most production failures do not happen on the instance itself.&lt;br&gt;
They are observed at the load balancer layer.&lt;/p&gt;

&lt;p&gt;So the system had to fail where users actually feel it.&lt;/p&gt;

&lt;p&gt;Once the target group showed the instance as healthy, traffic through the ALB was tested and verified.&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.amazonaws.com%2Fuploads%2Farticles%2F6j4ytewdo5u5fuxqnjv2.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.amazonaws.com%2Fuploads%2Farticles%2F6j4ytewdo5u5fuxqnjv2.png" alt=" " width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Opening the Network Path Correctly&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before generating traffic or failures, I validated the network layer.&lt;/p&gt;

&lt;p&gt;Security group rules were adjusted to allow:&lt;/p&gt;

&lt;p&gt;HTTP traffic on port 80 from the internet&lt;/p&gt;

&lt;p&gt;ALB to instance communication&lt;/p&gt;

&lt;p&gt;This might look trivial, but misconfigured security groups are one of the most common root causes of silent failures.&lt;/p&gt;

&lt;p&gt;Only after confirming the network path was correct did I move forward.&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.amazonaws.com%2Fuploads%2Farticles%2Fy2eymur3tyu9xklhzh0c.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.amazonaws.com%2Fuploads%2Farticles%2Fy2eymur3tyu9xklhzh0c.png" alt=" " width="800" height="157"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Generating Controlled Traffic&lt;/p&gt;

&lt;p&gt;Before breaking anything, I wanted to understand baseline behavior.&lt;/p&gt;

&lt;p&gt;Traffic was generated manually from the instance using curl loops and ApacheBench.&lt;/p&gt;

&lt;p&gt;This served two purposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirm the ALB was routing traffic correctly&lt;/li&gt;
&lt;li&gt;Establish a normal performance baseline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requests succeeded&lt;/li&gt;
&lt;li&gt;No errors were generated&lt;/li&gt;
&lt;li&gt;The system was stable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This baseline is important because later failures can be compared against it.&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.amazonaws.com%2Fuploads%2Farticles%2Fy57go3t38bxmb5kqjo12.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.amazonaws.com%2Fuploads%2Farticles%2Fy57go3t38bxmb5kqjo12.png" alt=" " width="800" height="197"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;## Introducing Failure Intentionally&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With everything working, I introduced failure deliberately.&lt;/p&gt;

&lt;p&gt;A small shell script was created that repeatedly stopped and restarted nginx.&lt;/p&gt;

&lt;p&gt;This was not random chaos.&lt;br&gt;
This was controlled instability.&lt;/p&gt;

&lt;p&gt;The script simulated a backend service that flaps under load or crashes intermittently.&lt;/p&gt;

&lt;p&gt;As expected:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ALB health checks began failing&lt;/li&gt;
&lt;li&gt;Users started receiving 502 Bad Gateway responses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was the exact failure pattern I wanted to detect and respond to.&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.amazonaws.com%2Fuploads%2Farticles%2Fx9a47uniwdsjpx3192br.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.amazonaws.com%2Fuploads%2Farticles%2Fx9a47uniwdsjpx3192br.png" alt=" " width="800" height="150"&gt;&lt;/a&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.amazonaws.com%2Fuploads%2Farticles%2Feqondw9ssvrgmcpvnytu.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.amazonaws.com%2Fuploads%2Farticles%2Feqondw9ssvrgmcpvnytu.png" alt=" " width="800" height="261"&gt;&lt;/a&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.amazonaws.com%2Fuploads%2Farticles%2Fgvu67c4txqaz7ddpez4e.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.amazonaws.com%2Fuploads%2Farticles%2Fgvu67c4txqaz7ddpez4e.png" alt=" " width="800" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Detecting the Failure Using CloudWatch
&lt;/h2&gt;

&lt;p&gt;Only after the failure existed did monitoring come into play.&lt;/p&gt;

&lt;p&gt;A CloudWatch alarm was created on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Namespace: AWS/ApplicationELB&lt;/li&gt;
&lt;li&gt;Metric: HTTPCode_ELB_5XX_Count&lt;/li&gt;
&lt;li&gt;Threshold: greater than or equal to 1 within 1 minute&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This alarm did one thing only.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It confirmed that the system could see the failure.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No automation yet.&lt;br&gt;
No recovery logic.&lt;/p&gt;

&lt;p&gt;Just detection.&lt;/p&gt;

&lt;p&gt;Once the backend instability continued, the alarm transitioned into ALARM state.&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.amazonaws.com%2Fuploads%2Farticles%2Fj586suegmjzo5s3bpjk9.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.amazonaws.com%2Fuploads%2Farticles%2Fj586suegmjzo5s3bpjk9.png" alt=" " width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing the Incident Collector Lambda
&lt;/h2&gt;

&lt;p&gt;Once the alarm could emit an event, something had to consume it.&lt;/p&gt;

&lt;p&gt;This responsibility belonged to a Lambda function I called the incident collector.&lt;/p&gt;

&lt;p&gt;The purpose of this function was deliberately limited.&lt;/p&gt;

&lt;p&gt;It did not fix anything.&lt;br&gt;
It did not analyze logs.&lt;br&gt;
It did not make decisions.&lt;/p&gt;

&lt;p&gt;Its only responsibility was to record the incident.&lt;/p&gt;

&lt;p&gt;When the EventBridge rule fired, the Lambda extracted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Alarm name&lt;/li&gt;
&lt;li&gt;Timestamp&lt;/li&gt;
&lt;li&gt;Symptom type&lt;/li&gt;
&lt;li&gt;Initial incident state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was the first time the system moved from detection into memory.&lt;/p&gt;

&lt;p&gt;An incident was no longer just an alert.&lt;br&gt;
It became a structured record.&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.amazonaws.com%2Fuploads%2Farticles%2Fv4zf5ehmhj8rehnhanvo.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.amazonaws.com%2Fuploads%2Farticles%2Fv4zf5ehmhj8rehnhanvo.png" alt=" " width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Converting an Alarm Into an Actionable Event
&lt;/h2&gt;

&lt;p&gt;At this stage, the system could detect failure, but detection alone is passive.&lt;/p&gt;

&lt;p&gt;An alarm changing state does not fix anything.&lt;br&gt;
It only tells humans that something went wrong.&lt;/p&gt;

&lt;p&gt;To move beyond that, the alarm had to become an event that other services could react to.&lt;/p&gt;

&lt;p&gt;This is where Amazon EventBridge was introduced.&lt;/p&gt;

&lt;p&gt;I created an EventBridge rule that listens specifically for CloudWatch alarm state change events.&lt;/p&gt;

&lt;p&gt;The rule was scoped carefully to avoid noise:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source set to CloudWatch&lt;/li&gt;
&lt;li&gt;Event type restricted to alarm state changes&lt;/li&gt;
&lt;li&gt;Filtered only when the alarm enters ALARM state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensured that only real failures triggered downstream logic.&lt;/p&gt;

&lt;p&gt;No polling.&lt;br&gt;
No scripts.&lt;br&gt;
No manual checks.&lt;/p&gt;

&lt;p&gt;The system was now event-driven.&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.amazonaws.com%2Fuploads%2Farticles%2F3yg1ol1rasmfj5zrxwzh.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.amazonaws.com%2Fuploads%2Farticles%2F3yg1ol1rasmfj5zrxwzh.png" alt=" " width="800" height="320"&gt;&lt;/a&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.amazonaws.com%2Fuploads%2Farticles%2Flroqyrkxwnbu7of97h29.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.amazonaws.com%2Fuploads%2Farticles%2Flroqyrkxwnbu7of97h29.png" alt=" " width="800" height="536"&gt;&lt;/a&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.amazonaws.com%2Fuploads%2Farticles%2Fvif0rpqdidgfgstlpcl3.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.amazonaws.com%2Fuploads%2Farticles%2Fvif0rpqdidgfgstlpcl3.png" alt=" " width="800" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Persistent Incident Memory With DynamoDB
&lt;/h2&gt;

&lt;p&gt;Alerts are transient.&lt;br&gt;
Logs rotate.&lt;br&gt;
Dashboards reset.&lt;/p&gt;

&lt;p&gt;Memory needs persistence.&lt;/p&gt;

&lt;p&gt;To store incident history, I created a DynamoDB table named incident_memory.&lt;/p&gt;

&lt;p&gt;The schema was intentionally simple:&lt;/p&gt;

&lt;p&gt;IncidentID as the partition key&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AlarmName&lt;/li&gt;
&lt;li&gt;Status&lt;/li&gt;
&lt;li&gt;Symptom&lt;/li&gt;
&lt;li&gt;Timestamp&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the incident collector Lambda executed, it wrote a new item with status set to OPEN.&lt;/p&gt;

&lt;p&gt;This mattered more than it looked.&lt;/p&gt;

&lt;p&gt;OPEN meant unresolved.&lt;br&gt;
OPEN meant the system knew work remained.&lt;br&gt;
OPEN meant recovery could be tracked.&lt;/p&gt;

&lt;p&gt;For the first time, the system had state.&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.amazonaws.com%2Fuploads%2Farticles%2F8s8qz8o9e7qoomt9riot.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.amazonaws.com%2Fuploads%2Farticles%2F8s8qz8o9e7qoomt9riot.png" alt=" " width="800" height="449"&gt;&lt;/a&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.amazonaws.com%2Fuploads%2Farticles%2Fdfwscfwzmq9ubhbhiug9.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.amazonaws.com%2Fuploads%2Farticles%2Fdfwscfwzmq9ubhbhiug9.png" alt=" " width="800" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Resolving the Incident Manually, Once
&lt;/h2&gt;

&lt;p&gt;At this point, I intentionally stopped automating.&lt;/p&gt;

&lt;p&gt;The backend issue was resolved manually by restarting nginx.&lt;/p&gt;

&lt;p&gt;This step was critical.&lt;/p&gt;

&lt;p&gt;The goal was never to remove humans from the loop.&lt;br&gt;
The goal was to learn from the first fix.&lt;/p&gt;

&lt;p&gt;Once nginx was restarted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Health checks recovered&lt;/li&gt;
&lt;li&gt;ALB stopped serving 5XX responses&lt;/li&gt;
&lt;li&gt;The CloudWatch alarm returned to OK state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This state transition was just as important as the failure itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Teaching the System How to Resolve the Incident
&lt;/h2&gt;

&lt;p&gt;With a successful manual resolution completed, I introduced the auto resolver Lambda.&lt;/p&gt;

&lt;p&gt;This function was not reactive in the traditional sense.&lt;/p&gt;

&lt;p&gt;It did not run blindly on every alarm.&lt;br&gt;
It did not attempt diagnosis.&lt;/p&gt;

&lt;p&gt;Instead, it followed a simple rule:&lt;/p&gt;

&lt;p&gt;If an incident exists in OPEN state and its resolution pattern is known, apply the same fix.&lt;/p&gt;

&lt;p&gt;For this incident type, the fix was clear:&lt;/p&gt;

&lt;p&gt;Restart nginx on the affected instance&lt;/p&gt;

&lt;p&gt;This action was executed using AWS Systems Manager Run Command.&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.amazonaws.com%2Fuploads%2Farticles%2Fs02hbg56i9vwqn7vtr79.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.amazonaws.com%2Fuploads%2Farticles%2Fs02hbg56i9vwqn7vtr79.png" alt=" " width="800" height="657"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No SSH keys.&lt;br&gt;
No open ports.&lt;br&gt;
No human login.&lt;/p&gt;

&lt;p&gt;Once the command succeeded, the incident status was updated to AUTO RESOLVED in DynamoDB.&lt;/p&gt;

&lt;p&gt;The system now had proof of recovery.&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.amazonaws.com%2Fuploads%2Farticles%2Fqkll1mo8zkj07pj5lbwx.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.amazonaws.com%2Fuploads%2Farticles%2Fqkll1mo8zkj07pj5lbwx.png" alt=" " width="800" height="230"&gt;&lt;/a&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.amazonaws.com%2Fuploads%2Farticles%2Fb5jdh470bm93vw023pe9.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.amazonaws.com%2Fuploads%2Farticles%2Fb5jdh470bm93vw023pe9.png" alt=" " width="800" height="306"&gt;&lt;/a&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.amazonaws.com%2Fuploads%2Farticles%2Fcoe6icjcwrxjd6fhblf0.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.amazonaws.com%2Fuploads%2Farticles%2Fcoe6icjcwrxjd6fhblf0.png" alt=" " width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This system did not prevent failures.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Services still crashed.&lt;br&gt;
Traffic still failed.&lt;br&gt;
Alarms still fired.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What changed was what happened after the first failure.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of starting from zero every time, the system began to reuse what it had already learned.&lt;br&gt;
The fix that worked once became a repeatable action, not tribal knowledge.&lt;/p&gt;

&lt;p&gt;That shift is subtle, but powerful.&lt;/p&gt;

&lt;p&gt;Most operational automation struggles because it tries to be intelligent too early.&lt;br&gt;
It guesses causes.&lt;br&gt;
It assumes fixes.&lt;br&gt;
It reacts without context.&lt;/p&gt;

&lt;p&gt;This approach was different.&lt;/p&gt;

&lt;p&gt;The system waited for a real incident.&lt;br&gt;
It observed how it was resolved.&lt;br&gt;
Then it remembered that resolution.&lt;/p&gt;

&lt;p&gt;Nothing more. Nothing less.&lt;/p&gt;

&lt;p&gt;Over time, this kind of design can reduce on-call fatigue, shorten recovery times, and preserve operational knowledge that would otherwise disappear when people change teams.&lt;br&gt;
Not because the system is smart, but because it is grounded in real outcomes.&lt;/p&gt;

&lt;p&gt;In cloud operations, reliability often comes not from predicting the future, but from respecting the past.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>automation</category>
      <category>cloudengineering</category>
    </item>
    <item>
      <title>AI-Augmented Cloud Operations</title>
      <dc:creator>Abhijeet Yadav</dc:creator>
      <pubDate>Wed, 07 Jan 2026 07:49:48 +0000</pubDate>
      <link>https://dev.to/shree_abhijeet/ai-augmented-cloud-operations-4c3p</link>
      <guid>https://dev.to/shree_abhijeet/ai-augmented-cloud-operations-4c3p</guid>
      <description>&lt;p&gt;I've spent the last year buried in AWS stuff. You know, chasing CloudWatch alerts, digging through logs, hunting weird cost spikes, and sorting random errors that pop up right when you want to call it a day.&lt;/p&gt;

&lt;p&gt;Cloud-ops isn't that smooth Afterall. Actually, it's getting way louder with alarms going off nonstop.&lt;/p&gt;

&lt;p&gt;Think about it. Monday rolls around and RDS CPU spikes so high for no reason. Tuesday hits with a Lambda retrying over and over in some endless loop. Come Wednesday, a cost alert keeps beeping because that Staging(testing) EC2 server never got shut down or just like me when I forgot to stop the policy of AMI &amp;amp; Snapshots. (It actually burnt my 262$ when I ignored the Alerts.)&lt;/p&gt;

&lt;p&gt;If you've messed with AWS a bit, you totally get the routine.&lt;/p&gt;

&lt;p&gt;Then it hit me hard. Most of our work, close to 70 percent, boils down to the same grind. We analyze logs. Recap the chaos. Pinpoint what failed. Guess the real culprit. &lt;br&gt;
Wash, rinse, repeat every week.&lt;/p&gt;

&lt;p&gt;Things shifted when I troubleshot a client's Lambda glitch using CloudWatch and Bedrock. I stumbled into a setup where the AI ripped through a huge log file. It summed everything up and zeroed in on the problem within seconds. Way quicker than me pounding coffee over a screen.&lt;/p&gt;

&lt;p&gt;That's the aha moment. AI won't take over jobs. Instead, it acts like that reliable buddy who stays up all night. It catches patterns we overlook and lets us tackle bigger challenges.&lt;/p&gt;

&lt;p&gt;I kept experimenting after that. Linked Bedrock with CloudWatch, Lambda, Code-Commit for code trails, Cost Explorer data, plus Slack alerts. Suddenly the whole thing locked into place.&lt;/p&gt;

&lt;p&gt;AWS lays it all out ready to go. Logs, metrics, events, billing details, repos, old incident records, models that actually reason through stuff.&lt;/p&gt;

&lt;p&gt;Observing It smartly and we can build an AI helper that runs around the clock like a solid junior dev. No futuristic talk. This runs right now in real accounts.&lt;/p&gt;

&lt;p&gt;How I assembled it step by step, using actual services and headaches I fixed, with code anyone can fire up. &lt;br&gt;
But not like any dry tutorials, this is just my numerous Attempts and trying different ways until I get the solution and hit the spot.&lt;/p&gt;

&lt;p&gt;I didn’t plan any of this properly. It sort of came together while handling a random RDS CPU alert. The alarm fired (again), and instead of going through the usual drill, I thought I’d quickly put something in place to save myself the back-and-forth.&lt;/p&gt;

&lt;p&gt;So the first thing I did was open the CloudWatch alarm to check the settings, mainly to confirm the threshold and the evaluation period. Since this is part of the story anyway.&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.amazonaws.com%2Fuploads%2Farticles%2Fc6g0b20x35dgopfehh0g.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.amazonaws.com%2Fuploads%2Farticles%2Fc6g0b20x35dgopfehh0g.png" alt=" " width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After confirming the alarm settings, I jumped into the logs.&lt;br&gt;
I wanted Lambda to automatically pick up a small window of logs, so I opened the RDS log group to see what format and timestamps I’d be dealing with.&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.amazonaws.com%2Fuploads%2Farticles%2F9rreik3iajfhr1125zyw.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.amazonaws.com%2Fuploads%2Farticles%2F9rreik3iajfhr1125zyw.png" alt=" " width="800" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once I saw the structure, I created a simple Lambda function. Nothing fancy Python, default runtime, one file. The idea was:&lt;/p&gt;

&lt;p&gt;Setting Up Slack and Wiring the Notifications&lt;/p&gt;

&lt;p&gt;Once I had the basic flow in my head, the next thing I needed was a clean way to get alerts somewhere I actually check. Email works sometimes, but Slack is where most of us live anyway. So I created a small Slack app just for this setup.&lt;/p&gt;

&lt;p&gt;The moment you open the Slack developer page, it looks something like this.&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.amazonaws.com%2Fuploads%2Farticles%2F7dxtjcjzmfpwu1puav4x.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.amazonaws.com%2Fuploads%2Farticles%2F7dxtjcjzmfpwu1puav4x.png" alt=" " width="384" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All I had to do was enable incoming webhooks for the workspace.&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.amazonaws.com%2Fuploads%2Farticles%2Fa44f9024tzttfqis1cf1.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.amazonaws.com%2Fuploads%2Farticles%2Fa44f9024tzttfqis1cf1.png" alt=" " width="400" height="210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then Slack asked me where exactly I wanted the messages to appear. I picked a fresh channel I made only for this experiment, mostly so I could keep the noise separate from everything else.&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.amazonaws.com%2Fuploads%2Farticles%2Fcnz48fftmxf72upltbeu.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.amazonaws.com%2Fuploads%2Farticles%2Fcnz48fftmxf72upltbeu.png" alt=" " width="400" height="181"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After approving it, Slack generated the webhook URL. This is the only thing Lambda needs to trigger messages into that channel.&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.amazonaws.com%2Fuploads%2Farticles%2Fp6yiqxsmx9ajfilx12zf.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.amazonaws.com%2Fuploads%2Farticles%2Fp6yiqxsmx9ajfilx12zf.png" alt=" " width="325" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I didn’t want to hardcode this webhook into Lambda though. Storing secrets in code is a recipe for embarrassment later. So I opened Secrets Manager and created a new secret for the webhook.&lt;/p&gt;

&lt;p&gt;With that done, the notification path was ready.&lt;br&gt;
Slack would receive the message.&lt;br&gt;
Secrets Manager would hold the sensitive piece.&lt;br&gt;
And Lambda would glue the two together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alarm fires → Lambda gets event → fetch 10 mins logs → send to Bedrock.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Wiring Slack into the Flow
&lt;/h2&gt;

&lt;p&gt;Once the idea was clear, I needed a place where all this noise could land. Slack was the obvious choice.&lt;/p&gt;

&lt;p&gt;I created a small Slack app just for this project. Nothing fancy, just a name, a workspace, and a clean slate.&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.amazonaws.com%2Fuploads%2Farticles%2F8sa8kcwlku5nb6lwbeb5.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.amazonaws.com%2Fuploads%2Farticles%2F8sa8kcwlku5nb6lwbeb5.png" alt=" " width="384" height="230"&gt;&lt;/a&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.amazonaws.com%2Fuploads%2Farticles%2F87x54psvm1qgpq2fhazj.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.amazonaws.com%2Fuploads%2Farticles%2F87x54psvm1qgpq2fhazj.png" alt=" " width="388" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Then I enabled incoming webhooks so external services could post messages into a channel&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.amazonaws.com%2Fuploads%2Farticles%2Fwvli17a8yizso09ztpd3.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.amazonaws.com%2Fuploads%2Farticles%2Fwvli17a8yizso09ztpd3.png" alt=" " width="400" height="210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After that, Slack asked which channel should receive the alerts. I created a fresh channel named #ai-ops and approved the app.&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.amazonaws.com%2Fuploads%2Farticles%2Frf1tve70xqkb8sd49nrt.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.amazonaws.com%2Fuploads%2Farticles%2Frf1tve70xqkb8sd49nrt.png" alt=" " width="400" height="181"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Once approved, Slack generated the webhook URL.&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.amazonaws.com%2Fuploads%2Farticles%2F5rzdtnvla688b6hocb4v.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.amazonaws.com%2Fuploads%2Farticles%2F5rzdtnvla688b6hocb4v.png" alt=" " width="325" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That tiny URL is the bridge between AWS and my phone lighting up at 2 AM.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Hiding the Webhook Where It Belongs
&lt;/h2&gt;

&lt;p&gt;Hardcoding the webhook into Lambda felt wrong, so I pushed it into Secrets Manager.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setting Up Secrets and Notifications&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the basic pieces started forming, I observed that I needed somewhere to store the Slack webhook properly. I could not do Hardcoding into Lambda, it felt messy and also slightly dangerous, so I moved it into Secrets Manager. It took just a minute but instantly made the whole thing feel cleaner. Almost like, ok fine, now this setup looks more aligned.&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.amazonaws.com%2Fuploads%2Farticles%2Foe5nojt5vspwitqjkmd7.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.amazonaws.com%2Fuploads%2Farticles%2Foe5nojt5vspwitqjkmd7.png" alt=" " width="800" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After saving, the secret was sitting quietly in AWS, invisible to anyone who doesn’t have permission.&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.amazonaws.com%2Fuploads%2Farticles%2Fpor0risap9lr2t9sxmpp.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.amazonaws.com%2Fuploads%2Farticles%2Fpor0risap9lr2t9sxmpp.png" alt=" " width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This one step instantly made the setup feel less hacky and more production-ish.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lambda That Holds Everything Together
&lt;/h2&gt;

&lt;p&gt;Now came the dispatcher.&lt;/p&gt;

&lt;p&gt;I created a new Lambda function using Python. No layers, no frameworks, just a single file.&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.amazonaws.com%2Fuploads%2Farticles%2Fgysslerrpji9zapv165t.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.amazonaws.com%2Fuploads%2Farticles%2Fgysslerrpji9zapv165t.png" alt=" " width="800" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then I added environment variables so Lambda would know:&lt;/p&gt;

&lt;p&gt;which bucket to write to&lt;/p&gt;

&lt;p&gt;where the Slack secret lives&lt;/p&gt;

&lt;p&gt;which region it is running in&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.amazonaws.com%2Fuploads%2Farticles%2Fetk3i0e71sypeq1isndl.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.amazonaws.com%2Fuploads%2Farticles%2Fetk3i0e71sypeq1isndl.png" alt=" " width="800" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After saving, the configuration finally looked complete.&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.amazonaws.com%2Fuploads%2Farticles%2F1tylj8by8ukc57kq8mzk.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.amazonaws.com%2Fuploads%2Farticles%2F1tylj8by8ukc57kq8mzk.png" alt=" " width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, the plumbing was done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pressing Run and Watching It Come Alive&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I triggered the Lambda manually using a small test payload:&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.amazonaws.com%2Fuploads%2Farticles%2Fdkwsvyjxpg7ns9uulty7.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.amazonaws.com%2Fuploads%2Farticles%2Fdkwsvyjxpg7ns9uulty7.png" alt=" " width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Within seconds, Slack pinged.&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.amazonaws.com%2Fuploads%2Farticles%2Fz2f7rul0nbx6eqyrlslk.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.amazonaws.com%2Fuploads%2Farticles%2Fz2f7rul0nbx6eqyrlslk.png" alt=" " width="800" height="876"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not a test message, A real alert from something I just built.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Capturing the Full Story in S3
&lt;/h2&gt;

&lt;p&gt;Every execution drops a JSON file into S3. That file contains the logs, timestamps, event data and whatever analysis is produced.&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.amazonaws.com%2Fuploads%2Farticles%2Fa1r2deo4h8zstu1k2gn3.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.amazonaws.com%2Fuploads%2Farticles%2Fa1r2deo4h8zstu1k2gn3.png" alt=" " width="800" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And when I opened the results folder, the structure was clean and traceable.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Everything Is Actually Connected
&lt;/h2&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.amazonaws.com%2Fuploads%2Farticles%2Fu6lpk7hnes2oc0yf5icl.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.amazonaws.com%2Fuploads%2Farticles%2Fu6lpk7hnes2oc0yf5icl.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is the entire setup I have running today.&lt;br&gt;
No Bedrock yet. No heavy ML. Just automation that removes one painful step from my daily cloud routine.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Actually Changed
&lt;/h2&gt;

&lt;p&gt;I didn’t build this system because I wanted to “do AI in the cloud”.&lt;br&gt;
I built it because I was tired of reacting blindly.&lt;/p&gt;

&lt;p&gt;Before this, every alert meant the same ritual.&lt;br&gt;
Open CloudWatch.&lt;br&gt;
Scroll through logs.&lt;br&gt;
Guess which 3 lines out of 50,000 actually mattered.&lt;br&gt;
Hope I didn’t miss the real problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now the flow is different.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An alarm fires.&lt;br&gt;
A message hits Slack.&lt;br&gt;
A full execution trace lands in S3.&lt;br&gt;
By the time I even open the console, the context is already waiting for me.&lt;/p&gt;

&lt;p&gt;That changes the entire mental state.&lt;br&gt;
You don’t start from confusion anymore.&lt;br&gt;
You start from evidence.&lt;/p&gt;

&lt;p&gt;What surprised me most is how small the system is.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1. One Lambda.&lt;/li&gt;
&lt;li&gt;2. One secret.&lt;/li&gt;
&lt;li&gt;3. One S3 folder.&lt;/li&gt;
&lt;li&gt;4. One Slack channel.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s enough to cut out a painful slice of daily cloud work.&lt;/p&gt;

&lt;p&gt;And the future part isn’t some wild AI promise. It’s extremely practical.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This same pattern can listen to cost anomalies.&lt;/li&gt;
&lt;li&gt;It can watch WAF logs.&lt;/li&gt;
&lt;li&gt;It can monitor security events.&lt;/li&gt;
&lt;li&gt;It can summarise incidents before anyone joins the bridge call.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not replacing engineers.&lt;br&gt;
Not auto-fixing production blindly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Just making sure the first thing you see is signal, not noise.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That’s the kind of automation I actually want in my career.&lt;br&gt;
Quiet, Helpful And already working in my account today.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>aiops</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
