<?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: Andres Moreno</title>
    <description>The latest articles on DEV Community by Andres Moreno (@andmoredev).</description>
    <link>https://dev.to/andmoredev</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%2F157668%2F1ef182ef-5181-45f2-8808-f5249c1f87e4.png</url>
      <title>DEV Community: Andres Moreno</title>
      <link>https://dev.to/andmoredev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/andmoredev"/>
    <language>en</language>
    <item>
      <title>Step Functions without ASL? Welcome Lambda Durable Functions</title>
      <dc:creator>Andres Moreno</dc:creator>
      <pubDate>Wed, 17 Dec 2025 00:05:00 +0000</pubDate>
      <link>https://dev.to/aws-builders/step-functions-without-asl-welcome-lambda-durable-functions-3io3</link>
      <guid>https://dev.to/aws-builders/step-functions-without-asl-welcome-lambda-durable-functions-3io3</guid>
      <description>&lt;p&gt;During re:Invent, AWS announced a new feature within AWS Lambda called durable functions. These are the same Lambda functions we all love, but they let you run multi-step workflows by keeping checkpoints and state. What does this mean? You can run similar functionality to what we’ve typically used AWS Step Functions for. But instead of using Amazon State Language, you can use familiar code and dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Terms and Concepts
&lt;/h2&gt;

&lt;p&gt;Let’s go over a few concepts around durable functions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Durable Function&lt;/strong&gt; - It’s a regular Lambda function that can pause and resume by making use of a checkpoint and replay mechanism.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checkpoint&lt;/strong&gt; - When the function executes a step or needs to wait for a callback, it will add a checkpoint that persists the current state and stops its execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replay&lt;/strong&gt; - Once the workflow is ready to resume execution, it will pick up from the last checkpoint rather than running the whole workflow again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Durable Execution&lt;/strong&gt; - The complete lifecycle of a durable function. From the moment it is triggered until it completes all defined steps and is closed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Durable Context&lt;/strong&gt; - The context that is provided to the Lambda handler. This contains the methods for the durable operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Durable Operations&lt;/strong&gt; - These are the operations that allow us to create a workflow within a Lambda function. Each step has built-in retries and automatically creates checkpoints when it runs. Let’s go over each of the operations at a high level.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Steps&lt;/strong&gt; - Run business logic and are defined by using the &lt;code&gt;context.step()&lt;/code&gt; operator.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wait States&lt;/strong&gt; - Planned pauses that cause the function to stop running until they are resumed. This can be used to wait for a specific amount of time, for an external callback, or for other specific conditions. These are defined using &lt;code&gt;context.wait()&lt;/code&gt; , &lt;code&gt;context.waitForCallback()&lt;/code&gt; or &lt;code&gt;context.waitForCondition()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallel&lt;/strong&gt; - There will be situations where you want to optimize for speed and run things concurrently to reduce execution time. The parallel operation is used for that, and is defined by using the &lt;code&gt;context.parallel()&lt;/code&gt; operator.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iterations&lt;/strong&gt; - To process an array of items in a loop, you will have to use the &lt;code&gt;context.map()&lt;/code&gt; operation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Invoke other Lambda functions&lt;/strong&gt; - You can invoke external Lambda functions from within a workflow. To do this, you will call the &lt;code&gt;context.invoke()&lt;/code&gt; operator.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nested Operations&lt;/strong&gt; - To run operations as a child of another operation, you will call &lt;code&gt;context.runInChildContext()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to create a Lambda durable function
&lt;/h2&gt;

&lt;p&gt;This is probably the best thing about all of this. Most of the setup is done the same way as a regular Lambda function, BECAUSE IT’S THE SAME RESOURCE!. To enable a durable context for the function, we need to make one change to our IaC and another to the code handler. Let’s go over these.&lt;/p&gt;

&lt;h3&gt;
  
  
  Infrastructure Setup
&lt;/h3&gt;

&lt;p&gt;To set it up in SAM, all you need to do is set the DurableConfig object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;ExampleFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.handler&lt;/span&gt;
      &lt;span class="na"&gt;CodeUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;workflows/example/&lt;/span&gt;
      &lt;span class="na"&gt;DurableConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ExecutionTimeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
        &lt;span class="na"&gt;RetentionPeriodInDays&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s go over these two properties in more detail:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ExecutionTimeout&lt;/strong&gt; - This is the total time, in seconds, for a full durable execution. This can be up to a full year.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RetentionPeriodInDays&lt;/strong&gt; - The number of days the durable execution history is retained after it is closed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Code Handler Setup
&lt;/h3&gt;

&lt;p&gt;To make this simpler for us, AWS has provided a new SDK called @aws/durable-execution-sdk-js. This contains everything that you’ll need to build workflows in a Lambda function. But right now, the only thing we need to have a durable context is to wrap our handler with the &lt;code&gt;withDurableExecution&lt;/code&gt; function.&lt;br&gt;
Yes, that is all!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;withDurableExecution&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws/durable-execution-sdk-js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;withDurableExecution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Your handler code&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With those things in place, we can now define the workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Durable Operations
&lt;/h3&gt;

&lt;p&gt;Now I want to go through the different durable operations in detail and show them in a working example. If you want to follow along, I have everything defined and deployable in &lt;a href="https://github.com/andmoredev/durable-functions/blob/main/workflows/durable-function-example/index.mjs" rel="noopener noreferrer"&gt;this GitHub location&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step
&lt;/h4&gt;

&lt;p&gt;At its most basic level, we have the step. This will create a checkpoint after it’s run, and you can define any logic you want within the step. I recommend splitting the actual business logic outside of the workflow definition to keep it clean and readable. This makes it easy to follow when there are issues with your workflow, and you can find the step that broke.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// Step 1: Initial step operation - process input data&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;workItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;processInputData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;processData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Human in the loop
&lt;/h4&gt;

&lt;p&gt;Whenever you want to wait for a human action, you will use the &lt;code&gt;waitForCallback&lt;/code&gt; operation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// Step 2: Wait for callback operation - pause for external event&lt;/span&gt;
  &lt;span class="c1"&gt;// Wait for external callback with timeout handling&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;callbackResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wait-for-external-callback&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callbackId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Submit callback ID to external system (simulated)&lt;/span&gt;
      &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Callback ID &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;callbackId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; submitted to external system`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="c1"&gt;// In real implementation, this would call an external API&lt;/span&gt;
      &lt;span class="c1"&gt;// await submitToExternalAPI(callbackId);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// 1 hour timeout&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will give us a &lt;code&gt;callbackId&lt;/code&gt; that we can use to resume our workflow. When testing, you can resume the workflow from the AWS Console, but in real life, you will need to do this programmatically. Thankfully, the SDK provides the SendDurableExecutionCallbackSuccessCommand and SendDurableExecutionCallbackFailureCommand in the Lambda client, which can be used to resume the workflow. You can also specify a timeout so the workflow does not wait forever.&lt;/p&gt;

&lt;p&gt;Once the command is sent the workflow will resume and continue processing.&lt;/p&gt;

&lt;h4&gt;
  
  
  Wait
&lt;/h4&gt;

&lt;p&gt;If you ever need to wait a specific amount of time, you can use the &lt;code&gt;wait&lt;/code&gt; operation, which lets you specify the amount of time you want to wait.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// Step 3: Simple wait operation - demonstrate time-based wait&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// Wait for 5 seconds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Parallel Processing
&lt;/h4&gt;

&lt;p&gt;When you have several operations that can be handled independently, you might want to run them in parallel to reduce the total processing time. That’s where the &lt;code&gt;parallel&lt;/code&gt; operation comes in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// Step 4: Parallel operations - process multiple work streams concurrently&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parallelResults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parallel&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;parallelTask1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;performDataValidation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;workItemsCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;parallelTask2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;performDataEnrichment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;workItemsCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;parallelTask3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;performQualityCheck&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will attempt to run all three operations in parallel. If you are worried about putting too much load on another service, you can set &lt;code&gt;concurrency&lt;/code&gt; limits so that not everything is processed at once.&lt;/p&gt;

&lt;h4&gt;
  
  
  Iterate Arrays
&lt;/h4&gt;

&lt;p&gt;I always say that programming is all about &lt;code&gt;ifs&lt;/code&gt; and &lt;code&gt;fors&lt;/code&gt;. So how do you handle the &lt;code&gt;for&lt;/code&gt; situation when working with durable functions? We have the &lt;code&gt;map&lt;/code&gt; operator!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// Step 5: Map operation - iterate over collection with checkpoints&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mapResults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;workItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`processItem-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;processedItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;processingTime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;processWorkItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// Simulate processing time based on priority&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;processingTime&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;processedItem&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might be asking yourself, Why not just use a regular &lt;code&gt;for&lt;/code&gt; loop? The answer is CHECKPOINTS. By using the &lt;code&gt;map&lt;/code&gt; operation, you get the benefit of having Lambda manage the checkpoint and the current state of where things left off. This helps immensely if it didn’t process the array completely.&lt;/p&gt;

&lt;h4&gt;
  
  
  Wait for condition
&lt;/h4&gt;

&lt;p&gt;There is also a special wait operation that waits for a specific condition to be met. For this, you will use the &lt;code&gt;waitForCondition&lt;/code&gt; operation. You can think of this operation as a &lt;code&gt;do/while&lt;/code&gt;, where it will keep iterating until the &lt;code&gt;while&lt;/code&gt; condition is met.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Step 6: Wait for condition - poll until external system is ready&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;conditionResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForCondition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;readinessCheck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;checkSystemReadiness&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;readinessCheck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ready&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="na"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;waitStrategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ready&lt;/span&gt;
          &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;shouldContinue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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="na"&gt;shouldContinue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This has two parameters:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;WaitForConditionCheckFunc&lt;/strong&gt; - Function that will check the current state and return the updated state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WaitForConditionConfig&lt;/strong&gt; - Holds the configuration for the initial state and the wait strategy used to determine when it should continue and how long to delay before triggering WaitForConditionCheckFunc again.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Invoke Lambda Function
&lt;/h4&gt;

&lt;p&gt;As much as you try to avoid it, you will have a reason to invoke a separate Lambda function that does its own processing. This will be done by using the &lt;code&gt;invoke&lt;/code&gt; operation. All you need to do is give it the function’s ARN and the expected payload.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// Step 7: Invoke another Lambda function&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;invokePayload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createInvokePayload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;workItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;executionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;invokeResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invoke-hello-world&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HELLO_WORLD_FUNCTION_ARN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;invokePayload&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Child contexts
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;runInChildContext&lt;/code&gt; operation creates an isolated execution context for a group of operations. These have their own checkpoint logging and can have multiple steps, waits and other operations. This is treated as a single unit for retry and recovery. Use these when you want to organize complex workflows, implement sub-workflows or isolate operations that should retry together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// Step 8: Run operations in child context for isolation&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;childContextResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runInChildContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;isolated-operations&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;childCtx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// These operations run in isolation with their own checkpoint log&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;childCtx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;processMetadata&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;processMetadataInChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;executionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;workItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;childCtx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;validateConfiguration&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;validateConfigurationInChild&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;validation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;childExecutionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;childCtx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;executionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;completedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Invoking &amp;amp; Troubleshooting
&lt;/h3&gt;

&lt;p&gt;Once your durable function is deployed, you will want to invoke the function. Well, surprise, surprise, this is done exactly the same way you invoke any Lambda function, which means you can use all your regular event sources like API Gateway, EventBridge, and SQS. All of what I’m about to do can be done using the CLI, but for demo reasons, I will be using the console so we can get better visualizations.&lt;/p&gt;

&lt;p&gt;When the Lambda is deployed with the &lt;code&gt;DurableConfig&lt;/code&gt; you will see a new Durable executions tab in the Lambda console.&lt;/p&gt;

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

&lt;p&gt;To create a new execution, I invoked the function using the standard testing tool in the Lambda console.&lt;/p&gt;

&lt;p&gt;Now, let’s look at the details of the execution.&lt;/p&gt;

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

&lt;p&gt;This view should look very similar to what we’ve seen in AWS Step Functions. It doesn’t have the nice workflow diagram, though. There are two sections here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Durable operations&lt;/strong&gt; - here we will see all the operations that have been executed. In our example above, only two items appear because it has the human-in-the-loop step and is waiting for someone to respond with the callback ID. We will do that in a second.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event history&lt;/strong&gt; - a more verbose list of what has happened. This is where you can track errors that occurred in a specific step and get a broader view of everything that was executed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, let me send the success message for the callback. I will be doing this directly from the console, as shown below. If you are looking for a way to do this programmatically, I’ve included a &lt;a href="https://github.com/andmoredev/durable-functions/blob/main/scripts/resume-workflow.mjs" rel="noopener noreferrer"&gt;script here&lt;/a&gt;. This script will take in the callback ID and send a success message to continue the flow.&lt;/p&gt;

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

&lt;p&gt;This will now continue and execute all of the steps we have defined in our workflow.&lt;/p&gt;

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

&lt;p&gt;You’ll notice that some operations have explicit names and others have random IDs. The reason is that I did not explicitly specify a name for all the operations, so it will generate a random one for us.&lt;/p&gt;

&lt;p&gt;Let’s walk through the operations.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WaitForCallback&lt;/strong&gt; - The duration was 9 minutes and 43 seconds. That is how long it took to write a few of the paragraphs above, and then press the “Send success” button.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wait&lt;/strong&gt; - As expected, this took the 5 seconds we had configured it for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallel&lt;/strong&gt; - This operation groups all sub-operations under it, making them easier to track.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Map&lt;/strong&gt; - Similarly to the parallel step, it will group all of the iterations under it.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WaitForCondition&lt;/strong&gt; - The special emphasis here is that it has a 2 in Retries. This means it had to iterate twice before the condition was met.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ChainedInvoke&lt;/strong&gt; - Not too much to see for this step. It simply invoked the Lambda function and continued on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RunInChildContext&lt;/strong&gt; - This one also groups all the child operations under it.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0xxlfr0vty4w0ky6sub4.png" alt="Run in child context operation" width="800" height="73"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows you to see the different operations and how they look in the console. You can get this information using the CLI or the SDK, and create your own visualizations if necessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;Woah! That was a lot! We went through the setup to enable and deploy a durable function, as well as how to build a workflow that uses all of the currently supported durable operations.&lt;/p&gt;

&lt;p&gt;The fact that I am now able to create long-running workflows where I can include any npm dependency directly without having to explicitly call external Lambda functions is a huge win compared to what we've been doing with Step Functions.&lt;/p&gt;

&lt;p&gt;I am going to keep playing around with this and will be doing a side-by-side comparison with AWS Step Functions. Stay tuned!&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

&lt;p&gt;Andres Moreno&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>lambda</category>
    </item>
    <item>
      <title>Log buffering with Lambda Powertools</title>
      <dc:creator>Andres Moreno</dc:creator>
      <pubDate>Fri, 28 Mar 2025 00:05:00 +0000</pubDate>
      <link>https://dev.to/aws-builders/log-buffering-with-lambda-powertools-1nh</link>
      <guid>https://dev.to/aws-builders/log-buffering-with-lambda-powertools-1nh</guid>
      <description>&lt;p&gt;The Lambda Powertools team released a new feature that allows your Lambda functions to buffer logs. That sounded cool, but I didn't understand how it would work or how logs would show in CloudWatch. I decided to try it out and provide a visual example of how it looks with different configuration options.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is log buffering?
&lt;/h3&gt;

&lt;p&gt;This is what the Lambda Powertools documentation says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Log buffering enables you to buffer logs for a specific request or invocation. Enable log buffering by passing &lt;strong&gt;logBufferOptions&lt;/strong&gt; when initializing a Logger instance. You can buffer logs at the &lt;strong&gt;WARNING&lt;/strong&gt;, &lt;strong&gt;INFO&lt;/strong&gt;,  &lt;strong&gt;DEBUG&lt;/strong&gt;, or &lt;strong&gt;TRACE&lt;/strong&gt; level, and flush them automatically on error or manually as needed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The three main terms from that explanation are &lt;em&gt;buffering&lt;/em&gt;, &lt;em&gt;level&lt;/em&gt;, and &lt;em&gt;flush&lt;/em&gt;. Let's explain each of these in the context of log buffering.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Buffering&lt;/strong&gt;—This means that the Lambda function will hold the logs in memory without sending them to CloudWatch unless necessary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Level&lt;/strong&gt; - The log level at which you want to start buffering the logs. You can choose which level to begin buffering at. For example, if you configure the log buffer to do it at the INFO level, it will buffer those logs and any log levels with a lower value. In this case, it will also buffer TRACE and DEBUG. Below are all the supported log levels and their numeric value.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;
&lt;strong&gt;Level&lt;/strong&gt;&lt;br&gt;
&lt;/th&gt;
&lt;th&gt;
&lt;strong&gt;Numeric Value&lt;/strong&gt;&lt;br&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TRACE&lt;br&gt;
&lt;/td&gt;
&lt;td&gt;6&lt;br&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DEBUG&lt;br&gt;
&lt;/td&gt;
&lt;td&gt;8&lt;br&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;INFO&lt;br&gt;
&lt;/td&gt;
&lt;td&gt;12&lt;br&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WARN&lt;br&gt;
&lt;/td&gt;
&lt;td&gt;16&lt;br&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ERROR&lt;br&gt;
&lt;/td&gt;
&lt;td&gt;20&lt;br&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CRITICAL&lt;br&gt;
&lt;/td&gt;
&lt;td&gt;24&lt;br&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SILENT&lt;br&gt;
&lt;/td&gt;
&lt;td&gt;28&lt;br&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flush&lt;/strong&gt; - This means the buffered logs will get sent to CloudWatch. You can tell Lambda to send the logs to troubleshoot in case of errors in different ways.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why buffer logs?
&lt;/h3&gt;

&lt;p&gt;At this point, you might ask yourself, why do I want to buffer logs? The reason might not be immediately apparent. Logs are usually only needed when your application hits an error. This means that we are constantly logging things for all the successful requests and producing logs that no one will see. By enabling the logging buffer, you will only print the logs whenever a specific scenario or error is hit, allowing you to reduce the number of logs produced, the noise and, even better, your logging costs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration options
&lt;/h3&gt;

&lt;p&gt;There are a few pieces of configuration for buffer logging that can be set when you initialize a logger to handle things according to your needs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;enabled&lt;/strong&gt;—this should be straightforward. Log buffering is turned off by default; in order to start using it, this parameter will need to be set to &lt;em&gt;true.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;maxBytes&lt;/strong&gt;—this is the max amount of bytes the buffer will hold in memory. If you hit the max limit, the buffer will start dropping older logs to accommodate the new ones. This configuration allows you to protect the Lambda function from running out of memory. The logger will be nice enough to let you know it did this whenever the logs are flushed. The default value for this is 20480 bytes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;bufferAtVerbosity&lt;/strong&gt;—this is where you will configure at which log level you want to start buffering. Whatever value you configure will be buffered, as will any level with a lower numeric value. The default log level set is DEBUG.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;flushOnErrorLog&lt;/strong&gt;—This is a boolean value that tells the buffer to log whenever it encounters a &lt;em&gt;logger.error&lt;/em&gt; call. When writing Lambda functions, we typically have a try/catch statement where we log any errors that get thrown. This helps automatically flush the logs when an error is caught and logged. The default value is &lt;em&gt;true&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But now the question is, what happens if I don't catch and log an error? Will I not be able to see the buffered logs? Thankfully, the Lambda Powertools team thought about this, and they allow you to flush the logs in two other ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; &lt;strong&gt;flushBufferOnUncaughtError&lt;/strong&gt;—This option is only allowed when injecting the Lambda context into the logger. This property will flush the logs whenever the Lambda function encounters any error and is no explicitly handled by your code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;flushBuffer function&lt;/strong&gt;—You might want to flush the logs whenever a specific code path is hit or other scenarios that are specific for your use case. To do this, the logger has a new function called &lt;em&gt;flushBuffer&lt;/em&gt; that will manually send all of the buffered logs into CloudWatch. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Let's see this in action!
&lt;/h3&gt;

&lt;p&gt;To enable this feature, all we have to do is set the enabled property to true in the &lt;em&gt;logBufferOptions&lt;/em&gt; object for the Logger constructor, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;logBufferOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, to test, we will add all the different types of logs to our Lambda handler like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Trace Log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Debug Log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Info Log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Warn Log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error Log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;critical&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Critical Log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we run the Lambda function and view the logs produced in CloudWatch, we get this:  &lt;/p&gt;

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

&lt;p&gt;The results were unexpected, but then I looked at the default values for the logger options to try and understand this better. There are two default values to keep in mind for this example: &lt;strong&gt;flushOnErrorLog&lt;/strong&gt; is enabled by default, and the buffer's default log level is DEBUG. With this, let's understand why the logs show in this specific order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The TRACE and DEBUG logs are added to the buffer because of the log level setting and are not shown because of this.&lt;/li&gt;
&lt;li&gt;The INFO and WARN logs are printed as is since they are not included in the buffer because of the log level setting.&lt;/li&gt;
&lt;li&gt;The TRACE and DEBUG logs are now shown because we hit the ERROR log, which means the logs are flushed before the ERROR log is printed.&lt;/li&gt;
&lt;li&gt;Finally, the ERROR and CRITICAL logs are printed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we were to run the same test but, in this scenario, don't log an error, we would really appreciate the value of the log buffer.&lt;/p&gt;

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

&lt;p&gt;From the image above, we can see that only the non-buffered logs made it to CloudWatch, meaning that if there is no error, we won't see or get charged for the buffered logs.&lt;/p&gt;

&lt;p&gt;Now, let's configure the logger to buffer at the WARN level.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;logBufferOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;bufferAtVerbosity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;WARN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can se, now we get fewer logs if we run our Lambda function because we are buffering at a higher level.&lt;/p&gt;

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

&lt;p&gt;For our next test, let's intentionally overload our buffer to make it hit the maximum size limit. For this example, we are going to manually flush the logs by calling the function. This means we'll need to turn off the flushOnErrorLog property. In addition to that, we are going to set the maxBytes property to 1000, to make the test easier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;logBufferOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;bufferAtVerbosity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;WARN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;flushOnErrorLog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;maxBytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And for the logging code, we'll do a simple &lt;code&gt;for&lt;/code&gt; loop and manually flush at the end.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;index&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="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;index&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="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Trace Log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Debug Log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Info Log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Warn Log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error Log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flushBuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we run this, Lambda Powertools will let us know that we've exceeded the buffer's size and that some of the logs were lost to make room for the newer ones.&lt;/p&gt;

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

&lt;p&gt;What happens if a single log exceeds the buffer's size? Well, let's try it. To do this, I will set the maxBytes property to 1 byte.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;logBufferOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;bufferAtVerbosity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;WARN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;flushOnErrorLog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;maxBytes&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we run the same code as above, we will see a message telling us the log could not be buffered because it was too big.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Wrap up
&lt;/h3&gt;

&lt;p&gt;I'm very excited to start using this in projects and see how the CloudWatch pricing can be impacted. The best part is that Lambda Powertools is taking care of most of the heavy lifting, and all we have to do is configure a few properties to have it behave the way we want it to. I really want to thank the Lambda Powertools team, as they keep impressing me with all the great functionality they deliver that keep making my life easier.&lt;/p&gt;

&lt;p&gt;Let me know what you think about this feature!&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

&lt;p&gt;Andres Moreno&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>lambda</category>
    </item>
    <item>
      <title>Using Amazon Cognito with the user-password flow</title>
      <dc:creator>Andres Moreno</dc:creator>
      <pubDate>Wed, 31 Jul 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/aws-builders/using-amazon-cognito-with-the-user-password-flow-3dce</link>
      <guid>https://dev.to/aws-builders/using-amazon-cognito-with-the-user-password-flow-3dce</guid>
      <description>&lt;p&gt;On my post called &lt;a href="https://www.andmore.dev/blog/api-cognito/" rel="noopener noreferrer"&gt;Secure API Gateway with Amazon Cognito using SAM&lt;/a&gt; I talked about different Auth terms and walked through a setup to use the &lt;a href="https://auth0.com/docs/get-started/authentication-and-authorization-flow/client-credentials-flow" rel="noopener noreferrer"&gt;Client Credentials Flow&lt;/a&gt;, but Cognito recently introduced &lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/05/amazon-cognito-tiered-pricing-m2m-usage/" rel="noopener noreferrer"&gt;pricing changes for machine-to-machine authentication&lt;/a&gt; that will make this cost us and my main goal is to do this while staying in the free tier for personal projects that will not be generating any income. That is why in this post I am going to setup Amazon Cognito using a different flow called user password-based authentication. With this type of authentication we are charged based on the Monthly Active Users (MAUs) and AWS gives you the first 50,000 MAUs for free, and in my case this will usually stay at 1 per project, so I should be fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does the user-password flow work?
&lt;/h2&gt;

&lt;p&gt;Initially I thought I could use &lt;a href="https://en.wikipedia.org/wiki/Basic_access_authentication" rel="noopener noreferrer"&gt;Basic Auth&lt;/a&gt; where you provide the encoded username and password in the &lt;em&gt;Authorization&lt;/em&gt; header but that is not the case. In the image below I have a picture that shows the interactions that happen to get an authenticated request using this flow.&lt;/p&gt;

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

&lt;p&gt;Let's dive deeper into each interaction&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Caller makes a request with the username and password to your API.&lt;/li&gt;
&lt;li&gt;API makes a call to Cognito to get the Access, Id and refresh tokens and returns it to the user&lt;/li&gt;
&lt;li&gt;User provides Id token in Authorization header with each API call.&lt;/li&gt;
&lt;li&gt;API verifies token with Cognito to make sure it's valid.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Setting it all up with SAM
&lt;/h2&gt;

&lt;p&gt;We are going to stick with the same architecture for the Amazon Cognito resources to simplify each APIs configuration and to be able to manage users from a single location.&lt;/p&gt;

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

&lt;p&gt;Now let's see how each of these pieces are set up using SAM.&lt;/p&gt;

&lt;h3&gt;
  
  
  Auth Stack
&lt;/h3&gt;

&lt;p&gt;This stack has the resources that will be consumed by our API stacks.&lt;br&gt;
Full auth stack setup can be found &lt;a href="https://github.com/andmoredev/cognito-auth" rel="noopener noreferrer"&gt;in this GitHub repository&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  1. Updates to the User Pool
&lt;/h4&gt;

&lt;p&gt;Unlike our M2M setup that didn't need any properties for the user pool, for this type of authentication we need to setup properties that are specifying the attributes to be hosted for the user such as email, names, etc.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  CognitoUserPool:
    Type: AWS::Cognito::UserPool
&lt;span class="gi"&gt;+   Properties:
+     UsernameAttributes:
+       - email
+     AutoVerifiedAttributes:
+       - email
+     VerificationMessageTemplate:
+       DefaultEmailOption: CONFIRM_WITH_LINK
+     EmailConfiguration:
+       EmailSendingAccount: COGNITO_DEFAULT
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user pool attributes are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;UsernameAttributes&lt;/strong&gt; - this is specifying what you allow as a user name. The options here are &lt;em&gt;email&lt;/em&gt; or &lt;em&gt;phone_number&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AutoVerifiedAttributes&lt;/strong&gt; - attributes that you allow to be verified automatically by Cognito. For example we are using &lt;em&gt;email&lt;/em&gt; this means Cognito will automatically send a verification email to the user. If we didn't set this attribute an administrator would have to manually verify users in Cognito.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VerificationMessageTemplate&lt;/strong&gt; - This is where you can set your own template for the email that will get sent to users to verify. For this example we are using a default option provided by Cognito where they will confirm by clicking a link. The other option is &lt;em&gt;CONFIRM_WITH_CODE&lt;/em&gt; where a user will receive a code and they have to enter it manually to verify.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EmailConfiguration&lt;/strong&gt; - This property allows us to setup the configuration for the sender email for the verification or any other communications happening from Cognito. In our case we are using &lt;em&gt;COGNITO_DEFAULT&lt;/em&gt; which reduces the amount of setup we need to get an Amazon SES verified email. The &lt;em&gt;COGNITO_DEFAULT&lt;/em&gt; has some &lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/quotas.html" rel="noopener noreferrer"&gt;limits&lt;/a&gt; that you will need to consider if you are using it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  API Stack
&lt;/h3&gt;

&lt;p&gt;Our API Stack is simplified when using this flow. Why? We do not need to create a resource server since we will not be using OAuth capabilities. &lt;/p&gt;

&lt;h4&gt;
  
  
  1. Delete &lt;em&gt;UserPoolResourceServer&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;As mentioned before, we don't need this resource anymore. So let's get rid of it from our stack by removing it from the template.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. User Pool Client Updates
&lt;/h4&gt;

&lt;p&gt;Below is the definition for our user pool client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  CognitoUserPoolClient:
    Type: AWS::Cognito::UserPoolClient
    Properties:
      UserPoolId: !Ref CognitoUserPoolId
&lt;span class="gd"&gt;-     GenerateSecret: true
-     AllowedOAuthFlows:
-       - client_credentials
-     AllowedOAuthScopes:
-       - layerless-esbuild/api
-     AllowedOAuthFlowsUserPoolClient: true
&lt;/span&gt;&lt;span class="gi"&gt;+     SupportedIdentityProviders:
+       - COGNITO
+     ExplicitAuthFlows:
+       - ALLOW_USER_PASSWORD_AUTH
+       - ALLOW_REFRESH_TOKEN_AUTH
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few changes need to be done in order for this authentication flow to work. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GenerateSecret&lt;/strong&gt; - We first get rid of this property since it is not needed for this flow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AllowedOAuthFlows&lt;/strong&gt; - When using the User/Password auth flow we do not need to set this property.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AllowedOAuthScopes&lt;/strong&gt; - Since we are not using an OAuth flow we do not need to set up scopes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SupportedIdentityProviders&lt;/strong&gt; - We are going to use &lt;em&gt;COGNITO&lt;/em&gt; as our only provider. You can configure different identity providers to simplify your users sign in by using Google, Facebook or any of the supported providers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ExplicitAuthFlows&lt;/strong&gt;: this is where we will configure the &lt;em&gt;ALLOW_USER_PASSWORD_AUTH&lt;/em&gt; which will allow us to authenticate using a username and a password. I've added &lt;em&gt;ALLOW_REFRESH_TOKEN_AUTH&lt;/em&gt; since it's required but we will not be going to do token refreshes in this post.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4. Updates to API Gateway
&lt;/h4&gt;

&lt;p&gt;The only thing that needs to change in the API Gateway is the removal of the &lt;em&gt;AuthorizationScopes&lt;/em&gt;. For this flow this is not needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  Auth:
    DefaultAuthorizer: ClientCognitoAuthorizer
    Authorizers:
      ClientCognitoAuthorizer:
        UserPoolArn: !Ref CognitoUserPoolArn
&lt;span class="gd"&gt;-       AuthorizationScopes:
-         - layerless-esbuild/echo
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;THAT'S IT!! We have now successfully setup our API to authenticate using the user-password flow.&lt;/p&gt;

&lt;p&gt;You can find the full working example in this &lt;a href="https://github.com/andmoredev/api-gateway-auth-with-cognito" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing with Postman
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Grab Client Id
&lt;/h3&gt;

&lt;p&gt;We only need the client id when authenticating with the user-password flow, this is because we are going to enter a username and password to authenticate and that is where the token will be coming from. So we will get this value from the console by going into our new application client.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fitnfon9wn7xsqst1y4rn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fitnfon9wn7xsqst1y4rn.png" alt="AWS Console showing an application client in Cognito where we can grab the client id" width="800" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Requesting auth tokens
&lt;/h3&gt;

&lt;p&gt;To request the tokens we need to call Amazon Cognito specifying we are doing an &lt;em&gt;InitiateAuth&lt;/em&gt; command.&lt;br&gt;
This will require us to make a POST call to &lt;em&gt;&lt;a href="https://cognito-idp.us-east-1.amazonaws.com" rel="noopener noreferrer"&gt;https://cognito-idp.us-east-1.amazonaws.com&lt;/a&gt;&lt;/em&gt;. The region will change depending on where you created your User Pool. We specify the command by adding the &lt;em&gt;X-Amz-Target&lt;/em&gt;, we also need to specify the &lt;em&gt;Content-Type&lt;/em&gt; since it's an AWS specific type. Below is a screenshot that shows how the headers should look.&lt;/p&gt;

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

&lt;p&gt;The body requires the following parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AuthFlow&lt;/strong&gt; - This is where we specify that we want to use the user-password flow by setting a value of &lt;em&gt;USER_PASSWORD_AUTH&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ClientId&lt;/strong&gt; - We will set the value to the one we grabbed in step 1.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AuthParameters&lt;/strong&gt; - in the object we will provide the &lt;em&gt;USERNAME&lt;/em&gt; and the &lt;em&gt;PASSWORD&lt;/em&gt; of our user.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;When you send this request you should get a response back with the &lt;em&gt;AccessToken&lt;/em&gt;, &lt;em&gt;IdToken&lt;/em&gt;, &lt;em&gt;RefreshToken&lt;/em&gt;, &lt;em&gt;TokenType&lt;/em&gt; and &lt;em&gt;ExpiresIn&lt;/em&gt;. This also returns the &lt;em&gt;ChallengeParameters&lt;/em&gt; but this flow does not require any challenge responses to be generated. (I've edited the full response from the picture to not expose the full keys).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv7z8c7i4fb2dcqs2s381.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv7z8c7i4fb2dcqs2s381.png" alt="Postman response body" width="800" height="235"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Make request
&lt;/h3&gt;

&lt;p&gt;Grabbing the &lt;em&gt;IdToken&lt;/em&gt; from the response we got from the request above we can now take that and use it to authorize the request to the API endpoint as seen below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc2uddj0i3sgvw4qn67jp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc2uddj0i3sgvw4qn67jp.png" alt="Postman auth config and successful request" width="800" height="261"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Authenticating automation calls
&lt;/h2&gt;

&lt;p&gt;Whenever I change authentication methods I get stuck setting it all up for my test automation. This requires us to do the same thing we did in Postman but programmatically. I am doing this by running a small NodeJS script that will make the call as shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initiateAuthResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;COGNITO_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/x-amz-json-1.1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-Amz-Target&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AWSCognitoIdentityProviderService.InitiateAuth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;ClientId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;AuthFlow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USER_PASSWORD_AUTH&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;AuthParameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;USERNAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;USERNAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;PASSWORD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PASSWORD&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;initiateAuthResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AuthenticationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;IdToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you look at the code above it should seem very familiar to what we did in Postman. Making a POST request with the necessary headers and body. Once you get the response you can do whatever you need with the tokens.&lt;/p&gt;

&lt;p&gt;I've provided 2 examples on how to handle the user credentials for this script to use within a CI/CD pipeline.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create user programmatically - In this example we are &lt;a href="https://www.andmore.dev/blog/create-cognito-user-programatically/" rel="noopener noreferrer"&gt;creating a Cognito user programmatically&lt;/a&gt; and using the credentials to authenticate. We do this by setting the values as environment variables using the &lt;em&gt;&amp;gt;&amp;gt; $GITHUB_ENV&lt;/em&gt; command. At the end of the run we delete the user so we don't have an endless amount of orphaned automation users.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;test-api-with-user-password-auth-inline-create-user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Portman With USER_PASSWORD_AUTH - Inline Create User&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ inputs.ENVIRONMENT }}&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configure AWS&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/configure-aws-credentials@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;aws-region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
          &lt;span class="na"&gt;role-to-assume&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.PIPELINE_EXECUTION_ROLE }}&lt;/span&gt;
          &lt;span class="na"&gt;role-session-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create-test-user&lt;/span&gt;
          &lt;span class="na"&gt;role-duration-seconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3600&lt;/span&gt;
          &lt;span class="na"&gt;role-skip-session-tagging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create Test User&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;username=$(uuidgen)@andmore.dev&lt;/span&gt;
          &lt;span class="s"&gt;password=$(uuidgen)G1%&lt;/span&gt;
          &lt;span class="s"&gt;echo "USERNAME=$username" &amp;gt;&amp;gt; $GITHUB_ENV;&lt;/span&gt;
          &lt;span class="s"&gt;echo "PASSWORD=$password" &amp;gt;&amp;gt; $GITHUB_ENV;&lt;/span&gt;
          &lt;span class="s"&gt;aws cognito-idp admin-create-user --user-pool-id ${{ inputs.COGNITO_USER_POOL_ID }} --username $username --message-action SUPPRESS&lt;/span&gt;
          &lt;span class="s"&gt;aws cognito-idp admin-set-user-password --user-pool-id ${{ inputs.COGNITO_USER_POOL_ID }} --username $username  --password $password --permanent&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test API&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;COGNITO_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.COGNITO_URL }}&lt;/span&gt;
          &lt;span class="na"&gt;CLIENT_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.COGNITO_CLIENT_ID }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;npm ci&lt;/span&gt;

          &lt;span class="s"&gt;node ./portman/get-auth-token/user-password-auth.mjs&lt;/span&gt;
          &lt;span class="s"&gt;npx @apideck/portman --cliOptionsFile portman/portman-cli.json --baseUrl ${{ inputs.BASE_URL }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Delete Test User&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;aws cognito-idp admin-delete-user --user-pool-id ${{ inputs.COGNITO_USER_POOL_ID }} --username $USERNAME&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Use GitHub Secrets - 
This is one of the simpler routes but requires to have a user already set up. All you need to do is store the credentials in GitHub secrets and reference them in the workflow.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;test-api-with-user-password-auth-github-secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Portman With USER_PASSWORD_AUTH - Load GitHub Secrets&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ inputs.ENVIRONMENT }}&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configure AWS&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/configure-aws-credentials@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;aws-region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
          &lt;span class="na"&gt;role-to-assume&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.PIPELINE_EXECUTION_ROLE }}&lt;/span&gt;
          &lt;span class="na"&gt;role-session-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;create-test-user&lt;/span&gt;
          &lt;span class="na"&gt;role-duration-seconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3600&lt;/span&gt;
          &lt;span class="na"&gt;role-skip-session-tagging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test API&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;COGNITO_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.COGNITO_URL }}&lt;/span&gt;
          &lt;span class="na"&gt;CLIENT_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.COGNITO_CLIENT_ID }}&lt;/span&gt;
          &lt;span class="na"&gt;USERNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.COGNITO_USERNAME }}&lt;/span&gt;
          &lt;span class="na"&gt;PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.COGNITO_PASSWORD }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;npm ci&lt;/span&gt;

          &lt;span class="s"&gt;node ./portman/get-auth-token/user-password-auth.mjs&lt;/span&gt;
          &lt;span class="s"&gt;npx @apideck/portman --cliOptionsFile portman/portman-cli.json --baseUrl ${{ inputs.BASE_URL }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two considerations to take into account here. If you are going to have a user per repository it might become a burden to maintain these users as your applications and repositories grow. On the other hand, if you share a single user with all your repos you might be introducing security vulnerabilities since it will be easier to have this password compromised. &lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;In this post we were able to update our authentication to use the user-password flow instead of M2M for our APIs, this allows us to stay within the Cognito free tier. We were able to verify that we can still authenticate with Postman and our test automations. Hopefully this shows the flexibility there is with Cognito and how you can configure it differently to satisfy your use cases. I will be exploring the other authentication flows so you can easily choose the one that makes more sense for you.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>sam</category>
      <category>cognito</category>
    </item>
    <item>
      <title>Creating users in Amazon Cognito programmatically</title>
      <dc:creator>Andres Moreno</dc:creator>
      <pubDate>Thu, 11 Jul 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/aws-builders/creating-users-in-amazon-cognito-programmatically-2a8j</link>
      <guid>https://dev.to/aws-builders/creating-users-in-amazon-cognito-programmatically-2a8j</guid>
      <description>&lt;p&gt;When you have CI/CD pipelines that run automated tests against your APIs you might need to dynamically create users in Amazon Cognito to run them. If that is the case you are in the right place. In this post we'll be going over what you need to do to create a valid user in Cognito to be used by your automation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create user in Cognito
&lt;/h2&gt;

&lt;p&gt;The first thing we need to do is to create a user in our Cognito User Pool. Since this is for automation purposes I am creating users using the &lt;em&gt;andmore.dev&lt;/em&gt; domain that I own. I want these users scoped to the specific repository that is running the CI/CD pipeline, so I'm generating a unique user with a UUID.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;uuidgen&lt;span class="si"&gt;)&lt;/span&gt;@andmore.dev
aws cognito-idp admin-create-user &lt;span class="nt"&gt;--user-pool-id&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;YOUR-USERPOOL-ID] &lt;span class="nt"&gt;--username&lt;/span&gt; &lt;span class="nv"&gt;$username&lt;/span&gt; &lt;span class="nt"&gt;--message-action&lt;/span&gt; SUPPRESS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the commands above, I am generating the username with a new uuid. Since I have email forwarding enabled for my domain, each new user will send me an email. To avoid this side effect I have added the &lt;code&gt;--message-actions&lt;/code&gt; property as &lt;code&gt;SUPPRESS&lt;/code&gt;, this will disable any messages from being sent.&lt;/p&gt;

&lt;p&gt;This is all we need right? I thought this too, but there is more. The user is created with a temporary password and is not usable yet. Since we are doing this for an automated process, we can't really go through the flow of opening the email, signing in and changing the password. So let's see what else we need to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verifying user
&lt;/h2&gt;

&lt;p&gt;Cognito offers a command that allows admins to set a users password, bypassing the manual steps mentioned above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;uuidgen&lt;span class="si"&gt;)&lt;/span&gt;G1%
aws cognito-idp admin-set-user-password &lt;span class="nt"&gt;--user-pool-id&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;YOUR-USERPOOL-ID] &lt;span class="nt"&gt;--username&lt;/span&gt; &lt;span class="nv"&gt;$username&lt;/span&gt;  &lt;span class="nt"&gt;--password&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt; &lt;span class="nt"&gt;--permanent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We first need to generate a password, to keep it simple I'm doing it by creating a new uuid and appending a capital letter, a number and a special character to satisfy the password policy set in my user pool. Now we can provide this to the &lt;code&gt;admin-set-user-password&lt;/code&gt; command and make sure we pass in the &lt;code&gt;--permanent&lt;/code&gt; flag, since by default it will set the password as temporary leaving us in the same spot as before.&lt;/p&gt;

&lt;p&gt;Now the user is all setup to be able to authenticate using any of the flows you have setup for your Cognito User Pool Client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delete the user
&lt;/h2&gt;

&lt;p&gt;You probably don't want to keep all of these users once your pipeline is done. To delete the user you will need to call the &lt;code&gt;admin-delete-user&lt;/code&gt; command with the generated username.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cognito-idp admin-delete-user &lt;span class="nt"&gt;--user-pool-id&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;YOUR-USERPOOL-ID] &lt;span class="nt"&gt;--username&lt;/span&gt; &lt;span class="nv"&gt;$username&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Security
&lt;/h2&gt;

&lt;p&gt;To be able to run these commands you'll need permissions to run these for your Cognito user pool. Below is an example IAM policy that will give you the necessary permissions to run all three commands.&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;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Action"&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="s2"&gt;"cognito-idp:AdminCreateUser"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"cognito-idp:AdminSetUserPassword"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"cognito-idp:AdminDeleteUser"&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;"Resource"&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="s2"&gt;"YOUR-USERPOOL-ARN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to restrict the access to this policy since with this level of access people could create and authenticate with them. I am adding these permissions to my pipeline execution role that allows GitHub to assume this role for a specific repository by setting the trust relationship of the IAM role as follows.&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;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"Federated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[YOUR-OIDC-PROVIDER-ARN]"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRoleWithWebIdentity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Condition"&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;"ForAllValues:StringLike"&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;"token.actions.githubusercontent.com:sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"repo:[YOUR-ORG]/[YOUR-REPOSITORY]:ref:refs/heads/[ALLOWED-BRANCH]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"token.actions.githubusercontent.com:aud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts.amazonaws.com"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;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;The &lt;em&gt;token.actions.githubusercontent.com:sub&lt;/em&gt; allows an array and wildcards so if you have a specific format for branching, you can play around with the condition properties to meet your needs while still being protected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;We were able to create and verify a user for our automation, as well as clean up after we are done so we are not left with orphaned users all over the place.&lt;br&gt;
I hope this is helpful to you. Cognito documentation is really not that straightforward and can be hard to find what you are looking for, this is why I'm trying to provide tutorials around Cognito, so stay tuned since I have more content lined up around this topic.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>cognito</category>
    </item>
    <item>
      <title>Secure API Gateway with Amazon Cognito using SAM</title>
      <dc:creator>Andres Moreno</dc:creator>
      <pubDate>Wed, 08 May 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/aws-builders/secure-api-gateway-with-amazon-cognito-using-sam-57k6</link>
      <guid>https://dev.to/aws-builders/secure-api-gateway-with-amazon-cognito-using-sam-57k6</guid>
      <description>&lt;h2&gt;
  
  
  Backstory
&lt;/h2&gt;

&lt;p&gt;I create a lot of APIs, these are for blog posts, for playing around with new functionality or tools that I've created for myself. All of these have been created without authentication in place. Not securing APIs can create data exposures for you, but it can also pose a financial risk to your accounts if a malicious user gets your endpoints. This is why I want to secure any API I create but I want this to be with minimal setup so that it's simple to replicate many times. &lt;/p&gt;

&lt;p&gt;We first need to understand a few concepts around what we're setting up.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Cognito?
&lt;/h2&gt;

&lt;p&gt;Amazon Cognito is a service provided by AWS that allows you to add authentication to your applications or services. It integrates natively with API Gateway to secure each endpoint.  &lt;/p&gt;

&lt;p&gt;Cognito has multiple layers where you can apply different types of configurations, this gives us the flexibility to get things setup for different use cases.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User Pool&lt;/strong&gt; - &lt;br&gt;
A Cognito user pool is the backbone to everything in Cognito. This is an &lt;a href="https://auth0.com/docs/authenticate/protocols/openid-connect-protocol" rel="noopener noreferrer"&gt;OpenID Connect identity provider&lt;/a&gt; which contains the user directory to authenticate and authorize users.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User Pool Domain&lt;/strong&gt; -&lt;br&gt;
The user pool domain is used to give the authentication url a better name for users and applications to authenticate with.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Resource Server&lt;/strong&gt; -&lt;br&gt;
&lt;a href="https://www.oauth.com/oauth2-servers/the-resource-server/" rel="noopener noreferrer"&gt;An OAuth 2.0 API server&lt;/a&gt; that validates that an access token contains the scopes that authorize the requested endpoint in the API.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User Pool Client&lt;/strong&gt; -&lt;br&gt;
A user pool client is a configuration within a user pool that directly interacts with your application that will be authenticating using Cognito.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Authentication Flows
&lt;/h2&gt;

&lt;p&gt;There are several authentication flows that you can use for your applications. &lt;a href="https://auth0.com/docs/get-started/authentication-and-authorization-flow" rel="noopener noreferrer"&gt;In this post from Auth0&lt;/a&gt; you can get a better understanding about which flow is better for your use case.&lt;br&gt;&lt;br&gt;
Since I am setting up very basic authentication to be able to test my API using Postman I will use the &lt;a href="https://auth0.com/docs/get-started/authentication-and-authorization-flow/client-credentials-flow" rel="noopener noreferrer"&gt;Client Credentials Flow (CCF)&lt;/a&gt; to allow us to authenticate our requests by getting an &lt;em&gt;access token&lt;/em&gt; by  sending a &lt;em&gt;client id&lt;/em&gt; and a &lt;em&gt;client secret&lt;/em&gt; to Cognito. This token is a &lt;a href="https://jwt.io/introduction" rel="noopener noreferrer"&gt;JSON Web Token (JWT)&lt;/a&gt;. The CCF is recommended when working with Machine-to-Machine (M2M) communication like CLIs, APIs, etc.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting it all up with SAM
&lt;/h2&gt;

&lt;p&gt;I don't want to have a Cognito User Pool per API created, to simplify this I will have a single Auth Stack that will contain the &lt;em&gt;User Pool&lt;/em&gt; and the &lt;em&gt;User Pool Domain&lt;/em&gt; resources. We will then share the data from this stack to our API stacks to be able to create the &lt;em&gt;Resource Server&lt;/em&gt; and &lt;em&gt;User Pool Clients&lt;/em&gt; separately. Below is a picture that shows this structure.&lt;/p&gt;

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

&lt;p&gt;Now let's see how each of these pieces are set up using SAM.&lt;/p&gt;
&lt;h3&gt;
  
  
  Auth Stack
&lt;/h3&gt;

&lt;p&gt;In this stack we are going to define the resources that will be consumed by other APIs to authenticate.&lt;br&gt;&lt;br&gt;
You can find my full setup for this stack &lt;a href="https://github.com/andmoredev/cognito-auth" rel="noopener noreferrer"&gt;in this GitHub repository&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  1. User Pool
&lt;/h4&gt;

&lt;p&gt;The User Pool doesn't require a lot of configuration when doing CCF. You are able to add more restrictions and configuration but as mentioned before, we are trying to keep it simple for now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;CognitoUserPool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Cognito::UserPool&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. User Pool Domain
&lt;/h4&gt;

&lt;p&gt;In my setup I'm using &lt;code&gt;andmoredev&lt;/code&gt; as my domain which makes our authentication base url look like &lt;em&gt;&lt;a href="https://andmoredev.auth.us-east-1.amazoncognito.com" rel="noopener noreferrer"&gt;https://andmoredev.auth.us-east-1.amazoncognito.com&lt;/a&gt;&lt;/em&gt;. You can setup up your own custom domain by setting up the &lt;code&gt;CustomDomainConfig&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;CognitoUserPoolDomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Cognito::UserPoolDomain&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;UserPoolId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;CognitoUserPool&lt;/span&gt;
      &lt;span class="na"&gt;Domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;andmoredev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to make sure other stacks can get access to the User Pool Id and ARN to be able to create the resource server and user pool client. To do this we are going to use SSM Parameters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;CognitoUserPoolIdParameter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::SSM::Parameter&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;/${AWS::StackName}/CognitoUserPoolId&lt;/span&gt;
      &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
      &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;CognitoUserPool&lt;/span&gt;

  &lt;span class="na"&gt;CognitoUserPoolArnParameter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::SSM::Parameter&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;/${AWS::StackName}/CognitoUserPoolArn&lt;/span&gt;
      &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
      &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;CognitoUserPool.Arn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can go and add authentication to our API stack using this user pool.&lt;/p&gt;

&lt;h3&gt;
  
  
  API Stack
&lt;/h3&gt;

&lt;p&gt;I will be adding authentication to an existing API &lt;a href="https://github.com/andmoredev/layerless-esbuild-lambda" rel="noopener noreferrer"&gt;in this GitHub repository&lt;/a&gt;. To look at the specific changes I made to get authentication working, you can look at the &lt;a href="https://github.com/andmoredev/layerless-esbuild-lambda/pull/52/files" rel="noopener noreferrer"&gt;PR where I did this&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Consuming Auth Stack resources
&lt;/h4&gt;

&lt;p&gt;We first need to get the user pool Id and ARN from SSM by adding them into the &lt;em&gt;Parameters&lt;/em&gt; section of our template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;CognitoUserPoolId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::SSM::Parameter::Value&amp;lt;String&amp;gt;'&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/andmoredev-auth/CognitoUserPoolId'&lt;/span&gt;

  &lt;span class="na"&gt;CognitoUserPoolArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::SSM::Parameter::Value&amp;lt;String&amp;gt;'&lt;/span&gt;
    &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/andmoredev-auth/CognitoUserPoolArn'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Resource Server
&lt;/h4&gt;

&lt;p&gt;We are creating a resource server with one scope that will be used to give access to all endpoints in our API. I will not go into more details on more advanced scope design in this post.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;LayerlessESBuildResourceServer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Cognito::UserPoolResourceServer&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;UserPoolId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;CognitoUserPoolId&lt;/span&gt;
      &lt;span class="na"&gt;Identifier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;layerless-esbuild&lt;/span&gt;
      &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Layerless ESBuild&lt;/span&gt;
      &lt;span class="na"&gt;Scopes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;ScopeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api&lt;/span&gt;
          &lt;span class="na"&gt;ScopeDescription&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow api access&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. User Pool Client
&lt;/h4&gt;

&lt;p&gt;Below is the definition for our user pool client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;CognitoTestAutomationClient&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Cognito::UserPoolClient&lt;/span&gt;
    &lt;span class="na"&gt;DependsOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PostmanResourceServer&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;UserPoolId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;CognitoUserPoolId&lt;/span&gt;
      &lt;span class="na"&gt;GenerateSecret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;AllowedOAuthFlows&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;client_credentials&lt;/span&gt;
      &lt;span class="na"&gt;AllowedOAuthScopes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;layerless-esbuild/api&lt;/span&gt;
      &lt;span class="na"&gt;AllowedOAuthFlowsUserPoolClient&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's talk about the properties we've here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UserPoolId - reference to the id of the user pool created in our Auth Stack.&lt;/li&gt;
&lt;li&gt;GenerateSecret - this is necessary for us to use with client credentials flow.&lt;/li&gt;
&lt;li&gt;AllowedOAuthFlows - we are telling Cognito that this client will only allow CCF&lt;/li&gt;
&lt;li&gt;AllowedOAuthScopes - We need to make sure this array contains scopes that have been defined in our resource server.&lt;/li&gt;
&lt;li&gt;AllowedOAuthFlowsUserPoolClient - This is what enables us to use standard OAuth functionality with our user pool client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've seen deployment scenarios where the resource server gets deployed after the client and we get an error saying the scope does not exist, this is the reason I am explicitly adding the &lt;code&gt;DependsOn&lt;/code&gt; for this resource.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Hooking it all up to API Gateway
&lt;/h4&gt;

&lt;p&gt;To tell our API Gateway to authenticate using our new cognito user pool we need to add the &lt;em&gt;Auth&lt;/em&gt; property, it will look something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;Auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;DefaultAuthorizer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClientCognitoAuthorizer&lt;/span&gt;
    &lt;span class="na"&gt;Authorizers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ClientCognitoAuthorizer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;UserPoolArn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;CognitoUserPoolArn&lt;/span&gt;
        &lt;span class="na"&gt;AuthorizationScopes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;layerless-esbuild/echo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are consuming the user pool ARN from the Auth Stack and allowing the scope that we've created in our resource server.&lt;/p&gt;

&lt;p&gt;THAT IS ALL! Now we can deploy this stack to be able to test it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing it with Postman
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Grab authentication data
&lt;/h3&gt;

&lt;p&gt;To test this we need to go into the AWS console and grab the Client Id and Client Secret that were generated. These are located in the Cognito service by selecting the new user pool and going in the &lt;em&gt;App integration&lt;/em&gt; section. In the bottom you will see your new app client, once you open it you will see something like the image below. You can copy the Client ID and Client secret from here, we will use these values in the next steps.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkdkeaqgb2n4xsi62f9tb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkdkeaqgb2n4xsi62f9tb.png" alt="AWS Console showing an application client in Cognito where we can grab the client id and secret" width="800" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please handle these values with care, if compromised someone could gain access to your APIs and do malicious things.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Test un-authenticated request
&lt;/h3&gt;

&lt;p&gt;To verify our API is secure we will first run an unauthenticated request. To do this we will call our endpoint without setting anything for authentication, when we send this request we should get a 401 - Unauthorized response as shown below.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9bvnx934mvdfk6xmgc9f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9bvnx934mvdfk6xmgc9f.png" alt="Postman request showing an unauthorized response" width="800" height="311"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Set authentication data in Postman
&lt;/h3&gt;

&lt;p&gt;With the values we will now use a new &lt;a href="https://learning.postman.com/docs/sending-requests/postman-vault/postman-vault-secrets/" rel="noopener noreferrer"&gt;Postman feature called &lt;strong&gt;Vaults&lt;/strong&gt;&lt;/a&gt; that allows us to securely store sensitive data. To do this we will go into the Vault section in the bottom of the window and add our secrets.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fonhgroiu4gpuyto2knnc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fonhgroiu4gpuyto2knnc.png" alt="Postman Vault window showing an item for clientId and clientSecret and their masked values" width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Setup request authentication
&lt;/h3&gt;

&lt;p&gt;Now back in the Postman request we can set the Authorization configuration. There are several parameters to set here.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Grant type - This will have a value of &lt;em&gt;Client Credentials&lt;/em&gt; specifying that we will be using the client credentials flow.&lt;/li&gt;
&lt;li&gt;Access Token URL - The value for your specific user pool will vary depending on your configured user pool domain. It will look something like this &lt;code&gt;https://[your-domain].auth.us-east-1.amazoncognito.com/oauth2/token&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Client ID - We will grab this from the vault by setting a value of &lt;code&gt;{{vault:clientId}}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Client Secret - Also from the vault with a value of &lt;code&gt;{{value:clientSecert}}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Scope - This will be based on what was set on the resource server. From our example it is &lt;code&gt;layerless-esbuild/echo&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2jx26qb2e2as85dutthw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2jx26qb2e2as85dutthw.png" alt="Postman Authorization Configuration with all the values mentioned above filled in" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Get the JWT
&lt;/h3&gt;

&lt;p&gt;We can now get a new access token by going to the bottom of the Authorization section and pressing the &lt;em&gt;Get New Access Token&lt;/em&gt; button. If successful you will receive a prompt where you can click a button that says &lt;em&gt;Use Token&lt;/em&gt;. By pressing that button you will get the value presented in the Access Token and it will be used in the &lt;em&gt;Authorization&lt;/em&gt; header of your request.   &lt;/p&gt;

&lt;p&gt;If we send the request now we will get a successful response.  &lt;/p&gt;

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

&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;Sadly security is often left as an afterthought, just like it happened to me with all the APIs I've created. In this post we were able to understand a few of the concepts relating to authentication and the resources necessary to set this up in AWS with Amazon Cognito. We also tested that our API is now secure and how we can get a token to authenticate against it.&lt;br&gt;&lt;br&gt;
I hope this allows people to add a basic layer of security to their APIs so that we make malicious users work a little bit more.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>serverless</category>
      <category>sam</category>
    </item>
    <item>
      <title>Using YAML anchors and aliases in a SAM template</title>
      <dc:creator>Andres Moreno</dc:creator>
      <pubDate>Sun, 31 Mar 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/aws-builders/using-yaml-anchors-and-aliases-in-a-sam-template-5g44</link>
      <guid>https://dev.to/aws-builders/using-yaml-anchors-and-aliases-in-a-sam-template-5g44</guid>
      <description>&lt;p&gt;Last month I wrote a post about &lt;a href="https://www.andmore.dev/blog/layerless-esbuild-lambda/" rel="noopener noreferrer"&gt;getting rid of Lambda Layers by using ESBuild&lt;/a&gt;. What I quickly learned is that the &lt;em&gt;Metadata&lt;/em&gt; attribute has to be copied and pasted for EVERY Lambda function in your stack. I tried using the &lt;em&gt;Global&lt;/em&gt; section in the SAM template and it turns out it's not supported. I started thinking about how I could reuse the same configuration across my template and found that YAML already has a functionality that does this called &lt;a href="https://www.educative.io/blog/advanced-yaml-syntax-cheatsheet#anchors" rel="noopener noreferrer"&gt;YAML Anchors and Aliases&lt;/a&gt;. In this post I will go through what YAML aliases and anchors are and how we can use them in SAM.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are YAML anchors and aliases?
&lt;/h2&gt;

&lt;p&gt;You can think of &lt;em&gt;anchors&lt;/em&gt; as assigning a value to a variable to be used in other places. The way you define an &lt;em&gt;anchor&lt;/em&gt; is by adding an &lt;em&gt;&amp;amp;&lt;/em&gt; on an entity with the name of the anchor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;personName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;person-name-properties&lt;/span&gt;
    &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
    &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above we have created an anchor for the &lt;em&gt;properties&lt;/em&gt; attribute of the &lt;em&gt;personName&lt;/em&gt; schema with the name &lt;em&gt;person-name-properties&lt;/em&gt;. Later in the file you can reference the anchor with an alias by using the &lt;em&gt;*&lt;/em&gt; symbol and the anchor name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;person&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*person-name-properties&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the YAML is proccessed the &lt;em&gt;person&lt;/em&gt; object will look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;person&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;YAML allows you to override and/or extend properties to be able to get it into the correct structure. To update the name object to have a &lt;em&gt;minLength&lt;/em&gt; for the &lt;em&gt;firstName&lt;/em&gt; and include a new attribute called &lt;em&gt;middleName&lt;/em&gt; we would do something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;person&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
        &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*person-name-properties&lt;/span&gt;
        &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;minLength&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
        &lt;span class="na"&gt;middleName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;If you look at the example above we are now using the &lt;em&gt;&amp;lt;&amp;lt;&lt;/em&gt; attribute which essentially &lt;em&gt;deconstructs&lt;/em&gt; whats defined in the anchor and allows you to overwrite something by adding the same attribute or add new ones in line. If we process the YAML above we would get the result below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;person&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;minLength&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
        &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="na"&gt;middleName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this functionality we can define our base &lt;em&gt;Metadata&lt;/em&gt; attributes for ESBuild and use them for our template right? .... right?&lt;br&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxh2hubgzfog8lg9rlge7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxh2hubgzfog8lg9rlge7.jpg" alt="SAM and YAML meme" width="500" height="500"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Why are YAML anchors not as straightforward with SAM?
&lt;/h2&gt;

&lt;p&gt;SAM is strict in what it accepts in the &lt;em&gt;template.yaml&lt;/em&gt; when doing a deployment, you will not see these errors when doing &lt;code&gt;sam build&lt;/code&gt; though, so be careful to make sure your template is actually deployable. Here is an example where I'm defining the &lt;em&gt;esbuild&lt;/em&gt; config metadata at the root of my template and consuming it in a function&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2010-09-09&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Layerless ESBuild Example&lt;/span&gt;
&lt;span class="na"&gt;Transform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless-2016-10-31&lt;/span&gt;

&lt;span class="na"&gt;esbuild&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;esbuild&lt;/span&gt;
  &lt;span class="na"&gt;BuildMethod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;esbuild&lt;/span&gt;
  &lt;span class="na"&gt;BuildProperties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;esm&lt;/span&gt;
    &lt;span class="na"&gt;Minify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;OutExtension&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.js=.mjs&lt;/span&gt;
    &lt;span class="na"&gt;Target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;es2020&lt;/span&gt;
    &lt;span class="na"&gt;Sourcemap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;EntryPoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;index.mjs&lt;/span&gt;
    &lt;span class="na"&gt;Banner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;js=import { createRequire } from 'module'; const require = createRequire(import.meta.url);&lt;/span&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;EchoFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;CodeUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src/functions/echo&lt;/span&gt;
      &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs20.x&lt;/span&gt;
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.handler&lt;/span&gt;
    &lt;span class="na"&gt;Metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*esbuild&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The built template after running &lt;code&gt;sam build&lt;/code&gt; looks like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2010-09-09'&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Layerless ESBuild Example&lt;/span&gt;
&lt;span class="na"&gt;Transform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless-2016-10-31&lt;/span&gt;
&lt;span class="na"&gt;esbuild&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;BuildMethod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;esbuild&lt;/span&gt;
  &lt;span class="na"&gt;BuildProperties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;esm&lt;/span&gt;
    &lt;span class="na"&gt;Minify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;OutExtension&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.js=.mjs&lt;/span&gt;
    &lt;span class="na"&gt;Target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;es2020&lt;/span&gt;
    &lt;span class="na"&gt;Sourcemap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;EntryPoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;index.mjs&lt;/span&gt;
    &lt;span class="na"&gt;Banner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;js=import { createRequire } from 'module'; const require = createRequire(import.meta.url);&lt;/span&gt;
&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;EchoFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;CodeUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;EchoFunction&lt;/span&gt;
      &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs20.x&lt;/span&gt;
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.handler&lt;/span&gt;
    &lt;span class="na"&gt;Metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;BuildMethod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;esbuild&lt;/span&gt;
      &lt;span class="na"&gt;BuildProperties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Banner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;js=import { createRequire } from 'module'; const require = createRequire(import.meta.url);&lt;/span&gt;
        &lt;span class="na"&gt;EntryPoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;index.mjs&lt;/span&gt;
        &lt;span class="na"&gt;Format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;esm&lt;/span&gt;
        &lt;span class="na"&gt;Minify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;OutExtension&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.js=.mjs&lt;/span&gt;
        &lt;span class="na"&gt;Sourcemap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;Target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;es2020&lt;/span&gt;
      &lt;span class="na"&gt;SamResourceId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;EchoFunction&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Doesn't look bad right? It looks like the anchor got referenced and placed correctly in my Lambda function.But when we run &lt;code&gt;sam deploy&lt;/code&gt; we'll get this error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Status: FAILED. Reason: Invalid template property or properties &lt;span class="o"&gt;[&lt;/span&gt;esbuild]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It doesn't like the &lt;em&gt;esbuild&lt;/em&gt; property we've defined since it's not part of the SAM template schema.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working around the limitation
&lt;/h2&gt;

&lt;p&gt;To avoid getting an error on deployment we can rename the property to &lt;em&gt;Metadata&lt;/em&gt; instead. This is an accepted property in SAM that will build and deploy successfully. Below you can see how I renamed &lt;em&gt;esbuild&lt;/em&gt; to &lt;em&gt;Metadata&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2010-09-09&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Layerless ESBuild Example&lt;/span&gt;
&lt;span class="na"&gt;Transform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless-2016-10-31&lt;/span&gt;

&lt;span class="na"&gt;Metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;esbuild&lt;/span&gt;
  &lt;span class="na"&gt;BuildMethod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;esbuild&lt;/span&gt;
  &lt;span class="na"&gt;BuildProperties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;esm&lt;/span&gt;
    &lt;span class="na"&gt;Minify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;OutExtension&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.js=.mjs&lt;/span&gt;
    &lt;span class="na"&gt;Target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;es2020&lt;/span&gt;
    &lt;span class="na"&gt;Sourcemap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;EntryPoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;index.mjs&lt;/span&gt;
    &lt;span class="na"&gt;Banner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;js=import { createRequire } from 'module'; const require = createRequire(import.meta.url);&lt;/span&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;EchoFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;CodeUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src/functions/echo&lt;/span&gt;
      &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs20.x&lt;/span&gt;
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.handler&lt;/span&gt;
    &lt;span class="na"&gt;Metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*esbuild&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is great! We can have several Lambda functions that share the same ESBuild configuration. But what happens when we want to define multiple anchors? If we were to add a second &lt;em&gt;Metadata&lt;/em&gt; attribute we will now have invalid YAML since it doesn't accept duplicate properties names at the same level. The funny thing is this actually builds and deploys correctly so if you have no linting in your processes this might work for you. But there is a better way we can define multiple anchors without sacrificing our linting process.&lt;br&gt;&lt;br&gt;
Instead of making the &lt;em&gt;Metadata&lt;/em&gt; property the anchor we can add multiple anchors under the &lt;em&gt;Metadata&lt;/em&gt; property. In the example below I am going to add a second anchor that contains only the &lt;em&gt;BuildProperties&lt;/em&gt; and I'll update the base ESBuild config to use it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2010-09-09&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Layerless ESBuild Example&lt;/span&gt;
&lt;span class="na"&gt;Transform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless-2016-10-31&lt;/span&gt;

&lt;span class="na"&gt;Metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;esbuild-properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;esbuild-properties&lt;/span&gt;
    &lt;span class="na"&gt;Format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;esm&lt;/span&gt;
    &lt;span class="na"&gt;Minify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;OutExtension&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.js=.mjs&lt;/span&gt;
    &lt;span class="na"&gt;Target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;es2020&lt;/span&gt;
    &lt;span class="na"&gt;Sourcemap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;EntryPoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;index.mjs&lt;/span&gt;
    &lt;span class="na"&gt;Banner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;js=import { createRequire } from 'module'; const require = createRequire(import.meta.url);&lt;/span&gt;

  &lt;span class="na"&gt;esbuild&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;esbuild&lt;/span&gt;
    &lt;span class="na"&gt;BuildMethod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;esbuild&lt;/span&gt;
    &lt;span class="na"&gt;BuildProperties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*esbuild-properties&lt;/span&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;EchoFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;CodeUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src/functions/echo&lt;/span&gt;
      &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs20.x&lt;/span&gt;
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.handler&lt;/span&gt;
    &lt;span class="na"&gt;Metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*esbuild&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've done it! We can now define reusable YAML objects in our SAM templates. If we wanted to extend or overwrite any properties we can use the &lt;em&gt;&amp;lt;&amp;lt;&lt;/em&gt; attribute.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;EchoFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;CodeUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src/functions/echo&lt;/span&gt;
      &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs20.x&lt;/span&gt;
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.handler&lt;/span&gt;
    &lt;span class="na"&gt;Metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;BuildMethod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;esbuild&lt;/span&gt;
      &lt;span class="na"&gt;BuildProperties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*esbuild-properties&lt;/span&gt;
        &lt;span class="na"&gt;Minify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;External&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@aws-sdk/*'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;By using standard YAML functionality and repurposing the SAM &lt;em&gt;Metadata&lt;/em&gt; property we were able to successfully setup reusable snippets of YAML for our SAM templates. This allows us to greatly reduce the size of the template we are maintaining, it also reduces the risk of errors by giving standard settings to be used throughout the whole file.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>sam</category>
    </item>
    <item>
      <title>Drop the layers, bundle up with ESBuild instead</title>
      <dc:creator>Andres Moreno</dc:creator>
      <pubDate>Wed, 07 Feb 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/aws-builders/drop-the-layers-bundle-up-with-esbuild-instead-464n</link>
      <guid>https://dev.to/aws-builders/drop-the-layers-bundle-up-with-esbuild-instead-464n</guid>
      <description>&lt;p&gt;I've seen a lot of posts around the problems that Lambda Layers bring, a very good one is called &lt;a href="https://aaronstuyvenberg.com/posts/why-you-should-not-use-lambda-layers" rel="noopener noreferrer"&gt;You shouldn't use Lambda Layers&lt;/a&gt; by &lt;a href="https://twitter.com/astuyve" rel="noopener noreferrer"&gt;AJ Stuyvenberg&lt;/a&gt;. In this post AJ explains the myths and cons of using Lambda Layers. What is not easy to find is clear examples on how to actually get rid of Lambda Layers by using a bundler. In this post we will go through a structure and configuration that allows us to remove Layers by using ESBuild to bundle the dependencies and shared code for your functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backstory
&lt;/h2&gt;

&lt;p&gt;I've been using Lambda Layers for a while as a way to share dependencies and code between different Lambda functions. This was done as a way to keep the code editable from the AWS console and reduce the amount of times we include an npm dependency in the package.json of functions. We had other problems than what AJ mentioned in his post, we noticed that once the project got to a certain size, deployments are a pain. Every deployment creates a new version of the layer which then creates an update for every single Lambda function that's consuming the layer. And this is why I started looking into moving away from layers and instead bundling with ESBuild.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/andmoredev/layerless-esbuild-lambda" rel="noopener noreferrer"&gt;Link to complete example on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring ESBuild for Lambda Functions in SAM
&lt;/h2&gt;

&lt;p&gt;The first thing we need to do, is to add the configuraton to let SAM know that we want to build our function using ESBuild. This is done using the 'Metadata' attributes as seen below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;BuildMethod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;esbuild&lt;/span&gt;
  &lt;span class="na"&gt;BuildProperties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;esm&lt;/span&gt;
    &lt;span class="na"&gt;OutExtension&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.js=.mjs&lt;/span&gt;
    &lt;span class="na"&gt;Target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;es2020&lt;/span&gt;
    &lt;span class="na"&gt;Minify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;Sourcemap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;EntryPoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;index.mjs&lt;/span&gt;
    &lt;span class="na"&gt;Banner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;js=import { createRequire } from 'module'; const require = createRequire(import.meta.url);&lt;/span&gt;
    &lt;span class="na"&gt;External&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@aws-sdk/client-secrets-manager'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break these down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;BuildMethod&lt;/strong&gt; - This is where we tell it to use &lt;code&gt;esbuild&lt;/code&gt; to bundle the Lambda function code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Format&lt;/strong&gt; - Since we are working with ECMAScript Modules we will want our format to be &lt;code&gt;esm&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OutExtension&lt;/strong&gt; - The artifact that gets outputted by &lt;code&gt;esbuild&lt;/code&gt; has a &lt;code&gt;.js&lt;/code&gt; extension, since we have specified &lt;code&gt;esm&lt;/code&gt; for the format, we need to output as &lt;code&gt;.mjs&lt;/code&gt; which is how NodeJS knows that it is working with ESM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Target&lt;/strong&gt; - Used to specify the ECMAScript version to use. Here we will be using the default value of &lt;code&gt;es2020&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minify&lt;/strong&gt; - this can help reduce the bundle size even more but it makes the code unreadable. For my example I will keep it as false so I can still read the code of my deployed function in the AWS console.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sourcemap&lt;/strong&gt; - Attribute to specify if you want to include the sourcemap in your function. Including this allows for a better troubleshooting experience. This is because you will be able to get the stack trace and line numbers from the original code and not the bundled, the downside is that this may have performance impact on your function since it makes it bigger.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EntryPoints&lt;/strong&gt; - specify the entry points for the application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Banner&lt;/strong&gt; - this is not documented by AWS. This property allows us to workaround an issue that ESBuild has with depenedencies that are using &lt;code&gt;require&lt;/code&gt;. &lt;a href="https://github.com/aws/aws-sam-cli/issues/4827#issuecomment-1574080427" rel="noopener noreferrer"&gt;Link to GitHub issue with the solution&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External&lt;/strong&gt; - Used to exclude specific packages from the bundle. In this case we are importing the &lt;code&gt;getSecret&lt;/code&gt; action from the &lt;code&gt;@aws-lambda-powertools/parameters/secrets&lt;/code&gt; package. This package requires the &lt;code&gt;@aws-sdk/client-secrets-manager&lt;/code&gt; as a dependency, since we know this is already included in the NodeJS 20.X runtime, we exclude it so we use the one included instead of us adding it.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;If you have a dependency that needs to be excluded for all functions my recommendation would be to put that in the devDependencies instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When we run &lt;code&gt;sam build&lt;/code&gt; ESBuild will take care of bundling the shared modules that are being imported in the function from the &lt;code&gt;src/shared&lt;/code&gt; directory. It will also look at any other dependencies and include them from the &lt;code&gt;node_modules&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency management improvements
&lt;/h2&gt;

&lt;p&gt;There are many ways to structure your project, the one shown in my example is by far the simplest one I could find to reduce the effort of managing npm dependency versions.&lt;br&gt;
How am I doing this? I have a single package.json that includes all the dev and non dev dependencies, when bundling with ESBuild it will take care of only including the dependencies needed and ignore the rest. By keeping a single package.json, we can reduce the amount of pull requests that a tool like Dependabot would open saving a lot of hours of reviewing, testing and merging.&lt;/p&gt;
&lt;h2&gt;
  
  
  Configuration for Common JS
&lt;/h2&gt;

&lt;p&gt;The use of ESBuild is not restricted to ESM only, if you are using Common JS the configuration is very similar, we need to exclude a few of the attributes. Below is an example for the same function in Common JS&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;BuildMethod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;esbuild&lt;/span&gt;
  &lt;span class="na"&gt;BuildProperties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Minify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;Target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;es2020&lt;/span&gt;
    &lt;span class="na"&gt;Sourcemap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;EntryPoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;index.js&lt;/span&gt;
    &lt;span class="na"&gt;External&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@aws-sdk/client-secrets-manager'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If you are interested in migrating from Common JS to ESM, I wrote an article on &lt;a href="https://www.andmore.dev/blog/update-commonjs-to-esm/" rel="noopener noreferrer"&gt;How to update Lambda functions from Common JS to ECMAScript&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;We learned how you can configure ESBuild to bunlde your functions code instead of using Layers. We were also able to keep a single source for the npm dependencies of our project which reduces the ongoing maintenance. I hope this helps you easily get setup with all your future projects.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>lambda</category>
    </item>
    <item>
      <title>Call APIs from Step Functions using SAM</title>
      <dc:creator>Andres Moreno</dc:creator>
      <pubDate>Tue, 23 Jan 2024 23:31:44 +0000</pubDate>
      <link>https://dev.to/aws-builders/call-apis-from-step-functions-using-sam-143l</link>
      <guid>https://dev.to/aws-builders/call-apis-from-step-functions-using-sam-143l</guid>
      <description>&lt;p&gt;&lt;a href="https://twitter.com/Benoit_Boure" rel="noopener noreferrer"&gt;Benoit Boure&lt;/a&gt; wrote an article last week on &lt;a href="https://benoitboure.com/calling-external-endpoints-with-step-functions-and-the-cdk" rel="noopener noreferrer"&gt;Calling External Endpoints With Step Functions and the CDK&lt;/a&gt;, but if you know me I love SAM and wanted to show the same setup but instead of using CDK we will be using SAM.&lt;/p&gt;

&lt;p&gt;I will not go into the details of how it works since Benoit did a great job of explaining that in the post mentioned above. &lt;/p&gt;

&lt;p&gt;We'll be using the &lt;a href="https://openweathermap.org/api" rel="noopener noreferrer"&gt;OpenWeather API&lt;/a&gt; to get the temperature of a place and check if we need to wear a warm jacket, light jacket or no jacket. Below is the state machine workflow diagram that we will be executing.&lt;br&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqut9irkjsqwfskts2n8e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqut9irkjsqwfskts2n8e.png" alt="Step function diagram" width="649" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/andmoredev/http-invoke-with-sam" rel="noopener noreferrer"&gt;Link to complete example on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Steps to follow
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Create EventBridge Connection
&lt;/h3&gt;

&lt;p&gt;The HTTP task uses an EventBridge Connection to authenticate the request. For the OpenWeather API we need to provide the API Key as a query parameter and not as an Authorization header as you would typically do on other APIs. To accomplish this we add the &lt;em&gt;InvocationHttParameters&lt;/em&gt; object where we add the query string parameter to the request with the name appid.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;OpenWeatherConnection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Events::Connection&lt;/span&gt;
  &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;openweather-connection'&lt;/span&gt;
    &lt;span class="na"&gt;AuthorizationType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;API_KEY&lt;/span&gt;
    &lt;span class="na"&gt;AuthParameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ApiKeyAuthParameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;ApiKeyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Authorization&lt;/span&gt;
        &lt;span class="na"&gt;ApiKeyValue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;OpenWeatherAPIKey&lt;/span&gt;
      &lt;span class="na"&gt;InvocationHttpParameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;QueryStringParameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;IsValueSecret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
            &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appid&lt;/span&gt;
            &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;OpenWeatherAPIKey&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Create Step Function Execution Policy
&lt;/h3&gt;

&lt;p&gt;There are several permissions needed to execute an endpoint using the HTTP task.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It needs access to the secret that the EventBridge Connection creates in Secrets Manager.&lt;/li&gt;
&lt;li&gt;Permission to retrieve the connection credentials from the EventBridge Connection.&lt;/li&gt;
&lt;li&gt;Permission to execute the InvokeHTTPEndpoint. In this case the resource needs to be the ARN of the state machine that is executing it. To avoid a cyclical dependency in SAM we are parsing the ARN ourselves. I'm adding a wildcard at the end to account for the unique identifier that AWS adds to the end of generated resource names.  We are also restricting the HTTP Invocation to only allow a GET to the OpenWeather API.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2012-10-17&lt;/span&gt;
    &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
        &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;secretsmanager:DescribeSecret&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;secretsmanager:GetSecretValue&lt;/span&gt;
        &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;OpenWeatherConnection.SecretArn&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
        &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;events:RetrieveConnectionCredentials&lt;/span&gt;
        &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;OpenWeatherConnection.Arn&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
        &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;states:InvokeHTTPEndpoint&lt;/span&gt;
        &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;arn:aws:states:${AWS::Region}:${AWS::AccountId}:stateMachine:ShouldIWearAJacketStateMachine*&lt;/span&gt;
        &lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;StringEquals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;states:HTTPMethod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GET&lt;/span&gt;
          &lt;span class="na"&gt;StringLike&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;states:HTTPEndpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;OpenWeatherBaseUrl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Calling endpoint from the Step Function
&lt;/h3&gt;

&lt;p&gt;With all that in place we can now add the step to our state machine as shown below.&lt;br&gt;&lt;br&gt;
We are specifying the HTTP method as GET, the connection ARN to our EventBridge Connection as the authentication, OpenWeather base URL and we are appending query parameters to get the temperature of the location and the units we want.&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="nl"&gt;"Get Temperature"&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;"Task"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:states:::http:invoke"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Parameters"&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;"Method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Authentication"&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;"ConnectionArn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${OpenWeatherConnectionArn}"&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;"ApiEndpoint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${OpenWeatherBaseUrl}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"QueryParameters"&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;"lat.$"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$.latitude"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"lon.$"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$.longitude"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"units"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"imperial"&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;"ResultSelector"&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;"temperature.$"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$.ResponseBody.main.temp"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;As you can see it is not that complicated to replace any Lambda Functions you have to call third party endpoints. The permissions can get a little bit complicated but once you understand them you can repeat them for other requests you need to do.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>stepfunctions</category>
    </item>
    <item>
      <title>How to update Lambda Functions from Common JS to ECMAScript</title>
      <dc:creator>Andres Moreno</dc:creator>
      <pubDate>Mon, 11 Dec 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/aws-builders/how-to-update-lambda-functions-from-common-js-to-ecmascript-2a2n</link>
      <guid>https://dev.to/aws-builders/how-to-update-lambda-functions-from-common-js-to-ecmascript-2a2n</guid>
      <description>&lt;p&gt;Version 5.0.0 of the &lt;a href="https://www.npmjs.com/package/@middy/core" rel="noopener noreferrer"&gt;@middy/core&lt;/a&gt; package no longer supports Common JS in favor of ECMAScript Modules (ESM). ESM improves performance by 2X as mentioned &lt;a href="https://middy.js.org/docs/upgrade/4-5/" rel="noopener noreferrer"&gt;in this post&lt;/a&gt;. Seeing this change prompted me to investigate what needs to be updated to get Lambda functions to be ESM to not fall behind on my dependency versions. In this post, we'll go through the 3 steps needed to go from Common JS to ESM.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Designate Lambda functions as ECMA Script Modules
&lt;/h2&gt;

&lt;p&gt;There are two ways you can designate a Lambda function as an ECMA Script Module.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update package.json
&lt;/h3&gt;

&lt;p&gt;You can add the &lt;strong&gt;type&lt;/strong&gt; attribute with a value of &lt;strong&gt;module&lt;/strong&gt; to the package.json as shown below.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F03zld8hdwd0knkze3cai.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F03zld8hdwd0knkze3cai.jpg" alt="package.json diff showing type attribute as module" width="800" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Update file extension
&lt;/h3&gt;

&lt;p&gt;You can also set the extension of the files to be &lt;em&gt;.mjs&lt;/em&gt; instead of &lt;em&gt;.js&lt;/em&gt; or &lt;em&gt;.cjs&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Update how you include dependencies
&lt;/h2&gt;

&lt;p&gt;In Common JS dependencies are included using the &lt;em&gt;require&lt;/em&gt; keyword, in ECMAScript with the &lt;em&gt;import&lt;/em&gt; keyword. Below we can see the code change needed to include dependencies in ESM.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F91wzim6xi28ejjrihvyz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F91wzim6xi28ejjrihvyz.jpg" alt="Diff file showing the difference between require and import for including modules" width="800" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Update how the &lt;em&gt;handler&lt;/em&gt; and other functions are exported
&lt;/h2&gt;

&lt;p&gt;The way you export functions in ESM is different than how it is done for Common JS. The export will have to change from &lt;code&gt;exports.functionName&lt;/code&gt; to &lt;code&gt;export const functionName&lt;/code&gt; as shown below.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnj4svgz30hn1qwstue4k.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnj4svgz30hn1qwstue4k.jpg" alt="Diff file showing how exporting functions changes" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Last words
&lt;/h2&gt;

&lt;p&gt;With these three changes you are now done, these are simple steps to take but might get more complicated depending on your project structure. If you are applying this to a more complicated project I recommend having test automation (unit tests, contract tests, etc) to verify no bugs are getting introduced.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Speed up new serverless application development with customized SAM templates</title>
      <dc:creator>Andres Moreno</dc:creator>
      <pubDate>Sat, 01 Jul 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/aws-builders/speed-up-new-serverless-application-development-with-customized-sam-templates-5fnk</link>
      <guid>https://dev.to/aws-builders/speed-up-new-serverless-application-development-with-customized-sam-templates-5fnk</guid>
      <description>&lt;p&gt;As you start setting patterns and best practices within your projects at some point you will want to share these to simplify the lives of other developers.&lt;br&gt;&lt;br&gt;
SAM allows you to initialize projects by using the &lt;em&gt;init&lt;/em&gt; command and selecting a template for your project. SAM has built-in templates to generate simple Hello-World starter projects (you can find the list of templates &lt;a href="https://github.com/aws/aws-sam-cli-app-templates" rel="noopener noreferrer"&gt;here&lt;/a&gt;). These will help you get started and get familiar with how to structure your serverless applications but as your applications grow you will want to add your own flavor to these. This is why SAM provides the ability to use custom templates to kick start applications that include your own designs.&lt;/p&gt;
&lt;h1&gt;
  
  
  How does &lt;code&gt;sam init&lt;/code&gt; work?
&lt;/h1&gt;

&lt;p&gt;We first need to understand how the &lt;em&gt;init&lt;/em&gt; command works.&lt;br&gt;&lt;br&gt;
&lt;code&gt;sam init&lt;/code&gt; uses &lt;em&gt;&lt;a href="https://www.cookiecutter.io/" rel="noopener noreferrer"&gt;cookiecutter&lt;/a&gt;&lt;/em&gt; to prompt and resolve all the values for a template.&lt;br&gt;&lt;br&gt;
You will first select if you want to use an existing AWS template or if you want to use a custom template. If you choose the latter you will have to provide the location of the template.&lt;br&gt;&lt;br&gt;
Once you have a template selected it will use cookiecutter to prompt the user for all the necessary information to do the proper replacements in the files.  &lt;/p&gt;
&lt;h1&gt;
  
  
  Creating a custom template
&lt;/h1&gt;

&lt;p&gt;Since SAM uses cookiecutter to generate a project you can use any of the capabilities described in their &lt;a href="https://cookiecutter.readthedocs.io/en/2.0.2/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Create project structure
&lt;/h2&gt;

&lt;p&gt;You give your project the necessary folder structure, as well as include any files needed within these folders. Usually you already have a project in place that you want to model from when creating a template, you can simply copy all of the files and folders from that existing project into your template project.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Identify replaceable values
&lt;/h2&gt;

&lt;p&gt;Now you need to identify what values you need to be entered by the person to initialize the project. These are things that will vary from project to project. For example, if you are working with a serverless API you could provide values for DynamoDB table name, API stage name, stack name, and Lambda Function runtime.&lt;br&gt;&lt;br&gt;
With the attributes identified you will now replace what you currently have with something that looks like this &lt;code&gt;{{ cookiecutter.project_name }}&lt;/code&gt;, this is what cookiecutter will be scanning for to be able to replace the values with the users input. This format can be applied to any spot within a file as well as for any folder or file names.&lt;/p&gt;

&lt;p&gt;In the screenshot below you can see how I'm using cookiecutter replacement format for a folder name as well as for a string within a file.&lt;br&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvd2a1ljxwr1zn13j0p6e.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvd2a1ljxwr1zn13j0p6e.jpg" alt="Screenshot of coockiecutter replacement format examples" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Setup cookiecutter metadata
&lt;/h2&gt;

&lt;p&gt;Cookiecutter needs to know which attributes to prompt the user for to be able to use the values as replacements in the template. This is done in a &lt;em&gt;cookiecutter.json&lt;/em&gt; file at the root of the template project. This is a JSON file that contains all the attributes identified in step #2, the file will look something like this:&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;"outputDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"service_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dynamodb_table_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&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;h1&gt;
  
  
  Using the custom template
&lt;/h1&gt;

&lt;p&gt;All you have to do now is run the &lt;code&gt;sam init&lt;/code&gt; command, when asked what the template source is you will choose &lt;em&gt;Custom Template Location&lt;/em&gt;. You have several options here that depend on where your template is currently located.&lt;br&gt;&lt;br&gt;
In my example below I used &lt;em&gt;git&lt;/em&gt;, where I specify the repository url where my template is located:&lt;br&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1cdce5x16o37cp8ppirk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1cdce5x16o37cp8ppirk.jpg" alt="Screenshot of terminal showing custom template example" width="800" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;We learned how to create a custom template for your serverless applications in 3 simple steps. Having your own template is valuable when your team starts growing and there are new applications regularly getting created. Reducing the amount of time developers spend creating folder structures and adding boilerplate functionality to an application is great so the time focused can be spent providing business value.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>sam</category>
    </item>
    <item>
      <title>How to Run the JavaScript AWS SDK Locally</title>
      <dc:creator>Andres Moreno</dc:creator>
      <pubDate>Tue, 31 Jan 2023 13:29:35 +0000</pubDate>
      <link>https://dev.to/aws-builders/running-aws-sdk-locally-1m1d</link>
      <guid>https://dev.to/aws-builders/running-aws-sdk-locally-1m1d</guid>
      <description>&lt;p&gt;When using Access Keys I got used to setting the default profile in my credentials file and it worked just fine. When I began having to manage more accounts and switched to SSO, updating the default profile becomes more of a pain. I will show you a couple of ways to authenticate the SDK using named profiles.&lt;/p&gt;

&lt;p&gt;I will be showing how to authenticate the AWS SDK against a specific AWS Account using the following methods:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Setting environment variables&lt;/li&gt;
&lt;li&gt;Getting credentials in the code&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Setting environment variables
&lt;/h2&gt;

&lt;p&gt;The easiest way you can run the AWS SDK from your local machine is by setting the AWS_PROFILE environment variable (if not set it will default to the &lt;em&gt;default&lt;/em&gt; profile).&lt;/p&gt;

&lt;p&gt;In your terminal you can set the AWS_PROFILE environment variable so the SDK can find the profile from the &lt;code&gt;~/.aws/config&lt;/code&gt; file and use those values. When using AWS SSO you will need to log in by running &lt;code&gt;aws sso login --profile my-profile&lt;/code&gt;.&lt;br&gt;
Look at &lt;a href="https://ben11kehoe.medium.com/you-only-need-to-call-aws-sso-login-once-for-all-your-profiles-41a334e1b37e" rel="noopener noreferrer"&gt;this Ben Kehoe's post&lt;/a&gt; for an explanation on how this command works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you set environment variables?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Linux/macOS&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;my-profile&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AWS_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;us-east-1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Powershell&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"my-profile"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;PS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;AWS_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Windows Command Prompt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;C:\&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;setx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;my-profile&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;C:\&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;setx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AWS_REGION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;us-east-1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now use any client by initializing it as shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SecretsManagerClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/client-secrets-manager&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secretsManagerClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SecretsManagerClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are more environment variables that you can set in you terminal to accomplish different things, &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html#envvars-list" rel="noopener noreferrer"&gt;here is a list of supported environment variables&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting credentials in code
&lt;/h2&gt;

&lt;p&gt;Another way to provide the credentials to the SDK is by using the &lt;strong&gt;credentials-provider&lt;/strong&gt; class. This requires your code to be aware of any parameters you need to set.&lt;br&gt;&lt;br&gt;
To make this work the same way as the environment variable route you will need to provide the &lt;em&gt;profile&lt;/em&gt; as an input in your code.&lt;/p&gt;

&lt;p&gt;Once you are signed in to your SSO with &lt;code&gt;aws sso login --profile my-profile&lt;/code&gt;, you can get the credentials using the &lt;code&gt;credential-providers&lt;/code&gt; package as shown below (If using the &lt;em&gt;default&lt;/em&gt; profile you can start the client the same way than with environment variables)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SecretsManagerClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/client-secrets-manager&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fromSSO&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@aws-sdk/credential-providers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;credentials&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fromSSO&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inputtedProfile&lt;/span&gt;
  &lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the AwsCredentialIdentityProvider object you can initialize any client by providing the &lt;strong&gt;credentials&lt;/strong&gt; and the &lt;strong&gt;region&lt;/strong&gt; as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secretsManagerClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SecretsManagerClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inputtedRegion&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Running the SDK from your local machine is very important and valuable to be able to provide tooling and improve your developer experience.&lt;br&gt;&lt;br&gt;
We went through some of the ways you can authenticate code that is being executed locally when you have several profiles  .&lt;br&gt;
There are other ways you can authenticate, so if these options do not work for you dueto organization security policies or any other reason, I recommend looking into the documentation or reach out to the community to find which one fits you best.&lt;br&gt;
&lt;a href="https://twitter.com/i/communities/1471503983839567878" rel="noopener noreferrer"&gt;Twitter AWS Community&lt;/a&gt;&lt;br&gt;
&lt;a href="//awsdevelopers.slack.com"&gt;AWS Developers Slack Workspace&lt;/a&gt;  &lt;/p&gt;

</description>
      <category>aws</category>
      <category>sdk</category>
      <category>iam</category>
      <category>sso</category>
    </item>
    <item>
      <title>Add Authentication to Portman API tests</title>
      <dc:creator>Andres Moreno</dc:creator>
      <pubDate>Fri, 25 Mar 2022 01:22:23 +0000</pubDate>
      <link>https://dev.to/aws-builders/add-authentication-to-portman-api-tests-3ide</link>
      <guid>https://dev.to/aws-builders/add-authentication-to-portman-api-tests-3ide</guid>
      <description>&lt;p&gt;When creating an API you want to have security to avoid bad usage and incurring cost by unintended use. Whenever we add a security layer to our APIs any automation that we have around it automatically becomes more complex. Portman provides ways to configure authentication to be used when it runs. In this post we will configuring Portman to successfully run against our secure API.&lt;/p&gt;

&lt;p&gt;We will be using the API that we already added Portman configuration to in the &lt;a href="https://www.andmore.dev/blog/getting-started-portman/" rel="noopener noreferrer"&gt;Get better API testing by using Portman post&lt;/a&gt;. First we need to add security to the API so it only allow access to calls made with a valid API key, after that we can configure Portman to use that API Key and successfully make all the requests needed.&lt;/p&gt;

&lt;p&gt;Here is the &lt;a href="https://github.com/andmoredev/lambdaless-api" rel="noopener noreferrer"&gt;source code&lt;/a&gt; for the end result. If you want to follow along clone/download &lt;a href="https://github.com/andmoredev/lambdaless-api/tree/f40b2b19f87f37b3bb6d7624f5f7c44130b0463e" rel="noopener noreferrer"&gt;this commit&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Add Security to API Gateway
&lt;/h1&gt;

&lt;p&gt;To add API Key authentication to the API we will be adding the &lt;em&gt;Auth&lt;/em&gt; property in the API resource in the SAM template as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;ProductsAPI&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Api&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;StageName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Lambdaless&lt;/span&gt;
        &lt;span class="na"&gt;Auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ApiKeyRequired&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;UsagePlan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;CreateUsagePlan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PER_API&lt;/span&gt;
            &lt;span class="na"&gt;UsagePlanName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ProductsAPIUsagePlan&lt;/span&gt;
        &lt;span class="na"&gt;DefinitionBody&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Fn::Transform'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Include&lt;/span&gt;
            &lt;span class="na"&gt;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;Location&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./products-openapi.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will also be adding the ApiKeyId in the Outputs section to help us retrieve the API Key later&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;ApiKeyId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;API Key ApiKeyId&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;ProductsAPIApiKey&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the API is deployed with these changes, it will now require an API Key to be passed in the header. This will cause the Portman tests to fail with a 403 since it is not providing the header yet. I will not go into any more details around how security works for API Gateway since it is not in the scope of this article.&lt;/p&gt;

&lt;h1&gt;
  
  
  Get the API Key
&lt;/h1&gt;

&lt;p&gt;There are two ways you can get the API Key that was generated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to the console and retrieve it from the API Gateway service&lt;/li&gt;
&lt;li&gt;With the CLI. Below I will be explaining how to do it from the CLI.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;First we need to get the API Id. We can do this by executing the following command:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;aws cloudformation describe-stacks --stack-name products-service&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The response for this command comes back with the Outputs defined in the SAM template where we have included the ApiKeyId. Copy the value presented in the OutputValue.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Now we need to run following command replacing the --api-key value with the value copied from the previous step to get the API Key that we can include in our headers:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;aws apigateway get-api-key --api-key REPLACE_WITH_APIID --include-value&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The API Key will be contained in the "value" attribute in the response. Copy this since we will be using it for the portman configuration in a bit.&lt;/p&gt;

&lt;h1&gt;
  
  
  Add security definitions in the OpenAPI
&lt;/h1&gt;

&lt;p&gt;If you try to run Portman right now you will get 403 Forbidden responses because Portman is not providing an API key to the request. Portman uses your OpenAPI definition to generate and run the tests, if we do not define the securitySchemes it will not know to apply them. So first we need to setup the securitySchemes supported by our API which in our case is only API Key, to do this we will add the following YAML under the &lt;em&gt;components&lt;/em&gt; section of our OpenAPI spec.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt; &lt;span class="na"&gt;securitySchemes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ApiKeyAuth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apiKey&lt;/span&gt;
      &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;header&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;X-API-KEY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OpenAPI supports several authenticaton schemes if you wish to learn more about these please look at the &lt;a href="https://swagger.io/docs/specification/authentication/" rel="noopener noreferrer"&gt;OpenAPI documentation here&lt;/a&gt;.&lt;br&gt;
Portman currently suports the API Key, HTTP basic auth and HTTP bearer token security schemes.&lt;/p&gt;

&lt;p&gt;In OpenAPI you can apply a different security scheme per endpoint, in our case we want to apply the same one across the whole API. To do this we will be adding the root level &lt;em&gt;security&lt;/em&gt; property to define it as shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;ApiKeyAuth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we run Portman again we will still get a 403 response since we are still not telling Portman what API Key to use.&lt;/p&gt;

&lt;h1&gt;
  
  
  Configure Portman to use an API Key
&lt;/h1&gt;

&lt;p&gt;Portman allows you to configure securityOverwrites in the globals section of your Pormtan config file.&lt;br&gt;
We will be updating ours to include an overwrite for the apiKey.&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;"securityOverwrites"&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;"apiKey"&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;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"INSERT THE API KEY HERE"&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="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;Now if we run Portman we wll get a successful run just as we did prior to adding security to the API.&lt;/p&gt;

&lt;h1&gt;
  
  
  Recap
&lt;/h1&gt;

&lt;p&gt;In this post we successfully tested a secure API using Portman by&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding security to the API&lt;/li&gt;
&lt;li&gt;Updating the OpenAPI spec to define the type of authentication to be used&lt;/li&gt;
&lt;li&gt;Configured Portman to successfully include an API key in the header of every request to be able to automate our API testing for secure APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Resources
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/andmoredev/lambdaless-api" rel="noopener noreferrer"&gt;Source Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://davekz.com/aws-sam-api-keys/" rel="noopener noreferrer"&gt;Template for adding API Keys to API Gateway&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://swagger.io/docs/specification/authentication/" rel="noopener noreferrer"&gt;OpenAPI security schemes documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/apideck-libraries/portman/blob/main/README.md" rel="noopener noreferrer"&gt;Portman documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>openapi</category>
      <category>test</category>
    </item>
  </channel>
</rss>
