<?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: Alan Blockley</title>
    <description>The latest articles on DEV Community by Alan Blockley (@alanblockley).</description>
    <link>https://dev.to/alanblockley</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%2F907470%2F8dd72567-eed3-4ee6-ab70-ea72bb44f8ac.jpeg</url>
      <title>DEV Community: Alan Blockley</title>
      <link>https://dev.to/alanblockley</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alanblockley"/>
    <language>en</language>
    <item>
      <title>Teaching AI to Behave: Structured Output with Amazon Bedrock in Production</title>
      <dc:creator>Alan Blockley</dc:creator>
      <pubDate>Sat, 28 Feb 2026 07:07:05 +0000</pubDate>
      <link>https://dev.to/aws-builders/teaching-ai-to-behave-structured-output-with-amazon-bedrock-in-production-31io</link>
      <guid>https://dev.to/aws-builders/teaching-ai-to-behave-structured-output-with-amazon-bedrock-in-production-31io</guid>
      <description>&lt;p&gt;There’s a moment when generative AI moves from interesting to useful. It usually happens the first time you integrate an LLM into a real system. Not a demo. Not a playground. An actual workflow with downstream dependencies.&lt;/p&gt;

&lt;p&gt;That’s when the problem appears. The model talks too much, too loosely, too creatively. Suddenly your architecture depends on parsing paragraphs.&lt;/p&gt;

&lt;p&gt;That’s where structured output changes everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem most builders hit
&lt;/h2&gt;

&lt;p&gt;When we first introduce AI into an application, the natural instinct is simple.&lt;/p&gt;

&lt;p&gt;Call the model.&lt;br&gt;&lt;br&gt;
Get text.&lt;br&gt;&lt;br&gt;
Extract what we need.&lt;/p&gt;

&lt;p&gt;It works. Until it doesn’t.&lt;/p&gt;

&lt;p&gt;To explore this properly, consider a platform that processes unstructured input, evaluates it using AI, and produces structured signals that feed downstream workflows.&lt;/p&gt;

&lt;p&gt;In that platform, AI isn’t decorative. It sits inside a workflow that turns unstructured input into structured signals the system can trust.&lt;/p&gt;

&lt;p&gt;In other words, the AI is part of a pipeline. It is not the destination.&lt;/p&gt;

&lt;p&gt;Free-form text becomes a liability.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You end up writing brittle regex.
&lt;/li&gt;
&lt;li&gt;You add defensive parsing.
&lt;/li&gt;
&lt;li&gt;You introduce silent failures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And worst of all, you lose trust in the result.&lt;/p&gt;

&lt;p&gt;So the goal wasn’t “use AI”.&lt;/p&gt;

&lt;p&gt;The goal was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Make AI behave like a component.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Why “respond with JSON” isn’t enough
&lt;/h2&gt;

&lt;p&gt;Most builders start by telling the model to return JSON.&lt;/p&gt;

&lt;p&gt;It feels structured. It looks structured. It often works.&lt;/p&gt;

&lt;p&gt;Until it doesn’t.&lt;/p&gt;

&lt;p&gt;You’ll see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Missing fields
&lt;/li&gt;
&lt;li&gt;Extra fields
&lt;/li&gt;
&lt;li&gt;Invalid JSON
&lt;/li&gt;
&lt;li&gt;Fields renamed subtly
&lt;/li&gt;
&lt;li&gt;Narrative leaking into structured sections
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That creates a new class of reliability work. AI repair logic.&lt;/p&gt;

&lt;p&gt;Ironically, the more important the workflow becomes, the more defensive code you write around the AI.&lt;/p&gt;

&lt;p&gt;Which is backwards.&lt;/p&gt;

&lt;p&gt;The system shouldn’t adapt to the model.&lt;br&gt;&lt;br&gt;
The model should adapt to the system.&lt;/p&gt;


&lt;h2&gt;
  
  
  The reliability problem behind “just return JSON”
&lt;/h2&gt;

&lt;p&gt;There’s a deeper issue that most builders eventually discover.&lt;/p&gt;

&lt;p&gt;LLMs don’t generate JSON.&lt;br&gt;&lt;br&gt;
They generate text that looks like JSON.&lt;/p&gt;

&lt;p&gt;That distinction matters.&lt;/p&gt;

&lt;p&gt;Even with careful prompting you will eventually see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Token truncation producing partial objects
&lt;/li&gt;
&lt;li&gt;Valid JSON with missing required fields
&lt;/li&gt;
&lt;li&gt;Subtle type drift (strings vs numbers)
&lt;/li&gt;
&lt;li&gt;Extra properties the system didn’t ask for
&lt;/li&gt;
&lt;li&gt;Structurally valid output that is semantically wrong
&lt;/li&gt;
&lt;li&gt;Streaming responses cut mid-structure
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are unusual. They are a natural side effect of probabilistic generation.&lt;/p&gt;

&lt;p&gt;Which means asking for JSON is not a reliability strategy.&lt;/p&gt;

&lt;p&gt;It is a hope.&lt;/p&gt;

&lt;p&gt;Production systems shouldn’t depend on hope.&lt;/p&gt;


&lt;h2&gt;
  
  
  Enter Amazon Bedrock Structured Outputs
&lt;/h2&gt;

&lt;p&gt;Amazon Bedrock’s structured output capability gives you a way to solve this properly.&lt;/p&gt;

&lt;p&gt;Instead of asking the model to return JSON, you define a schema. Bedrock enforces that schema at generation time.&lt;/p&gt;

&lt;p&gt;The shift is subtle but important.&lt;/p&gt;

&lt;p&gt;You stop hoping the model follows instructions.&lt;br&gt;&lt;br&gt;
You give the model a contract.&lt;/p&gt;

&lt;p&gt;That contract becomes architecture.&lt;/p&gt;

&lt;p&gt;If the model cannot produce valid output, the call fails early. Production systems prefer predictable failure.&lt;/p&gt;


&lt;h2&gt;
  
  
  Where it fits in the system
&lt;/h2&gt;

&lt;p&gt;Inside the platform, structured output sits within an automated evaluation workflow.&lt;/p&gt;

&lt;p&gt;At a high level:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Unstructured content is submitted via an upload or API
&lt;/li&gt;
&lt;li&gt;The backend extracts relevant text or signals
&lt;/li&gt;
&lt;li&gt;Bedrock evaluates the content against defined criteria
&lt;/li&gt;
&lt;li&gt;The result is persisted as a structured record
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before structured output, the evaluation step was fragile.&lt;/p&gt;

&lt;p&gt;After structured output, it became deterministic.&lt;/p&gt;

&lt;p&gt;The AI now returns a predictable object that the system can trust.&lt;/p&gt;


&lt;h2&gt;
  
  
  Designing the schema (this is the real work)
&lt;/h2&gt;

&lt;p&gt;The biggest learning wasn’t technical.&lt;/p&gt;

&lt;p&gt;It was architectural.&lt;/p&gt;

&lt;p&gt;A schema forces clarity.&lt;/p&gt;

&lt;p&gt;You have to decide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What the system actually needs
&lt;/li&gt;
&lt;li&gt;What the model should not invent
&lt;/li&gt;
&lt;li&gt;Which fields drive decisions versus explanation
&lt;/li&gt;
&lt;li&gt;Where uncertainty must be visible
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice, the schema focuses on signals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Overall match score
&lt;/li&gt;
&lt;li&gt;Criteria alignment breakdown
&lt;/li&gt;
&lt;li&gt;Strengths
&lt;/li&gt;
&lt;li&gt;Gaps
&lt;/li&gt;
&lt;li&gt;Confidence
&lt;/li&gt;
&lt;li&gt;Recommendation summary
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The schema intentionally biases toward signals, not prose.&lt;/p&gt;


&lt;h2&gt;
  
  
  Code sample 1: define a compact schema contract
&lt;/h2&gt;

&lt;p&gt;This is a simplified schema that captures decision signals without turning the model into a report writer.&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"additionalProperties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&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;"overall_score"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"minimum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;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;"maximum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&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;"confidence"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"minimum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;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;"maximum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&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;"strengths"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"items"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"gaps"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"items"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"recommendation"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"required"&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="s2"&gt;"overall_score"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"strengths"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gaps"&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;A couple of intentional choices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;additionalProperties: false&lt;/code&gt; keeps the output tight and predictable.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;required&lt;/code&gt; is small. You can add fields later, but start with what the system truly needs.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Code sample 2: invoke Bedrock with structured output
&lt;/h2&gt;

&lt;p&gt;This example uses the Bedrock Runtime &lt;code&gt;converse&lt;/code&gt; API. The important part is that the schema is sent as part of the request, and you treat the structured output as the primary payload.&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="n"&gt;bedrock&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-runtime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;schema&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;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object&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;additionalProperties&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;properties&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overall_score&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;number&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;minimum&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maximum&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confidence&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;number&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;minimum&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maximum&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;strengths&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;array&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;items&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&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;gaps&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;array&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;items&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&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;recommendation&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;required&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overall_score&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;confidence&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;strengths&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;gaps&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="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&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;role&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;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&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;text&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;Evaluate the input against the criteria.&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&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;Return ONLY structured output that matches the schema.&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INPUT:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;input_text&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="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CRITERIA:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;criteria_text&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="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bedrock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;converse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;modelId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_MODEL_ID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;inferenceConfig&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;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;maxTokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;outputConfig&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;structuredOutput&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;schema&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;structured&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output&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;structuredOutput&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;data&lt;/span&gt;&lt;span class="sh"&gt;"&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;structured&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;overall_score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;structured&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confidence&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;Two practical notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep &lt;code&gt;temperature&lt;/code&gt; low for decision workflows.&lt;/li&gt;
&lt;li&gt;Keep your schema small so it is easy to satisfy and easy to test.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Thoughtworks context: why this mattered internally
&lt;/h2&gt;

&lt;p&gt;Within Thoughtworks, much of the platform work around delivery readiness, capability intelligence, and internal decision support depends on consistent signals rather than narrative interpretation.&lt;/p&gt;

&lt;p&gt;Structured output allowed us to standardise how AI contributes to internal decision-making without introducing additional manual validation steps.&lt;/p&gt;

&lt;p&gt;Instead of reviewing long AI summaries, teams can consume structured signals directly inside workflows. This reduces friction in essential procedures.&lt;/p&gt;

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




&lt;h2&gt;
  
  
  Code sample 3: validate and version the contract
&lt;/h2&gt;

&lt;p&gt;Structured output reduces drift, but you still want a clean contract boundary inside your own code. In practice that means validating the response and versioning the schema.&lt;/p&gt;

&lt;p&gt;Here is a minimal validation pattern using Pydantic. It gives you a single place to enforce types and defaults.&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;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="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conlist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;confloat&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EvaluationV1&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;overall_score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;confloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;le&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;confloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;le&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;strengths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;conlist&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;min_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default_factory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;gaps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;conlist&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;min_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default_factory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;recommendation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&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;def&lt;/span&gt; &lt;span class="nf"&gt;parse_evaluation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;EvaluationV1&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;EvaluationV1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Example usage:
# evaluation = parse_evaluation(structured)
# store(evaluation.model_dump(), schema_version="v1")
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This buys you two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your downstream code stays stable even if your prompts evolve.&lt;/li&gt;
&lt;li&gt;You can change the contract intentionally by introducing &lt;code&gt;EvaluationV2&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why this matters beyond AI
&lt;/h2&gt;

&lt;p&gt;Structured output creates a boundary.&lt;/p&gt;

&lt;p&gt;It turns AI from a conversation into a service dependency.&lt;/p&gt;

&lt;p&gt;Once that boundary exists:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validation shifts left
&lt;/li&gt;
&lt;li&gt;Persistence becomes straightforward
&lt;/li&gt;
&lt;li&gt;Observability improves
&lt;/li&gt;
&lt;li&gt;Testing becomes possible
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can now write tests against the shape of AI output.&lt;/p&gt;

&lt;p&gt;That is a meaningful step toward production readiness.&lt;/p&gt;




&lt;h2&gt;
  
  
  Practical lessons learned
&lt;/h2&gt;

&lt;p&gt;Smaller schemas work better.&lt;br&gt;&lt;br&gt;
Separate signals from explanation.&lt;br&gt;&lt;br&gt;
Confidence changes automation.&lt;br&gt;&lt;br&gt;
Treat schema as versioned contract.&lt;br&gt;&lt;br&gt;
Failure modes improve.&lt;/p&gt;




&lt;h2&gt;
  
  
  What changed in the platform
&lt;/h2&gt;

&lt;p&gt;Introducing structured output didn’t add complexity.&lt;/p&gt;

&lt;p&gt;It removed it.&lt;/p&gt;

&lt;p&gt;The workflow became:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;more predictable
&lt;/li&gt;
&lt;li&gt;easier to debug
&lt;/li&gt;
&lt;li&gt;safer to evolve
&lt;/li&gt;
&lt;li&gt;easier to test
&lt;/li&gt;
&lt;li&gt;cheaper to maintain
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI began to feel less magical and more like infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Zooming out
&lt;/h2&gt;

&lt;p&gt;We are moving from prompt engineering toward interface design. The question is no longer what you should ask the model. The question is what contract should exist between the model and the system. Structured output makes that explicit and nudges architects to design AI the same way we design APIs.&lt;/p&gt;

&lt;p&gt;In early AI integrations, the hardest part is not calling the model. It is trusting the result. Structured output helps earn that trust, not because it makes the model smarter, but because it makes the system clearer. And clarity scales. For builders, that is the real value.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>aws</category>
      <category>json</category>
      <category>programming</category>
    </item>
    <item>
      <title>Breaking News: AWS Bedrock Lands in Sydney</title>
      <dc:creator>Alan Blockley</dc:creator>
      <pubDate>Tue, 09 Apr 2024 23:01:10 +0000</pubDate>
      <link>https://dev.to/aws-builders/breaking-news-aws-bedrock-lands-in-sydney-36go</link>
      <guid>https://dev.to/aws-builders/breaking-news-aws-bedrock-lands-in-sydney-36go</guid>
      <description>&lt;p&gt;I’m in Sydney this week for the AWS Summit Sydney and I’ve just been left with much excitement after a MASSIVE announcement.&lt;/p&gt;

&lt;p&gt;This must be one of the year's most anticipated announcements (in our region).  I’ve had customers ask me every other week when this is happening.  I can now say… TODAY rather than SOON. &lt;/p&gt;

&lt;p&gt;I can practically hear you all leaning forward, eager to know what it is. Well, hold onto your seats because here it is: AWS Bedrock has officially landed in Sydney (ap-southeast-02)!&lt;/p&gt;

&lt;p&gt;That’s right!  AWS Bedrock is now available in Sydney (ap-southeast-02)&lt;/p&gt;

&lt;p&gt;What does this mean for us Aussies? Lower latency for our Gen AI Workloads and the ability to respect data sovereignty. &lt;/p&gt;

&lt;p&gt;Lower latency for Australian customers writing Gen AI Workloads&lt;br&gt;
Data sovereignty can be respected &lt;/p&gt;

&lt;p&gt;Available immediately (by request) are the following models:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Titan Text G1 - Lite&lt;/li&gt;
&lt;li&gt;Titan Text G1 - Express&lt;/li&gt;
&lt;li&gt;Titan Multimodal Embeddings G1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;**Anthropic&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude 3 Sonnet&lt;/li&gt;
&lt;li&gt;Claude 3 Haiku&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cohere&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Embed English&lt;/li&gt;
&lt;li&gt;Embed Multilingual&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Mistral AI&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mistral 7B Instruct&lt;/li&gt;
&lt;li&gt;Mixtral 8x7B Instruct&lt;/li&gt;
&lt;li&gt;Mistral Large&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, what are we waiting for? Let's dive in and start building something amazing right here in our local Sydney region!&lt;/p&gt;

&lt;p&gt;In fact.  First thing today I asked Claude 3 Haiku in Sydney to give me a Haiku, in celebration of it's launch in Sydney.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Write me a Haiku about being an LLM hosted in Sydney&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;&lt;br&gt;
Artificial mind&lt;br&gt;
Housed in Sydney's bustling heart&lt;br&gt;
Serve humans with grace&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Learn more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://aws.amazon.com/bedrock/"&gt;Amazon Bedrock&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://ap-southeast-2.console.aws.amazon.com/bedrock/home?region=ap-southeast-2#/overview"&gt;Get Started in Sydney&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>bedrock</category>
      <category>aws</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Build an AI image catalogue! - Claude 3 Haiku</title>
      <dc:creator>Alan Blockley</dc:creator>
      <pubDate>Mon, 18 Mar 2024 22:26:26 +0000</pubDate>
      <link>https://dev.to/aws-builders/build-an-ai-image-catalogue-claude-3-haiku-34d7</link>
      <guid>https://dev.to/aws-builders/build-an-ai-image-catalogue-claude-3-haiku-34d7</guid>
      <description>&lt;p&gt;&lt;strong&gt;Building an Intelligent Photo Album with Amazon Bedrock&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you’re into your Machine Learning/Artificial Intelligence/Generative AI and have been living under a rock, it may be news to you that Anthropic have now released their latest version of the Claude Large Language Model (LLM), which they have predictively named, Claude 3.   But what’s not so predictable is that not only is this the third generation of Claude, but there are also three different variations of Claude.&lt;/p&gt;

&lt;p&gt;In this post, we’re going to explore the creation of a basic, serverless, image cataloguing application using Claude 3 and explore how it enhances the functionality of our AI photo album application. Powered by Amazon Bedrock, this application leverages the poetic prowess of Claude 3 Haiku to provide insightful summaries of uploaded images. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxswoebomedccw32f3zpm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxswoebomedccw32f3zpm.png" alt="Image description" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In today's digital era, our memories are often immortalized through the lens of a camera, capturing moments that evoke a myriad of emotions. As our photo collections grow, organizing and making sense of these images can become a daunting task. However, with the emergence of artificial intelligence (AI) and cloud computing, we now have the opportunity to create intelligent solutions that automate and enrich the photo management process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Announcing Claude 3
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“The next generation of Claude: A new standard for intelligence” - &lt;a href="https://www.anthropic.com/news/claude-3-family"&gt;https://www.anthropic.com/news/claude-3-family&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When Claude 3 was announced they came up with not just one new model, but three:&lt;/p&gt;

&lt;p&gt;Haiku (Available on Amazon Bedrock)&lt;br&gt;
Sonnet (Available on Amazon Bedrock)&lt;br&gt;
Opus (Coming soon to your favourite AI provider starting with B… and ending in rock)&lt;/p&gt;

&lt;p&gt;Now whilst I write this in celebration of the Claude 3 family, I’m not going to go into all the differing benchmark charts as I’m sure you’ve already seen, however, the links and resources in this post should provide the benchmarks you seek if this is something you’re interested in. &lt;/p&gt;

&lt;p&gt;What really got my attention was the overall impact that the whole Claude 3 family has on the LLM landscape.  Bold strokes of improvement across all three variances including; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Near-instant results&lt;/li&gt;
&lt;li&gt;Strong vision capabilities&lt;/li&gt;
&lt;li&gt;Fewer refusals&lt;/li&gt;
&lt;li&gt;Improved accuracy&lt;/li&gt;
&lt;li&gt;Long context and near-perfect recall&lt;/li&gt;
&lt;li&gt;Responsible design&lt;/li&gt;
&lt;li&gt;Easier to use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Source: &lt;a href="https://www.anthropic.com/news/claude-3-family"&gt;https://www.anthropic.com/news/claude-3-family&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What got my attention the most was the claim of “Strong vision capabilities”.  How strong?  That’s something I wanted to test out.&lt;/p&gt;

&lt;p&gt;I originally started writing this little project about 2 weeks before the release of Claude 3 Haiku,  using Claude 3 Sonnet.  The appeal was that Claude 3 Sonnet was offering unmatched vision capabilities. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The Claude 3 models have sophisticated vision capabilities on par with other leading models. They can process a wide range of visual formats, including photos, charts, graphs and technical diagrams. We’re particularly excited to provide this new modality to our enterprise customers, some of whom have up to 50% of their knowledge bases encoded in various formats such as PDFs, flowcharts, or presentation slides.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4pxeou41wv9l1jtt5dul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4pxeou41wv9l1jtt5dul.png" alt="Image description" width="800" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(OK I caved in and put a benchmark chart in - worth it though!)&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Exploring Claude 3 Haiku
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“Claude 3 Haiku: our fastest model yet” - &lt;a href="https://www.anthropic.com/news/claude-3-haiku"&gt;https://www.anthropic.com/news/claude-3-haiku&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The moment Haiku was released, my colleagues at the other end of the work meeting I was attending at the time were greeted with a physical whoop of joy.  I was already excited by Claude 3 sonnet so having the next in the generation arrive was like Christmas had come early. &lt;/p&gt;

&lt;p&gt;I also knew this meant that I could change my large language model from Sonnet to Haiku.  &lt;/p&gt;

&lt;p&gt;The benefits it provided to my proof of concept were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The fastest model in the Claude 3 family&lt;/li&gt;
&lt;li&gt;The most cost-effective model in the Claude 3 family. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means a lot, especially in a proof of concept like this.  This gives us the speed of execution to develop fast, fail fast and iterate on solutions a lot quicker.  It also means that we’re able to control the costs of the application.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Speed is essential for our enterprise users who need to quickly analyze large datasets and generate timely output for tasks like customer support. Claude 3 Haiku is three times faster than its peers for the vast majority of workloads, processing 21K tokens (~30 pages) per second for prompts under 32K tokens [1]. It also generates swift output, enabling responsive, engaging chat experiences and the execution of many small tasks in tandem.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This speed also means the ability to analyze documents at a much faster rate than ever with a faster recall of information.  &lt;/p&gt;

&lt;p&gt;In our use case, we want to visualize our images by using descriptive language to help create a catalogue of images with a summary and to also have it provide a category for the image. &lt;/p&gt;

&lt;p&gt;This can create powerful use cases such as making art more accessible for the blind or improve operational overhead in managing catalogs of stock images in a creative studio. &lt;/p&gt;
&lt;h2&gt;
  
  
  Let’s start building!
&lt;/h2&gt;

&lt;p&gt;By the end of this post, I hope to have demonstrated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Seamlessly integrate Claude 3 Haiku into a photo album app &lt;/li&gt;
&lt;li&gt;Calling Amazon Bedrock from a serverless application&lt;/li&gt;
&lt;li&gt;Basic Architecture that is needed to achieve this.&lt;/li&gt;
&lt;li&gt;Some lessons learned with Prompt Engineering&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  The “Lego Bricks”
&lt;/h3&gt;

&lt;p&gt;The basic architecture that we’re going to build looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ta5y2kkogghmcjmqygg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ta5y2kkogghmcjmqygg.png" alt="Image description" width="720" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I liken AWS Services to Lego bricks.  There are many of them and when put together in varying combinations, they can make many unique different builds.  In this build, we’ll be using:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amazon S3&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To receive images and send them to be processed using S3 Events&lt;/li&gt;
&lt;li&gt;To store and host processed images and a static-hosted HTML page
&lt;strong&gt;API Gateway&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;To accept a GET request from a static-hosted HTML page
&lt;strong&gt;AWS Lambda&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;To receive the S3 event and process the image&lt;/li&gt;
&lt;li&gt;To accept a GET request and provide the user with the image data
&lt;strong&gt;Amazon Bedrock&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;To leverage Calude 3 and provide an AI-generated summary and category
&lt;strong&gt;Amazon DynamoDB&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;To store the information generated by Claude 3 / Amazon Bedrock&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve utilised the AWS Serverless Application Model (SAM) framework to help build and deploy this as well to keep things as easy as possible. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;“SAM and Bedrock? Surely not?!?”&lt;/em&gt; - Remember, Amazon Bedrock is a serverless service and can be called by an API or an SDK.  The power is yours to call Bedrock any way you want to.  &lt;/p&gt;

&lt;p&gt;And last but not least, our language of choice.  Python for our AWS Lambda functions and there’s a small sprinkling of old-school Javascript to assist the front-end HTML. &lt;/p&gt;

&lt;p&gt;If you’re keen to get this setup yourself feel free to browse the repo and get started yourself - &lt;a href="https://github.com/alanblockley/bedrock-haiku-image-catalog"&gt;https://github.com/alanblockley/bedrock-haiku-image-catalog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Otherwise, for more explanation, keep reading. &lt;/p&gt;
&lt;h3&gt;
  
  
  Prompt Engineering - No such thing as too much
&lt;/h3&gt;

&lt;p&gt;When writing this little experiment I started with a very typical MVP style approach.  Get something working.  We’ve all been there, right?  But in this process, I fell victim to also shirking on my prompt for Claude 3.  My original prompt was something like&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Provide me a summary of this image and give it a short category”.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In my defence, it worked.  But as any good builder/coder/developer, I read the documentation… actually, no I didn’t… Have you read IKEA instructions, I’ve been burnt for life!  But instead, I sought out a peer review from a very good friend of mine.  He swiftly explained that this was a bad prompt.  But I didn’t get it.  Yes, I’ve done ML/AI/PartyRock/Gen AI stuff but I’ve never really had to write anything in anger until now. &lt;/p&gt;

&lt;p&gt;The explanation of why this is a bad prompt can be complex.  To make it easier to understand, let’s think of it in human terms, after all this is an emulation of human intelligence.  In your prompt you need to allow for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Time for the model to think about what it’s doing - When we are thinking about something or trying to work on something, we generally think it through 3 or 4 times maybe, sometimes we’ll jot down some notes as well. &lt;/li&gt;
&lt;li&gt;Structure your output - Claude likes XML tags, so maybe have Claude structure it’s output.  Ask it to put it’s thinking into XML tags such as &lt;code&gt;&amp;lt;SCRATCHPAD&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;NOTES&amp;gt;&lt;/code&gt;, closing them respectively.  Then have it put the output of value, your output, into another XML tag such as &lt;code&gt;&amp;lt;OUTPUT&amp;gt;&amp;lt;/OUTPUT&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The more detail the better.  Assume you’re explaining this task for the first time to a trainee.  If a trainee runs this task for the first time and gets it wrong, you didn’t explain it well enough and Claude definitely won’t get it right. 
Give it a purpose or a role.  It’s not good to expect a toaster to dispense ice.  So ensure that your model is given a contextual purpose to work to.  * This will help make your answers more accurate. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whilst all of these actions will help find the accuracy you need in your AI tasks, they’ll also make things more consistent as well.  With this advice, my prompt ended up taking up 34 lines and told a story to the model, not just a quick 1 line action that could be interpreted vaguely.  Have a play with it and see what you think.&lt;/p&gt;

&lt;p&gt;And don’t forget a little bit of logic in your code to help extract the data from the XML tags.  I’ve included a Python example in my repo. &lt;/p&gt;
&lt;h3&gt;
  
  
  The Lambda Functions
&lt;/h3&gt;

&lt;p&gt;In our architecture, we’ve detailed two functions.  One that processes images and one that retrieves the stored data. &lt;/p&gt;

&lt;p&gt;For the sake of simplicity, there’s a third function, not detailed in the diagram, which takes the image and copies it to the image store but also renames the image to a random, unique UUID.  This is to help indexing and also removes any bias from the AI generation of image data.  The model then has no concept of image name.  You could also add MIME checking, AV scanning and other checks here and also utilise this bucket to receive from anywhere such as AWS Transfer Family or a web front end via Presigned URLs. &lt;/p&gt;

&lt;p&gt;I’ve included the functions in a public repository for you to play with - &lt;a href="https://github.com/alanblockley/bedrock-haiku-image-catalog"&gt;https://github.com/alanblockley/bedrock-haiku-image-catalog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the sake of making this an interesting read, I’ve only included a copy/paste of the function that calls Amazon Bedrock.   This function performs the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decode Image Name: Extracts the name of the uploaded image from the S3 event and removes any obfuscation caused by URL formatting of the name.&lt;/li&gt;
&lt;li&gt;Retrieve Image Type: Determines the MIME type of the uploaded image using the S3 object key.&lt;/li&gt;
&lt;li&gt;Retrieve Image Data: Fetches the image data from the designated S3 bucket.&lt;/li&gt;
&lt;li&gt;Encode Image Data: Converts the image data to a base64-encoded string for processing by the Claude 3 Haiku model.&lt;/li&gt;
&lt;li&gt;Generate Summary: Utilizes the Claude 3 Haiku model to produce a summary of the image content.&lt;/li&gt;
&lt;li&gt;Extract Summary: Parses the response from the Claude 3 Haiku model to extract the generated summary.&lt;/li&gt;
&lt;li&gt;Store Summary in DynamoDB: Stores the extracted summary along with relevant image details (e.g., object ID) in a DynamoDB table for future retrieval.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This lambda function will also handle the streaming output that Bedrock provides and format it in such a way that it can be inserted into a DynamoDB table. &lt;/p&gt;

&lt;p&gt;Also note, that I asked for structured data from the model.  So when Claude presents me with this data, it’s creating a JSON string within the XML tags which I then use to continue processing the data. &lt;/p&gt;
&lt;h4&gt;
  
  
  Summarise Image - Code
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os
import json
import boto3
import base64
import urllib.parse
from botocore.exceptions import ClientError


IMAGE_TABLE = os.environ['IMAGE_TABLE']
IMAGE_BUCKET = os.environ['IMAGE_BUCKET']


s3 = boto3.client('s3')
dynamodb = boto3.resource('dynamodb')
bedrock_runtime = boto3.client('bedrock-runtime')


def extract_substring(text, trigger_str, end_str):
   # Find string between two values (Thanks for this Mike!)
   last_trigger_index = text.rfind(trigger_str)
   if last_trigger_index == -1:
       return ""
   next_end_index = text.find(end_str, last_trigger_index)
   if next_end_index == -1:
       return ""
   substring = text[last_trigger_index + len(trigger_str):next_end_index]
   return substring


def decode_object_name(object_name):
   # Decode the object name from the URL-encoded format
   return urllib.parse.unquote_plus(object_name)


# Function to get the image and turn it into a base64 string
def get_image_base64(bucket, key):
   try:
       response = s3.get_object(Bucket=bucket, Key=key)
   except ClientError as e:
       print(e)
       return False
   else:
       image = response['Body'].read()
       return image


def get_image_type(bucket, key):
   # Get the Mime type using object key and head_object
   # Must use head_object


   try:
       response = s3.head_object(Bucket=bucket, Key=key)
   except ClientError as e:
       print(e)
       return False
   else:
       content_type = response['ContentType']
       print(content_type)
       return content_type


def generate_summary(image_base64, content_type):
   # Generate a summary of the input image using the Bedrock Runtime and claude3 model
   # Must usee invoke_model


   prompt = """Your purpose is to catalog images based upon common categories.
Create a structured set of data in json providing a summary of the image and a very short, generalised, image category.  Do not return any narrative language.
Before you provide any output, show your working in &amp;lt;scratchpad&amp;gt; XML tags.
JSON fields must be labelled image_summary and image_category.


Example json structure is:


&amp;lt;json&amp;gt;
{
   "image_summary": SUMMARY OF THE IMAGE,
   "image_category": SHORT CATEGORY OF THE IMAGE,
}
&amp;lt;/json&amp;gt;


Examples of categorie are:


Animals
Nature
People
Travel
Food
Technology
Business
Education
Health
Sports
Arts
Fashion
Backgrounds
Concepts
Holidays

Output the json structure as a string in &amp;lt;json&amp;gt; XML tags.  Do not return any narrative language.


Look at the images in detail, looking for people, animals, landmarks or features and where possible try to identify them.
"""

   response = bedrock_runtime.invoke_model(
       modelId='anthropic.claude-3-haiku-20240307-v1:0',
       contentType='application/json',
       accept='application/json',
       body=json.dumps({
           "anthropic_version": "bedrock-2023-05-31",
           "max_tokens": 1000,
           "system": prompt,
           "messages": [
               {
                   "role": "user",
                   "content": [
                       {
                           "type": "image",
                           "source": {
                               "type": "base64",
                               "media_type": content_type,
                               "data": image_base64
                           }
                       },
                       # {
                       #     "type": "text",
                       #     "text": prompt
                       # }
                   ]
               }
           ]
       })
   )


   print(response)
   return json.loads(response.get('body').read())


def store_summary(object_id, summary, category):
   # Store the summary in DynamoDB
   # Must use put_item

   table = dynamodb.Table(IMAGE_TABLE)
   table.put_item(
       Item={
           'id': object_id,
           'summary': summary,
           'category': category
       }
   )


def lambda_handler(event, context):


   print(json.dumps(event, indent=4))
   object_name = decode_object_name(event['Records'][0]['s3']['object']['key'])


   image_type = get_image_type(IMAGE_BUCKET, object_name)
   image_base64 = get_image_base64(IMAGE_BUCKET, object_name)


   if not image_base64:
       return {
           'statusCode': 500,
           'body': json.dumps('Error getting image')
       }
   else:
       image_base64 = base64.b64encode(image_base64).decode('utf-8')
       response_body = generate_summary(image_base64, image_type)

       print(response_body)

       summary = response_body['content'][0]['text']
       json_summary = json.loads(extract_substring(summary, "&amp;lt;json&amp;gt;", "&amp;lt;/json&amp;gt;"))

       image_summary = json_summary['image_summary']
       image_category = json_summary['image_category']
       store_summary(object_name, image_summary, image_category)


   return {
       'statusCode': 200,
       'body': json.dumps('Summary stored in DynamoDB')
   }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: To call a bedrock-friendly version of boto3 we’re making use of a Lambda Layer.  This must be built into your account before deploying this SAM template.  You will then need to include the ARN of the layer as a Parameter as you deploy.  To build the boto3 lambda layer:&lt;/p&gt;

&lt;p&gt;Open Cloud Shell from the AWS Console and type the following commands&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  mkdir ./bedrock-layer
  cd ./bedrock-layer
  mkdir ./python
  pip3 install -t ./python/ boto3
  zip -r ../bedrock-layer.zip .
  cd ..

  aws lambda publish-layer-version \
  --layer-name bedrock-layer \
  --zip-file fileb://bedrock-layer.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source:- &lt;br&gt;
&lt;a href="https://www.linkedin.com/posts/mikegchambers_serverless-python-activity-7154258975926964224-IL4G"&gt;https://www.linkedin.com/posts/mikegchambers_serverless-python-activity-7154258975926964224-IL4G&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The Front End
&lt;/h3&gt;

&lt;p&gt;To use the information we’ve generated in a useful way I created a very simple front end.  This just lists any data in the dynamoDB and gives a thumbnail. &lt;/p&gt;

&lt;p&gt;I’ve kept this as basic as possible.  The hero of the story here after all is Claude… not my horrific javascript skills. &lt;/p&gt;

&lt;p&gt;As you can see, Claude has done pretty well at creating a summary and categorising these images.  This could then lead to further features being added such as search engines in large collections or services like Amazon Polly to explain what the image is. &lt;/p&gt;

&lt;p&gt;&lt;a href="/aws-bedrock-claude3-haiku-imagecatalog-3.png" class="article-body-image-wrapper"&gt;&lt;img src="/aws-bedrock-claude3-haiku-imagecatalog-3.png" alt="Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Imagine how good this would be at Pictionary! &lt;/p&gt;
&lt;h3&gt;
  
  
  The SAM Template
&lt;/h3&gt;

&lt;p&gt;The glue that holds this all together is a SAM template defining the resources in a declarative language that we’re all familiar with.&lt;/p&gt;

&lt;p&gt;I’ve kept this template as simple as possible, including a SimpleTable for the DynamoDB table and separating resources between the frontend and the backend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: &amp;gt;
 AI Photo album


Globals:
 Api:
   Cors:
     AllowMethods: "'*'"
     AllowHeaders: "'*'"
     AllowOrigin: "'*'"
     AllowCredentials: "'*'"
 Function:
   Timeout: 30
   MemorySize: 256
   Runtime: python3.11
   Environment:
     Variables:
       INCOMING_BUCKET: !Sub '${Prefix}-${Workload}-incoming'
       IMAGE_BUCKET: !Sub '${Prefix}-${Workload}-images'
       IMAGE_TABLE: !Ref ImageTable
   Architectures:
     - arm64


Parameters:
 Prefix:
   Type: String
   Description: Prefix for all resources
   Default: my-ml


  Workload:
   Type: String
   Description: Workload of the application
   Default: photo-album


 BedrockLayerArn:
   Type: String
   Description: ARN of the Bedrock layer
   Default: arn:aws:lambda:us-west-2:168420111683:layer:bedrock-layer:1


Resources:
### Backend resources
 ImageTable:
   Type: AWS::Serverless::SimpleTable
   Properties:
     PrimaryKey:
       Name: id
       Type: String
     TableName: !Sub '${Prefix}-${Workload}-image-table'


 IncomingBucket:
   Type: AWS::S3::Bucket
   Properties:
     BucketName: !Sub '${Prefix}-${Workload}-incoming'
     AccessControl: Private
     PublicAccessBlockConfiguration:
       BlockPublicAcls: true
       BlockPublicPolicy: true
       IgnorePublicAcls: true
       RestrictPublicBuckets: true


 ImageBucket:
   Type: AWS::S3::Bucket
   Properties:
     BucketName: !Sub '${Prefix}-${Workload}-images'
     AccessControl: Private
     PublicAccessBlockConfiguration:
       BlockPublicAcls: true
       BlockPublicPolicy: true
       IgnorePublicAcls: true
       RestrictPublicBuckets: true


 RenameImageFunction:
   Type: AWS::Serverless::Function
   Properties:
     CodeUri: rename_image/
     Handler: app.lambda_handler
     Layers:
       - !Ref BedrockLayerArn
     Policies:
       - S3ReadPolicy:
           BucketName: !Sub '${Prefix}-${Workload}-incoming'
       - S3WritePolicy:
           BucketName: !Sub '${Prefix}-${Workload}-images'
     Events:
       S3ObjectCreated:
         Type: S3
         Properties:
           Bucket:
             Ref: IncomingBucket
           Events: s3:ObjectCreated:*


 SummariseImageFunction:
   Type: AWS::Serverless::Function
   Properties:
     CodeUri: summarise_image/
     Handler: app.lambda_handler
     Layers:
       - !Ref BedrockLayerArn
     Policies:
       - DynamoDBWritePolicy:
           TableName: !Ref ImageTable
       - S3ReadPolicy:
           BucketName: !Sub '${Prefix}-${Workload}-images'
       - Statement:
           - Sid: Bedrock
             Effect: Allow
             Action:
               - bedrock:InvokeModel
             Resource: !Sub 'arn:aws:bedrock:${AWS::Region}::foundation-model/*'
     Events:
       S3ObjectCreated:
         Type: S3
         Properties:
           Bucket:
             Ref: ImageBucket
           Events: s3:ObjectCreated:*


 GetImagesFunction:
   Type: AWS::Serverless::Function
   Properties:
     CodeUri: get_images/
     Handler: app.lambda_handler
     Policies:
       - DynamoDBReadPolicy:
           TableName: !Ref ImageTable
     Events:
       Api:
         Type: Api
         Properties:
           Path: /images
           Method: get


### Front end resources
 ImageHostingOriginaccessidentity:
     Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
     Properties:
       CloudFrontOriginAccessIdentityConfig:
         Comment: !Sub ${Prefix}-${Workload}-originaccessidentity


 ImageHostingBucketPolicy:
   Type: AWS::S3::BucketPolicy
   Properties:
     Bucket: !Ref ImageBucket
     PolicyDocument:
       Statement:
         -
           Action:
             - "s3:GetObject"
           Effect: "Allow"
           Resource:
             Fn::Join:
               - ""
               -
                 - "arn:aws:s3:::"
                 -
                   Ref: ImageBucket
                 - "/*"
           Principal:
             CanonicalUser: !GetAtt ImageHostingOriginaccessidentity.S3CanonicalUserId


 ImageHostingCloudFront:
   Type: AWS::CloudFront::Distribution
   Properties:
     DistributionConfig:
       Enabled: true
       Comment: !Sub ${Prefix}-${Workload}-distribution
       DefaultRootObject: index.html
       CustomErrorResponses:
         - ErrorCode: 400
           ResponseCode: 200
           ResponsePagePath: "/error.html"
         - ErrorCode: 403
           ResponseCode: 200
           ResponsePagePath: "/error.html"
         - ErrorCode: 404
           ResponseCode: 200
           ResponsePagePath: "/error.html"       
       Origins:
       - Id: ImageBucket
         DomainName: !Sub ${ImageBucket}.s3.${AWS::Region}.amazonaws.com     
         S3OriginConfig:
           OriginAccessIdentity: !Join [ "", [ "origin-access-identity/cloudfront/", !Ref ImageHostingOriginaccessidentity ] ]

       DefaultCacheBehavior:                   
         TargetOriginId: ImageBucket
         ViewerProtocolPolicy: redirect-to-https
         Compress: false
         CachePolicyId: "4135ea2d-6df8-44a3-9df3-4b5a84be39ad"
         ResponseHeadersPolicyId: "5cc3b908-e619-4b99-88e5-2cf7f45965bd"
         OriginRequestPolicyId: "88a5eaf4-2fd4-4709-b370-b4c650ea3fcf"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Some of the most important lessons I learned in this experience were not about Amazon Bedrock.  Whilst I utilized Bedrock to call our new friend, Sir Claude the third of Haiku, I spent more time working on my prompt than I did calling Bedrock.  Many iterations of this prompt happened and also varying changes between system prompts and multi-modal prompts (The ability to use images and text in a prompt). &lt;/p&gt;

&lt;p&gt;As we conclude our exploration of Haiku and its role in our photo album application, we're reminded of the boundless possibilities that AI and cloud computing offer in reshaping our digital landscape.  I see this being used in many use cases but I can’t escape the ability to use this to make life easier who may be vision impaired:&lt;/p&gt;

&lt;p&gt;A guided tour of an art museum&lt;br&gt;
Assistive technology when going through daily life such as shopping for groceries&lt;br&gt;
Advanced screen reading. &lt;/p&gt;

&lt;p&gt;Some of these use cases can even lead to the betterment of social inclusion for people with disabilities and vision impairment. &lt;/p&gt;

&lt;p&gt;There are many commercial use cases for Claude 3’s vision capabilities including and not limited to marketing, hospitality, healthcare/wellness and many more but I’ll leave these to your imagination, as the opportunities are endless.&lt;/p&gt;

&lt;h3&gt;
  
  
  What next?
&lt;/h3&gt;

&lt;p&gt;Curious to explore the world of Claude 3 Haiku and its applications beyond photo management? Dive deeper into AWS services and AI technologies, and discover the endless possibilities of AI models in enriching our digital experiences. Share your thoughts and insights in the comments on my LinkedIn or below, and let's embark on an AI journey together!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/bedrock/"&gt;https://aws.amazon.com/bedrock/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.anthropic.com/news/claude-3-family"&gt;https://www.anthropic.com/news/claude-3-family&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/alanblockley/bedrock-haiku-image-catalog"&gt;https://github.com/alanblockley/bedrock-haiku-image-catalog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>serverless</category>
      <category>machinelearning</category>
      <category>aws</category>
    </item>
    <item>
      <title>Essential Steps for Securing Your AWS Environment</title>
      <dc:creator>Alan Blockley</dc:creator>
      <pubDate>Mon, 19 Feb 2024 03:51:40 +0000</pubDate>
      <link>https://dev.to/aws-builders/essential-steps-for-securing-your-aws-environment-lbf</link>
      <guid>https://dev.to/aws-builders/essential-steps-for-securing-your-aws-environment-lbf</guid>
      <description>&lt;p&gt;Today, I want to dive into a topic that's been buzzing around lately: security in AWS environments. As we kick off the year with new intentions and goals, it's crucial to ensure that our AWS setups are fortified against potential threats. So, let's delve into some fundamental steps you can take to bolster security within your AWS infrastructure.&lt;/p&gt;

&lt;p&gt;A lot of customers have been talking about security recently. It’s the start of the year, new intentions means they want to get involved with different standards, etc. But the first place to start is with some basic foundations in security or as I call it, Well-Architected Security. &lt;/p&gt;

&lt;p&gt;Start with the basic foundations of security before you go big. Otherwise, you're going to find there's a lot of work to do later. &lt;/p&gt;

&lt;p&gt;Let’s go through my top 10 steps for securing your AWS Environment&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fol9un6nh2u40dlxw7cwy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fol9un6nh2u40dlxw7cwy.png" alt="Image description" width="450" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 1: Implement a Multi-Account Architecture
&lt;/h2&gt;

&lt;p&gt;So the first step that I would take with security is to separate those workloads, have a multi-account architecture. This sounds complicated, but it's not. All we're looking for is a couple of AWS accounts, one for your production environment that nobody touches, except for when you push your deployments into production and then have another account for playing around in, like a sandbox or development account. It doesn't have to be anything big. And don't forget you don't actually pay for an AWS account unless you put something in an account. All accounts are free until you start using them. So having that dev account there is great.&lt;/p&gt;

&lt;p&gt;What this means is, if you make mistakes in the development account with data or your application, you're not going to impact that production workload. &lt;/p&gt;

&lt;p&gt;So as far as the multi-account architecture is concerned, you've got a production account, you've got a development account and what you've got next is a management account. This account sits above the other two in the organization and creates a consolidated billing account, so you only get one bill. So that's nice and convenient as well. &lt;/p&gt;

&lt;p&gt;But ultimately you've separated those workloads out and you're looking after your data security.&lt;/p&gt;

&lt;h3&gt;
  
  
  - Bonus Tip
&lt;/h3&gt;

&lt;p&gt;Have an audit account because when you go for those security standards, they're going to look for an audit trail, they're going to look for logging, and they're going to look for how you query that as well. What's an audit account? An audit account is something where you consolidate all your cloud trails into one single place from each of those accounts. &lt;/p&gt;

&lt;p&gt;So think big, long-term. You've only got a production account and a playground account right now. But what if in three years time you've actually got an account in London? An account in the US? You've got multiple accounts for different tenancies. You want to consolidate all that logging into one place because if you have an incident, you want to make sure you can get to that information quickly and easily. And also it means you've protected that log file as well, because if you do have an incident, nobody can erase those cloud trials from your production account. &lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 2: Design a Custom VPC Configuration
&lt;/h2&gt;

&lt;p&gt;My second tip is all about your network design. Don't just dive in playing with that default VPC configuration that's been given to you within your account because at the end of the day, it looks great to get you started, but you don't want to be using a public subnet all the time. &lt;/p&gt;

&lt;p&gt;Create yourself, your own custom VPC configuration and also create it to have three layers of security to it;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the first layer, have a public subnet. Just put your load balances there for anything that needs to be exposed to the Internet. Or maybe you've got a reverse proxy or a VPN server. &lt;/li&gt;
&lt;li&gt;The second layer to your VPC is going to be a private subnet. I'd put all my compute workloads here like Lambda Functions, EC2 instances and Fargate tasks as well. That way, you've separated your compute workload from the Internet, make sure you've got a NAT Gateway, so those resources can get out to the Internet for their updates. But ultimately, the only way into these workloads should be via the load balancer. &lt;/li&gt;
&lt;li&gt;And then, lastly, the third tier. This is the secure tier. Or, as I like to call it, the data's tier. I put all my Managed AWS services here that look after my data. RDS, EFS, FSX, etc. And it keeps that data secure. Why is it secure? Well, first of all, there’s no Internet gateway or NAT Gateway. There's also a network access control list (NACL) across the bottom of this as well, which then means you're blocking that from the public subnet. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the only way to get to your data is through the compute layer. So the flow of data starts off at the Internet, goes to a load balancer, then comes into your compute layer and then goes into your data layer if that data request is needed. But there's no way anything entering or compromising your public subnet should be able to get to your data. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flhkqg660alj9afry6mxw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flhkqg660alj9afry6mxw.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 3: Prioritize Patch Management
&lt;/h2&gt;

&lt;p&gt;Patch management! If you've got EC2 instances, then make sure you patch them. There's nothing worse than an exposed EC2 Instance on the Internet. You want to make sure you patch those against any Common vulnerabilities and Exploits (CVEs).  Whilst we all know about Microsoft patch Tuesdays, don't forget your Linux hosts as well. You can use AWS Systems Manager, Maintenance Windows and Patch Manager to automate these on a regular basis. &lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 4: Optimize Authentication with Single Sign-On
&lt;/h2&gt;

&lt;p&gt;Now, let's talk about how you log in to AWS. The main recommendation at the moment from AWS is to use a single sign on provider. AWS provides a single sign-on provider in the form of IAM Identity Centre. Now, this is great because this means you consolidate all the logins for those multiple accounts that we just created as part of my first Step, into one place.It also means you can federate that off to your favorite identity provider as well, such as Google, Azure AD or whatever it might be as this is a primary feature of IAM Identity Center. &lt;/p&gt;

&lt;p&gt;What makes SSO so secure though? IAM Identity Center makes sure that you are using temporary credentials at all times. You log in with your preferred identity provider, and then it gives you a fresh new AWS token in the background so you can access all the AWS resources and expires them at approximately 8 to 12 hours, depending on what you've configured. This is great. This means there's no static credentials lying around nothing that can get leaked. And it even provides you with things like IAM access keys for CLI access. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fywfscidkvl4qnght12xm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fywfscidkvl4qnght12xm.png" alt="Image description" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  - Bonus Tip
&lt;/h3&gt;

&lt;p&gt;If you've got IAM User Access Keys set up for service accounts, then make sure you rotate those every 90 days. This is to make sure we reduce the potential of an attack. You can create a scheduled process for this, a business process, that is designed to have someone rotate those keys and then apply them to the service as well. But make sure you remember to do this every 90 days. &lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 5: Secure the Root Account
&lt;/h2&gt;

&lt;p&gt;This next one is all about the root account. If your root account is still in use, if you're still logging in with that magic email address that Amazon let you register with, get rid of it. You should be setting a strong password, a hardware MFA token and locking it away with a safe.  The root account has privileges that could damage your business and it’s too risky to allow it to be compromised. &lt;/p&gt;

&lt;h3&gt;
  
  
  - Bonus Tip #1
&lt;/h3&gt;

&lt;p&gt;Make sure that the root account has not got IAM User Access Keys attached to it either because these are stored in plain text and can be assumed from anywhere so make sure there's no access keys.&lt;/p&gt;

&lt;h3&gt;
  
  
  - Bonus Tip #2
&lt;/h3&gt;

&lt;p&gt;Make sure you've got MFA enabled not just on your root account, but also across your organization as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 6: Manage Permissions Effectively
&lt;/h2&gt;

&lt;p&gt;It's always important to make sure you know who's got access to what in your environment. I often find that sometimes the AWS Managed IAM Roles/Permission Sets can actually give away too much in terms of permissions. We want our developers to do things. We want them to be productive, but sometimes you might now want them to have access to Amazon SageMaker or other services that might be expensive to play with when not properly educated. So look at those permissions. Look at what people have and then make sure you create custom roles and permission sets for your staff. Also, make sure you're checking them regularly as well. What if somebody's moved roles? Do they need those permissions? &lt;/p&gt;

&lt;h3&gt;
  
  
  - Bonus Tip
&lt;/h3&gt;

&lt;p&gt;Service Control Policies (SCP) can also allow for controlling different permissions at an organizational level and making sure you know who has access to what.  You can still maintain the access that they need if they need administrative access to the account, but from a service control policy at an organizational level, you can then limit services and regions.  You can also enforce compliance too. &lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 7: Enhance Remote Access Security
&lt;/h2&gt;

&lt;p&gt;When you're remotely accessing your EC2 instances. Try not to use SSH or RDP. Keep these protocols closed away from the world. Instead, use AWS Systems Manager, Session Manager.  This service allows you to log on directly to the EC2 instance from inside the AWS Management Console and get your favorite bash prompt or PowerShell prompt. If you're a Windows user and you want to use the Windows Interface, you could also use AWS Systems Manager, Fleet Manager instead to get a remote desktop. &lt;/p&gt;

&lt;p&gt;Using these two features of Systems Manager you can close off port 22, you can close off port 3389 and access the EC2s through the console. From a compliance perspective, this also means that you're logging access to that EC2 instance against AWS CloudTrail with the username creating accountability. &lt;/p&gt;

&lt;h3&gt;
  
  
  - Bonus Tip
&lt;/h3&gt;

&lt;p&gt;If you want to track what's going on on the command line from the Session Manager, you can log those commands to CloudWatch Logs as well. Just make sure you rotate those logs regularly so they don't get too big.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 8: Enable AWS GuardDuty
&lt;/h2&gt;

&lt;p&gt;Make sure you've got AWS GuardDuty enabled. GuardDuty is a lifesaver when it comes to not knowing what's going on in your account. I always tell a story about this.&lt;/p&gt;

&lt;p&gt;THis was during my tenure in a previous role, I've seen this with a real account in real time where GuardDuty alerted us to an issue. And what it's alerting us to is something that we can't see in our usual regions or environments. It alerted us to a cluster of EC2 Instances being launched in another region. Now, as a customer, I was working in Sydney. Why would I be bothered about any other region? But GuardDuty told me that somebody had compromised our accounts by the usage of leaked IAM credentials, and we were able to stop the launch of resources in the wrong region at that time, saving us a lot of money and saving us our security reputation &lt;/p&gt;

&lt;h3&gt;
  
  
  - Bonus Tip
&lt;/h3&gt;

&lt;p&gt;Look into AWS Security Hub! Security Hub is a service that consolidates all the other security services within AWS into a single pane of glass view.  GuardDuty can send alerts to Security Hub alongside other services and then show you remediation items as you go along, giving you traffic light reports and even a percentage score of your security position. It works with many frameworks, so you'd be able to tune it to exactly what your frameworks might be &lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 9: Protect Web Applications with WAF
&lt;/h2&gt;

&lt;p&gt;Application protection with a Web Application Firewall (WAF). If you've not got a WAF, you're very possibly opening yourself up to all sorts of risk. A WAF can be tuned to your run time or hosted operating systems. It can protect you against the top 10 threats on the Internet at the time or against botnets. You can also create custom rules such as whitelists or block lists to be able to control the traffic coming in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 10: Implement Encryption Everywhere
&lt;/h2&gt;

&lt;p&gt;Final tip for security today is encryption. Encrypt everything! As Werner Vogels would say;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Dance like nobody's watching and encrypt like everybody is”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Make sure you encrypt those EBS volumes on creation. Setup your account so that every volume is encrypted by default. Use custom KMS keys, if possible. Make sure S3 buckets and RDS instances are encrypted as well. Every service should have an option for encryption. Use it. Don't lose it (your data). &lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Securing your AWS environment is an ongoing process that requires diligence and proactive measures. By implementing these essential steps, you can fortify your infrastructure against potential threats and mitigate security risks effectively. Remember, prioritizing security is key to maintaining the integrity and resilience of your AWS environment.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
    </item>
    <item>
      <title>Amazon Cognito - Function over form</title>
      <dc:creator>Alan Blockley</dc:creator>
      <pubDate>Thu, 18 Jan 2024 10:54:12 +0000</pubDate>
      <link>https://dev.to/alanblockley/amazon-cognito-function-over-form-26ae</link>
      <guid>https://dev.to/alanblockley/amazon-cognito-function-over-form-26ae</guid>
      <description>&lt;p&gt;Many individuals, builders or backend developers, often grapple with the challenge of balancing function and form. Ultimately, the success of our application hinges on ensuring that the Minimum Viable Product (MVP), Proof of Concept (PoC), or prototype functions smoothly before investing substantial resources into a flashy frontend.&lt;/p&gt;

&lt;p&gt;However, there lies a conundrum – how do we control access to our application without prematurely exposing it to the entire internet?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aFlkzoex--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uiaym6d4p8qmv16hxgbl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aFlkzoex--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uiaym6d4p8qmv16hxgbl.png" alt="Image description" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why am I sharing this journey with you? During the holidays, I ventured into the realm of building a new project. While delving into the backend intricacies involving services like API Gateway, Lambda, and a plethora of DynamoDB databases, one unavoidable aspect surfaced – the need for a frontend. Even if it's just a basic HTML page, having a visual representation of the backend processes is crucial.&lt;/p&gt;

&lt;p&gt;In this blog post, I aim to share my experiences with AWS Cognito. It's been a five-year mission, boldly going where many have gone before, yet I've always found myself struggling as a BUILDER.&lt;/p&gt;

&lt;p&gt;By the end of this we’ll step through what I have learned and try to provide some code examples for you to take away and use yourself. &lt;/p&gt;

&lt;h1&gt;
  
  
  Introducing Amazon Cognito
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;“With Amazon Cognito, you can add user sign-up and sign-in features and control access to your web and mobile applications. Amazon Cognito provides an identity store that scales to millions of users, supports social and enterprise identity federation, and offers advanced security features to protect your consumers and business. Built on open identity standards, Amazon Cognito supports various compliance regulations and integrates with frontend and backend development resources.” - &lt;a href="https://aws.amazon.com/cognito/"&gt;https://aws.amazon.com/cognito/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In my past life as a PHP Developer I’d write web applications with a specific business function.  I’d create some level of users table, with an encrypted password and some questionable business logic as to how to access this table.  I’d be left with having to maintain the DB, the table, the code and the server to ensure that user authentication was safe and secure. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Did someone order a portion of operational overhead with a side of operational headache?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Amazon Cognito is designed to take away the hassle of looking after authentication methods, userpools or even federation of users.  A serverless, managed service that also has a very generous feature set and free tier.  Yep… FREE! For up to 50,000 Monthly active users or 50 Monthly federated users (Federated infers social logins such as Azure AD, Google, Facebook, Apple and Amazon).  It provides an authentication mechanism for your application which can then also provide IAM privileges to resources in your infrastructure such as API Gateway, DynamoDB et al. &lt;/p&gt;

&lt;h1&gt;
  
  
  The Problem
&lt;/h1&gt;

&lt;p&gt;I’ll admit to trying my hand at Cognito several times over and getting basic knowledge of how it works but never actually getting something that is working to a level of productivity.  I always ended up in the past with some random token or some obfuscated URL and then a severe lack of time to troubleshoot / educate myself on how to properly use these elements. &lt;/p&gt;

&lt;p&gt;However in this example, I had a WHOLE festive holiday to work this out once and for all, and it really wasn’t that complicated once I got to the bottom of it. &lt;/p&gt;

&lt;h1&gt;
  
  
  The Theory
&lt;/h1&gt;

&lt;p&gt;Yes there’s a boring theory section to this but it’ll make sense eventually.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Workflow
&lt;/h2&gt;

&lt;p&gt;In order to successfully implement Cognito you’ll need a user form, to capture basic information (username and password), you send an InitiateAuth call, which sends back a series of challenge and response messages (SMS MFA,etc) to then eventually be sent an array of tokens to be used in authentication against others AWS services. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LCuSMlqW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3roc8brfdvgnhw255vus.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LCuSMlqW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3roc8brfdvgnhw255vus.png" alt="Image description" width="328" height="261"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Source - &lt;br&gt;
(&lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html"&gt;https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;The key portion of this for our example is the return of the Access, ID and refresh token.  This then allows us to implement the architecture that we’ll be more familiar with, below;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KJC7QagJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jobih5a1pismasa19w82.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KJC7QagJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jobih5a1pismasa19w82.png" alt="Image description" width="800" height="727"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s look at the definition of each token, based on the AWS Docs:&lt;/p&gt;
&lt;h2&gt;
  
  
  ID token
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“The ID token is a JSON Web Token (JWT) that contains claims about the identity of the authenticated user, such as name, email, and phone_number. You can use this identity information inside your application. The ID token can also be used to authenticate users to your resource servers or server applications.” - &lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-id-token.html"&gt;https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-id-token.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Access token
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“The user pool access token contains claims about the authenticated user, a list of the user's groups, and a list of scopes. The purpose of the access token is to authorize API operations. Your user pool accepts access tokens to authorize user self-service operations. For example, you can use the access token to grant your user access to add, change, or delete user attributes.” - &lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-access-token.html"&gt;https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-access-token.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Refresh token
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“You can use the refresh token to retrieve new ID and access tokens. By default, the refresh token expires 30 days after your application user signs into your user pool. When you create an application for your user pool, you can set the application's refresh token expiration to any value between 60 minutes and 10 years.” - &lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-refresh-token.html"&gt;https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-refresh-token.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;
  
  
  The Implementation
&lt;/h1&gt;

&lt;p&gt;In practice this now starts to make more sense, now we understand what the different tokens are used for. &lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before following the below, make sure you’ve done the below;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Grab a copy of the supporting Github repo (&lt;a href="https://github.com/alanblockley/cognito-javascript-example"&gt;https://github.com/alanblockley/cognito-javascript-example&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Setup an instance of Cognito in your AWS account.  A Cloudformation template has been provided for you in the attached Github repo. &lt;/li&gt;
&lt;li&gt;Enter your pool id and client id into the &lt;code&gt;config.js&lt;/code&gt;, supplied in the repo.  The config.js file should go in the same location as the js file calling it. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now this is done, let’s get started.&lt;/p&gt;
&lt;h2&gt;
  
  
  Registration
&lt;/h2&gt;

&lt;p&gt;Every good authentication method needs a way of registering users.  How else are your users going to sign up? &lt;/p&gt;

&lt;p&gt;Let’s give it an interface!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;form id="registerForm" novalidate onsubmit="return false"&amp;gt;
    &amp;lt;div class="form-group"&amp;gt;
        &amp;lt;label for="emailInputRegister"&amp;gt;Username&amp;lt;/label&amp;gt;
        &amp;lt;input type="text" class="form-control" id="emailInputRegister" placeholder="Enter email"&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;div class="form-group"&amp;gt;
        &amp;lt;label for="passwordInputRegister"&amp;gt;Password&amp;lt;/label&amp;gt;
        &amp;lt;input type="password" class="form-control" id="passwordInputRegister" placeholder="Enter Password"&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;div class="form-group"&amp;gt;
        &amp;lt;label for="confirmationPassword"&amp;gt;Password&amp;lt;/label&amp;gt;
        &amp;lt;input type="password" class="form-control" id="confirmationPassword" placeholder="Confirm Password"&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;button type="submit" class="btn btn-primary btn-block"&amp;gt;Register&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;

...

&amp;lt;script src="js/register.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script type="text/JavaScript"&amp;gt;
    $('#registerForm').on("submit", function (event) {
      registerUser();
      event.preventDefault();
    })
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;[ &lt;a href="https://github.com/alanblockley/cognito-javascript-example/blob/main/html/register.html"&gt;https://github.com/alanblockley/cognito-javascript-example/blob/main/html/register.html&lt;/a&gt; ]&lt;/p&gt;

&lt;p&gt;Nothing too clever here. Just accepting an email address and a password, plus a confirmation that the password was spelt correctly. &lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section of the HTML file we’re including the register.js file and then defining a trigger.  This can be done in the one file normally but I like to keep triggers in line for visibility. &lt;/p&gt;

&lt;p&gt;It’s worth noting that we also need to call a couple of other AWS specific scripts just above our script section;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script src="assets/js/config.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src="js/vendor/aws-cognito-sdk.min.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src="js/vendor/amazon-cognito-identity.min.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These two reference libraries that allow us to call Amazon Cognito via the SDK, natively in Javascript, the other is our configuration file for all Amazon Cognito calling scripts. &lt;/p&gt;

&lt;p&gt;This trigger referenced calls the below function;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Function to register a user
function registerUser() {
   // Get user input values from the registration form
   var email = document.getElementById("emailInputRegister").value;
   var password = document.getElementById("passwordInputRegister").value;
   var confirmationPassword = document.getElementById("confirmationPassword").value;

   // Check if the entered passwords match
   if (password != confirmationPassword) {
       // Display an alert if passwords do not match
       alert("Passwords do not match");
       return;
   }


   // Display an alert with user registration information
   alert("Registering user with email: " + email + " and password: " + password);


   // Configuration data for Cognito user pool
   poolData = {
       UserPoolId : _config.cognito.userPoolId,
       ClientId : _config.cognito.userPoolClientId
   }


   // Create a new Cognito user pool object
   var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
   var attributeList = [];


   // Create an attribute for the user's email
   var dataEmail = {
       Name : 'email',
       Value : email
   }


   var attributeEmail = new AmazonCognitoIdentity.CognitoUserAttribute(dataEmail);


   // Add the email attribute to the attribute list
   attributeList.push(attributeEmail);


   // Sign up the user with Cognito user pool
   userPool.signUp(email.replace("@", "_"), password, attributeList, null, function(err, result){
       if (err) {
           // Display an alert if there's an error during user registration
           alert(err.message || JSON.stringify(err));
           return;
       }
       // User registration successful
       cognitoUser = result.user;
       alert('User registered successfully');


       // Display a success message to the user
       document.getElementById("successMsg").innerHTML = "Check email for verification code";
       $('#successMsg').show();
   });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;[ &lt;a href="https://github.com/alanblockley/cognito-javascript-example/blob/main/js/register.js"&gt;https://github.com/alanblockley/cognito-javascript-example/blob/main/js/register.js&lt;/a&gt; ]&lt;/p&gt;

&lt;p&gt;The JavaScript you've got here is all about making user registration smooth using Amazon Cognito. It kicks off by grabbing what the user typed in for email, password, and the confirmation. &lt;/p&gt;

&lt;p&gt;Quick check to see if the passwords match, and if not, it throws a friendly alert. Assuming all's good, it sets up a user using the Amazon Cognito service, putting together some pre-configured info. The user's email is added as an attribute, and then it's signup time with Cognito. If all goes well, a success message pops up, telling the user to check their email for a verification code. &lt;/p&gt;

&lt;p&gt;It relies on the Amazon Cognito Identity SDK to handle the heavy lifting and brings together user registration, password checks, and Cognito integration for a seamless experience on a web app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Login
&lt;/h2&gt;

&lt;p&gt;Using a simple login.html we’re able to now send basic username and password information, registered in our previous example, to Cognito and receive a token. &lt;/p&gt;

&lt;p&gt;Again, we will now set up an interface to login.  It’s not pretty, but remember, we’re not front end developers.  Function over form!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;form id="signinForm" novalidate onsubmit="return false"&amp;gt;
  &amp;lt;div class="form-group"&amp;gt;
      &amp;lt;label for="emailInputSignin"&amp;gt;Username&amp;lt;/label&amp;gt;
      &amp;lt;input type="text" class="form-control" id="emailInputSignin" placeholder="Enter email"&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;div class="form-group"&amp;gt;
      &amp;lt;label for="passwordInputSignin"&amp;gt;Password&amp;lt;/label&amp;gt;
      &amp;lt;input type="password" class="form-control" id="passwordInputSignin" placeholder="Password"&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;button type="submit" class="btn btn-primary btn-block"&amp;gt;Login&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;

...

&amp;lt;script src="js/vendor/login.js"&amp;gt;&amp;lt;/script&amp;gt;

&amp;lt;script type="text/JavaScript"&amp;gt;
  $('#signinForm').on("submit", function (event) {
    userLogin();
    event.preventDefault();
  })
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;[ &lt;a href="https://github.com/alanblockley/cognito-javascript-example/blob/main/html/login.html"&gt;https://github.com/alanblockley/cognito-javascript-example/blob/main/html/login.html&lt;/a&gt; ]&lt;/p&gt;

&lt;p&gt;And don’t forget our libraries and config.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script src="assets/js/config.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src="js/vendor/aws-cognito-sdk.min.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src="js/vendor/amazon-cognito-identity.min.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This trigger in our not so pretty login page calls the below function;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Function to handle user login with local storage
function userLogin() {

    // See if the id_token already exists in session storage
    let id_token = sessionStorage.getItem("id_token");

    if (id_token) {
        // If id_token exists, user is already logged in
        alert("Already logged in");
    } else {
        // Get user input values for email and password from the signin form
        var email = document.getElementById("emailInputSignin").value;
        var password = document.getElementById("passwordInputSignin").value;

        // Configuration data for Cognito user pool
        poolData = {
            UserPoolId : _config.cognito.userPoolId,
            ClientId : _config.cognito.userPoolClientId
        }

        // Create a new Cognito user pool object
        var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
        var userData = {
            Username : email,
            Pool : userPool
        }

        // Create a new Cognito user object
        var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);

        // Authenticate the user with Cognito using provided credentials
        cognitoUser.authenticateUser(new AmazonCognitoIdentity.AuthenticationDetails({
            Username : email,
            Password : password
        }), {
            onSuccess: function (result) {
                // If authentication is successful
                console.log('Successfully logged in');

                // Get and store access, ID, and refresh tokens in session storage
                var access_token = result.getAccessToken().getJwtToken();
                var id_token = result.getIdToken().getJwtToken();
                var refresh_token = result.refreshToken.token;

                sessionStorage.setItem("id_token", id_token);
                sessionStorage.setItem("access_token", access_token);
                sessionStorage.setItem("refresh_token", refresh_token);

                // Display success message to the user
                alert("Successfully logged in");

                // Additional actions or navigation can be added here
            },
            onFailure: function(err) {
                // If authentication fails, display an error message
                alert(err.message || JSON.stringify(err));
                return;
            }
        });
    }
}

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

&lt;/div&gt;



&lt;p&gt;This piece of JavaScript is all about handling user logins, and it's got a clever twist with local storage. It first checks if there's an existing ID token stashed in the session storage. If it finds one, it means the user is already logged in, and a friendly "already logged in" alert pops up. But if not, it goes on to grab the user's email and password from the signin form. &lt;/p&gt;

&lt;p&gt;Then, it sets up the user pool with the familiar Cognito data and creates a new Cognito user using the provided credentials. When it tries to authenticate the user, if successful, it logs a victory message, snags the access, ID, and refresh tokens, and stores them in the session storage. &lt;/p&gt;

&lt;p&gt;A triumphant "Successfully logged in" alert signals the completion. You can practically smell the success from here!&lt;/p&gt;

&lt;p&gt;Once we have these two methods implemented in practice, we log in, see our wonderful app and we can carry on developing.  One of the gotchas you’ll find is that after about an hour, your app will start giving you 401 errors in the development console. &lt;/p&gt;

&lt;p&gt;This is because your access and ID token have expired.  So what do we do now?  &lt;/p&gt;

&lt;p&gt;This is where the refresh token comes in! &lt;/p&gt;

&lt;h2&gt;
  
  
  Refresh token
&lt;/h2&gt;

&lt;p&gt;One of the tricks in your code has to be to acknowledge when your resources start returning HTTP 401 errors.  This can stop the whole show if not dealt with. &lt;/p&gt;

&lt;p&gt;It’s up to you and your logic how you detect 401 errors but below is an example Javascript function to refresh the token to then keep things moving.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Function to refresh the Amazon Cognito authentication token
function refreshToken() {

    // Retrieve the existing id_token and refresh_token from session storage
    let id_token = sessionStorage.getItem("id_token");
    let refresh_token = sessionStorage.getItem("refresh_token");

    // Check if id_token exists
    if (id_token) {
        // Configuration data for Cognito user pool
        poolData = {
            UserPoolId : _config.cognito.userPoolId,
            ClientId : _config.cognito.userPoolClientId
        }

        // Create a new Cognito user pool object
        var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
        var userData = {
            Username : id_token, // Using id_token as the username for refreshing
            Pool : userPool
        }

        // Create a new Cognito user object
        var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);

        // Create a CognitoRefreshToken object using the refresh_token
        var cognitoRefreshToken = new AmazonCognitoIdentity.CognitoRefreshToken({RefreshToken: refresh_token});

        // Refresh the session using the refresh token
        cognitoUser.refreshSession(cognitoRefreshToken, (err, session) =&amp;gt; {
            if (err) {
                // Log any errors during token refresh
                console.log(err);
                return;
            }

            // Get and update the new id_token and access_token
            id_token = session.getIdToken().getJwtToken();
            access_token = session.getAccessToken().getJwtToken();

            // Store the new tokens in session storage
            sessionStorage.setItem("id_token", id_token);
            sessionStorage.setItem("access_token", access_token);

            // Now that we have new tokens, additional actions can be performed
            // ...

            // Display a success message to the user
            alert('Auth token refreshed');
        });
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final JavaScript function, &lt;code&gt;refreshToken&lt;/code&gt;, is designed to handle the refreshing of Amazon Cognito authentication tokens, specifically the &lt;code&gt;id_token&lt;/code&gt; and &lt;code&gt;access_token&lt;/code&gt;. It begins by retrieving the existing &lt;code&gt;id_token&lt;/code&gt; and &lt;code&gt;refresh_token&lt;/code&gt; from the session storage. &lt;/p&gt;

&lt;p&gt;The function checks if an &lt;code&gt;id_token&lt;/code&gt; exists and proceeds to configure the Cognito user pool with predefined pool data. It then creates a Cognito user object using the &lt;code&gt;id_token&lt;/code&gt; as the username for refreshing. A &lt;code&gt;CognitoRefreshToken&lt;/code&gt; object is created using the existing &lt;code&gt;refresh_token&lt;/code&gt;. The function then attempts to refresh the session using the &lt;code&gt;cognitoRefreshToken&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;If successful, it updates the &lt;code&gt;id_token&lt;/code&gt; and &lt;code&gt;access_token&lt;/code&gt; with the new tokens obtained from the refreshed session and stores them back in the session storage. Additionally, the function provides a placeholder comment indicating that additional actions can be performed with the new tokens. &lt;/p&gt;

&lt;p&gt;Finally, a success message is displayed to the user via an alert. In summary, this function ensures that the user's authentication tokens stay up-to-date by refreshing them when needed, contributing to a more seamless and secure user experience in applications using Amazon Cognito.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/alanblockley/cognito-javascript-example"&gt;Supporting source code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault"&gt;Event: preventDefault() method&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html"&gt;Using tokens with user pools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-getting-started.html"&gt;Getting started with Amazon Cognito&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>javascript</category>
      <category>cognito</category>
    </item>
    <item>
      <title>Introducing WeatherFIT (And PartyRock): Your Ultimate Style Wingman Powered by AI Magic!</title>
      <dc:creator>Alan Blockley</dc:creator>
      <pubDate>Thu, 16 Nov 2023 17:26:17 +0000</pubDate>
      <link>https://dev.to/aws-builders/introducing-weatherfit-and-partyrock-your-ultimate-style-wingman-powered-by-ai-magic-2149</link>
      <guid>https://dev.to/aws-builders/introducing-weatherfit-and-partyrock-your-ultimate-style-wingman-powered-by-ai-magic-2149</guid>
      <description>&lt;p&gt;Step into a world where fashion meets futuristic intelligence with the groundbreaking WeatherFIT app! Forget the fashion guesswork; now you can stride confidently into any day, any event, anywhere, thanks to the sensational power of PartyRock, an Amazon Bedrock Playground.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XPWF4LKn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3dbfv7pcnonhsx3p52dc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XPWF4LKn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3dbfv7pcnonhsx3p52dc.png" alt="Image description" width="800" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Get Ready to Rock Your Wardrobe&lt;/em&gt; - Picture this: you're about to head out – work, party, or just a casual shopping spree – and you're wondering, "What the heck do I wear?" Enter WeatherFIT, your personal fashion oracle. This app doesn’t just predict the weather; it crafts personalized fashion advice based on your location, ensuring you're always looking on point.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Outfit Suggestions Tailored to You&lt;/em&gt; - WeatherFIT doesn't stop at giving basic recommendations; it dives deep into your wardrobe dilemmas. Warm day? Cool night? Rainy morning? WeatherFIT has you covered, suggesting outfits that not only match the weather but also make you stand out. And here's the kicker – it even tells you if you need an umbrella or sunscreen. Now that's next-level fashion foresight!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Style Comes to Life&lt;/em&gt; - But wait, there's more! WeatherFIT doesn’t just talk the talk; it walks the runway with personalized outfit illustrations. See your ideal outfit come to life in stunning AI-generated images, catering to both the gents and the ladies. Now you're not just dressing; you're making a style statement.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BCIwXR0z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1tv180vrixddjhcs7kyp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BCIwXR0z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1tv180vrixddjhcs7kyp.png" alt="Image description" width="800" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  NEWSFLASH - The Tech Behind the Glam - PartyRock Unleashed!
&lt;/h1&gt;

&lt;p&gt;The genius behind WeatherFIT is PartyRock – an Amazon Bedrock Playground that turns app creation into an electrifying experience. No coding skills are required! Just a few clicks, a dash of creativity, and boom – you've got an app that's as exciting as your fashion choices.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“PartyRock, an Amazon Bedrock Playground, is a generative AI app building playground that makes it easy to experiment hands-on with prompt engineering in an intuitive and fun way. In just a few clicks you can build apps to play with generative AI in a variety of ways”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Easy as 1-2-3&lt;/em&gt; - Don't worry if you're not a tech whiz. PartyRock is designed for everyone. PartyRock's user-friendly web-based UI lets you dive into the excitement of app creation without an AWS account. It's as easy as asking for fashion advice from your high-tech best friend (aka WeatherFIT).&lt;/p&gt;

&lt;p&gt;Here’s how I created this little genius app;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I registered for PartyRock in a matter of seconds with my favorite federated identity.&lt;/li&gt;
&lt;li&gt;Click “Build your own app”&lt;/li&gt;
&lt;li&gt;Give PartyRock a prompt “an application that would suggest what to wear based upon the weather and to make recommendations for male and female”.
That’s it!
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once it generated my app, I added two more widgets to create an AI-generated image of the output for males and females and positioned them where I wanted.   The end result was surprisingly on point.  Just like the advice WeatherFIT gives!&lt;/p&gt;

&lt;p&gt;The extra widgets are further examples of prompt engineering inside of Amazon Bedrock, but the real magic is the ability to reference a previous prompt or output by using &lt;code&gt;@&lt;/code&gt; tags. &lt;/p&gt;

&lt;p&gt;This application took me no more than 5 minutes to create!  In fact I've spent longer writing this post to celebrate the release of PartyRock than making my first app.&lt;/p&gt;

&lt;p&gt;Get started today with your own application - &lt;a href="https://partyrock.aws/"&gt;https://partyrock.aws/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;“Everyone can build AI apps with PartyRock, an Amazon Bedrock Playground”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hCSF4yt3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2vwlrpf75eq6k29fngpc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hCSF4yt3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2vwlrpf75eq6k29fngpc.png" alt="Image description" width="800" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>awscommunity</category>
      <category>awsambassador</category>
      <category>partyrockplayground</category>
    </item>
    <item>
      <title>Demystifying permissions within AWS SAM resources</title>
      <dc:creator>Alan Blockley</dc:creator>
      <pubDate>Wed, 12 Jul 2023 11:02:35 +0000</pubDate>
      <link>https://dev.to/alanblockley/demystifying-permissions-within-aws-sam-resources-2h63</link>
      <guid>https://dev.to/alanblockley/demystifying-permissions-within-aws-sam-resources-2h63</guid>
      <description>&lt;p&gt;Permission seekers and control freaks, this one is for you!  &lt;/p&gt;

&lt;p&gt;In the complex world of AWS Serverless Application Model (SAM), understanding permissions is the key to unlocking secure serverless magic. Picture this: An IAM policy, an AWS Connector and a function policy template walk into a bar... Okay, this is not the start of a bad joke, but your application will be if you don’t manage your permissions appropriately. &lt;/p&gt;

&lt;p&gt;In this weeks post, we'll explore the different permission mechanisms with some code examples for the visual learners amongst us. Prepare to navigate the realm of AWS SAM permissions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UaKxHYJy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zygixgmvch3usu4jwack.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UaKxHYJy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zygixgmvch3usu4jwack.png" alt="Image description" width="713" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  IAM Policies, AWS Connectors and SAM Policy Templates
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;IAM Policy Statements: For the Control Freaks of Permission Management&lt;/strong&gt; - IAM policies are for the detailed orientated system administrators and solutions architects amongst us.  Oh, how they love to be in control! These fine-grained superheroes (the IAM Policies not the Systadmins) allow you to define who can access and perform specific actions within your AWS realm. With the power of IAM policies, you can micromanage permissions like a boss. It's like assigning bouncers to protect your AWS resources and ensuring that only the chosen ones get past the velvet rope.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Inline IAM Policy within a function&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Resources:
  MyLambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: python3.8
      Handler: lambda_function.handler
      CodeUri: ./lambda_function
    Policies:
      - Statement:
          - Effect: Allow
            Action: s3:GetObject
            Resource: arn:aws:s3:::my-bucket/*
          - Effect: Allow
            Action: dynamodb:PutItem
            Resource: arn:aws:dynamodb:us-east-1:123456789012:table/my-table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Separate IAM Policy, attached to a function.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Resources:
  MyLambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: python3.8
      Handler: lambda_function.handler
      CodeUri: ./lambda_function
      Policies:
        - !Ref MyLambdaFunctionPolicy

  MyLambdaFunctionPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: MyLambdaFunctionPolicy
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action: s3:GetObject
            Resource: arn:aws:s3:::my-bucket/*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;N.B.&lt;/strong&gt; It’s also worth noting you can use AWS Managed IAM Policies and any other accepted CloudFormation mechanism here too if required. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-permissions-cloudformation.html"&gt;https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-permissions-cloudformation.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Connectors: The Predefined Dance Partners of SAM&lt;/strong&gt; - Now, let's meet the AWS Connectors, the happy meal of the permission world! These connectors are almost like pre-made boxes of permission happiness where you pick your main, your side and your toy!&lt;/p&gt;

&lt;p&gt;They come with predefined permissions, like ready-made menu options, making integration a breeze. Fancy your cheeseburger with a side of Amazon S3? The S3 Connector has got you covered. Want to savour the flavours of DynamoDB?  The DynamoDB Connector will satisfy those cravings.  They simplify integration and package it into fewer lines than an AWS IAM Policy.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Serverless Function using Connector&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Resources:
  MyLambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: python3.8
      Handler: lambda_function.handler
      CodeUri: ./lambda_function
    Events:
      MyS3Event:
        Type: S3
        Properties:
          Bucket: 'my-bucket'
          Events: s3:ObjectCreated:*
    Connectors:
      S3BucketConnector:
        Properties:
          Destination: 
            Id: 'my-bucket'
          Permissions:
            - Read
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/managing-permissions-connectors.html"&gt;https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/managing-permissions-connectors.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SAM Policy Templates: Function-Specific Permission Ninjas&lt;/strong&gt; - Enter the SAM Policy Template, the stealthy ninjas of permissions. With these policies, you can grant specific permissions to your Lambda functions without breaking a sweat. It's like having a team of specialised martial arts experts guarding each function. Whether you define policies inline or reference existing IAM policies, these ninjas ensure your functions have the right permissions, without causing unnecessary chaos. Talk about precision and elegance!  To top this off, it’s much shorter than writing a whole IAM Policy statement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Serverless Function using Connector
Resources:
  MyLambdaFunction:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: python3.8
      Handler: lambda_function.handler
      CodeUri: ./lambda_function
      Policies:
        - DynamoDBReadWritePolicy:
            TableName: my-table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-policy-templates.html"&gt;https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-policy-templates.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Differences and Use Cases: SAM Permissions in Action
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---z6AFjfo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wz1paz56kzyh0uodaz16.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---z6AFjfo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wz1paz56kzyh0uodaz16.png" alt="Image description" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a nutshell, here's a breakdown of the differences and use cases for IAM Policies, AWS Connectors, and SAM Policy Templates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;IAM Policies: Offer fine-grained control over permissions at the user or role level. Ideal for managing access to multiple AWS resources or services with precision.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AWS Connectors: Simplify integration with specific AWS services by providing predefined permissions out of the box. Perfect for streamlined integration without the need for manual IAM policy configuration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SAM Policy Templates: Grant function-specific permissions within your SAM template, eliminating the need for separate IAM policies. Well-suited for managing permissions at the function level with a focus on individual functions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choose IAM Policies for granular control, AWS Connectors for simplified service integration, and SAM Policy Templates for function-specific permissions. Tailor your approach based on your specific requirements, balancing control and convenience.&lt;/p&gt;

&lt;h2&gt;
  
  
  In closing...
&lt;/h2&gt;

&lt;p&gt;And that concludes today’s lecture, permission aficionados! We’ve mastered the art of AWS SAM permissions together. IAM Policies, AWS Connectors, and SAM Policy Templates are your permission pals, each with its unique style. IAM policies provide control, Connectors are the happy meal of the permission world, and function policies bring stealthy precision. Embrace the right approach for your use cases and security needs, ensuring your serverless applications operate through the AWS SAM world with style. &lt;/p&gt;

&lt;p&gt;Stay tuned for more AWS SAM adventures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-permissions.html"&gt;https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-permissions.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Demystifying the Basic Anatomy of an AWS SAM Template</title>
      <dc:creator>Alan Blockley</dc:creator>
      <pubDate>Wed, 12 Jul 2023 11:00:57 +0000</pubDate>
      <link>https://dev.to/alanblockley/demystifying-the-basic-anatomy-of-an-aws-sam-template-2h1o</link>
      <guid>https://dev.to/alanblockley/demystifying-the-basic-anatomy-of-an-aws-sam-template-2h1o</guid>
      <description>&lt;p&gt;In my previous post, we looked at the basic concept of AWS SAM and the high-level problem it solves. If that wasn't enough for you, keep reading!&lt;/p&gt;

&lt;p&gt;AWS Serverless Application Model (SAM) templates are the building blocks of serverless applications on Amazon Web Services (AWS). Understanding the basic anatomy of an AWS SAM template is essential for harnessing the full power of serverless development. In this blog post, we'll delve into the fundamental elements that make up an AWS SAM template, unlocking the secrets to crafting scalable and efficient serverless applications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eytcyN_N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ggiq5n30m1bpettiorsw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eytcyN_N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ggiq5n30m1bpettiorsw.png" alt="Image description" width="346" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;YAML at Your Service&lt;/em&gt; - An AWS SAM template is written in YAML (YAML Ain't Markup Language), a human-readable and expressive data serialisation format. YAML allows for a concise and structured representation of your serverless application's resources and configurations. (Just watch those tabs/spaces, IYKYK!)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Transforming the Magic&lt;/em&gt; - At the beginning of your AWS SAM template, you'll find the Transform section. This is where the transformation magic happens.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transform: AWS::Serverless-2016-10-31
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above declaration instructs AWS CloudFormation to use the AWS SAM syntax and capabilities to deploy and manage your serverless application. Why? Because SAM offers a lot of flexibility by providing special resource definitions that can save an engineer a lot of time NOT writing CloudFormation! Don't panic though, you can still use CloudFormation resources too!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/format-version-structure.html"&gt;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/format-version-structure.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Parameters: Flexible Configurations&lt;/em&gt; - In the Parameters section, you can define customizable inputs to your AWS SAM template. Parameters allow for flexible configuration options, enabling you to modify aspects of your serverless application during deployment. You can specify parameter types, and default values, and even apply constraints to ensure the correct inputs are provided.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Parameters:
  Stage:
    Type: String
    Default: Prod
    Description: Deployment stage of the application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html"&gt;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Resources. The Building Blocks of Your Application&lt;/em&gt; - The heart of your AWS SAM template lies within the Resources section. Here, you define the AWS resources required for your serverless application, such as AWS Lambda functions, API Gateway endpoints, DynamoDB tables, and more. Each resource is represented by a unique logical ID and its corresponding properties.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resources-section-structure.html"&gt;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resources-section-structure.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Functions: The Powerhouses of Your Application&lt;/em&gt; - Within the Resources section, AWS Lambda functions are defined under the AWS::Serverless::Function resource type. These functions encapsulate your code logic and can be triggered by various events, such as API Gateway requests or scheduled events. Specify the function's runtime, handler, memory allocation, and other properties to unleash its full potential.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: python3.8
      Handler: lambda_function.handler
      CodeUri: ./lambda_function
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html"&gt;https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Event Sources: Triggers for Your Functions&lt;/em&gt; - To invoke your AWS Lambda functions, you'll need event sources. Event sources define the triggers that initiate the execution of your functions. They can be API Gateway endpoints, S3 bucket notifications, or even custom event sources. Connect your functions to the relevant event sources in the Resources section to enable seamless and event-driven serverless architecture.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: python3.8
      Handler: lambda_function.handler
      CodeUri: ./lambda_function
      Events:
        HelloWorldApi:
          Type: Api
          Properties:
            Path: /hello
            Method: get
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-eventsource.html"&gt;https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-eventsource.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Outputs: Communicating the Magic&lt;/em&gt; - Outputs allow you to communicate important information from your AWS SAM template to other resources or external systems. You can define outputs to export values such as API endpoint URLs or resource ARNs. These outputs can be referenced and utilized by other AWS CloudFormation stacks or external applications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html"&gt;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Parameters:
  Stage:
    Type: String
    Default: Prod
    Description: Deployment stage of the application

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: python3.8
      Handler: lambda_function.handler
      CodeUri: ./lambda_function
      Events:
        HelloWorldApi:
          Type: Api
          Properties:
            Path: /hello
            Method: get

Outputs:
  HelloWorldApiUrl:
    Description: URL of the Hello World API endpoint
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/${Stage}/hello"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et Voila!!! You've now unlocked the basic anatomy of an AWS SAM template. By understanding the different sections and elements, you have the power to craft sophisticated and scalable serverless applications. With YAML as your trusty tool and the knowledge of transforms, resources, functions, event sources, outputs, and parameters, you can architect magical serverless experiences. Stay tuned for more exciting adventures in AWS SAM, where we'll explore advanced features and techniques to take your serverless applications to new heights. Happy serverless coding!&lt;br&gt;
Source: &lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-template-anatomy.html"&gt;https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-template-anatomy.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>serverless</category>
    </item>
    <item>
      <title>AWS SAM Demystified</title>
      <dc:creator>Alan Blockley</dc:creator>
      <pubDate>Wed, 05 Jul 2023 01:43:10 +0000</pubDate>
      <link>https://dev.to/alanblockley/aws-sam-demystified-544h</link>
      <guid>https://dev.to/alanblockley/aws-sam-demystified-544h</guid>
      <description>&lt;p&gt;Are you intrigued by the concept of serverless computing and the wonders it can bring to your applications? Well, get ready for a mind-blowing adventure into the world of AWS Serverless Application Model (SAM)! In this article, we'll take you on a journey to demystify AWS SAM and explore its key features.&lt;/p&gt;

&lt;p&gt;Buckle up and prepare to unleash the magic of serverless development!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vGSammkB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kspzw0dia1yf52jnyltw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vGSammkB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kspzw0dia1yf52jnyltw.png" alt="Image description" width="571" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Firstly let's start by understanding the essence of AWS SAM. Let's start at the beginning. AWS SAM is a powerful framework designed to simplify the development, testing, and deployment of serverless applications on Amazon Web Services (AWS). It's like having a spellbook filled with pre-defined templates, ready to enchant your applications and take away the complexities of infrastructure management.&lt;/p&gt;

&lt;p&gt;AWS SAM is driven by three pillars;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fswhWJ_---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eefjez2czxdeqfdj3v5z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fswhWJ_---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eefjez2czxdeqfdj3v5z.png" alt="Image description" width="720" height="1306"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The SAM template is your ticket to serverless stardom. It allows you to describe the AWS resources needed for your application, including functions, event sources, permissions, and more. With its simplified syntax and predefined resource types, you'll be conjuring serverless applications in no time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  AWSTemplateFormatVersion: '2010-09-09'
  Transform: AWS::Serverless-2016-10-31
  Description: &amp;gt;
    sam-app

    Sample SAM Template for sam-app

  Globals:
    Function:
      Timeout: 3
      MemorySize: 128

  Resources:
    HelloWorldFunction:
      Type: AWS::Serverless::Function 
      Properties:
        CodeUri: hello_world/
        Handler: app.lambda_handler
        Runtime: python3.9
        Architectures:
          - x86_64
        Events:
          HelloWorld:
            Type: Api 
            Properties:
              Path: /hello
              Method: get

  Outputs:
    HelloWorldApi:
      Description: "API Gateway endpoint URL for Prod stage for Hello World function"
      Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
    HelloWorldFunction:
      Description: "Hello World Lambda Function ARN"
      Value: !GetAtt HelloWorldFunction.Arn
    HelloWorldFunctionIamRole:
      Description: "Implicit IAM Role created for Hello World function"
      Value: !GetAtt HelloWorldFunctionRole.Arn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SAM CLI (Command Line Interface) is your trusty sidekick in the realm of local development. It enables you to test and debug your serverless applications on your local machine using Docker. You can simulate AWS Lambda and API Gateway locally, sparing you the need to repeatedly deploy your code to AWS during development.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  $ sam local invoke

  ...

  {"statusCode": 200, "body": "{\"message\": \"hello world\"}"}%

  $ sam local start-api

  ...

  2023-06-07 15:07:08 127.0.0.1 - - [07/Jun/2023 15:07:08] "GET /hello HTTP/1.1" 200 -
  2023-06-07 15:07:08 127.0.0.1 - - [07/Jun/2023 15:07:08] "GET /favicon.ico HTTP/1.1" 403 -

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m9-2XQmJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yzcr8nv6xm0gielvmcj7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m9-2XQmJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yzcr8nv6xm0gielvmcj7.png" alt="Image description" width="720" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With a flick of your wrist, SAM empowers you to package and deploy your serverless applications effortlessly. It bundles your code and dependencies, generating deployment artefacts ready to be deployed using AWS CloudFormation. Deploying your application becomes as simple as casting a spell!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  $ sam deploy --guided

  Configuring SAM deploy
  ======================

  Looking for config file [samconfig.toml] :  Found
  Reading default arguments  :  Success

  Setting default arguments for 'sam deploy'
  =========================================
  Stack Name [sam-app]: hello-world
  AWS Region [ap-southeast-2]:
  #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
  Confirm changes before deploy [Y/n]:
  #SAM needs permission to be able to create roles to connect to the resources in your template
  Allow SAM CLI IAM role creation [Y/n]:
  #Preserves the state of previously provisioned resources when an operation fails
  Disable rollback [y/N]:
  HelloWorldFunction may not have authorization defined, Is this okay? [y/N]: y
  Save arguments to configuration file [Y/n]: n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5GpasyTH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h5kigb1fc9mmchio2whn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5GpasyTH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h5kigb1fc9mmchio2whn.png" alt="Image description" width="720" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AWS SAM embraces the thriving serverless ecosystem by supporting popular programming languages, frameworks, and tools. Whether you're a Python charmer, a Node.js ninja, or a Java guru, SAM has got you covered. It's a gateway to a vibrant community of serverless enthusiasts, who share knowledge and reusable components.&lt;/p&gt;

&lt;p&gt;A detailed list of runtimes can be found here - &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html"&gt;https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that's it! You've gained a solid foundation to embark on your serverless journey. In this article, we've unravelled the essence of AWS SAM, explored its three pillars, mastered the SAM template, and discovered the joys of local development, packaging, and deployment. Get ready for the next adventure where we'll dive deeper into the mystical world of AWS SAM and uncover more powerful spells for serverless success. Stay curious, keep exploring, and embrace the magic of AWS SAM!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>serverless</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
