<?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: Sauveer Ketan</title>
    <description>The latest articles on DEV Community by Sauveer Ketan (@sauveer_ketan).</description>
    <link>https://dev.to/sauveer_ketan</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%2F3351238%2Fa7591faf-fabf-4d37-9d94-11ee94e06c84.jpg</url>
      <title>DEV Community: Sauveer Ketan</title>
      <link>https://dev.to/sauveer_ketan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sauveer_ketan"/>
    <language>en</language>
    <item>
      <title>Your Lambda Has All the Right Permissions — So Why Can't It Reach DynamoDB?</title>
      <dc:creator>Sauveer Ketan</dc:creator>
      <pubDate>Wed, 25 Mar 2026 13:01:46 +0000</pubDate>
      <link>https://dev.to/sauveer_ketan/your-lambda-has-all-the-right-permissions-so-why-cant-it-reach-dynamodb-3jn1</link>
      <guid>https://dev.to/sauveer_ketan/your-lambda-has-all-the-right-permissions-so-why-cant-it-reach-dynamodb-3jn1</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A real-world VPC + Lambda DNS puzzle that'll make you double-check your security groups forever.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🧩 The Scenario
&lt;/h2&gt;

&lt;p&gt;Your team deploys a Lambda function that needs to read and write to a DynamoDB table. The setup looks solid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Lambda is &lt;strong&gt;attached to a VPC&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ A &lt;strong&gt;VPC Endpoint for DynamoDB&lt;/strong&gt; is configured (Gateway type)&lt;/li&gt;
&lt;li&gt;✅ Lambda's &lt;strong&gt;IAM role&lt;/strong&gt; has the correct DynamoDB permissions (&lt;code&gt;dynamodb:GetItem&lt;/code&gt;, &lt;code&gt;dynamodb:PutItem&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;✅ The Lambda's &lt;strong&gt;Security Group&lt;/strong&gt; has an outbound rule: &lt;code&gt;All TCP → 0.0.0.0/0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;DNS resolution&lt;/strong&gt; and &lt;strong&gt;DNS hostnames&lt;/strong&gt; are enabled on the VPC&lt;/li&gt;
&lt;li&gt;✅ An &lt;strong&gt;EC2 instance in the same VPC&lt;/strong&gt; can successfully resolve the DynamoDB DNS name AND access the table&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yet, Lambda invocations keep failing with a connection/timeout error. It simply &lt;strong&gt;cannot resolve the DynamoDB DNS name&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🤔 Take a Moment — What Do You Think Is Wrong?
&lt;/h2&gt;

&lt;p&gt;You've verified IAM. You've verified the VPC endpoint. DNS is enabled at the VPC level. Even an EC2 in the same VPC works perfectly.&lt;/p&gt;

&lt;p&gt;The Lambda function is not in a public subnet issue. It's not a missing route table entry.&lt;/p&gt;

&lt;p&gt;Seriously — take 30 seconds. What's different between how an EC2 instance handles DNS vs. a Lambda function locked down by a Security Group?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
...
...
...
...
...
Think about the OSI model. 🤔
...
...
...
...
...
...
What protocol does DNS primarily use?
...
...
...
...
...
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  💡 The Answer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The Lambda's Security Group was missing an outbound rule for UDP traffic.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Specifically, this rule was absent:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Port Range&lt;/th&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Custom UDP&lt;/td&gt;
&lt;td&gt;UDP&lt;/td&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;td&gt;0.0.0.0/0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Or more broadly (to cover both TCP and UDP for DNS):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Port Range&lt;/th&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;All traffic&lt;/td&gt;
&lt;td&gt;All&lt;/td&gt;
&lt;td&gt;All&lt;/td&gt;
&lt;td&gt;0.0.0.0/0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🔬 Why This Happens — The Root Cause Explained
&lt;/h2&gt;

&lt;h3&gt;
  
  
  DNS Uses UDP (Primarily)
&lt;/h3&gt;

&lt;p&gt;DNS queries almost always go out over &lt;strong&gt;UDP port 53&lt;/strong&gt;. TCP port 53 is used only for larger responses (like zone transfers or responses exceeding 512 bytes). For typical hostname resolution — like resolving &lt;code&gt;dynamodb.us-east-1.amazonaws.com&lt;/code&gt; — &lt;strong&gt;UDP is the default protocol&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The Security Group had:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Outbound: All TCP → 0.0.0.0/0   ✅
Outbound: All UDP → 0.0.0.0/0   ❌ MISSING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So when Lambda tried to resolve the DynamoDB endpoint DNS name before establishing a connection, the UDP DNS query was &lt;strong&gt;silently dropped&lt;/strong&gt; by the Security Group. No DNS resolution = no connection = timeout.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Did EC2 Work But Lambda Didn't?
&lt;/h3&gt;

&lt;p&gt;This is the clever part. The EC2 instance likely had a broader Security Group — perhaps &lt;code&gt;All traffic → 0.0.0.0/0&lt;/code&gt; — or one that explicitly included UDP. Since nobody usually thinks about restricting outbound on EC2 instances during testing, it just worked.&lt;/p&gt;

&lt;p&gt;Lambda, being more of a "locked-down by default" environment in VPCs, often gets a tighter security group — and &lt;strong&gt;TCP-only outbound is a very common mistake&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Didn't the VPC DNS Setting Matter Here?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;enableDnsResolution&lt;/code&gt; on the VPC controls whether instances &lt;em&gt;can use&lt;/em&gt; the Route 53 Resolver (at the VPC base IP + 2, e.g., &lt;code&gt;10.0.0.2&lt;/code&gt;). But that resolver is still accessed over &lt;strong&gt;UDP port 53&lt;/strong&gt; from the Lambda's network interface. If the Security Group blocks outbound UDP, the Lambda ENI (Elastic Network Interface) can never reach the resolver — regardless of the VPC-level DNS settings.&lt;/p&gt;




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

&lt;p&gt;Update the Lambda's Security Group outbound rules to include UDP:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 1 — Minimal / Precise (recommended for production):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Direction&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Outbound&lt;/td&gt;
&lt;td&gt;Custom UDP&lt;/td&gt;
&lt;td&gt;UDP&lt;/td&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;td&gt;VPC CIDR or 0.0.0.0/0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outbound&lt;/td&gt;
&lt;td&gt;Custom TCP&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;443&lt;/td&gt;
&lt;td&gt;0.0.0.0/0 (for HTTPS to DynamoDB endpoint)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Option 2 — Simple / Permissive (fine for dev/test):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Direction&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Outbound&lt;/td&gt;
&lt;td&gt;All traffic&lt;/td&gt;
&lt;td&gt;All&lt;/td&gt;
&lt;td&gt;All&lt;/td&gt;
&lt;td&gt;0.0.0.0/0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🗝️ Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;DNS runs on UDP port 53&lt;/strong&gt; — always allow outbound UDP 53 in Lambda security groups when using VPC.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"All TCP" ≠ "All Traffic"&lt;/strong&gt; — a very easy mistake to make in the AWS console.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EC2 working ≠ Lambda will work&lt;/strong&gt; — EC2 and Lambda often have different Security Groups with different outbound rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPC Endpoints don't bypass DNS resolution&lt;/strong&gt; — Lambda still needs to resolve the DynamoDB hostname before the VPC endpoint routing kicks in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Silence is not golden in Security Groups&lt;/strong&gt; — blocked UDP traffic produces no error, just a timeout, making this particularly tricky to debug.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🛠️ Quick Debugging Checklist for Lambda-in-VPC DNS Issues
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Does the Security Group allow &lt;strong&gt;outbound UDP 53&lt;/strong&gt;?&lt;/li&gt;
&lt;li&gt;[ ] Does the Security Group allow &lt;strong&gt;outbound TCP 443&lt;/strong&gt; (for AWS service endpoints)?&lt;/li&gt;
&lt;li&gt;[ ] Is DNS Resolution enabled on the VPC?&lt;/li&gt;
&lt;li&gt;[ ] Is DNS Hostnames enabled on the VPC?&lt;/li&gt;
&lt;li&gt;[ ] Is there a route in the subnet's route table pointing to the VPC Endpoint?&lt;/li&gt;
&lt;li&gt;[ ] Does the VPC Endpoint policy allow the Lambda's IAM role?&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Found this useful? Drop a 💬 comment with your own sneaky AWS networking gotchas — there are plenty of them out there!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Follow for more real-world AWS and Linux troubleshooting posts in this series.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>vpc</category>
      <category>troubleshooting</category>
    </item>
    <item>
      <title>Strands AI Functions: Write Python Functions in Natural Language</title>
      <dc:creator>Sauveer Ketan</dc:creator>
      <pubDate>Wed, 25 Mar 2026 07:21:45 +0000</pubDate>
      <link>https://dev.to/sauveer_ketan/strands-ai-functions-write-python-functions-in-natural-language-156o</link>
      <guid>https://dev.to/sauveer_ketan/strands-ai-functions-write-python-functions-in-natural-language-156o</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This post is written for architects and developers already familiar with Amazon Strands Agents SDK.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AWS's experimental new library lets you write AI-powered Python functions in natural language — and the LLM writes the implementation at runtime.&lt;/p&gt;

&lt;p&gt;Most real-world agentic workflows still require a lot of traditional code. For example, imagine accepting an uploaded invoice file in an unknown format and converting it into a clean, normalized DataFrame for use in the rest of the workflow. With traditional code, you write format-detection logic, transformation pipelines, prompt templates, response parsers, and retry loops — dozens of lines before you've even gotten to the business logic. What if you could just describe what you want and let the model figure out the rest?&lt;/p&gt;

&lt;p&gt;That's exactly what &lt;strong&gt;Strands AI Functions&lt;/strong&gt; is designed to do. Released by AWS as part of the newly launched Strands Labs experimental organization, AI Functions is a Python library that gives developers a disciplined, intent-based approach to build reliable, AI-powered pipelines — without writing traditional prompt orchestration, parsing, and retry logic for the AI components.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"At a high level, AI Functions lets you describe intent, while the framework handles execution, correction, and validation."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What Is Strands Labs?
&lt;/h2&gt;

&lt;p&gt;Before diving into AI Functions, a quick note on where it comes from. In early 2026, AWS launched &lt;strong&gt;Strands Labs&lt;/strong&gt; — a separate GitHub organization designed as an innovation sandbox for experimental agentic AI projects. Think of it as the frontier research wing of the Strands Agents SDK.&lt;/p&gt;

&lt;p&gt;Strands Labs launched with three projects: &lt;strong&gt;Robots&lt;/strong&gt; (physical AI agents), &lt;strong&gt;Robots Sim&lt;/strong&gt; (simulation environments), and &lt;strong&gt;AI Functions&lt;/strong&gt; — the one that should immediately catch the attention of any developer or architect building AI-powered pipelines.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI Functions is &lt;strong&gt;experimental&lt;/strong&gt;. Expect breaking changes. It is not yet production-ready, but the concepts are production-relevant today — understanding them now puts you ahead of the curve.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Core Idea: Functions Written in Natural Language
&lt;/h2&gt;

&lt;p&gt;An AI Function looks like a normal Python function decorated with &lt;code&gt;@ai_function&lt;/code&gt;. But instead of writing code in the function body, you write a docstring in natural language that describes what the function should do.&lt;/p&gt;

&lt;p&gt;AI Functions are implemented on top of the Strands Agent runtime. Any valid option of &lt;code&gt;strands.Agent&lt;/code&gt; (such as &lt;code&gt;model&lt;/code&gt;, &lt;code&gt;tools&lt;/code&gt;, &lt;code&gt;system_prompt&lt;/code&gt;) can be passed in the decorator.&lt;/p&gt;

&lt;p&gt;When an AI Function is called, the library will automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Strands agent&lt;/li&gt;
&lt;li&gt;Generate a prompt based on the docstring template and the provided arguments&lt;/li&gt;
&lt;li&gt;Parse and validate the result&lt;/li&gt;
&lt;li&gt;Return it as a typed Python object&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From the outside, it behaves like any other Python function.&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;from&lt;/span&gt; &lt;span class="n"&gt;ai_functions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ai_function&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MeetingSummary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;attendees&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;action_items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nd"&gt;@ai_function&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;summarize_meeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transcripts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MeetingSummary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Write a summary of the following meeting in less than 50 words.
    &amp;lt;transcripts&amp;gt;
    {transcripts}
    &amp;lt;/transcripts&amp;gt;
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="c1"&gt;# Call it just like any normal Python function
&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;summarize_meeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transcript_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function takes typed inputs, returns a typed Pydantic model, and the library handles everything in between — creating the agent, running the model, parsing the output, and returning a validated Python object. To the rest of your codebase, it's just a function call.&lt;/p&gt;




&lt;h2&gt;
  
  
  Post-Conditions
&lt;/h2&gt;

&lt;p&gt;This is the feature that sets Strands AI Functions apart from every other "just call the LLM" approach. The core philosophy is that you should &lt;strong&gt;never rely on prompt engineering alone&lt;/strong&gt; to guarantee output correctness. Instead, you define &lt;strong&gt;post-conditions&lt;/strong&gt; — validation functions that run after the AI produces its output.&lt;/p&gt;

&lt;p&gt;If a post-condition fails, the library automatically feeds the error back to the agent in a self-correcting loop, up to a configurable number of attempts. Your pipeline either gets a validated result or fails cleanly — no silent garbage output sneaking through.&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;from&lt;/span&gt; &lt;span class="n"&gt;ai_functions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ai_function&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PostConditionResult&lt;/span&gt;

&lt;span class="c1"&gt;# Standard Python validator
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_length&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;MeetingSummary&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&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;summary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;50&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;Summary is &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; words, must be ≤ 50&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Or use another AI Function as a validator!
&lt;/span&gt;&lt;span class="nd"&gt;@ai_function&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_style&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;MeetingSummary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;PostConditionResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Check if the summary uses bullet points and provides sufficient context.
    &amp;lt;summary&amp;gt;{response.summary}&amp;lt;/summary&amp;gt;
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="nd"&gt;@ai_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_conditions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;check_length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check_style&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;max_attempts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;summarize_meeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transcripts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MeetingSummary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Write a concise meeting summary. &amp;lt;transcripts&amp;gt;{transcripts}&amp;lt;/transcripts&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that a post-condition can itself be an AI Function — enabling sophisticated validation of stylistic or semantic constraints that would be impossible to express in pure Python logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Three Pillars of AI Functions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Natural Language Instructions
&lt;/h3&gt;

&lt;p&gt;Describe what you want in plain language, either as a docstring or a returned string from the function body.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Post-Conditions
&lt;/h3&gt;

&lt;p&gt;Define explicit validation rules that the AI output must satisfy, triggering automatic self-correcting retries.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Python Integration
&lt;/h3&gt;

&lt;p&gt;AI Functions aim to feel like a natural extension of the programming language itself, enabling new kinds of programming patterns and abstractions. They return real Python objects and integrate directly into existing codebases, rather than producing raw text.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Universal Data Loader: A Good Use Case
&lt;/h2&gt;

&lt;p&gt;Consider one of the most compelling examples in the official documentation. You're building a webapp that accepts invoice uploads in any format — JSON, CSV, PDF, SQLite. Normally, you'd write format-detection logic and separate transformation pipelines for each format.&lt;/p&gt;

&lt;p&gt;With AI Functions and &lt;code&gt;code_execution_mode="local"&lt;/code&gt; enabled, the agent inspects the file at runtime, determines the format, writes the appropriate loading and transformation code, and returns a properly-typed Pandas DataFrame — complete with schema validation via a post-condition.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When using a Python executor (&lt;code&gt;code_execution_mode="local"&lt;/code&gt;), all input variables to the AI function are automatically loaded into the Python environment. This means the agent can directly reference and manipulate these variables in the generated code without needing to parse them from the prompt.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@ai_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;post_conditions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;check_invoice_dataframe&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;code_execution_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;local&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;code_executor_additional_imports&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;pandas&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;sqlite3&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;def&lt;/span&gt; &lt;span class="nf"&gt;import_invoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    The file `{path}` contains purchase logs. Extract them into a DataFrame
    with columns: product_name (str), quantity (int), price (float),
    purchase_date (datetime).
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="c1"&gt;# Works on JSON, CSV, SQLite - the agent figures it out
&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;import_invoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data/invoice.json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;df2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;import_invoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data/invoice.sqlite3&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;p&gt;In practice, this works best when combined with strong post-conditions and constrained execution environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚠️ Security Note
&lt;/h3&gt;

&lt;p&gt;Right now, Strands AI Functions support only &lt;code&gt;"local"&lt;/code&gt; execution. Local code execution carries inherent risk. AWS recommends running this inside a Docker container or sandbox environment. Remote sandboxed execution is on the roadmap. Things to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run local execution in read-only filesystems&lt;/li&gt;
&lt;li&gt;Use network restrictions&lt;/li&gt;
&lt;li&gt;Strip secrets from runtime environment&lt;/li&gt;
&lt;li&gt;Treat AI-generated code as untrusted by default&lt;/li&gt;
&lt;li&gt;Add observability — because each agent step is explicit, failures and retries are inspectable, making it far easier to debug than ad-hoc prompt pipelines&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Async and Parallel Workflows
&lt;/h2&gt;

&lt;p&gt;AI Functions support async definitions natively, enabling parallel agentic workflows that dramatically reduce wall-clock time. In the stock report example from the official documentation, two research agents run concurrently using &lt;code&gt;asyncio.gather()&lt;/code&gt; before their results are combined into a final report — a pattern that maps perfectly onto real-world multi-step analysis pipelines.&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="nd"&gt;@ai_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[...])&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;research_news&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Research and summarize current news for: {stock}&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="nd"&gt;@ai_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[...])&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;research_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;past_days&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Retrieve 30-day historical prices for {stock} using yfinance.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stock_workflow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Both agents run in parallel
&lt;/span&gt;    &lt;span class="n"&gt;news&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;research_news&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;research_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;past_days&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;write_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;news&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prices&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  AI Functions as Tools in Multi-Agent Systems
&lt;/h2&gt;

&lt;p&gt;Here's where it gets architecturally interesting — AI Functions can be registered as tools within other agents — both other AI Functions and regular Strands Agents. This creates a composable, hierarchical agent architecture where each layer does exactly what it's best at.&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="nd"&gt;@ai_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Search the web and return a summary&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[...])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;websearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Research `{query}` online and return a summary of findings.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="nd"&gt;@ai_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;websearch&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  &lt;span class="c1"&gt;# websearch is now a tool for this agent
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;report_writer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Research the following topic and write a report: {topic}&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="c1"&gt;# Also works with regular Strands Agents:
# agent = Agent(model=..., tools=[websearch])
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why This Matters for AWS Architects
&lt;/h2&gt;

&lt;p&gt;If you're working with Strands, this library slots in naturally. The default model is Claude on Bedrock, and you can swap in any Strands-supported model.&lt;/p&gt;

&lt;p&gt;For architects and platform engineers, the important takeaway is not just the library itself, but the &lt;strong&gt;pattern it represents&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Separation of intent from implementation&lt;/strong&gt; — declare what you need, not how to do it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic guardrails around non-deterministic AI&lt;/strong&gt; using post-conditions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composable, reusable AI components&lt;/strong&gt; — functions as first-class building blocks for agent graphs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native Python support&lt;/strong&gt; — return real objects, not raw strings, maintaining type safety across your pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As the lines between traditional software engineering and AI engineering continue to blur, frameworks like Strands AI Functions point toward a near future where the "implementation" of a function and the "intent" of a function can finally be decoupled — the developer specifies the intent, the model fulfills it, and post-conditions enforce it.&lt;/p&gt;




&lt;h2&gt;
  
  
  When NOT to Use AI Functions
&lt;/h2&gt;

&lt;p&gt;These functions are inherently non-deterministic, which makes them a poor fit for certain scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Low-latency paths&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hard real-time requirements&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strong deterministic compliance constraints&lt;/strong&gt; (e.g., finance)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, AI Functions can incur higher costs than deterministic pipelines due to repeated LLM invocations (retries, validation passes, and tool calls). Cost controls and limits are therefore essential.&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;If you want to experiment hands-on, getting started is refreshingly simple. The official documentation includes a clean QuickStart with the meeting summarizer example, and the &lt;a href="https://github.com/strands-labs" rel="noopener noreferrer"&gt;Strands Labs GitHub&lt;/a&gt; has complete examples for stock report generation, multi-agent orchestration, and context management for long-running tasks. The &lt;a href="https://aws.amazon.com/blogs/" rel="noopener noreferrer"&gt;AWS Blog release post&lt;/a&gt; also provides more details.&lt;/p&gt;

</description>
      <category>strandagents</category>
      <category>aws</category>
      <category>generativeai</category>
      <category>python</category>
    </item>
    <item>
      <title>My AWS Golden Jacket Playbook: Study Methods That Work for Working Parents</title>
      <dc:creator>Sauveer Ketan</dc:creator>
      <pubDate>Tue, 18 Nov 2025 11:49:02 +0000</pubDate>
      <link>https://dev.to/sauveer_ketan/my-aws-golden-jacket-playbook-study-methods-that-work-for-working-parents-18c</link>
      <guid>https://dev.to/sauveer_ketan/my-aws-golden-jacket-playbook-study-methods-that-work-for-working-parents-18c</guid>
      <description>&lt;p&gt;Picture this: Already tired from work (1PM — 11 PM), you also spent one hour to make your one-year-old go to sleep, it's 1:30 AM, bleary-eyed, you're studying various reasons behind overfitting in a ML model while tomorrow you have to give a demo to a client. This was the reality of my 2.5-month sprint to earning the AWS Golden Jacket.&lt;/p&gt;

&lt;p&gt;After 16+ years in IT and countless late-night experiences, I thought I had seen it all. But nothing quite prepared me for the whirlwind that would be my journey to earning the AWS Golden Jacket.&lt;/p&gt;

&lt;p&gt;AWS Golden Jacket is a special way to acknowledge the talent demonstrated by AWS Partners. Earning an AWS Gold Jacket involves holding all active AWS certifications and submitting an application through your company's AWS alliance partner. It was 12 certifications in my case; I completed the final one in Aug 2025.&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%2F425mlqk0wclq08wnxtfe.jpg" 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%2F425mlqk0wclq08wnxtfe.jpg" alt=" " width="800" height="655"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;h2&gt;
  
  
  The Starting Line: 6 Down, 6 to Go
&lt;/h2&gt;

&lt;p&gt;Let me be honest: I didn't start from scratch. As an AWS Solutions Architect with extensive AWS experience, I already had 6 AWS certifications under my belt including two professional ones and security specialty. I can say that except machine learning certifications, I already had a lot of knowledge for other certifications. The Golden Jacket wasn't even on my radar initially. I got a push from the leadership of my organization and thought, why not! I just wanted to deepen my understanding of AWS services, and get familiar with those services I hadn't worked with extensively, for example those related to machine learning.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 2.5-Month Marathon
&lt;/h2&gt;

&lt;p&gt;Between May 30th and August 14th, 2025, I tackled six certifications back-to-back. No firm deadline, just pure momentum. Six certifications in 2.5 months while maintaining a full-time job and being a father to a one-year-old. Let's just say my coffee consumption reached unprecedented levels, and sometimes sleep became a luxury.&lt;/p&gt;

&lt;p&gt;Here's what my typical day actually looked like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Morning to Evening:&lt;/strong&gt; Regular work hours (mix of office and work-from-home). Study in between, whenever I could.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Babysitting:&lt;/strong&gt; My one-year-old had other plans for my "focused study time." He'd knock on my home office door and demand attention whenever he felt like it. Whatever I planned — locking doors, scheduled study blocks — it didn't work. I had to spend multiple precious hours with my baby, and honestly, I wouldn't have it any other way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;10:00–11:00 PM:&lt;/strong&gt; Finally finishing office work and daily responsibilities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;11:00 PM — 1:00/2:00 AM:&lt;/strong&gt; My golden study window — when the house was quiet and I could focus. Those late-night hours became sacred. While my family slept, I'd dive into my study routine, knowing that these 2–3 hours were all I had before another demanding day began. On weekends, I got a few hours extra hours, if I was lucky.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My Method: One Source, Max Speed
&lt;/h2&gt;

&lt;p&gt;Let me share what actually worked for me. Relying on few trusted study material sources is the key. I feel, there is no need to follow multiple sources for same things. I have followed similar methods for my previous certifications, and learning anything in general, also. I also hold 10 Azure certifications and 2 Google Cloud certifications, including their topmost Solution Architect certifications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Batching Certs for Efficiency
&lt;/h3&gt;

&lt;p&gt;I clubbed the certifications based on their content and decided the order in which I will take them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Developer Associate + SysOps Administrator Associate&lt;/strong&gt; (similar operational concepts)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Engineer Associate&lt;/strong&gt; (standalone but foundation for ML)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ML Associate + ML Specialty&lt;/strong&gt; (complementary ML focus)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced Networking Specialty&lt;/strong&gt; (the beast everyone fears)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Primary Learning Source
&lt;/h3&gt;

&lt;p&gt;Stephane Maarek's Udemy courses were my primary sources. His teaching style is perfect for working professionals — clear, comprehensive, and practical. The fact that he collaborates with experts like Frank Kane for machine learning, Abhishek Singh, and Chetan Agrawal for various domains means you're getting top-tier instruction across all AWS services. For some tricky concepts, I also used Gen AI (ChatGPT, Gemini), for example — confusion matrix in Machine Learning or various BGP configurations in Advanced Networking Specialty. There are many other good courses and tests, like Jon Bonso, Adrian Cantrill, Neal Davis, etc., but I like sticking with a single source.&lt;/p&gt;

&lt;h3&gt;
  
  
  Speed Learning
&lt;/h3&gt;

&lt;p&gt;I watched most content at 1.5x to 2x speed, depending on my familiarity with the topic. New concepts got the full treatment at normal speed, but review materials flew by at 2x. Time was precious when you only have 2–3 hours a night.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS Console Walkthrough
&lt;/h3&gt;

&lt;p&gt;When time permitted, I'd dive into the AWS console to explore service configurations hands-on. For simpler services like App Runner, I'd actually create resources to understand the workflow. For complex, expensive services like Redshift or EMR clusters, I'd navigate through the creation process without deploying — just to familiarize myself with configuration options, pricing models, and architectural considerations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mind Mapping
&lt;/h3&gt;

&lt;p&gt;I love mind-maps. I used SimpleMind app on my phone to create detailed mind maps during study sessions. Being able to quickly capture concepts and relationships on my phone meant I could review them anywhere — during lunch breaks, while travelling, even while my son played around me.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A sample snapshot of one of my mind-maps - &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%2Fx9iabwspc20rvfoich48.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%2Fx9iabwspc20rvfoich48.png" alt="AWS Networking" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Practice Tests
&lt;/h3&gt;

&lt;p&gt;If I had buffer time before exams, I also took Udemy practice tests from same creators. They don't just test knowledge — they teach you how AWS wants you to think about problems. I will update my mind maps during their revision also.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Review Strategy
&lt;/h3&gt;

&lt;p&gt;After finishing the course and tests, I'd abandon new content entirely and just review my SimpleMind maps. Having all the key concepts in visual format made last-minute reviews incredibly efficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Toughest Moments (Spoiler: It Wasn't Advanced Networking)
&lt;/h2&gt;

&lt;p&gt;The Advanced Networking specialty is a tough one, but not as much as it is pointed everywhere. It is considered to be the toughest, but I think it depends on your prior experience. Tricky part is hybrid networking if you don't have hands-on experience, but that can be learned through focused study to clear this certification. Diving deep into Direct Connect Gateway, VPC Lattice, and BGP routing at midnight was an exhilarating experience.&lt;/p&gt;

&lt;p&gt;For me the toughest exam experience was &lt;strong&gt;Data Engineer Associate&lt;/strong&gt;. I thought I am ready, but in the exam most questions felt similar and many times it was difficult to distinguish between choices. It was also reflected in my score, which was 78%, my lowest score in any certification. While I have scored 80+ in all others.&lt;/p&gt;

&lt;p&gt;The real challenge was the daily grind. Sometimes, out of pure frustration and exhaustion, I'd just watch a sitcom episode and call it a night. Some weeks were so hectic that I didn't study even on weekends — I just relaxed instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Actually Learned (Beyond the Certs)
&lt;/h2&gt;

&lt;p&gt;Journey is more important than the result, and the real learning was deeper:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Humility:&lt;/strong&gt; Even with 16 years of experience, and 7+ of them in AWS, there's always more to learn. AWS's breadth is genuinely mind-boggling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern Recognition:&lt;/strong&gt; Our brains are pattern recognition machines. You start seeing how AWS services interconnect in ways that aren't obvious from the outside. Everything truly is connected. Patterns and analogies are also a good way to learn and remember concepts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Perspective Shifting:&lt;/strong&gt; Each specialty certification forced me to think like a different type of practitioner — a ML engineer, a security architect, a network engineer. This perspective shift was invaluable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time Management:&lt;/strong&gt; Squeezing study time in between everything else, learning to maximize those precious late-night hours taught me efficiency.&lt;/p&gt;

&lt;p&gt;The real reward isn't the jacket itself — it's the confidence to walk into any AWS conversation knowing I can contribute meaningfully rather than being completely clueless (I mainly mean Machine Learning!).&lt;/p&gt;

&lt;h2&gt;
  
  
  For Those Considering the Journey
&lt;/h2&gt;

&lt;p&gt;I won't recommend this sprint approach to anyone, instead do it gradually by making a plan.&lt;/p&gt;

&lt;p&gt;My advice: Take key certifications first that align with your work area. Certifications alone open a few doors, but in interviews, you need real knowledge. Real knowledge comes through practice and hands-on experience. If your job doesn't provide this, invest in your personal AWS account for practical learning.&lt;/p&gt;

&lt;p&gt;Even if you're not planning certifications, study the materials and take practice tests to enhance your knowledge. The structured learning path is valuable regardless.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;The Golden Jacket is just a trophy. AWS releases new services constantly, and existing services evolve rapidly. The real challenge is staying current and applying this knowledge to solve real-world problems.&lt;/p&gt;

&lt;p&gt;I keep my eyes on AWS updates constantly because in cloud computing — and the IT industry generally — the moment you stop learning is when you start falling behind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Sixteen years in IT taught me that technology changes, but the fundamentals of good architecture remain constant: understand your requirements, choose the right tools, and never stop questioning your assumptions.&lt;/p&gt;

&lt;p&gt;The AWS Golden Jacket represents technical breadth, but more importantly, it represents a commitment to continuous learning. In a field that evolves as rapidly as cloud computing, that commitment matters more than any certification badge.&lt;/p&gt;

&lt;p&gt;To my fellow cloud architects out there — especially those juggling career advancement with family life — whether you're pursuing your first AWS cert or your twelfth, remember that the journey is just as valuable as the destination. Every late-night study session, every failed practice exam, and every "aha!" moment at 1 AM contributes to making you a better architect.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have you pursued AWS certifications while balancing work and family? What strategies worked for you? Share your experience in the comments!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>career</category>
      <category>studytips</category>
      <category>certifications</category>
    </item>
    <item>
      <title>Building Better on AWS: A Practical Guide to the Well-Architected Framework</title>
      <dc:creator>Sauveer Ketan</dc:creator>
      <pubDate>Mon, 17 Nov 2025 09:10:41 +0000</pubDate>
      <link>https://dev.to/sauveer_ketan/building-better-on-aws-a-practical-guide-to-the-well-architected-framework-1l2p</link>
      <guid>https://dev.to/sauveer_ketan/building-better-on-aws-a-practical-guide-to-the-well-architected-framework-1l2p</guid>
      <description>&lt;p&gt;AWS is huge, hundreds of services and thousands of features. So many services, so many possibilities, and hence, so many ways to mess things up.&lt;/p&gt;

&lt;p&gt;A misconfigured S3 bucket. That's all it took for Capital One to lose 100 million customer records in 2019. What if someone had asked the right questions about their architecture?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if there is a free tool to systematically review your entire environment and workloads against AWS best practices? To top it up, what if AWS pays you to fix these risks?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;How do we build better on AWS? That's where the AWS Well-Architected Framework comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Is This Framework?
&lt;/h2&gt;

&lt;p&gt;Think of the AWS Well-Architected Framework as your architectural guardrails — a set of battle-tested best practices that AWS has compiled from working with thousands of customers. It's not some rigid rulebook that you have to follow to the letter. Instead, it's more like having a seasoned architect sitting next to you, asking the right questions about your workloads and environment and providing you feedback and action plan.&lt;/p&gt;

&lt;p&gt;The framework is built on six pillars, and each one addresses a crucial aspect of your cloud architecture. Each of these contain design principles, questions, and best practices. Design principles are high level guidelines and best practices are actual recommendations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Operational Excellence&lt;/strong&gt; is about deploying, running and monitoring systems to deliver business value. The operational excellence pillar contains best practices for organizing your team, deploying your workload, operating it at scale, and evolving it over time. You will see DevOps and ITIL processes here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security&lt;/strong&gt; covers protecting data, systems, and environments. This isn't just about checking compliance boxes — it's about actually understanding your entire security spectrum — preventive and detective. In one recent case, a Palo Alto ec2 server was down and no one knew why — AWS had sent the health notification to stop and start it because of degraded hardware, but no one was receiving those emails. It was going to a single person who had left the organization!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reliability&lt;/strong&gt; ensures your workload performs its intended function correctly and consistently. This includes the ability to operate and test the workload through its total lifecycle with resilience. &lt;strong&gt;Latest Oct 2025 AWS outage&lt;/strong&gt; must have made everyone realize the importance of baking reliability into critical workloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance Efficiency&lt;/strong&gt; is the ability to use cloud resources efficiently to meet performance requirements, and to maintain that efficiency as demand changes and technologies evolve. A normally highly performant system might face performance bottlenecks during peak demand period, if not planned properly. &lt;strong&gt;For example,&lt;/strong&gt; Pre-warming is one of the ways to handle this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost Optimization&lt;/strong&gt; means avoiding unnecessary costs. This pillar has saved organizations literal thousands of dollars. We have found EC2 instances from a proof-of-concept that had been running forgotten for eight months. We have found DMS instances running 2 years after migration completed. We have found thousands of unattached EBS volumes and unnecessary snapshots. These are just a few examples. As the current wisdom says, while architecting workloads, &lt;strong&gt;cost should always be considered as a non-functional requirement.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sustainability&lt;/strong&gt; pillar focuses on minimizing the environmental impact of your cloud workloads, especially energy consumption and efficiency. It's about being smart with resource usage — not just for the planet, but for your wallet too.** For example,** AWS Graviton-based Amazon EC2 instances use up to 60% less energy than comparable EC2 instances for the same performance. They also provide the best price performance for cloud workloads running on Amazon EC2. Over 70,000 customers have used AWS Graviton to build efficient and performant workloads as of now (2025).&lt;/p&gt;

&lt;p&gt;AWS has provided an excellent mind-map for this, where different entities are clickable and lead to relevant documentation.&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%2Fj5dd3viqdr1hn0fxlb6b.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%2Fj5dd3viqdr1hn0fxlb6b.png" alt=" " width="790" height="782"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check it at &lt;a href="https://wa.aws.amazon.com/wat.map.en.html" rel="noopener noreferrer"&gt;Map of the AWS Well-Architected Framework&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter the AWS Well-Architected Tool
&lt;/h2&gt;

&lt;p&gt;Now, you might be thinking, "This all sounds great, but how do I actually apply this to my architecture?" There are 6 pillars, 57 different questions, and multiple best practices against each of these questions. That's where the AWS Well-Architected Tool (WA Tool) becomes your best friend.&lt;/p&gt;

&lt;p&gt;The WA Tool is a free service available in the AWS console that helps you review your workloads against these pillars. It's basically an interactive questionnaire that walks you through each pillar, asking you specific questions about your architecture.&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%2F74ce1zsohzi50ucd6hvz.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%2F74ce1zsohzi50ucd6hvz.png" alt=" " width="800" height="157"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Actually Works
&lt;/h3&gt;

&lt;p&gt;Here's what the experience looks like in practice:&lt;/p&gt;

&lt;p&gt;You start by defining a workload. This could be anything — a microservice, an entire application, or even a data pipeline. The tool then presents you with a series of questions for each pillar. These aren't yes/no questions; they're thoughtful, sometimes challenging questions that make you really think about your design decisions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For example,&lt;/strong&gt; under Security, you might get asked: "How do you protect your network resources?" The tool then offers multiple choice answers based on best practices, and you select what applies to your workload.&lt;/p&gt;

&lt;p&gt;What I really appreciate is that for each question, there's context. The tool explains why the question matters and what the implications are of different approaches. It's educational.&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%2Fq8z8yemdqnsky3puzr74.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%2Fq8z8yemdqnsky3puzr74.png" alt=" " width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Important Features of WA Tool
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Consolidated Reports:&lt;/strong&gt; Not only for your individual workloads, but if you're managing multiple workloads, you can generate reports across all of them. This is invaluable for getting an organizational view of your cloud architecture health.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Risk Identification:&lt;/strong&gt; After you complete the review, you can generate a report which shows high-risk issues (HRIs) and medium-risk issues (MRIs). These aren't generic warnings — they're specific to what you told the tool about your architecture. On the dashboard, we can see visualization for all workloads also. Seeing those red flags visualized really helps prioritize what to tackle first.&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%2Fuby7xo73sgbv4vemiisx.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%2Fuby7xo73sgbv4vemiisx.png" alt=" " width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improvement Plans:&lt;/strong&gt; The tool doesn't just point out problems; it suggests remediation steps. Each identified risk comes with links to documentation, whitepapers, and specific AWS services that can help address the issue.&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%2F22fl17mejwg18y1zil5x.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%2F22fl17mejwg18y1zil5x.png" alt=" " width="706" height="787"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Milestones:&lt;/strong&gt; You can save snapshots of your reviews over time. This is fantastic for tracking improvements. For example, we can run quarterly reviews and would be able to show the executives how we've systematically reduced our high-risk items from 12 to 2 over last quarter. This will make the investment in improvements really tangible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lenses:&lt;/strong&gt; Beyond the standard six pillars included in default WAF lens, AWS offers specialized lenses for specific use cases. There's a DevOps lens, Serverless Lens, a SaaS Lens, and several others. There are industry specific lenses like Healthcare and Financial services, which are very helpful considering compliance requirements of these industries. Of course, there is a Generative AI lens now, the hottest IT industry buzzword right now. These include relevant questions and best practices for their areas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom Lenses:&lt;/strong&gt; If your organization has specific standards or requirements, you can create custom lenses. For example, enterprises can use this to encode their security policies or compliance requirements directly into the review process. These custom lenses can also be shared with other accounts or your entire AWS organization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Review Templates:&lt;/strong&gt; These help in standardization. You can create review templates in AWS WA Tool that contain pre-filled answers for Well-Architected Framework and custom lens best practice questions. Well-Architected review templates reduce the need to manually fill in the same answers for best practices that are common across multiple workloads when performing a Well-Architected review, and they help drive consistency and standardization of best practices across teams and workloads. You can create a review template to answer common best practice questions or create notes, which can be shared with another IAM user or account, or an organization or organizational unit in the same AWS Region. You can define a workload from a review template, which helps scale common best practices and reduce redundancy across your workloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Profiles:&lt;/strong&gt; You can create profiles to provide your business context, and identify goals you'd like to accomplish when performing a Well-Architected review. AWS Well-Architected Tool uses the information gathered from your profile to help you focus on a prioritized list of questions that are relevant to your business during the workload review. Attaching a profile to your workload also helps you see which risks are prioritized for you to address with your improvement plan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Funding:&lt;/strong&gt; AWS pays you for fixing your issues! Businesses can receive $5,000 in AWS credits to offset the cost of remediating issues identified during an AWS Well-Architected Framework (WAF) Review as of this writing. To qualify, you must partner with a certified Well-Architected Partner to conduct the review. Check with your AWS partner or AWS TAM on this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Insights
&lt;/h2&gt;

&lt;p&gt;Let me share some practical wisdom from actually using this framework:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start Small:&lt;/strong&gt; Don't try to review your entire infrastructure in one sitting. Pick one critical workload and go through the exercise thoroughly. Start with non-prod environment to get a hang of it. Maybe your newest project where you can actually implement changes quickly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The First Review Is Always Humbling:&lt;/strong&gt; Even systems designed by experienced architects will have gaps. That's okay — that's the point. The framework represents the collective wisdom of thousands of AWS architects. It's supposed to teach you something. Also, cloud is always evolving and bringing in better ways to do things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make It a Team Activity:&lt;/strong&gt; Running through the questions with your team is way more valuable than having one person fill it out alone. For example, the discussion around "How do you test reliability?" might reveal that the developers thought they had comprehensive testing, but ops was manually verifying deployments. This insight alone can prevent a future incident.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;High-Risk Doesn't Always Mean "Drop Everything":&lt;/strong&gt; Context matters. For example, tool can flag a development environment for not having multi-region failover. Technically a risk, but for a dev environment? Not worth the complexity. Use your judgment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The 80/20 Rule Applies:&lt;/strong&gt; Pareto principle comes into play here also, just like almost everywhere else. About 20% of the recommendations typically address 80% of your actual risk. Focus on the high-risk items first, especially around security and reliability. You can optimize costs and performance iteratively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Revisit Regularly:&lt;/strong&gt; Your architecture isn't static, and neither should your Well-Architected reviews be. It is recommended to conduct quarterly reviews for production workloads. New features get added, traffic patterns change, and AWS releases new services that might better address your needs. For example, security groups can be shared across VPCs and accounts now, making their centralized management possible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use It for New Projects:&lt;/strong&gt; Here's a pro tip — run through the relevant questions before you build something new. Use the Well-Architected questions as a checklist during design phases. It's way easier to build security in from the start than to retrofit it later.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real Example
&lt;/h2&gt;

&lt;p&gt;Let me tell you about a project from some time back. A client had migrated one of their data centers to cloud 3 years back, but there was no proper cloud team and very few things were properly configured. For example, they did not even have default EBS encryption enabled, which can be done easily in seconds at account level. Their monthly AWS bill was creeping up, and they couldn't figure out why. I was mainly engaged for cost optimization, with secondary emphasis on everything else.&lt;/p&gt;

&lt;p&gt;We ran a Well-Architected review focusing heavily on the Cost Optimization first. The review revealed several issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple instances in shut down state for months or years.&lt;/li&gt;
&lt;li&gt;Their RDS instances were over-provisioned for average load.&lt;/li&gt;
&lt;li&gt;Hundreds of unattached EBS volumes.&lt;/li&gt;
&lt;li&gt;Their Backup was being retained forever (20 TB of snapshots). They had no such compliance requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Cost Optimization pillar helped us identify these issues systematically. Within two months, we had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decommissioned unused servers&lt;/li&gt;
&lt;li&gt;Right-sized EC2 and RDS instances&lt;/li&gt;
&lt;li&gt;Deleted unattached EBS volumes&lt;/li&gt;
&lt;li&gt;Modified backup retention policy to 3 weeks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result? Their monthly bill showed good improvement. More importantly, systems and a regular rhythm was put in place. During this process, we enabled Compute Optimizer, Trusted Advisor and Cost Optimization Hub. We set up budget alerts and cost anomaly detection alerts. Teams started receiving notifications to gain visibility into their bills. A bi-weekly call was set up in place to constantly review the findings and assign action items.&lt;/p&gt;

&lt;p&gt;After this, we did a review for other pillars and created a comprehensive action plan. The pillars were customized so that instead of individual workloads, we were focusing on the entire landing zone. Now they have a well architected landing zone, lower bill, and fewer incidents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started Today
&lt;/h2&gt;

&lt;p&gt;If you want to try this out, here's what you should do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log into the AWS Console and search for "Well-Architected Tool"&lt;/li&gt;
&lt;li&gt;Click "Define workload" and pick something meaningful but manageable&lt;/li&gt;
&lt;li&gt;Set aside 2–3 hours with your team&lt;/li&gt;
&lt;li&gt;Go through one or two pillars thoroughly rather than rushing through all six&lt;/li&gt;
&lt;li&gt;Focus on understanding the "why" behind each question&lt;/li&gt;
&lt;li&gt;Generate your report and prioritize the high-risk items&lt;/li&gt;
&lt;li&gt;Create tickets or action items for addressing the gaps&lt;/li&gt;
&lt;li&gt;Schedule your next review in 3–6 months&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The framework isn't magic — it won't automatically fix your architecture. But it will give you a systematic way to think about your systems, identify blind spots, and continuously improve. And that's exactly what separates good cloud architectures from great ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources for Deep Dive
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Documentation:&lt;/strong&gt; &lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/framework/welcome.html" rel="noopener noreferrer"&gt;AWS Well-Architected Framework&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Well Architected Lab:&lt;/strong&gt; &lt;a href="https://wellarchitectedlabs.com/" rel="noopener noreferrer"&gt;AWS Well-Architected Labs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Well-Architected Tool Workshop:&lt;/strong&gt; &lt;a href="https://catalog.workshops.aws/well-architected-tool/en-US" rel="noopener noreferrer"&gt;Workshop&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Have you used the Well-Architected Framework? Have you found any surprising issues in your WA reviews? I'd love to hear about it in the comments.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudcomputing</category>
      <category>architecture</category>
      <category>devops</category>
    </item>
    <item>
      <title>Agentic AI — Automating AWS Tasks with Amazon Bedrock Agents and a Custom Web App</title>
      <dc:creator>Sauveer Ketan</dc:creator>
      <pubDate>Thu, 18 Sep 2025 19:32:22 +0000</pubDate>
      <link>https://dev.to/sauveer_ketan/agentic-ai-automating-aws-tasks-with-amazon-bedrock-agents-and-a-custom-web-app-3e56</link>
      <guid>https://dev.to/sauveer_ketan/agentic-ai-automating-aws-tasks-with-amazon-bedrock-agents-and-a-custom-web-app-3e56</guid>
      <description>&lt;p&gt;Amazon Bedrock Agents are conversational AI applications that extend the capabilities of Large Language Models (LLMs) by enabling them to understand multi-step user requests and orchestrate actions across different systems. They act as intelligent intermediaries, breaking down complex tasks, reasoning about the required steps, maintaining conversation context, and using defined tools to interact with external services to fulfill user goals.&lt;/p&gt;

&lt;p&gt;You can leverage Bedrock Agents extensively for AWS operations by integrating them with AWS services via Action Groups linked to tools like Lambda functions or APIs. This allows agents to perform automated resource management tasks, such as listing EC2 instances, enumerating S3 buckets, or creating new servers based on natural language commands. They can also retrieve operational information for troubleshooting or provide a simplified, conversational interface for users to interact with and manage AWS resources without needing deep technical expertise or direct console access.&lt;/p&gt;

&lt;p&gt;An action group defines actions that the agent can help the user perform. For example, listing EC2 servers. You define the parameters and information that the agent must elicit from the user for each action in the action group to be carried out. You also decide how the agent handles the parameters and information it receives from the user and where it sends the information it elicits from the user. Knowledge bases can also be configured optionally.&lt;/p&gt;

&lt;p&gt;This article will guide you through setting up a Bedrock Agent with Action Groups and then building a custom web application around it. We will create and integrate AWS Lambda functions that allow the agent to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List existing EC2 instances&lt;/li&gt;
&lt;li&gt;List existing S3 buckets
&lt;/li&gt;
&lt;li&gt;Create a new EC2 instance with specified parameters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This demonstrates how you can leverage large language models (LLMs) orchestrated by Bedrock Agents to perform actions in your AWS account based on natural language commands, and then provide a user-friendly interface for those actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To follow along with this tutorial, you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS Account&lt;/strong&gt;: An active AWS account with necessary permissions to create and manage Bedrock Agents, IAM roles, Lambda functions, EC2 instances, and view S3 buckets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Bedrock Access&lt;/strong&gt;: Ensure Amazon Bedrock is enabled in your account and the AWS region you plan to use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bedrock Model Access&lt;/strong&gt;: You need access to a supported model for Bedrock Agents, such as Anthropic Claude. Follow the steps in the Bedrock console under "Model access" to enable this&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Enable Bedrock Model Access
&lt;/h2&gt;

&lt;p&gt;Under the Amazon Bedrock console, go to "Model access" (bottom left). Click "Modify access," select the Anthropic Claude Sonnet 3.5 models, and submit. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: If you are in India, your payment mode should be invoice-based, not card-based, to avoid errors about an invalid payment mode.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After a few minutes, you will get access, and these models will become active in your console.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Create AWS Lambda Functions
&lt;/h2&gt;

&lt;p&gt;We will create three Lambda functions. Each function performs a specific AWS task and returns the result in a format Bedrock Agents expect. Deploy these functions in the same AWS region where you configure your Bedrock Agent.&lt;/p&gt;

&lt;p&gt;You can find the complete Python code for these functions in the following GitHub repository:&lt;br&gt;
&lt;strong&gt;&lt;a href="https://github.com/sauveerk/projects/tree/main/Code/Gen-AI-Bedrock-Agents" rel="noopener noreferrer"&gt;https://github.com/sauveerk/projects/tree/main/Code/Gen-AI-Bedrock-Agents&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Lambda Function 1: List EC2 Instances
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;This function, &lt;code&gt;action_group_ec2&lt;/code&gt;, lists the IDs of all EC2 instances in the current region&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM Permissions Needed&lt;/strong&gt;: &lt;code&gt;ec2:DescribeInstances&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Lambda Function 2: List S3 Buckets
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;This function, &lt;code&gt;action_group_s3&lt;/code&gt;, lists the S3 buckets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM Permissions Needed&lt;/strong&gt;: &lt;code&gt;s3:ListBuckets&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Lambda Function 3: Create EC2 Instance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;This function, &lt;code&gt;action_group_create_ec2&lt;/code&gt;, creates an EC2 server. It takes the instance type and region as parameters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM Permissions Needed&lt;/strong&gt;: &lt;code&gt;ec2:RunInstances&lt;/code&gt;, &lt;code&gt;ec2:DescribeInstances&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Step 3: Create Bedrock Agent and Action Groups
&lt;/h2&gt;

&lt;p&gt;In the Bedrock console, navigate to "Agents" under "Builder Tools." Click "Create agent." Give your agent a name and a description (e.g., "AWS Resource Manager Agent").&lt;/p&gt;

&lt;p&gt;After the agent is created, click on it and select "Edit" in the Agent Builder.&lt;/p&gt;

&lt;p&gt;Select "Create and use a new service role" and define instructions for the agent. This prompt guides the agent's behavior. Something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are an AI assistant that can help users manage their AWS resources. You can list EC2 instances, list S3 buckets, and create new EC2 instances.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the "Action Groups" section, click "Add." Give the action group a name (e.g., &lt;code&gt;Ec2ManagementActions&lt;/code&gt;). Provide a description (e.g., "Actions for managing EC2 resources"). Under "Action group invocation," select "Define with Function details."&lt;/p&gt;

&lt;p&gt;Select "Quick create a new Lambda function." This will create a Lambda function and add a resource-based policy to it so that it can be invoked by the Bedrock agent. Provide the function name and description. No parameters are required for this function as it only lists EC2 instances.&lt;/p&gt;

&lt;p&gt;You can select "Enable confirmation of action group function" here. The agent will ask for confirmation before proceeding with the invocation. It is disabled by default. Click save to create the action group.&lt;/p&gt;

&lt;p&gt;Click on the action group again. You can see the newly created Lambda function. Click "View," which will take you to the Lambda console. Copy the code from the GitHub repo given above and paste it into the function, then deploy it.&lt;/p&gt;

&lt;p&gt;Go to the Lambda function configuration, increase the timeout duration. Then, go to the "Permissions" section and add additional permissions to the role being used by the Lambda function. In this case, it is &lt;code&gt;ec2:DescribeInstances&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now the Lambda function is ready. You can test it by giving a test event in the appropriate format:&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;"messageVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"function"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bedrock-agent-list-ec2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"inputText"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"sessionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"888900372248953"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"agent"&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"agent-aws-resources"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DRAFT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VGGUJUPSEC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"alias"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TSTALIASID"&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;"actionGroup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"action_group_ec2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sessionAttributes"&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;"promptSessionAttributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow similar steps to add the other two action groups. For the EC2 create function, remember to add the parameters also.&lt;/p&gt;

&lt;p&gt;Save the agent in the agent builder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Test the Bedrock Agent
&lt;/h2&gt;

&lt;p&gt;Once your agent is created, you can test its ability to invoke your Lambda functions from the Bedrock console. On the agent details page, click the "Prepare" button. This compiles the agent configuration. Wait for the status to show "Prepared." Click the Prepare button after configuring the agent.&lt;/p&gt;

&lt;p&gt;Stay on the agent details page and use the "Test" panel on the right. Because we enabled confirmation for these action groups, it will decide which action groups to use and then ask us for confirmation. Confirm these.&lt;/p&gt;

&lt;p&gt;After invoking these two action groups and corresponding Lambda functions, the agent returns the response.&lt;/p&gt;

&lt;p&gt;If you click on "Show trace," you can see the thought process and steps used by the agent. It shows two trace steps, which you can expand to see the details.&lt;/p&gt;

&lt;p&gt;Let's take it one step further. Prompt to create an EC2 instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create a t2.nano EC2 instance in ap-south-1 region.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the AWS console, you can also see the same instance ID and IP address.&lt;/p&gt;

&lt;p&gt;In the trace, we can see how the agent has identified the correct action group and invoked it.&lt;/p&gt;

&lt;p&gt;As I had provided all required parameters in the prompt, it was able to directly invoke the third Lambda. Otherwise, it will ask for parameters. Let's try that by giving the prompt: "create an ec2 server."&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2 — Agentic AI Web App for End Users
&lt;/h2&gt;

&lt;p&gt;The article above outlined how to create a powerful agent that can manage AWS resources using natural language. If we want to use this for regular use, for example by operations teams, we can create a web app around it with a nice web interface. This allows users to interact with the agent without needing to access the Bedrock console.&lt;/p&gt;

&lt;p&gt;In this section, we will create a simple web app around our agent. The full app code is present here, and it can be cloned:&lt;br&gt;
&lt;strong&gt;&lt;a href="https://github.com/sauveerk/projects/tree/main/Code/WebApp-Bedrock-Agent" rel="noopener noreferrer"&gt;https://github.com/sauveerk/projects/tree/main/Code/WebApp-Bedrock-Agent&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1: Modify Action Group Behavior and Create Agent Alias
&lt;/h3&gt;

&lt;p&gt;We had enabled the confirmation to test the behavior of our agent by selecting "Enable confirmation of action group function" for action groups that list S3 and EC2 servers. Let's disable this to simplify our web app code.&lt;/p&gt;

&lt;p&gt;Go to the action group that lists EC2 servers. Check the "disabled" button and click save. Verify that this is also disabled for other action groups. Save the agent and prepare it.&lt;/p&gt;

&lt;p&gt;To use the agent programmatically, we need to create an alias. On the agent page, under the "Alias" section, click "Create." Give it a name and keep the default choice — "Create a new version and associate it to this alias." Click on "Create alias."&lt;/p&gt;

&lt;p&gt;Click on the newly created alias, and it will open the alias page. Use the "Test" button to verify that you are not being asked for confirmation.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2: Python Program
&lt;/h3&gt;

&lt;p&gt;This is the Python code using which we can invoke our model programmatically:&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;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;botocore.exceptions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ClientError&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;invoke_bedrock_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Invoke a Bedrock agent with the given prompt.

    Args:
        prompt (str): The prompt to send to the agent

    Returns:
        Optional[Dict]: The agent&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s response or None if an error occurs
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;bedrock_agent_runtime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bedrock-agent-runtime&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&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;Error creating Bedrock client: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error: Prompt must be a non-empty string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&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;bedrock_agent_runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;agentId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR_AGENT_ID&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;agentAliasId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;YOUR_AGENT_ALIAS_ID&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;sessionId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;inputText&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Handle the event stream response
&lt;/span&gt;        &lt;span class="n"&gt;full_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;completion&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;chunk&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&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;chunk_data&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;chunk&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;bytes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;full_response&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;chunk_data&lt;/span&gt;

        &lt;span class="c1"&gt;# Parse the complete response if needed
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;full_response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&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;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;full_response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSONDecodeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="c1"&gt;# If response is not JSON, return as plain text
&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;response&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;full_response&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error: Empty response from Bedrock Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ClientError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&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 API Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&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;Unexpected error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Enter your prompt: &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="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;invoke_bedrock_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Agent Response:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&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="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&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;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&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="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed to get response from agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's test the program. We can see that it responds correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Create Python Flask Web App
&lt;/h3&gt;

&lt;p&gt;Use this project structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;your_project_directory/
├── app.py
├── agent_invoke.py
└── templates/
    └── index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the code for &lt;code&gt;app.py&lt;/code&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;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;agent_invoke&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;invoke_bedrock_agent&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;extract_response_text&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="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&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="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;response_dict&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;loads&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="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;response_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;

        &lt;span class="c1"&gt;# Check for 'response' field in the dictionary
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;response&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response_dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;response&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="c1"&gt;# Check for 'completion' field as fallback
&lt;/span&gt;        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;completion&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response_dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;completion&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="c1"&gt;# Return the full response if no known fields are found
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response_dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&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;Error processing response: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&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="n"&gt;methods&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;GET&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;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;home&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="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&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;user_prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;raw_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;invoke_bedrock_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_prompt&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="nf"&gt;extract_response_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index.html&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="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the code for &lt;code&gt;index.html&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Bedrock Agent Interface&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;800px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nt"&gt;textarea&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#007bff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0056b3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;.response-box&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#ccc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;white-space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pre-wrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Bedrock Agent Interface&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"prompt"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Enter your prompt:&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"prompt"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"prompt"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ request.form.get('prompt', '') }}&lt;span class="nt"&gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;

        {% if response %}
        &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Agent Response:&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"response-box"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ response }}&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        {% endif %}
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Test the Web App
&lt;/h3&gt;

&lt;p&gt;Go to the project directory and run the program using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will start a local server. You can access the app on &lt;code&gt;localhost:5000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's try the prompt "How many ec2 servers I have." Press submit. You can see the response on the web page.&lt;/p&gt;

&lt;p&gt;Let's try another prompt: "Create an ec2 server of type t2.micro in ap-south-1 region."&lt;/p&gt;

&lt;p&gt;We can see that the server has been created. Let's verify it in the AWS console.&lt;/p&gt;

&lt;p&gt;If you check again, you will now have two servers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Clean Up
&lt;/h3&gt;

&lt;p&gt;Remember to clean up the resources you created to avoid incurring unnecessary costs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terminate any EC2 instances created during testing&lt;/li&gt;
&lt;li&gt;Delete the Bedrock Agent&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 6: Additional Considerations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Prompt Quality&lt;/strong&gt;: The quality of your prompt significantly impacts the agent's ability to correctly understand user intent and use the right tools. Be clear and specific.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Throttling&lt;/strong&gt;: As there are service quotas applicable to the LLMs, you might face throttling errors. Check the service quota, wait, and retry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User Interface&lt;/strong&gt;: Creating custom web applications that leverage Bedrock Agents for AWS operations is a very compelling use case for many organizations. It enhances accessibility by enabling users who are not experts in the AWS console, CLI, or SDKs to perform pre-defined operational tasks. These can also assist technical teams and increase operational efficiency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customization vs. Amazon Q&lt;/strong&gt;: Amazon Q in the console is a great starting point and provides general AWS assistance. AWS will keep integrating more functionality into Q in console and also Gen AI capabilities in other services. However, when you define action groups and knowledge bases for your agent that are unique to your organization, you can provide a tailored user experience and workflow integration. A custom web application built on Bedrock Agents allows you to create a highly specialized, integrated, and controlled operational tool tailored precisely to your organization's unique environment, processes, and user needs, going beyond the generic capabilities of a console-level assistant. It's about building your organization's intelligent assistant for its specific operational challenges.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article demonstrates how to build intelligent AWS automation using Amazon Bedrock Agents with custom web interfaces.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>bedrock</category>
      <category>agenticai</category>
      <category>aiops</category>
    </item>
    <item>
      <title>The Hidden Realities of Cloud Migration: Lessons from the Trenches</title>
      <dc:creator>Sauveer Ketan</dc:creator>
      <pubDate>Sun, 13 Jul 2025 19:06:03 +0000</pubDate>
      <link>https://dev.to/sauveer_ketan/the-hidden-realities-of-cloud-migration-lessons-from-the-trenches-54cb</link>
      <guid>https://dev.to/sauveer_ketan/the-hidden-realities-of-cloud-migration-lessons-from-the-trenches-54cb</guid>
      <description>&lt;p&gt;In theory, cloud migration is straightforward. You assess, plan, and execute. In practice, however, it's far more complex. While playbooks provide a solid framework, the real world often throws situations that demand adaptability and human intervention.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Textbook View: Assess, Mobilize, Migrate
&lt;/h2&gt;

&lt;p&gt;AWS's migration playbook outlines three distinct phases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Assess&lt;/strong&gt;: Understand your current environment and build a compelling business case for migration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mobilize&lt;/strong&gt;: Prepare your AWS foundation, define your architecture, and finalize your migration plan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migrate&lt;/strong&gt;: Execute the move of your workloads and data to AWS. In this phase, migration is divided into two stages, initialize and implement.&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%2F58hjy16wwz0zglbgzxq3.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%2F58hjy16wwz0zglbgzxq3.png" alt="Phases of a large Migration" width="800" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This structured approach sounds simple enough. Yet, once you start planning, you quickly realize that cloud migration success isn't just about technology; it's equally about people, processes, and preparedness.&lt;/p&gt;

&lt;p&gt;When service providers present their proposals to clients, this difference in exposure often leads to heated discussions. Clients with no prior migration experience try to downplay the role of "known unknowns" and "unknown unknowns", they look for shorter timeline with fewer number of people. While the service providers with prior experience try to accommodate these unknowns based on their prior experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools
&lt;/h2&gt;

&lt;p&gt;AWS provides a robust suite of tools to streamline cloud migrations, encompassing discovery, planning, and execution phases. &lt;strong&gt;Migration Evaluator&lt;/strong&gt;, used in the initial Assess phase, is crucial for building a data-driven business case. &lt;strong&gt;AWS Migration Hub&lt;/strong&gt; acts as a central console, offering a unified view of your migration progress and integrating with various services. For detailed assessment and dependency mapping, &lt;strong&gt;AWS Application Discovery Service&lt;/strong&gt; helps gather crucial information about on-premises servers, applications, and their interdependencies. When it comes to the actual "lift-and-shift" of servers and virtual machines, &lt;strong&gt;AWS Application Migration Service (AWS MGN)&lt;/strong&gt; is the go-to solution, automating server replication and cutover with minimal downtime, making it efficient for migrating diverse workloads, including those you might find with legacy systems.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Cloud Migration Factory on AWS solution&lt;/strong&gt; is designed to coordinate and automate manual processes for large-scale migrations involving a substantial number of applications. This solution helps enterprises improve performance and prevents long cutover windows by providing an orchestration platform for migrating workloads to AWS at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Transform for VMware&lt;/strong&gt; is a newer service in the toolkit. It is an agentic AI service which automates application discovery and dependency mapping, network translation, wave planning, and server migration while optimizing EC2 instance selection to accelerate VMware workloads migration.&lt;/p&gt;

&lt;p&gt;While AWS provides a robust suite of native tools, the broader cloud migration ecosystem also includes a variety of &lt;strong&gt;AWS Partner solutions&lt;/strong&gt;. Cloudamize, modelizeIT, Flexera, RiverMeadow, etc., are a few of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Really Happens: Real-World Cloud Migration
&lt;/h2&gt;

&lt;p&gt;Despite rigorous preparation, detailed runbooks, and sophisticated migration tooling, we've consistently encountered numerous challenges. Here are few concrete lessons learned from actual cloud migrations (many of them) — issues that only became clear through firsthand experience. Here my focus is on rehost (lift-and-shift) migrations only.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Gaps in Dependency Mapping
&lt;/h3&gt;

&lt;p&gt;Even with advanced automated discovery tools and thorough application team walkthroughs, some critical interdependencies inevitably slipped through the cracks. These hidden connections became glaringly apparent only during the high-pressure cutover windows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;: Always supplement automated discovery tool outputs with detailed, in-depth application interviews and meticulous system-level dependency validation.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Overprovisioning and Cost Optimization
&lt;/h3&gt;

&lt;p&gt;A common initial misstep was placing all production databases on expensive io2 volumes, based on an assumption of high IOPS needs. In reality, most systems didn't require such high performance.&lt;/p&gt;

&lt;p&gt;Mid-migration, we shifted our default storage strategy to gp3. Post-migration, we diligently monitored actual IOPS metrics and only upgraded volumes where necessary. We also planned and migrated the io2 volumes back to gp3, which is easy to do in AWS.&lt;/p&gt;

&lt;p&gt;On similar lines, application teams sometimes want to have as much CPU and RAM as on-premises, fearing application performance degradation. It should be based on rightsizing recommended by the assessment tools, and decision should not be left in the hands of application teams (this requires senior stakeholders' buy-in and support). Resizing ec2 instances is easy and quick in AWS if needed post-migration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommendation&lt;/strong&gt;: Baseline your actual IOPS needs using actual metrics and avoid making assumptions about storage requirements. Rightsize ec2 instances based on assessment tool recommendation.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. COTS Applications Can Be Complicated
&lt;/h3&gt;

&lt;p&gt;Commercial Off-the-Shelf (COTS) applications often introduce unique hurdles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some had unsupported licensing models in AWS.&lt;/li&gt;
&lt;li&gt;Others demanded minimum CPU or RAM allocations and did not work. Despite low actual utilization and hence smaller ec2 size recommended by the assessment tools.&lt;/li&gt;
&lt;li&gt;Certain applications, like Tableau, could not be lifted and shifted directly due to architectural or licensing constraints.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Takeaway&lt;/strong&gt;: Thoroughly review vendor support statements and validate technical feasibility with Proof of Concepts (PoCs) early in the project lifecycle. Discuss the plan with the vendor and engage them for migration window. Involve application teams during test cutovers, full-fledged testing might not be possible at this stage, see if some sanity tests can be performed.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Unexpected Machine Password Resets on Windows
&lt;/h3&gt;

&lt;p&gt;A subtle yet disruptive issue involved monthly Windows machine password resets. Systems failed to join Active Directory during migration if their machine account password changed during the cutover window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;: Implement pre-checks for password age and force resets before migration where necessary to ensure smooth domain joining.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Third-Party Tooling and Licensing
&lt;/h3&gt;

&lt;p&gt;Many organizations rely on various third-party tools (monitoring, security, etc.) Tools crucial for post-migration access and verification, such as BeyondTrust PowerBroker, often had limited number of licenses and reassignment were required. This bottleneck caused significant delays in validation efforts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;: Proactively align tool licensing with peak migration activity requirements to prevent unexpected hold-ups.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. fstab Issues in Linux Systems
&lt;/h3&gt;

&lt;p&gt;Outdated fstab entries for outdated NFS mounts or Universally Unique Identifiers (UUIDs) for disks might lead to boot failures in Linux systems. In some cases, manual intervention via rescue mode was required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommendation&lt;/strong&gt;: Gracefully reboot servers few days prior to cutover. This simple step can surface latent boot-time issues before they impact your migration window.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. On-Premise NFS Mounts Introduced Latency
&lt;/h3&gt;

&lt;p&gt;We discovered that some applications, after migration, were referencing static content over Network File System (NFS) mounts from on-premise NFS servers. This introduced significant latency, impacting performance.&lt;/p&gt;

&lt;p&gt;These mounts had to be migrated to AWS services like Amazon EFS or FSx. AWS Storage Gateway is also an option for hybrid cloud scenarios where some on-premise data might still need to be accessed. In some instances, we needed to build fallback mechanisms directly into the applications. In other cases, applications had to be rolled back, to be migrated later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommendation&lt;/strong&gt;: This takes us back to first point above, i.e., dependency mapping and proper testing before migration where needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Anti-Virus Interference with NFS
&lt;/h3&gt;

&lt;p&gt;In one migration, a perplexing performance issue arose when anti-virus software was found to be scanning NFS-mounted directories. While ping and traceroute showed no network issues, application performance dropped dramatically. Because whenever anything was being uploaded to NFS server, anti-virus software was scanning it. The servers running anti-virus software were low on memory, a graceful reboot, fixed the issue. It took few hours and multiple people to figure the issue, as no single person had access to all the components involved. This is an excellent example of "unknown unknowns."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mitigation&lt;/strong&gt;: Support team incorporated in their playbook monthly anti-virus server reboots to alleviate this subtle yet impactful problem. Similar measures will be useful for other centralized tooling servers.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Legacy Systems Need Special Handling
&lt;/h3&gt;

&lt;p&gt;Older systems, such as Windows 2008 servers, RHEL 5, etc., presented unique challenges. They could be migrated to older Xen based hypervisor only and hence had fewer options for ec2 servers. In a very special case, we migrated a Windows 2003 server with 1 GB of RAM. There was limited support available for these legacy OS and sometimes it was multiple hit and trials to successfully migrate them. Some configuration changes might also be required, for example, installing ENA drivers on RHEL 6 servers if we want to put them on nitro instances — missing which might lead to issues and troubleshooting.&lt;/p&gt;

&lt;p&gt;If OS upgrade is an option, better consider that during migration planning rather than relying on legacy OS.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. Decommission
&lt;/h3&gt;

&lt;p&gt;In one migration, after rigorous assessment, it came out that out of around 500 servers, around 80 could be decommissioned. Leading to huge savings. Here is an interesting idea — even if you are not migrating to cloud in near future, why not do a full-fledged assessment periodically to realize such waste of resources.&lt;/p&gt;

&lt;p&gt;Retire is one of the 7 Rs of migration (Retire, Retain, Rehost, Relocate, Replatform, Repurchase, and Refactor) and a very important one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Operational &amp;amp; Technical Observations
&lt;/h2&gt;

&lt;p&gt;Beyond specific application-level issues, several broader operational and technical challenges emerged:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AWS Limits&lt;/strong&gt;: In few migrations, we were migrating hundreds of servers per week. We frequently encountered AWS service limits, including those for snapshots, API calls, and AWS MGN (or CloudEndure before that). This necessitated requesting additional accounts and quota increases. Plan for these in advance and get them increased beforehand.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disk Attachment Limits&lt;/strong&gt;: Some source systems exceeded EC2 disk attachment limits, requiring architectural restructuring to accommodate this. This is an edge case, but often these systems are critical systems. It should be taken as a key consideration during source assessment and target architecture design within the Mobilize phase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Oracle ASM FD&lt;/strong&gt;: We had multiple Oracle ASM FD servers. During one of the migrations after troubleshooting with AWS, it came out that while Oracle ASM (Automatic Storage Management) was supported by CloudEndure, not ASM FD (Filter Drive). AWS provided amazing support and gave a fix for this after few weeks, and we were able to successfully migrate these servers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;F5 iRules and ALB&lt;/strong&gt;: Load balancer behaviors differed between on-premises F5 iRules and AWS ALB. During planning it came out that this will lead to refactoring of the applications. One example of this is, client IP handling, which was required by few applications directly, and it was working fine with on-prem F5 which was using direct pass-through, but ALB puts them in x-forwarded-for headers. These kind of scenarios provide opportunity to adopt other cloud-native features and modernize the applications.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hardcoded IPs&lt;/strong&gt;: A common problem, especially in development environments, was hardcoded IP addresses within TLS certificates and application configurations, complicating the migration process. These were often noticed after the migration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ENI Pre-Provisioning&lt;/strong&gt;: In specific scenarios, Elastic Network Interfaces (ENIs) had to be pre-created and preserved to ensure static IPs are known beforehand, so that they can be configured in firewalls and load balancers to avoid downtime. If this step is missed, it might lead to backout of the applications.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Process and Collaboration Insights
&lt;/h2&gt;

&lt;p&gt;Effective process and strong collaboration were vital to navigating these complexities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Weekly Lessons Learned (LL) calls&lt;/strong&gt;: We instituted weekly LL calls every Tuesday post-migration to review and capture insights.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centralized Documentation&lt;/strong&gt;: All issues, along with their resolutions, were meticulously documented and stored in a central SharePoint portal for easy access and reuse.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mandatory Review&lt;/strong&gt;: All migration engineers were required to read, contribute to, and reuse this living documentation. They were randomly asked to give a walkthrough of these.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS TAM Coordination&lt;/strong&gt;: Close coordination with AWS Technical Account Managers (TAMs) proved invaluable in resolving roadblocks and accelerating issue resolution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runbook Updates&lt;/strong&gt;: Runbooks were continuously updated after each migration wave, incorporating real-world field feedback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Roster Updates&lt;/strong&gt;: Multiple teams need to be available during migration window — various infra support teams, application teams, vendor support for COTS applications, etc. Based on lessons learnt we rigorously updated our rosters and ensured the engagement.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recommendations for Future Migrations
&lt;/h2&gt;

&lt;p&gt;Based on our experiences, here are key recommendations for any organization embarking on a cloud migration journey:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Expect Surprises&lt;/strong&gt;: Always anticipate the unexpected, especially when dealing with legacy configurations and potential human errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create Buffer Bandwidth&lt;/strong&gt;: Build in buffer capacity for both your engineering team and your project schedule to absorb unforeseen challenges. A single issue, and one of your engineers might be engaged for hours to fix that. While you had planned that she will handle 10 servers during migration!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make Graceful Pre-Migration Reboots Standard&lt;/strong&gt;: Implement pre-migration reboots as a standard procedure to surface latent boot-time issues before they impact your cutover window.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate Tools and Licensing&lt;/strong&gt;: Thoroughly validate all tools and their licensing requirements for both pre- and post-migration activities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document Every Issue&lt;/strong&gt;: Treat every encountered issue and its fix as a critical piece of your live migration playbook. Document everything diligently. Diligently have lessons learnt discussions after every wave. Update your runbooks and rosters accordingly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Cloud migration is more than just a technological shift; it's a comprehensive change management initiative that impacts systems, teams, and deeply ingrained assumptions. No matter how meticulously you plan, real-world migrations will invariably expose issues that only practical experience can help you resolve.&lt;/p&gt;

&lt;p&gt;The key to a successful migration lies in your ability to focus on learning, diligent documentation, and constant adaptation. That's what truly transforms a good migration strategy into a great one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are some unexpected challenges you've faced during cloud migrations, and how did you overcome them?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>migration</category>
      <category>rehost</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
