<?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: Ntombizakhona Mabaso</title>
    <description>The latest articles on DEV Community by Ntombizakhona Mabaso (@ntombizakhona).</description>
    <link>https://dev.to/ntombizakhona</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F899317%2Ffabcc82d-22a3-410a-b5b9-869687d73e09.jpg</url>
      <title>DEV Community: Ntombizakhona Mabaso</title>
      <link>https://dev.to/ntombizakhona</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ntombizakhona"/>
    <language>en</language>
    <item>
      <title>DevOps Engineer Professional Exam Guide</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Fri, 24 Jul 2026 19:07:37 +0000</pubDate>
      <link>https://dev.to/aws-builders/devops-engineer-professional-exam-guide-4j35</link>
      <guid>https://dev.to/aws-builders/devops-engineer-professional-exam-guide-4j35</guid>
      <description>&lt;h2&gt;
  
  
  📘 The &lt;strong&gt;AWS Certified DevOps Engineer – Professional (DOP-C02)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;AWS Certified DevOps Engineer – Professional (DOP-C02)&lt;/strong&gt; isn't an exam you pass by memorising AWS services. It's an exam about &lt;strong&gt;building, automating, operating, and improving production systems&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This series follows the &lt;strong&gt;official DOP-C02 Exam Guide (Version 1.6)&lt;/strong&gt;, with every article mapped directly to one or more &lt;strong&gt;official task statements&lt;/strong&gt;. Rather than covering services in isolation, each article focuses on the skills AWS expects a DevOps engineer to demonstrate in &lt;em&gt;real-world&lt;/em&gt; environments.&lt;/p&gt;

&lt;p&gt;Unlike my &lt;a href="https://dev.tourl"&gt;&lt;strong&gt;Developer Associate&lt;/strong&gt;&lt;/a&gt; series, this guide is unapologetically &lt;strong&gt;terminal-first&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At the Associate level, the AWS Management Console is a useful learning tool. But, at the Professional level, excessive &lt;strong&gt;clickops&lt;/strong&gt; becomes a liability. DevOps is built on automation, repeatability, and Infrastructure as Code. Not clicking through web pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏗️ Hands-On Principles
&lt;/h2&gt;

&lt;p&gt;Every practical exercise in this series follows the same principles.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Terminal-first:&lt;/strong&gt; Everything is executed from your local machine or the AWS CloudShell.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automation over clickops:&lt;/strong&gt; If it can be accomplished using the AWS CLI, Infrastructure as Code, or a script, that's the approach we'll take.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reproducible by design:&lt;/strong&gt; Every command is copy-and-pasteable, and every environment can be recreated from version-controlled code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production mindset:&lt;/strong&gt; Infrastructure is treated as code, not something manually configured through the console.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean up after yourself:&lt;/strong&gt; Every hands-on lab includes teardown steps to avoid leaving billable resources running.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Local Environment Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before beginning the series, you should have the following installed and configured:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS CLI v2&lt;/li&gt;
&lt;li&gt;Git&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jq&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;A code editor (Visual Studio Code recommended)&lt;/li&gt;
&lt;li&gt;AWS SAM CLI&lt;/li&gt;
&lt;li&gt;AWS CDK&lt;/li&gt;
&lt;li&gt;CloudFormation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recommended tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cfn-lint&lt;/code&gt; for CloudFormation template validation&lt;/li&gt;
&lt;li&gt;AWS Session Manager plugin for secure instance access without SSH keys or bastion hosts&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Exam Overview
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Item&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Exam Code&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DOP-C02 (Exam Guide Version 1.6)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Questions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;75 total (65 scored, 10 unscored)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Question Types&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multiple Choice and Multiple Response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Duration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;180 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scoring&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;100–1,000 (Passing score: 750)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Exam Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;USD $300&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Certification Validity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3 years&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;AWS uses a &lt;strong&gt;compensatory scoring model&lt;/strong&gt;, meaning you do not need to pass every domain individually. Strong performance in one domain can offset weaker performance in another.&lt;/p&gt;




&lt;h2&gt;
  
  
  Target Candidate
&lt;/h2&gt;

&lt;p&gt;AWS recommends at least &lt;strong&gt;two years of experience&lt;/strong&gt; provisioning, operating, and managing AWS environments, along with practical experience in software development lifecycles, automation, and scripting.&lt;/p&gt;

&lt;p&gt;The certification validates your ability to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement and manage CI/CD systems on AWS&lt;/li&gt;
&lt;li&gt;Build automated testing and deployment pipelines&lt;/li&gt;
&lt;li&gt;Implement Infrastructure as Code at scale&lt;/li&gt;
&lt;li&gt;Automate security controls, governance, and compliance&lt;/li&gt;
&lt;li&gt;Design highly available, scalable, and self-healing systems&lt;/li&gt;
&lt;li&gt;Build monitoring, logging, and observability solutions&lt;/li&gt;
&lt;li&gt;Automate operational and incident response workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Topics Outside the Scope
&lt;/h3&gt;

&lt;p&gt;The exam does &lt;strong&gt;not&lt;/strong&gt; expect deep expertise in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Advanced networking theory and routing algorithms&lt;/li&gt;
&lt;li&gt;Database design or query optimization&lt;/li&gt;
&lt;li&gt;Full-stack application development&lt;/li&gt;
&lt;li&gt;Writing application business logic&lt;/li&gt;
&lt;li&gt;Deep security architecture beyond operational implementation&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Domains, Weightings, and Task Statements
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Domain&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;&lt;code&gt;Weight&lt;/code&gt;&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Task Statements&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1. SDLC Automation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;22%&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;1.1&lt;/strong&gt; CI/CD pipelines · &lt;strong&gt;1.2&lt;/strong&gt; Automated testing · &lt;strong&gt;1.3&lt;/strong&gt; Artifact management · &lt;strong&gt;1.4&lt;/strong&gt; Deployment strategies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2. Configuration Management and Infrastructure as Code&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;17%&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;2.1&lt;/strong&gt; IaC and reusable components · &lt;strong&gt;2.2&lt;/strong&gt; Multi-account and multi-Region automation · &lt;strong&gt;2.3&lt;/strong&gt; Automation at scale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3. Resilient Cloud Solutions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;15%&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;3.1&lt;/strong&gt; High availability · &lt;strong&gt;3.2&lt;/strong&gt; Scalability · &lt;strong&gt;3.3&lt;/strong&gt; Automated recovery (RTO/RPO)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;4. Monitoring and Logging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;15%&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;4.1&lt;/strong&gt; Collecting logs and metrics · &lt;strong&gt;4.2&lt;/strong&gt; Auditing and analysis · &lt;strong&gt;4.3&lt;/strong&gt; Automated monitoring and event management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;5. Incident and Event Response&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;14%&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;5.1&lt;/strong&gt; Event sources · &lt;strong&gt;5.2&lt;/strong&gt; Automated configuration changes · &lt;strong&gt;5.3&lt;/strong&gt; Failure investigation and remediation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;6. Security and Compliance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;17%&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;6.1&lt;/strong&gt; IAM at scale · &lt;strong&gt;6.2&lt;/strong&gt; Security controls and data protection · &lt;strong&gt;6.3&lt;/strong&gt; Security monitoring and auditing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;Throughout this series, you'll (we'll...I'll) build and manage AWS resources from &lt;strong&gt;y(our) own local machine&lt;/strong&gt; using the tools professional engineers rely on every day:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS CLI&lt;/li&gt;
&lt;li&gt;Infrastructure as Code (CloudFormation, AWS SAM, and AWS CDK)&lt;/li&gt;
&lt;li&gt;Shell scripting&lt;/li&gt;
&lt;li&gt;Git&lt;/li&gt;
&lt;li&gt;JSON tooling&lt;/li&gt;
&lt;li&gt;Automation workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AWS Console still appears when it provides useful visibility or helps explain a concept, but it is never the primary way of completing a task.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡The goal isn't simply to pass the exam. It's to build the habits and workflows expected of a professional DevOps engineer.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;🏗️&lt;/p&gt;

</description>
      <category>aws</category>
      <category>certification</category>
      <category>cloud</category>
      <category>devops</category>
    </item>
    <item>
      <title>My Developer Associate Certification Journey and Resources to Certify With Confidence</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Thu, 23 Jul 2026 20:18:18 +0000</pubDate>
      <link>https://dev.to/aws-builders/my-developer-associate-certification-journey-and-resources-to-certify-with-confidence-402l</link>
      <guid>https://dev.to/aws-builders/my-developer-associate-certification-journey-and-resources-to-certify-with-confidence-402l</guid>
      <description>&lt;p&gt;☁️ &lt;strong&gt;Exam Guide:&lt;/strong&gt; Developer Associate&lt;br&gt;
&lt;strong&gt;Resources to Certify With Confidence&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;My Developer Associate Certification Journey&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🧭 First Things First
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A Note on Renewal
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;One lesson I learned the hard way came from my earlier certification journey.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As I mentioned in my &lt;a href="https://dev.to/aws-builders/my-solutions-architect-associate-certification-journey-and-resources-to-certify-with-confidence-5h16"&gt;Solutions Architect Associate Certification Journey&lt;/a&gt;, I let that certification expire instead of just taking the Professional Version. Looking back, I wish I hadn't done that, but hey, as they say: &lt;strong&gt;&lt;code&gt;Hindsight = 20/20.&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
We live and we learn, and I lived and I learned to renew my Developer Associate by taking the DevOps Engineer Professional.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.credly.com/badges/c7d3c765-9158-4318-bf69-e5b1529da4e8" rel="noopener noreferrer"&gt;Ntombizakhona Mabaso - AWS Certified Developer Associate&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So basically, instead of monotonously sitting the same Associate exam again, I prepared for and passed the AWS Certified DevOps Engineer – Professional by writing out &lt;a href="https://dev.to/ntombizakhona/series/38998"&gt;this series.&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Thankfully, AWS allows a higher-level certification to automatically renew qualifying lower-level certifications, therefore it was or rather is a far more efficient way to stay current while continuing to grow my skills.&lt;/p&gt;

&lt;p&gt;But wait, just after I booked my DevOps Exam, AWS introduced another excellent renewal or rather "maintain" option through AWS Skill Builder, many or rather some select certifications can now be renewed through maintenance by completing what needs to be completed thus extending the certification without retaking the full paid exam. If you’re not ready to move up to a Professional certification yet, it’s an excellent way to keep your credential active while planning your next step.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Honestly, retaking the same exams again is super monotonous, find ways to renew with higher certs or maintain with Skill Builder&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  📘Resources
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Hands-On Experience Is Imperative &amp;amp; Non-Negotiable
&lt;/h3&gt;

&lt;p&gt;If there’s one piece of advice I’d give every Developer Associate candidate, it’s this:&lt;br&gt;
Build. &lt;em&gt;Build.&lt;/em&gt; &lt;strong&gt;Build!&lt;/strong&gt; Build more than you read.&lt;/p&gt;

&lt;p&gt;It's called the &lt;strong&gt;Developer&lt;/strong&gt; Associate, and not the &lt;strong&gt;Theorist&lt;/strong&gt; Associate afterall.&lt;/p&gt;

&lt;p&gt;Reading documentation and watching videos will teach you what services do.&lt;br&gt;
Building applications teaches you why they behave the way they do.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why is a Lambda function timing out?&lt;/li&gt;
&lt;li&gt;Why can’t a function connect to an RDS database?&lt;/li&gt;
&lt;li&gt;Why is DynamoDB throttling requests?&lt;/li&gt;
&lt;li&gt;Which deployment strategy minimizes downtime?&lt;/li&gt;
&lt;li&gt;Which IAM permission is missing?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These questions become much easier when you’ve already encountered and fixed the problem yourself.&lt;/p&gt;

&lt;p&gt;There’s simply no substitute for practical experience.&lt;br&gt;
&lt;em&gt;Read to understand.&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Build to remember.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🏗️ Hands On
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Remember Through Building
&lt;/h3&gt;

&lt;p&gt;The AWS Certified Developer – Associate isn’t just a test of whether you know AWS services. It’s a test of whether you understand how developers actually build, deploy, secure, and troubleshoot applications on AWS.&lt;/p&gt;

&lt;p&gt;Unlike the Cloud Practitioner or even the Solutions Architect Associate, this certification expects you to think like an engineer. You’ll be asked to identify the best SDK to use, debug IAM permission issues, optimize Lambda functions, configure deployment strategies, and understand how AWS services interact in real-world applications.&lt;/p&gt;

&lt;p&gt;If I could recommend only a handful of practical exercises, they’d be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build a REST API using Lambda, API Gateway, and DynamoDB&lt;/li&gt;
&lt;li&gt;Deploy applications using AWS SAM&lt;/li&gt;
&lt;li&gt;Create a CI/CD pipeline with CodePipeline and CodeBuild&lt;/li&gt;
&lt;li&gt;Secure APIs with Amazon Cognito&lt;/li&gt;
&lt;li&gt;Monitor applications using CloudWatch Logs, Metrics, and AWS X-Ray&lt;/li&gt;
&lt;li&gt;Debug IAM permission failures&lt;/li&gt;
&lt;li&gt;Connect Lambda to an RDS database inside a VPC&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🛠️ Practice Exams
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Accelerated And Targeted Learning With Artificial Intelligence
&lt;/h3&gt;

&lt;p&gt;Reading the documentation and building projects are two of the most important steps in preparing for the AWS Certified Developer – Associate exam. &lt;/p&gt;

&lt;p&gt;However, there is a third step that many candidates overlook: &lt;strong&gt;identifying the gaps in your knowledge.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where practice exams and artificial intelligence become incredibly powerful.&lt;/p&gt;

&lt;p&gt;Practice exams don't just tell you whether you answered correctly. They reveal what you don't know. Every incorrect answer represents an opportunity to strengthen your understanding before exam day.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Unlike traditional practice exams&lt;/em&gt;, where every candidate receives essentially the same questions regardless of their strengths or weaknesses, artificial intelligence acts as an intelligent study companion. It adapts to your learning needs by identifying knowledge gaps, explaining difficult concepts in different ways, generating targeted practice questions, and focusing on the areas where you need the most improvement. &lt;/p&gt;

&lt;p&gt;Instead of simply measuring what you know, AI helps you continuously improve what you don't know, making every study session more personalized and effective.&lt;/p&gt;

&lt;p&gt;But don't stop at reviewing the explanation provided by the practice exam.&lt;/p&gt;

&lt;p&gt;Instead, use AI as your personal tutor.&lt;/p&gt;

&lt;p&gt;For every question you answer incorrectly, ask AI to explain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why the correct answer is correct.&lt;/li&gt;
&lt;li&gt;Why each incorrect option is wrong.&lt;/li&gt;
&lt;li&gt;When you would use each service instead.
Real-world examples of the architecture.&lt;/li&gt;
&lt;li&gt;Common exam traps related to the question.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, if you miss a question comparing Amazon SQS and Amazon EventBridge, don't simply memorise the answer. Ask AI to explain the differences, provide use cases, compare pricing models, discuss delivery guarantees, and even generate similar exam questions until the concept clicks.&lt;/p&gt;

&lt;p&gt;You can also ask AI to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simplify complex AWS documentation.&lt;/li&gt;
&lt;li&gt;Create flashcards from your weak areas.&lt;/li&gt;
&lt;li&gt;Generate realistic exam questions.&lt;/li&gt;
&lt;li&gt;Quiz you interactively.&lt;/li&gt;
&lt;li&gt;Compare similar AWS services.&lt;/li&gt;
&lt;li&gt;Explain diagrams and architectures.&lt;/li&gt;
&lt;li&gt;Write sample code using the AWS SDK.&lt;/li&gt;
&lt;li&gt;Suggest small hands-on projects based on topics you're struggling with.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of my favourite techniques is to treat AI like an AWS instructor rather than a search engine. Instead of asking, "What's the answer?", ask, "Teach me this concept as if I'm preparing for the AWS Certified Developer – Associate exam." The quality of the explanations and your understanding will improve dramatically.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember, the goal isn't to score 100% on a practice exam.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The goal is to uncover every weak spot before the real exam does.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By combining theory, hands-on building, practice exams, and AI-assisted learning, you create a continuous feedback loop that reinforces your knowledge. &lt;/p&gt;

&lt;p&gt;Every mistake becomes a lesson.&lt;br&gt;
Every lesson becomes experience&lt;br&gt;
And every experience brings you one step closer to passing with confidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Finally
&lt;/h2&gt;

&lt;p&gt;The AWS Certified Developer – Associate isn’t about memorizing services it’s about understanding how cloud applications are built, secured, deployed, and maintained.&lt;br&gt;
If you focus on &lt;strong&gt;building&lt;/strong&gt; real projects, &lt;strong&gt;troubleshooting&lt;/strong&gt; real problems, and learning from every AI-Simulated/Enhanced Practice Exam, you’ll not only be well prepared for the certification. You’ll become a stronger cloud engineer and developer in the process.&lt;/p&gt;

&lt;p&gt;An AWS Certified Developer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; &lt;a href="https://dev.to/ntombizakhona/series/38998"&gt;&lt;strong&gt;Exam Guide - Developer Associate&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; &lt;a href="https://aws.amazon.com/certification/recertification/" rel="noopener noreferrer"&gt;&lt;strong&gt;AWS Recertification&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; &lt;a href="https://skillbuilder.aws/certification/recertification" rel="noopener noreferrer"&gt;&lt;strong&gt;Maintain With Skillbuilder&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>certification</category>
      <category>cloud</category>
      <category>developer</category>
    </item>
    <item>
      <title>Technologies And Concepts: Cheat Sheet for Developer Associate (DVA-C02)</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Tue, 21 Jul 2026 18:07:09 +0000</pubDate>
      <link>https://dev.to/aws-builders/technologies-and-concepts-cheat-sheet-for-developer-associate-dva-c02-2jbp</link>
      <guid>https://dev.to/aws-builders/technologies-and-concepts-cheat-sheet-for-developer-associate-dva-c02-2jbp</guid>
      <description>&lt;p&gt;&lt;strong&gt;Exam Guide:&lt;/strong&gt; Developer - Associate&lt;br&gt;
&lt;strong&gt;Technologies And Concepts Cheat Sheet&lt;/strong&gt;&lt;br&gt;
📘 Cheat Sheet&lt;/p&gt;

&lt;h2&gt;
  
  
  1 | Services
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Compute
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Service&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;What It Does&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Key Points&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lambda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Serverless Functions&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;15 min&lt;/code&gt; timeout, &lt;code&gt;10240 MB&lt;/code&gt; memory max, &lt;code&gt;1000&lt;/code&gt; default concurrency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EC2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Virtual Servers&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Instance profiles for IAM roles, user data for bootstrap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ECS/Fargate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Container Orchestration&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Task roles for IAM, Fargate = serverless containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Elastic Beanstalk&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;PaaS Deployment&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;.ebextensions&lt;/code&gt;&lt;/strong&gt; for config, supports rolling/immutable/blue-green&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Storage &amp;amp; Databases
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Service&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;What It Does&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Key Points&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DynamoDB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;NoSQL key-value&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Partition keys, GSI/LSI, query vs scan, DAX for caching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;S3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Object Storage&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;SSE-S3/SSE-KMS/SSE-C, lifecycle policies, presigned URLs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ElastiCache&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;In-memory Cache&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;Redis&lt;/code&gt;&lt;/strong&gt; (complex types, persistence) vs &lt;strong&gt;&lt;code&gt;Memcached&lt;/code&gt;&lt;/strong&gt; (simple, multi-threaded)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RDS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Relational Database&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;RDS Proxy for Lambda connection pooling, read replicas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OpenSearch&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Search &amp;amp; Analytics&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Full-text search, log analytics&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  API &amp;amp; Integration
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Service&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;What It Does&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Key Points&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Gateway&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;REST/HTTP/WebSocket APIs&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Stages, authorizers, caching, request validation, throttling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SQS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Message Queue&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;Standard&lt;/code&gt;&lt;/strong&gt; (at-least-once) vs &lt;strong&gt;&lt;code&gt;FIFO&lt;/code&gt;&lt;/strong&gt; (exactly-once), visibility timeout, DLQ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SNS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Pub/sub messaging&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Fanout, filter policies, message attributes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EventBridge&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Event Bus&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Pattern matching, content-based filtering, multiple targets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kinesis&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Real-time Streaming&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Shards, partition keys, parallelization factor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Step Functions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Workflow Orchestration&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;Standard&lt;/code&gt;&lt;/strong&gt; (long-running) vs &lt;strong&gt;&lt;code&gt;Express&lt;/code&gt;&lt;/strong&gt; (high-volume, short)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Service&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;What It Does&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Key Points&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IAM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Access Management&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Policies, roles, least privilege, STS AssumeRole&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cognito&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;User Auth&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;User Pools&lt;/code&gt;&lt;/strong&gt; (tokens) vs &lt;strong&gt;&lt;code&gt;Identity Pools&lt;/code&gt;&lt;/strong&gt; (AWS credentials)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;KMS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Key Management&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Envelope encryption, 4 KB limit, key rotation, cross-account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Secrets Manager&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Secret Storage&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Auto-rotation, $0.40/secret/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSM Parameter Store&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Config Storage&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;Standard&lt;/code&gt;&lt;/strong&gt; (free) vs &lt;strong&gt;&lt;code&gt;Advanced&lt;/code&gt;&lt;/strong&gt;, SecureString type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ACM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;SSL/TLS Certificates&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Free public certs, auto-renewal, can't export&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  CI/CD
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Service&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;What It Does&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Key Points&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CodeCommit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Git Repository&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Triggers pipelines on push&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CodeBuild&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Build Service&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;buildspec.yml&lt;/code&gt;&lt;/strong&gt;, supports Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CodeDeploy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Deployment Service&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;appspec.yml&lt;/code&gt;&lt;/strong&gt;, blue/green/canary/rolling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CodePipeline&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;CI/CD Orchestration&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Source → Build → Test → Deploy stages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SAM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Serverless Framework&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;template.yaml&lt;/code&gt;&lt;/strong&gt;, sam build/deploy, local testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CloudFormation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;IaC&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Templates, stacks, change sets, !Sub, !Ref, parameters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AppConfig&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Runtime Config&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Feature flags, gradual rollout, validation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Observability
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Service&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;What It Does&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Key Points&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CloudWatch Logs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Log storage &amp;amp; Query&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Logs Insights query language, log groups, retention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CloudWatch Metrics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Metric Tracking&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Custom metrics, EMF, math expressions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CloudWatch Alarms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Alerting&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Metric alarms, composite alarms, SNS actions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;X-Ray&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Distributed Tracing&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Segments, subsegments, annotations (indexed), metadata (not indexed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CloudTrail&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;API Audit Log&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Who did what? when? &amp;amp; Debug permission issues&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  2 | Numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Item&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;Limit&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lambda timeout&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;15 minutes&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lambda memory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;128 MB – 10,240 MB&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lambda /tmp storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;512 MB – 10,240 MB&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Lambda deployment&lt;/strong&gt; (zip)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;50 MB&lt;/code&gt; compressed, &lt;code&gt;250 MB&lt;/code&gt; uncompressed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Lambda deployment&lt;/strong&gt; (container)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10 GB&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lambda layers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;5&lt;/code&gt; per function, &lt;code&gt;250 MB&lt;/code&gt; total unzipped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Lambda concurrency&lt;/strong&gt; (default)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;1,000&lt;/code&gt; per region&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;KMS direct encryption&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;4 KB&lt;/code&gt; max&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SQS message size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;256 KB&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SQS visibility timeout&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0s – 12 hours&lt;/code&gt; (default 30s)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SQS retention&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;1 minute – 14 days&lt;/code&gt; (default 4 days)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SNS message size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;256 KB&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DynamoDB item size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;400 KB&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DynamoDB GSI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;20&lt;/code&gt; per table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DynamoDB LSI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;5&lt;/code&gt; per table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Gateway timeout&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;29 seconds&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Gateway payload&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10 MB&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;S3 object size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;5 TB&lt;/code&gt; (5 GB per PUT, use multipart for larger)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Secrets Manager secret size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;64 KB&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;SSM Parameter Store&lt;/strong&gt; (standard)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;4 KB&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;SSM Parameter Store&lt;/strong&gt; (advanced)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;8 KB&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;*&lt;em&gt;Step Functions Standard *&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;1 year&lt;/code&gt; execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Step Functions Express&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;5 minutes&lt;/code&gt; execution&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  3 | Patterns
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Decoupling
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SQS:&lt;/strong&gt; point-to-point, one consumer, buffering&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SNS:&lt;/strong&gt; fanout, multiple consumers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SNS + SQS:&lt;/strong&gt; fanout with reliable delivery&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EventBridge:&lt;/strong&gt; complex routing, content-based filtering&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Caching
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DAX:&lt;/strong&gt; DynamoDB reads only, microsecond latency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ElastiCache Redis:&lt;/strong&gt; general purpose, complex data types&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ElastiCache Memcached:&lt;/strong&gt; simple caching, multi-threaded&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudFront:&lt;/strong&gt; edge caching for APIs and static content&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway cache:&lt;/strong&gt; per-stage, per-method caching&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Authentication
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cognito User Pool:&lt;/strong&gt; user sign-up/sign-in, JWT tokens&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cognito Identity Pool:&lt;/strong&gt; temporary AWS credentials&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda authorizer:&lt;/strong&gt; custom auth logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM authorization:&lt;/strong&gt; service-to-service with SigV4&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deployment
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AllAtOnce:&lt;/strong&gt; fastest, highest risk&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canary:&lt;/strong&gt; small % first, then all (safest)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linear:&lt;/strong&gt; gradual rollout over time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blue/Green:&lt;/strong&gt; two environments, instant switch&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Error Handling
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DLQ:&lt;/strong&gt; capture failed messages (SQS, Lambda async)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda Destinations:&lt;/strong&gt; route success AND failure (preferred over DLQ)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ReportBatchItemFailures:&lt;/strong&gt; partial batch failure for SQS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BisectBatchOnFunctionError:&lt;/strong&gt; split Kinesis batch to isolate bad records&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step Functions:&lt;/strong&gt; retry and catch at the workflow level&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4 | Versus
&lt;/h2&gt;

&lt;h3&gt;
  
  
  SQS Standard vs FIFO
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Standard&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;FIFO&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Throughput&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Unlimited&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;300 msg/s (3000 batched)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ordering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Best effort&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Guaranteed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Delivery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;At least once&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Exactly once&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deduplication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;No&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Yes (5 min window)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Secrets Manager vs Parameter Store
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Secrets Manager&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Parameter Store&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Auto Rotation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Built-in&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;DIY&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;$0.40/secret/month&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Free (standard)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Max Size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;64 KB&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;4 KB / 8 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-Account&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Yes&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Yes (advanced)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Step Functions Standard vs Express
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Standard&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Express&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Max Duration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;1 year&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;5 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Execution Model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Exactly once&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;At least once&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Per state transition&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Per execution + duration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use Case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Long-running workflows&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;High-volume, short tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  5 | Cram
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Lambda in VPC&lt;/strong&gt; loses internet access → needs NAT Gateway or VPC endpoints&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB FilterExpression&lt;/strong&gt; doesn't reduce read capacity consumed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GSIs&lt;/strong&gt; are eventually consistent only. No strongly consistent reads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB uses Decimal, not float&lt;/strong&gt; in Python&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TTL deletion&lt;/strong&gt; can take up to 48 hours after expiration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway timeout&lt;/strong&gt; is 29 seconds. Can't be increased&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQS FIFO&lt;/strong&gt; throughput is 300 msg/s (3000 with batching) vs Standard (unlimited)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda Destinations&lt;/strong&gt; only work with async invocations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provisioned concurrency&lt;/strong&gt; costs money even when idle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSE-C:&lt;/strong&gt; you manage the key, AWS doesn't store it. Lose the key = lose the data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudFormation !Ref&lt;/strong&gt; returns different things for different resources (ARN, name, ID)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SAM transforms&lt;/strong&gt; to CloudFormation: &lt;code&gt;sam build&lt;/code&gt; is required before &lt;code&gt;sam deploy&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;X-Ray annotations&lt;/strong&gt; are indexed and searchable. &lt;strong&gt;Metadata&lt;/strong&gt; is not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EMF&lt;/strong&gt; is cheaper than PutMetricData for Lambda custom metrics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQS long polling&lt;/strong&gt; &lt;code&gt;(WaitTimeSeconds &amp;gt; 0)&lt;/code&gt; reduces empty responses and costs&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;🏗️&lt;/p&gt;

</description>
      <category>aws</category>
      <category>certification</category>
      <category>cloud</category>
      <category>developer</category>
    </item>
    <item>
      <title>Optimize Applications By Using AWS Services And Features | 🏗️ Build A Performance Optimisation Lab</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Sat, 18 Jul 2026 20:29:39 +0000</pubDate>
      <link>https://dev.to/aws-builders/optimize-applications-by-using-aws-services-and-features-build-a-performance-optimisation-lab-1j3</link>
      <guid>https://dev.to/aws-builders/optimize-applications-by-using-aws-services-and-features-build-a-performance-optimisation-lab-1j3</guid>
      <description>&lt;p&gt;&lt;strong&gt;Exam Guide:&lt;/strong&gt; Developer - Associate&lt;br&gt;
&lt;strong&gt;🏗️ Domain 4:&lt;/strong&gt;  &lt;strong&gt;&lt;em&gt;Troubleshooting And Optimization&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
📘 &lt;strong&gt;Task 3:&lt;/strong&gt; &lt;em&gt;Optimize Applications By Using AWS Services And Features&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This task is about making applications faster, cheaper, and more efficient. You need to understand Lambda concurrency and memory tuning, caching at every layer (CloudFront, API Gateway, ElastiCache), messaging optimization with SNS filter policies, and how to read metrics to find bottlenecks. Choose the right optimization for a given symptom such as high latency, throttling, slow stream processing, or runaway cost, and etc.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  📘 Concepts
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Lambda Concurrency Types
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Type&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;What It Does&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Use Case&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Unreserved&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Shared pool&lt;/em&gt; (default 1,000 per region)&lt;/td&gt;
&lt;td&gt;Pay per invocation&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Default for most functions&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reserved&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Guarantees capacity AND caps the function&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;No extra cost&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Protect downstream services, guarantee capacity&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Provisioned&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Pre-warms execution environments&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Pay even when idle&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Eliminate cold starts for latency-sensitive functions&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;em&gt;Reserved concurrency&lt;/em&gt; does double duty. It guarantees a function gets that many concurrent executions &lt;em&gt;AND&lt;/em&gt; prevents it from exceeding that number. Use it to stop a busy function from starving others, or to protect a downstream database from too many connections. &lt;em&gt;Provisioned concurrency&lt;/em&gt; is the only thing that eliminates cold starts, but you pay for it 24/7.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  The Concurrency Formula
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Concurrent executions = (invocations per second) × (average duration in seconds)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Example: 100 requests/second × 0.5 seconds = 50 concurrent executions needed.&lt;/p&gt;
&lt;h3&gt;
  
  
  Memory and CPU Relationship
&lt;/h3&gt;

&lt;p&gt;Lambda allocates CPU proportionally to memory. At 1,769 MB you get one full vCPU. More memory means more CPU, which can make a function run faster. Sometimes fast enough that the higher per-ms cost is offset by the shorter duration.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;Memory&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Relative CPU&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Typical Result&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;128 MB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Fraction&lt;/strong&gt; of a vCPU&lt;/td&gt;
&lt;td&gt;Cheapest per-ms, &lt;em&gt;slowest&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;512 MB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;~0.3&lt;/strong&gt; vCPU&lt;/td&gt;
&lt;td&gt;Often &lt;em&gt;the sweet spot&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1,769 MB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;1&lt;/strong&gt; full vCPU&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Fast&lt;/em&gt;, higher per-ms cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;10,240 MB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;~6&lt;/strong&gt; vCPUs&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Fastest&lt;/em&gt;, for CPU-bound work&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Caching Layers
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Layer&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Service&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;What It Caches&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;TTL Control&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Edge&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;CloudFront&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Static content, API responses&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Cache policies, headers&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;API Gateway cache&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Endpoint responses&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Per-stage, per-method&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Application&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;ElastiCache (Redis/Memcached)&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Query results, sessions&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Set in code&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Database&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;DAX&lt;/em&gt; (DynamoDB only)&lt;/td&gt;
&lt;td&gt;DynamoDB reads&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Item + query cache TTL&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Caching Patterns
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Pattern&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;How It Works&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cache-aside (lazy loading)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Check cache → miss → fetch from DB → store&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Read-heavy, tolerates some staleness&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Write-through&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Write to cache AND DB simultaneously&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Consistency-sensitive reads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Write-behind&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Write to cache, async write to DB&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;High write throughput&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TTL-based&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Entries expire after a set time&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Most general-purpose caching&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;em&gt;Cache-aside&lt;/em&gt; is the most common pattern. The risk is a &lt;em&gt;cache stampede&lt;/em&gt; (many simultaneous misses hitting the DB). &lt;em&gt;Write-through&lt;/em&gt; keeps the cache fresh but adds write latency.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  CloudFront Cache Keys
&lt;/h3&gt;

&lt;p&gt;The cache key determines what makes a request unique. The fewer components in the key, the higher the cache hit rate.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cache Key Component&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Effect on Hit Rate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;No headers or query strings&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Highest hit rate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Whitelist only what matters&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Balanced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Forward everything&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lowest hit rate &lt;em&gt;(every request unique)&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  SNS Subscription Filter Policies
&lt;/h3&gt;

&lt;p&gt;Filter policies let each subscriber receive only the messages it cares about, reducing downstream processing and cost.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Filter Scope&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Filters On&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;MessageAttributes&lt;/strong&gt; (default)&lt;/td&gt;
&lt;td&gt;Message attribute key/value pairs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MessageBody&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fields inside the message body JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Key Metrics for Finding Bottlenecks
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;&lt;code&gt;Metric&lt;/code&gt;&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Service&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;What It Signals&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;Duration&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Lambda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Slow function or slow downstream call&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;IteratorAge&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Lambda&lt;/strong&gt; (Kinesis/DynamoDB)&lt;/td&gt;
&lt;td&gt;Consumer falling behind the stream&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;ApproximateAgeOfOldestMessage&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;SQS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Consumer too slow, queue backing up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;Throttles&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Lambda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hitting concurrency limits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;ConsumedReadCapacityUnits&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;DynamoDB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hot partition or under-provisioning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;CacheHitRate&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;CloudFront / API Gateway&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cache effectiveness&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  🏗️ Build A Performance Optimization Lab
&lt;/h2&gt;

&lt;p&gt;Build a &lt;strong&gt;Performance Optimization Lab&lt;/strong&gt; using the AWS Console:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Lambda function tuned across memory settings to find the cost/performance sweet spot&lt;/li&gt;
&lt;li&gt;Reserved concurrency configured to protect a downstream service&lt;/li&gt;
&lt;li&gt;API Gateway caching enabled and tested for cache hits&lt;/li&gt;
&lt;li&gt;An SNS topic with subscription filter policies routing messages selectively&lt;/li&gt;
&lt;li&gt;CloudWatch metrics queries to identify bottlenecks&lt;/li&gt;
&lt;/ul&gt;


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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ntombizakhona.medium.com/list/cloud-glossary-528956a3c181" rel="noopener noreferrer"&gt;&lt;strong&gt;An AWS Account&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Part I
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Tune Lambda Memory for Cost and Performance
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Create a CPU-Bound Function&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;1&lt;/em&gt;&lt;/strong&gt; Open the &lt;strong&gt;Lambda&lt;/strong&gt; console → &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Function name:&lt;/strong&gt; &lt;code&gt;MemoryTuningDemo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; Python 3.13&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;2&lt;/em&gt;&lt;/strong&gt; Click &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;3&lt;/em&gt;&lt;/strong&gt; Paste this CPU-bound code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    A CPU-bound function that computes prime numbers.
    CPU-bound work benefits directly from more memory (= more CPU),
    making it ideal for demonstrating memory tuning.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;limit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;primes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;is_prime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;is_prime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;is_prime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;primes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;primesFound&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primes&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;durationMs&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;memoryConfigured&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;memory_limit_in_mb&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;&lt;strong&gt;&lt;em&gt;4&lt;/em&gt;&lt;/strong&gt; Click &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Run the Memory Tuning Experiment&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;5&lt;/em&gt;&lt;/strong&gt; Go to &lt;strong&gt;Configuration&lt;/strong&gt; → &lt;strong&gt;General configuration&lt;/strong&gt; → &lt;strong&gt;Edit&lt;/strong&gt; → set &lt;strong&gt;Timeout&lt;/strong&gt; to &lt;strong&gt;&lt;code&gt;1 min 0 sec&lt;/code&gt;&lt;/strong&gt; → &lt;strong&gt;Save&lt;/strong&gt; (so the function doesn't time out at low memory)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;6&lt;/em&gt;&lt;/strong&gt; Create a test event named &lt;strong&gt;PrimeTest&lt;/strong&gt; with &lt;code&gt;{}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;7&lt;/em&gt;&lt;/strong&gt; Now run the experiment. For each memory setting below, go to &lt;strong&gt;Configuration&lt;/strong&gt; → &lt;strong&gt;General configuration&lt;/strong&gt; → &lt;strong&gt;Edit&lt;/strong&gt; → change &lt;strong&gt;Memory&lt;/strong&gt; → &lt;strong&gt;Save&lt;/strong&gt;, then run the test and record the results.&lt;/p&gt;

&lt;p&gt;You'll see duration drop sharply as memory increases, then flatten out. The point where it flattens is your &lt;em&gt;sweet spot.&lt;/em&gt; More memory past that just costs more without speeding things up.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;For CPU-bound work, increasing memory often &lt;em&gt;reduces&lt;/em&gt; total cost because the function finishes much faster. The AWS Lambda Power Tuning tool (a Step Functions state machine) automates this experiment and produces a cost/performance graph. Memory and CPU scale together.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part II
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Use Reserved Concurrency to Protect Downstream Services
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;The Scenario&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Imagine &lt;code&gt;MemoryTuningDemo&lt;/code&gt; calls a downstream database that can only handle 10 simultaneous connections. If Lambda scales to 100 concurrent executions, it overwhelms the database. Reserved concurrency caps this.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Configure Reserved Concurrency&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;1&lt;/em&gt;&lt;/strong&gt; On &lt;code&gt;MemoryTuningDemo&lt;/code&gt;, go to &lt;strong&gt;Configuration&lt;/strong&gt; → &lt;strong&gt;Concurrency and recursion detection&lt;/strong&gt; → &lt;strong&gt;Concurrency&lt;/strong&gt; → &lt;strong&gt;Edit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/em&gt; Select &lt;strong&gt;Reserve concurrency&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;3&lt;/em&gt;&lt;/strong&gt; Set &lt;strong&gt;Reserved concurrency&lt;/strong&gt; to &lt;code&gt;10&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;4&lt;/em&gt;&lt;/strong&gt; Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Now this function can never have more than 10 concurrent executions. Any invocations beyond that are throttled (for async sources) or receive a 429 (for synchronous sources).&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Observe Throttling
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;5&lt;/em&gt;&lt;/strong&gt; Open &lt;strong&gt;CloudShell&lt;/strong&gt; and run a burst of async invocations&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="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;seq &lt;/span&gt;1 50&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;aws lambda invoke &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--function-name&lt;/span&gt; MemoryTuningDemo &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--invocation-type&lt;/span&gt; Event &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--payload&lt;/span&gt; &lt;span class="s1"&gt;'{"limit": 1000000}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    /dev/null &amp;amp;
&lt;span class="k"&gt;done
&lt;/span&gt;&lt;span class="nb"&gt;wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;6&lt;/em&gt;&lt;/strong&gt; Open the &lt;strong&gt;CloudWatch&lt;/strong&gt; console → &lt;strong&gt;Metrics&lt;/strong&gt; → &lt;strong&gt;Lambda&lt;/strong&gt; → &lt;strong&gt;By Function Name&lt;/strong&gt; → &lt;code&gt;MemoryTuningDemo&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;7&lt;/em&gt;&lt;/strong&gt; Add the &lt;strong&gt;Throttles&lt;/strong&gt; and &lt;strong&gt;ConcurrentExecutions&lt;/strong&gt; metrics&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;8&lt;/em&gt;&lt;/strong&gt; You'll see &lt;code&gt;ConcurrentExecutions&lt;/code&gt; cap at 10 and &lt;code&gt;Throttles&lt;/code&gt; climb as excess invocations are rejected&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Reserved concurrency is free and acts as both a guarantee and a ceiling. It's the answer when a question situation describes a Lambda function overwhelming a downstream resource (RDS connections, a third-party API rate limit). For throttled async invocations, the events are retried. For synchronous, the caller gets a 429.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part III
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Enable and Test API Gateway Caching
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Create an API&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;1&lt;/em&gt;&lt;/strong&gt; Open the &lt;strong&gt;API Gateway&lt;/strong&gt; console → &lt;strong&gt;Create API&lt;/strong&gt; → &lt;strong&gt;REST API&lt;/strong&gt; → &lt;strong&gt;Build&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;2&lt;/em&gt;&lt;/strong&gt; &lt;strong&gt;API name:&lt;/strong&gt; &lt;code&gt;CachingDemoAPI&lt;/code&gt; → &lt;strong&gt;Create API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;3.&lt;/em&gt;&lt;/strong&gt; &lt;strong&gt;Create resource&lt;/strong&gt; → Resource name: &lt;code&gt;products&lt;/code&gt; → &lt;strong&gt;Create resource&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;4&lt;/em&gt;&lt;/strong&gt; Select &lt;code&gt;/products&lt;/code&gt; → &lt;strong&gt;Create method&lt;/strong&gt; → &lt;strong&gt;GET&lt;/strong&gt; → &lt;strong&gt;Lambda Function&lt;/strong&gt; (proxy) → select &lt;code&gt;MemoryTuningDemo&lt;/code&gt; → &lt;strong&gt;Create method&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Deploy to a Stage&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/em&gt; &lt;strong&gt;Deploy API&lt;/strong&gt; → &lt;strong&gt;New stage&lt;/strong&gt; → Stage name: &lt;code&gt;prod&lt;/code&gt; → &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Enable Caching on the Stage&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;6&lt;/em&gt;&lt;/strong&gt; In the left sidebar, click &lt;strong&gt;Stages&lt;/strong&gt; → select &lt;code&gt;prod&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;7&lt;/em&gt;&lt;/strong&gt; Under &lt;strong&gt;Stage details&lt;/strong&gt;, click &lt;strong&gt;Edit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;8&lt;/em&gt;&lt;/strong&gt; Enable &lt;strong&gt;Cache settings&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Provision API cache:&lt;/strong&gt; Enabled&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache capacity:&lt;/strong&gt; 0.5 GB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;9&lt;/em&gt;&lt;/strong&gt; Click &lt;strong&gt;Save changes&lt;/strong&gt; (the cache takes a few minutes to provision)&lt;/p&gt;

&lt;h4&gt;
  
  
  Enable Caching on the Method
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;10&lt;/em&gt;&lt;/strong&gt; With the &lt;code&gt;prod&lt;/code&gt; stage selected, expand the resource tree and click the &lt;strong&gt;GET&lt;/strong&gt; method under &lt;code&gt;/products&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;11&lt;/em&gt;&lt;/strong&gt; Under &lt;strong&gt;Method settings&lt;/strong&gt;, edit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enable method cache:&lt;/strong&gt; checked&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache TTL:&lt;/strong&gt; 300 seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;12&lt;/em&gt;&lt;/strong&gt; Save&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Test Cache Behaviour&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;13&lt;/em&gt;&lt;/strong&gt; Copy the stage &lt;strong&gt;Invoke URL&lt;/strong&gt; and call the endpoint a few times:&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="c"&gt;# First call — cache miss (slower, hits Lambda)&lt;/span&gt;
curl &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Time: %{time_total}s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; https://YOUR_API/prod/products

&lt;span class="c"&gt;# Subsequent calls — cache hit (faster, served from cache)&lt;/span&gt;
curl &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Time: %{time_total}s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; https://YOUR_API/prod/products
curl &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Time: %{time_total}s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; https://YOUR_API/prod/products
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;14&lt;/em&gt;&lt;/strong&gt; The first call is slower (cache miss → Lambda runs). Subsequent calls within the TTL are much faster (served from cache, Lambda not invoked).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;15&lt;/em&gt;&lt;/strong&gt; Check &lt;strong&gt;CloudWatch&lt;/strong&gt; → API Gateway metrics → &lt;code&gt;CacheHitCount&lt;/code&gt; and &lt;code&gt;CacheMissCount&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;API Gateway caching is configured per-stage and per-method. The cache key is based on the request parameters you specify. Caching reduces backend load and latency but can serve stale data within the TTL. You can force a cache bypass with the &lt;code&gt;Cache-Control: max-age=0&lt;/code&gt; header (if you enable that option).&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part IV
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Optimize Messaging with SNS Filter Policies
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Create an SNS Topic
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;1&lt;/em&gt;&lt;/strong&gt; Open the &lt;strong&gt;SNS&lt;/strong&gt; console → &lt;strong&gt;Topics&lt;/strong&gt; → &lt;strong&gt;Create topic&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;2&lt;/em&gt;&lt;/strong&gt; &lt;strong&gt;Type:&lt;/strong&gt; Standard&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;3&lt;/em&gt;&lt;/strong&gt; &lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;order-events&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;4&lt;/em&gt;&lt;/strong&gt; Click &lt;strong&gt;Create topic&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Create Two SQS Queues&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/em&gt; Open the &lt;strong&gt;SQS&lt;/strong&gt; console → &lt;strong&gt;Create queue&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;premium-orders&lt;/code&gt; → &lt;strong&gt;Create queue&lt;/strong&gt;
&lt;em&gt;&lt;strong&gt;6&lt;/strong&gt;&lt;/em&gt; Create another:&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;all-orders&lt;/code&gt; → &lt;strong&gt;Create queue&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Subscribe the Queues with Filter Policies&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;7&lt;/em&gt;&lt;/strong&gt; Back in &lt;strong&gt;SNS&lt;/strong&gt; → &lt;code&gt;order-events&lt;/code&gt; topic → &lt;strong&gt;Create subscription&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;8&lt;/em&gt;&lt;/strong&gt; For the premium queue:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Protocol:&lt;/strong&gt; Amazon SQS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Endpoint:&lt;/strong&gt; select the &lt;code&gt;premium-orders&lt;/code&gt; queue ARN&lt;/li&gt;
&lt;li&gt;Expand &lt;strong&gt;Subscription filter policy&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filter policy scope:&lt;/strong&gt; Message attributes&lt;/li&gt;
&lt;li&gt;Policy:
&lt;/li&gt;
&lt;/ul&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;"orderType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"premium"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"orderTotal"&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="nl"&gt;"numeric"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="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;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Create subscription&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;9&lt;/em&gt;&lt;/strong&gt; Create another subscription for &lt;code&gt;all-orders&lt;/code&gt; with &lt;strong&gt;no filter policy&lt;/strong&gt; (it receives everything)&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Test the Filtering&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;10&lt;/em&gt;&lt;/strong&gt; On the &lt;code&gt;order-events&lt;/code&gt; topic, click &lt;strong&gt;Publish message&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;11&lt;/em&gt;&lt;/strong&gt; Publish a premium order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Message body:&lt;/strong&gt; &lt;code&gt;{"orderId": "ORD-001", "amount": 250}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Message attributes&lt;/strong&gt;, add:

&lt;ul&gt;
&lt;li&gt;Name: &lt;code&gt;orderType&lt;/code&gt;, Type: String, Value: &lt;code&gt;premium&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Name: &lt;code&gt;orderTotal&lt;/code&gt;, Type: Number, Value: &lt;code&gt;250&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Publish message&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;12&lt;/em&gt;&lt;/strong&gt; Publish a standard order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Message body:&lt;/strong&gt; &lt;code&gt;{"orderId": "ORD-002", "amount": 30}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Message attributes:

&lt;ul&gt;
&lt;li&gt;Name: &lt;code&gt;orderType&lt;/code&gt;, Type: String, Value: &lt;code&gt;standard&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Name: &lt;code&gt;orderTotal&lt;/code&gt;, Type: Number, Value: &lt;code&gt;30&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Publish message&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Verify the Results&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;13&lt;/strong&gt;&lt;/em&gt; Open &lt;strong&gt;SQS&lt;/strong&gt; → &lt;code&gt;all-orders&lt;/code&gt; → &lt;strong&gt;Send and receive messages&lt;/strong&gt; → &lt;strong&gt;Poll for messages&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Both orders appear (no filter)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;14&lt;/em&gt;&lt;/strong&gt; Open &lt;strong&gt;SQS&lt;/strong&gt; → &lt;code&gt;premium-orders&lt;/code&gt; → &lt;strong&gt;Poll for messages&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only the premium order ($250) appears. The standard order was filtered out&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💡 &lt;strong&gt;Filter policies are evaluated by SNS before delivery, so filtered-out messages never reach the subscriber thus saving SQS, Lambda, and processing costs. Without filter policies, every subscriber receives every message and must filter in code (wasteful). The default scope filters on message attributes; set the scope to &lt;code&gt;MessageBody&lt;/code&gt; to filter on body content instead.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Part V
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Identify Bottlenecks with CloudWatch Metrics
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Common Bottleneck Patterns&lt;/strong&gt;
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Symptom&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Likely Bottleneck&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;High Lambda Duration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Slow downstream call&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Cache the result, check X-Ray traces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;High IteratorAge&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Lambda can't keep up with the stream&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Increase parallelization factor, add shards&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SQS &lt;code&gt;ApproximateAgeOfOldestMessage&lt;/code&gt; growing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Consumer too slow&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Add consumers, increase batch size&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DynamoDB throttling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Hot partition or low capacity&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Redesign keys, switch to on-demand&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Gateway 429&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Throttle limit reached&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Raise limits, add caching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;High memory usage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Memory leak or large payloads&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Profile code, stream large data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Query Lambda Performance with Logs Insights&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;1&lt;/em&gt;&lt;/strong&gt; Open &lt;strong&gt;CloudWatch&lt;/strong&gt; → &lt;strong&gt;Logs&lt;/strong&gt; → &lt;strong&gt;Logs Insights&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;2&lt;/em&gt;&lt;/strong&gt; Select &lt;code&gt;/aws/lambda/MemoryTuningDemo&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;3&lt;/em&gt;&lt;/strong&gt; Run this query to spot memory over-provisioning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"REPORT"&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt; &lt;span class="k"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;maxMemoryUsed&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;avgMemMB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;maxMemoryUsed&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;peakMemMB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;memorySize&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;configuredMB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;avgDurationMs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;pct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p95Ms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;pct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p99Ms&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;4&lt;/em&gt;&lt;/strong&gt; If &lt;code&gt;peakMemMB&lt;/code&gt; is far below &lt;code&gt;configuredMB&lt;/code&gt;, you're over-provisioned on memory. If &lt;code&gt;p99Ms&lt;/code&gt; is much higher than &lt;code&gt;avgDurationMs&lt;/code&gt;, you have tail latency worth investigating with X-Ray.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Using X-Ray to Pinpoint the Slow Call&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;A trace makes the bottleneck obvious.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API Gateway: 5ms
└── Lambda: 2500ms
    ├── DynamoDB GetItem: 15ms
    ├── External API call: 2200ms   ← bottleneck
    └── SQS SendMessage: 8ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The external API is the problem. Fixes: cache the response, make the call asynchronous (send to SQS), or add a circuit breaker to fail fast.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Match the symptom to the metric.&lt;/strong&gt; &lt;em&gt;"Stream processing is delayed"&lt;/em&gt; → &lt;code&gt;IteratorAge&lt;/code&gt;. &lt;em&gt;"Queue is backing up"&lt;/em&gt; → &lt;code&gt;ApproximateAgeOfOldestMessage&lt;/code&gt;. &lt;em&gt;"Which downstream call is slow?"&lt;/em&gt; → &lt;code&gt;X-Ray trace&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏗️ What You Built | 📘 Exam Concepts Recap
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;What You Built&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Exam Concept&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ran a memory-tuning experiment across settings&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Memory = CPU.&lt;/em&gt;  finding the cost/performance sweet spot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Set reserved concurrency to 10 and observed throttles&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Reserved concurrency as guarantee + ceiling&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Burst-invoked to trigger throttling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;ConcurrentExecutions and Throttles metrics&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Enabled API Gateway caching per stage and method&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Reducing backend load and latency with caching&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Measured cache hits vs misses with curl timing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Cache TTL behavior and CacheHitCount metric&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Created SNS subscriptions with filter policies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Selective message delivery to reduce cost&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Published messages with matching/non-matching attributes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;MessageAttributes filter scope&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Queried &lt;code&gt;@maxMemoryUsed&lt;/code&gt; and percentiles in Logs Insights&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Detecting over-provisioning and tail latency&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mapped symptoms to metrics (IteratorAge, etc.)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Choosing the right diagnostic for a bottleneck&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ⚠️ Clean Up Protocol
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway&lt;/strong&gt; → Delete &lt;code&gt;CachingDemoAPI&lt;/code&gt; (this also removes the provisioned cache)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda&lt;/strong&gt; → Delete &lt;code&gt;MemoryTuningDemo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SNS&lt;/strong&gt; → Delete the &lt;code&gt;order-events&lt;/code&gt; topic (removes subscriptions)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQS&lt;/strong&gt; → Delete &lt;code&gt;premium-orders&lt;/code&gt; and &lt;code&gt;all-orders&lt;/code&gt; queues&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM&lt;/strong&gt; → Delete the Lambda execution role&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch&lt;/strong&gt; → Delete the log group for the function&lt;/li&gt;
&lt;/ol&gt;




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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reserved concurrency&lt;/strong&gt; = guarantee + throttle (free). &lt;strong&gt;Provisioned concurrency&lt;/strong&gt; = no cold starts (paid even when idle).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory = CPU.&lt;/strong&gt; For CPU-bound work, more memory can lower total cost by finishing faster. Lambda Power Tuning finds the sweet spot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache-aside&lt;/strong&gt; is the most common caching pattern. Check cache → miss → fetch → store.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudFront cache keys:&lt;/strong&gt; fewer components = higher hit rate. Whitelist only what affects the response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway caching&lt;/strong&gt; is per-stage and per-method, with a configurable TTL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SNS filter policies&lt;/strong&gt; stop unwanted messages before delivery saving downstream cost. Default scope is message attributes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IteratorAge&lt;/strong&gt; = stream consumer falling behind. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ApproximateAgeOfOldestMessage&lt;/strong&gt; = SQS queue backing up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;X-Ray traces&lt;/strong&gt; are the fastest way to find which downstream call is the bottleneck.&lt;/li&gt;
&lt;li&gt;Match the &lt;strong&gt;symptom to the metric&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-concurrency.html" rel="noopener noreferrer"&gt;Understanding Lambda function scaling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/provisioned-concurrency.html" rel="noopener noreferrer"&gt;Configuring provisioned concurrency for a function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-caching.html" rel="noopener noreferrer"&gt;Cache settings for REST APIs in API Gateway&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/sns/latest/dg/sns-message-filtering.html" rel="noopener noreferrer"&gt;Amazon SNS message filtering&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonElastiCache/latest/dg/Strategies.html" rel="noopener noreferrer"&gt;Caching strategies for Memcached&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/alexcasalboni/aws-lambda-power-tuning" rel="noopener noreferrer"&gt;AWS Lambda Power Tuning&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;🏗️&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>certification</category>
      <category>developer</category>
    </item>
    <item>
      <title>Instrument Code For Observability | 🏗️ Build An Observable Order Service</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Tue, 14 Jul 2026 20:20:30 +0000</pubDate>
      <link>https://dev.to/aws-builders/instrument-code-for-observability-build-an-observable-order-service-1amb</link>
      <guid>https://dev.to/aws-builders/instrument-code-for-observability-build-an-observable-order-service-1amb</guid>
      <description>&lt;p&gt;&lt;strong&gt;Exam Guide:&lt;/strong&gt; Developer - Associate&lt;br&gt;
&lt;strong&gt;🏗️ Domain 4:&lt;/strong&gt;  &lt;strong&gt;&lt;em&gt;Troubleshooting And Optimization&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
📘 &lt;strong&gt;Task 2:&lt;/strong&gt; &lt;em&gt;Instrument Code For Observability&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Observability goes beyond &lt;em&gt;monitoring&lt;/em&gt;: it's about understanding the internal state of your system from its external outputs. This task tests your ability to instrument applications with AWS X-Ray for distributed tracing, implement structured logging with correlation IDs, set up CloudWatch alarms and notifications, and build health check endpoints. X-Ray concepts (traces, segments, subsegments, annotations vs metadata), structured logging best practices, and alarm configuration.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  📘Concepts
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Logging vs Monitoring vs Observability
&lt;/h3&gt;

&lt;p&gt;These three concepts build on each other. &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Aspect&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Logging&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Monitoring&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Observability&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What It Is&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Recording discrete events&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Tracking predefined metrics over time&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Understanding system behaviour from outputs&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Question It Answers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;What happened?&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Is the system healthy?&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Why is the system behaving this way?&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Text entries&lt;/em&gt; (structured or unstructured)&lt;/td&gt;
&lt;td&gt;Numeric time-series data&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Logs + metrics + traces combined&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Proactive vs Reactive&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Reactive.&lt;/em&gt; Search after an issue&lt;/td&gt;
&lt;td&gt;Proactive. &lt;em&gt;Alerts before users notice&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Both.&lt;/strong&gt; Explore unknown unknowns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS Service(s)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;CloudWatch Logs&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;CloudWatch Metrics, Alarms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;X-Ray, CloudWatch Logs Insights, ServiceLens&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;Example&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;&lt;code&gt;ERROR: DynamoDB timeout on table Orders&lt;/code&gt;&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Lambda Duration &amp;gt; 5s for 3 consecutive periods&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Trace shows DynamoDB call took 4.8s due to throttling&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;The Three Pillars Of Observability&lt;/strong&gt;
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Pillar&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;What It Provides&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;AWS Service&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Logs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Detailed event records for debugging&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;CloudWatch Logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Metrics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Numeric measurements for alerting and trending&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;CloudWatch Metrics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Traces&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Request flow across distributed services&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;AWS X-Ray&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Monitoring&lt;/strong&gt; tells you something is wrong. &lt;strong&gt;Observability&lt;/strong&gt; tells you why. &lt;strong&gt;Metrics&lt;/strong&gt; show a problem &lt;em&gt;(high latency)&lt;/em&gt; but you need &lt;strong&gt;traces&lt;/strong&gt; to find the root cause &lt;em&gt;(a specific downstream service is slow)&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  AWS X-Ray Concepts
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;em&gt;X-Ray&lt;/em&gt; is the answer to "&lt;em&gt;How do you find which service is causing latency?&lt;/em&gt;"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;X-Ray provides distributed tracing. It tracks requests as they flow through your application across multiple services.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Concept&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;What It Is&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trace&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;The complete journey of a single request through all services&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;API Gateway → Lambda → DynamoDB → SNS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Segment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;A unit of work done by a single service&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;The Lambda function's execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Subsegment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;A subdivision of a segment for more detail&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;A specific DynamoDB call within the Lambda function&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trace ID&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Unique identifier for the entire trace&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1-5f84c7a1-abcdef1234567890&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sampling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Controls what percentage of requests are traced&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Default: first request each second + 5% of additional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Service Map&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Visual representation of your architecture and request flow&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Shows services as nodes with latency and error rates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trace Map&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Visual representation of a single trace&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Shows the path of one request through services&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  X-Ray Annotations vs Metadata
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Annotations&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Metadata&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What It Stores&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Key-value pairs&lt;/em&gt; (string, number, boolean)&lt;/td&gt;
&lt;td&gt;Any JSON-serializable object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Indexed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Yes. Searchable in the X-Ray console&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;No. Not searchable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use For&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Filtering and grouping traces&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Storing detailed debug data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Max Size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;50 annotations per trace&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;No practical limit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;&lt;code&gt;userId: "CUST-001"&lt;/code&gt;, &lt;code&gt;orderType: "premium"&lt;/code&gt;&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Full request/response bodies, stack traces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Search Syntax&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;&lt;code&gt;annotation.userId = "CUST-001"&lt;/code&gt;&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Cannot search by metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use &lt;strong&gt;annotations&lt;/strong&gt; for values you want to search and filter by user IDs, order types, environment names. &lt;br&gt;
Use &lt;strong&gt;metadata&lt;/strong&gt; for detailed debugging data you only need when examining a specific trace. &lt;br&gt;
Annotations are indexed and searchable, metadata is not.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡&lt;em&gt;"How do you find all traces for a specific user?"&lt;/em&gt; The answer is annotations.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  X-Ray Sampling Rules
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Setting&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;Default&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reservoir&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;1&lt;/code&gt; per second&lt;/td&gt;
&lt;td&gt;Guaranteed minimum traces per second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;5%&lt;/code&gt; (0.05)&lt;/td&gt;
&lt;td&gt;Percentage of additional requests to trace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Service Name&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;*&lt;/code&gt; (all)&lt;/td&gt;
&lt;td&gt;Which services the rule applies to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HTTP Method&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;*&lt;/code&gt; (all)&lt;/td&gt;
&lt;td&gt;Filter by GET, POST, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;URL Path&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;*&lt;/code&gt; (all)&lt;/td&gt;
&lt;td&gt;Filter by specific paths&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Priority&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;10000&lt;/code&gt; (default)&lt;/td&gt;
&lt;td&gt;Lower number = higher priority&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Custom Sampling Rule Example:&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Trace &lt;strong&gt;100%&lt;/strong&gt; of requests to &lt;code&gt;/api/orders&lt;/code&gt; &lt;em&gt;(high-value endpoint)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Trace &lt;strong&gt;1%&lt;/strong&gt; of requests to &lt;code&gt;/api/health&lt;/code&gt; &lt;em&gt;(noisy, low-value)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;The default sampling rule traces the first request each second plus 5% of additional requests. This keeps costs manageable for high-traffic applications. You can create custom rules to trace more (or fewer) requests for specific paths or services. Sampling rules are evaluated in priority order. Lower priority number wins.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  X-Ray Integration with AWS Services
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Service&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;How to Enable&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;What Gets Traced&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lambda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Toggle "Active tracing" in function configuration&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Function execution, SDK calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Gateway&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Enable X-Ray tracing on the stage&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;API request processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ECS/Fargate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Run X-Ray daemon as sidecar container&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Application SDK calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EC2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Install and run X-Ray daemon&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Application SDK calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Elastic Beanstalk&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Enable in &lt;code&gt;.ebextensions&lt;/code&gt; or console&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Application SDK calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SNS/SQS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Automatic when upstream service is traced&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Message propagation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;For Lambda, enabling &lt;em&gt;"Active tracing"&lt;/em&gt; is all you need for basic traces. To add custom subsegments and annotations, you need the X-Ray SDK in your code. The X-Ray daemon runs automatically in Lambda yiu don't need to install it. For EC2 and ECS, you must install and run the daemon yourself.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Structured Logging Best Practices
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Practice&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Why It Matters&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use JSON Format&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Machine-parseable, works with Logs Insights&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"level":"ERROR","message":"timeout"}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Include Correlation ID&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Track requests across services&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"correlationId":"abc-123","service":"orders"}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Include Request Context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Debug without searching multiple logs&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"requestId":"req-1","userId":"user-5"}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use Log Levels&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Filter by severity&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;DEBUG, INFO, WARN, ERROR, FATAL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avoid Sensitive Data&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Security and compliance&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Never log passwords, tokens, PII&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Include timestamps&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Accurate event ordering&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;ISO 8601 format: &lt;code&gt;2024-01-15T10:30:00Z&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Correlation IDs for Distributed Tracing
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Correlation ID&lt;/strong&gt; is a unique identifier that follows a request across all services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client → API Gateway → Lambda A → SQS → Lambda B → DynamoDB
         correlationId: "abc-123" flows through every service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Where to Set It&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;How&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Gateway&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pass &lt;code&gt;X-Correlation-Id&lt;/code&gt; header or generate in mapping template&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lambda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Read from event headers or generate if missing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SQS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Include in message attributes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SNS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Include in message attributes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DynamoDB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Not applicable (use in logs only)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Correlation IDs complement X-Ray traces. X-Ray gives you the visual trace map. Correlation IDs let you search logs across services for the same request. Use both together: add the correlation ID as an X-Ray annotation so you can find the trace from the log and vice versa.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  CloudWatch Alarms
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Alarm Component&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;What It Does&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Options&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Metric&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;What you're monitoring&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Any CloudWatch metric (built-in or custom)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Statistic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;How to aggregate data points&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Average, Sum, Min, Max, p99, SampleCount&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Period&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Time window for each data point&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;10s, 30s, 60s, 300s, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Evaluation periods&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;How many periods to evaluate&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;1-100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Datapoints to alarm&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;How many periods must breach threshold&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;1 to evaluation periods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Threshold&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;The value that triggers the alarm&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Any number&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Comparison&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;How to compare metric to threshold&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&amp;gt;, &amp;gt;=, &amp;lt;, &amp;lt;=&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Actions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;What happens when alarm fires&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;SNS notification, Auto Scaling, EC2 action, Lambda&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Missing data&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;How to treat gaps in data&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;missing, notBreaching, breaching, ignore&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Alarm States:&lt;/strong&gt;
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;State&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OK&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Metric is within the threshold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ALARM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Metric has breached the threshold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;INSUFFICIENT_DATA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Not enough data to determine state&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Composite Alarms
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Standard Alarm&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Composite Alarm&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Based On&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;A single metric&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Multiple alarms combined with AND/OR/NOT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use Case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Monitor one metric&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Reduce alarm noise, complex conditions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Error rate &amp;gt; 5%&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Error rate &amp;gt; 5% AND latency &amp;gt; 2s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Actions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;SNS, Auto Scaling, EC2, Lambda&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;SNS, Lambda&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Composite alarms reduce alert fatigue. Instead of getting separate alerts for high errors and high latency, create a composite alarm that only fires when both conditions are true. Use &lt;code&gt;AND&lt;/code&gt; for &lt;em&gt;"all conditions must be true"&lt;/em&gt; and &lt;code&gt;OR&lt;/code&gt; for &lt;em&gt;"any condition triggers."&lt;/em&gt; Composite alarms can suppress actions on child alarms to prevent duplicate notifications.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Health Check Patterns
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Pattern&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;What It Checks&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Response&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shallow Health Check&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Is the service running?&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;200 OK (no dependency checks)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deep Health Check&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Are all dependencies healthy?&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;200 OK with dependency status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Liveness Probe&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;I_s the process alive?_&lt;/td&gt;
&lt;td&gt;200 OK (minimal check)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Readiness Probe&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Can the service handle requests?&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;200 OK only when fully initialized&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Deep Health Check Response Example:&lt;/strong&gt;
&lt;/h4&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;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"healthy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-20T10:30:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&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;"dynamodb"&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="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"healthy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"latency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12ms"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sqs"&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="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"healthy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"latency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8ms"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"external-api"&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="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"degraded"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"latency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2500ms"&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;blockquote&gt;
&lt;p&gt;💡&lt;strong&gt;Health checks should have their own endpoint (e.g., &lt;code&gt;/health&lt;/code&gt;) that doesn't require authentication. Shallow checks are fast and cheap: use them for load balancer health checks. Deep checks verify dependencies: use them for monitoring dashboards. Don't make deep health checks too expensive. Cache dependency status for a few seconds.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🏗️ Build An Observable Order Service
&lt;/h2&gt;

&lt;p&gt;Build an &lt;strong&gt;Observable Order Service&lt;/strong&gt; from scratch using the AWS Console:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Lambda function with X-Ray active tracing enabled&lt;/li&gt;
&lt;li&gt;Custom X-Ray subsegments and annotations in the code&lt;/li&gt;
&lt;li&gt;Structured JSON logging with correlation IDs&lt;/li&gt;
&lt;li&gt;CloudWatch alarms with SNS email notifications&lt;/li&gt;
&lt;li&gt;A health check endpoint that verifies dependencies&lt;/li&gt;
&lt;li&gt;Traces visible in the X-Ray console service map&lt;/li&gt;
&lt;/ul&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ntombizakhona.medium.com/amazon-web-services-a8e57a9c6084" rel="noopener noreferrer"&gt;&lt;strong&gt;An AWS Account&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ntombizakhona.medium.com/python-in-the-cloud-1462f61e3293" rel="noopener noreferrer"&gt;&lt;strong&gt;Python&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Part I
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create a Lambda Function with X-Ray Tracing
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Create the Function
&lt;/h4&gt;

&lt;p&gt;Open the &lt;strong&gt;Lambda&lt;/strong&gt; console → &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Function name:&lt;/strong&gt; &lt;code&gt;ObservableOrderService&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; Python 3.13&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;→ Click &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 02:&lt;/strong&gt; Enable X-Ray Active Tracing
&lt;/h4&gt;

&lt;p&gt;Go to &lt;strong&gt;Configuration&lt;/strong&gt; → &lt;strong&gt;Monitoring and operations tools&lt;/strong&gt; → &lt;strong&gt;Edit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Under &lt;strong&gt;CloudWatch Application Signals and AWS X-Ray&lt;/strong&gt; → &lt;strong&gt;✔ Lambda services traces&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 03:&lt;/strong&gt; Add the X-Ray SDK Layer
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;The instrumented code below uses &lt;code&gt;aws_xray_sdk&lt;/code&gt; for custom subsegments and annotations. That package is **not&lt;/strong&gt; in the Lambda runtime (and the AWSSDKPandas layer doesn't include it), so you'll get &lt;code&gt;No module named 'aws_xray_sdk'&lt;/code&gt; unless you add it. Build a layer in &lt;strong&gt;CloudShell&lt;/strong&gt; (no local setup needed):**&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; xray-layer/python
pip &lt;span class="nb"&gt;install &lt;/span&gt;aws-xray-sdk &lt;span class="nt"&gt;-t&lt;/span&gt; xray-layer/python/
&lt;span class="nb"&gt;cd &lt;/span&gt;xray-layer &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; zip &lt;span class="nt"&gt;-r&lt;/span&gt; ../xray-sdk-layer.zip python/ &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ Download it (&lt;strong&gt;Actions → Download file → &lt;code&gt;xray-sdk-layer.zip&lt;/code&gt;&lt;/strong&gt;), then in the Lambda console:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;→ &lt;strong&gt;Layers&lt;/strong&gt; → &lt;strong&gt;Edit&lt;/strong&gt; → &lt;strong&gt;Add layer&lt;/strong&gt; → &lt;strong&gt;Create a new layer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ &lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;aws-xray-sdk&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ &lt;strong&gt;Source code:&lt;/strong&gt; &lt;code&gt;Create from a .zip file&lt;/code&gt; → &lt;strong&gt;Choose file&lt;/strong&gt; → Upload the zip → &lt;strong&gt;Compatible runtimes &lt;em&gt;optional&lt;/em&gt;:&lt;/strong&gt; &lt;code&gt;Python 3.13&lt;/code&gt; → &lt;strong&gt;Create&lt;/strong&gt; → &lt;strong&gt;Copy &lt;code&gt;arn&lt;/code&gt;&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;→ Open &lt;code&gt;ObservableOrderService&lt;/code&gt; → &lt;strong&gt;Code&lt;/strong&gt; → &lt;strong&gt;Layers&lt;/strong&gt; → &lt;strong&gt;Edit&lt;/strong&gt; → &lt;strong&gt;Add a layer&lt;/strong&gt; → &lt;strong&gt;Specify an ARN&lt;/strong&gt; → &lt;strong&gt;Paste &lt;code&gt;arn&lt;/code&gt;&lt;/strong&gt; → &lt;strong&gt;Verify&lt;/strong&gt; → &lt;strong&gt;Add&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Prefer no layer?&lt;/strong&gt; You can skip the SDK entirely and rely on &lt;strong&gt;active tracing&lt;/strong&gt; alone It auto-traces the invocation and any boto3/HTTP calls without importing anything. You'd just lose in-code custom subsegments/annotations. If you go that route, remove the &lt;code&gt;aws_xray_sdk&lt;/code&gt; imports and &lt;code&gt;xray_recorder&lt;/code&gt; calls from the code below.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;X-Ray permissions:&lt;/strong&gt; if traces don't appear, attach &lt;strong&gt;&lt;code&gt;AWSXRayDaemonWriteAccess&lt;/code&gt;&lt;/strong&gt; to the function's execution role (Configuration → Permissions → role → Add permissions). Active tracing needs &lt;code&gt;xray:PutTraceSegments&lt;/code&gt; and &lt;code&gt;xray:PutTelemetryRecords&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Alternatively, you can use the &lt;code&gt;aws_xray_sdk&lt;/code&gt; by packaging it with your function code. For this tutorial, we'll use the built-in boto3 tracing capabilities.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 04:&lt;/strong&gt; Add Instrumented Code
&lt;/h4&gt;

&lt;p&gt;Replace the function code with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_xray_sdk.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;xray_recorder&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_xray_sdk.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;patch_all&lt;/span&gt;

&lt;span class="nf"&gt;patch_all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_correlation_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;X-Correlation-Id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;x-correlation-id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gen-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;structured_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;correlation_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%Y-%m-%dT%H:%M:%SZ&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gmtime&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;level&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;correlationId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;correlation_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ObservableOrderService&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;
    &lt;span class="p"&gt;}))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;correlation_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_correlation_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/process&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c1"&gt;# Facade-segment fix: annotate our OWN subsegment, not the Lambda root.
&lt;/span&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;xray_recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;in_subsegment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;request-handler&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;subsegment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;subsegment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_annotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;correlationId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;correlation_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;subsegment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_annotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;subsegment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;request&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nf"&gt;structured_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INFO&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Request received&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;correlation_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                       &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requestId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws_request_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/health&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;health&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;health_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;correlation_id&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;process_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;correlation_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;health_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;correlation_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;xray_recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;in_subsegment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;HealthCheck&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;subsegment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;subsegment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_annotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;checkType&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deep&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sts&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get_caller_identity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;latency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sts&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;healthy&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;latency&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;latency&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;ms&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sts&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unhealthy&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
        &lt;span class="n"&gt;overall&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;healthy&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;healthy&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;degraded&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="nf"&gt;structured_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INFO&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Health check: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;overall&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;correlation_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dependencies&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;overall&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;healthy&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;503&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;overall&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;correlationId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;correlation_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dependencies&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;correlation_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;xray_recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;in_subsegment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ValidateInput&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;subsegment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;orderId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ORD-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;customerId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;subsegment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_annotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;orderId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;subsegment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_annotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;validationResult&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;failed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;structured_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WARN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Missing customerId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;correlation_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;order_id&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;customerId is required&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;correlationId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;correlation_id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;subsegment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_annotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;validationResult&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;passed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;xray_recorder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;in_subsegment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ProcessBusinessLogic&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;subsegment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;subsegment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_annotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;orderId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;subsegment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_annotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;customerId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;processing_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;processing_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;subsegment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;processingDetails&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;orderId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;processingTime&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;processing_time&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])},&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;order&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;structured_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INFO&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Order processed successfully&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;correlation_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customerId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;processingTime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;processing_time&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Order processed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;orderId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;customerId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;correlationId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;correlation_id&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;→ Click &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;If the &lt;code&gt;aws_xray_sdk&lt;/code&gt; import fails, you'll need to add it as a Lambda layer. Create a layer with the X-Ray SDK package, or use the simplified version without the SDK (X-Ray will still trace boto3 calls automatically with active tracing enabled).&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part II
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Add Custom Subsegments and Annotations
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Test the Function
&lt;/h4&gt;

&lt;p&gt;→ Go to the &lt;strong&gt;Test&lt;/strong&gt; tab&lt;/p&gt;

&lt;p&gt;→ Create a test event:&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;"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;"process"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"orderId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-2026-001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"customerId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CUST-100"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"productId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PROD-A"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"headers"&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="nl"&gt;"X-Correlation-Id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test-corr-001"&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;→ Click &lt;strong&gt;Test&lt;/strong&gt; → ⚠️ **Verify the response includes the correlation ID&lt;/p&gt;

&lt;p&gt;→ Run it several more times with different order IDs&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 02:&lt;/strong&gt; Test the Health Check
&lt;/h4&gt;

&lt;p&gt;→ Create another test event&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;"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;"health"&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;→ Click &lt;strong&gt;Test&lt;/strong&gt; → ⚠️ &lt;strong&gt;Verify the health check response with dependency status&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 03:&lt;/strong&gt; Test Validation Failure
&lt;/h4&gt;

&lt;p&gt;Create a test event without customerId&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;"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;"process"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"orderId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-2026-002"&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;→ Click &lt;strong&gt;Test&lt;/strong&gt; → ⚠️ &lt;strong&gt;verify the 400 response with validation error&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Part III
&lt;/h2&gt;

&lt;h4&gt;
  
  
  View Traces in the X-Ray Console
&lt;/h4&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Open the X-Ray Trace Map
&lt;/h4&gt;

&lt;p&gt;→ Open the &lt;strong&gt;X-Ray&lt;/strong&gt; console (or &lt;strong&gt;CloudWatch&lt;/strong&gt; → &lt;strong&gt;Trace Map&lt;/strong&gt; → &lt;strong&gt;ObservableOrderService&lt;/strong&gt;)&lt;/p&gt;

&lt;p&gt;→ Set the time range to the last 30 minutes&lt;/p&gt;

&lt;p&gt;→ You should see a service map showing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Client&lt;/strong&gt; → &lt;strong&gt;ObservableOrderService&lt;/strong&gt; (Lambda Context) → &lt;strong&gt;ObservableOrderService&lt;/strong&gt; (Lambda Function)&lt;/li&gt;
&lt;li&gt;If the function called other AWS services, those appear as nodes too&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 02:&lt;/strong&gt; Examine Individual Traces
&lt;/h4&gt;

&lt;p&gt;→ Click on the &lt;strong&gt;ObservableOrderService&lt;/strong&gt; node in the service map&lt;/p&gt;

&lt;p&gt;→ Click &lt;strong&gt;View traces&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Click on a specific trace to see the timeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Lambda segment (total execution time)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Initialization&lt;/strong&gt; subsegment (cold start, if applicable)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ValidateInput&lt;/strong&gt; subsegment (your custom subsegment)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ProcessBusinessLogic&lt;/strong&gt; subsegment (your custom subsegment)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HealthCheck&lt;/strong&gt; subsegment (for health check requests)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 03:&lt;/strong&gt; Search Traces by Annotation
&lt;/h4&gt;

&lt;p&gt;→ Go to &lt;strong&gt;Traces&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ In the filter expression box, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   annotation.correlationId = "test-corr-001"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;→ Click &lt;strong&gt;Run query&lt;/strong&gt; →  &lt;strong&gt;You should see only the traces with that correlation ID&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡This is exactly why annotations are indexed and metadata is not. You can search for &lt;code&gt;annotation.userId = "CUST-100"&lt;/code&gt; to find all traces for a specific user. You cannot search by metadata values. Use annotations for the fields you'll want to filter by. Use metadata for the detailed data you'll examine once you've found the right trace.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part IV
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Set Up CloudWatch Alarms with SNS Notifications
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Create an SNS Topic
&lt;/h4&gt;

&lt;p&gt;→ Open the &lt;strong&gt;SNS&lt;/strong&gt; console → &lt;strong&gt;Topics&lt;/strong&gt; → &lt;strong&gt;Create topic&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;→ &lt;strong&gt;Type:&lt;/strong&gt; &lt;code&gt;Standard&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ &lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;OrderServiceAlerts&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ Click &lt;strong&gt;Create topic&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 02:&lt;/strong&gt; Create an Email Subscription
&lt;/h4&gt;

&lt;p&gt;→ Click &lt;strong&gt;Create subscription&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ &lt;strong&gt;Protocol:&lt;/strong&gt; &lt;code&gt;Email&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ &lt;strong&gt;Endpoint:&lt;/strong&gt; &lt;code&gt;your email address&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ Click &lt;strong&gt;Create subscription&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Check your email and &lt;strong&gt;confirm the subscription&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 03:&lt;/strong&gt; Create an Error Rate Alarm
&lt;/h4&gt;

&lt;p&gt;→ Open the &lt;strong&gt;CloudWatch&lt;/strong&gt; console → &lt;strong&gt;Alarms&lt;/strong&gt; → &lt;strong&gt;Create alarm&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Click &lt;strong&gt;Select metric&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Navigate: &lt;strong&gt;Lambda&lt;/strong&gt; → &lt;strong&gt;By Function Name&lt;/strong&gt; → &lt;code&gt;ObservableOrderService&lt;/code&gt; → &lt;strong&gt;Errors&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ Click &lt;strong&gt;Select metric&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Statistic:&lt;/strong&gt; Sum&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Period:&lt;/strong&gt; 60 seconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Threshold type:&lt;/strong&gt; Static&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whenever Errors is:&lt;/strong&gt; Greater than 0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Datapoints to alarm:&lt;/strong&gt; 1 out of 1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;→  &lt;strong&gt;Notification:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Alarm state trigger:&lt;/strong&gt; &lt;code&gt;In alarm&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SNS topic:&lt;/strong&gt; &lt;code&gt;OrderServiceAlerts&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;→ &lt;strong&gt;Alarm name:&lt;/strong&gt; &lt;code&gt;OrderService-Errors&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ Click &lt;strong&gt;Create alarm&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 04:&lt;/strong&gt; Create a Duration Alarm
&lt;/h4&gt;

&lt;p&gt;→ Create another alarm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Metric:&lt;/strong&gt; Lambda → &lt;code&gt;ObservableOrderService&lt;/code&gt; → &lt;strong&gt;Duration&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Statistic:&lt;/strong&gt; p95&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Period:&lt;/strong&gt; 300 seconds (5 minutes)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Threshold:&lt;/strong&gt; Greater than 3000 (3 seconds)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Datapoints to alarm:&lt;/strong&gt; 2 out of 3&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SNS topic:&lt;/strong&gt; &lt;code&gt;OrderServiceAlerts&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alarm name:&lt;/strong&gt; &lt;code&gt;OrderService-HighLatency&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 05:&lt;/strong&gt; Create a Composite Alarm
&lt;/h4&gt;

&lt;p&gt;→ Go to &lt;strong&gt;Alarms&lt;/strong&gt; → &lt;strong&gt;Create alarm&lt;/strong&gt; → &lt;strong&gt;Create composite alarm&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;→ &lt;strong&gt;Alarm rule:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   ALARM("OrderService-Errors") AND ALARM("OrderService-HighLatency")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;→ &lt;strong&gt;Notification:&lt;/strong&gt; &lt;code&gt;OrderServiceAlerts&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ &lt;strong&gt;Alarm name:&lt;/strong&gt; &lt;code&gt;OrderService-Critical&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;→ Click &lt;strong&gt;Create alarm&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;This composite alarm only fires when both errors AND high latency occur simultaneously indicating a real problem, not just a transient spike.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part V
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Verify the Complete Observability Stack
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Generate Traffic and Verify&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;→ Go back to the Lambda function and run various test events:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Normal orders (several times)&lt;/li&gt;
&lt;li&gt;Health checks&lt;/li&gt;
&lt;li&gt;Validation failures (missing customerId)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;→ &lt;strong&gt;Check CloudWatch Logs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open the log group &lt;code&gt;/aws/lambda/ObservableOrderService&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Verify structured JSON logs with correlation IDs&lt;/li&gt;
&lt;li&gt;Use Logs Insights to query:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   parse @message '"correlationId":"*","service":"*"' as corrId, svc
   | filter corrId like /test-corr/
   | fields @timestamp, @message
   | sort @timestamp desc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;→ &lt;strong&gt;Check X-Ray:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;View the trace map. Verify your function appears&lt;/li&gt;
&lt;li&gt;Search traces by annotation&lt;/li&gt;
&lt;li&gt;Examine subsegment timing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;→ &lt;strong&gt;Check CloudWatch Alarms:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify alarms are in OK state&lt;/li&gt;
&lt;li&gt;Trigger errors to test the alarm → check your email for the SNS notification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;→ &lt;strong&gt;Check the Dashboard (optional):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a dashboard combining Lambda metrics, custom metrics, and Logs Insights queries&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 A fully observable application has all three pillars working together: structured logs with correlation IDs (searchable in Logs Insights), metrics with alarms (proactive alerting), and traces with annotations (distributed request tracking). The correlation ID ties them all together. From a log entry, you can find the X-Ray trace, and from a trace, you can search the logs.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🏗️ What You Built | 📘 Exam Concepts Recap
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;What You Built&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Exam Concept&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Added structured logging with correlation IDs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Tracking a request across services&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Created custom X-Ray subsegments&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Measuring specific code sections for bottlenecks&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Added X-Ray annotations and metadata&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Annotations are indexed/searchable. Metadata is not&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Searched traces by &lt;code&gt;annotation.correlationId&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Filtering traces by indexed fields&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Enabled active tracing on Lambda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;X-Ray daemon runs automatically, no install&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Created error and latency CloudWatch alarms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Proactive alerting with SNS notifications&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Built a composite alarm with AND logic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Reducing alert noise by combining conditions&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tied logs + metrics + traces via correlation ID&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;The three pillars of observability working together&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ⚠️ Clean Up Protocol
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Lambda&lt;/strong&gt; → Delete &lt;code&gt;ObservableOrderService&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SNS&lt;/strong&gt; → Delete the &lt;code&gt;OrderServiceAlerts&lt;/code&gt; topic (this also deletes subscriptions)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch&lt;/strong&gt; → Delete all three alarms (Errors, HighLatency, Critical composite)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch&lt;/strong&gt; → Delete the log group &lt;code&gt;/aws/lambda/ObservableOrderService&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM&lt;/strong&gt; → Delete the Lambda execution role&lt;/li&gt;
&lt;/ol&gt;




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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Observability = logs + metrics + traces.&lt;/strong&gt; Monitoring tells you something is wrong. Observability tells you why. X-Ray is the answer when the question asks about finding the root cause of latency in distributed systems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;X-Ray annotations&lt;/strong&gt; are indexed and searchable: use them for filtering traces by user ID, order ID, etc. &lt;strong&gt;Metadata&lt;/strong&gt; is not searchable: use it for detailed debugging data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;X-Ray subsegments&lt;/strong&gt; let you measure specific parts of your code. Create subsegments for database calls, external API calls, and business logic to identify bottlenecks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correlation IDs&lt;/strong&gt; track a request across services. Generate one at the entry point and pass it through headers, message attributes, and log entries. Add it as an X-Ray annotation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured JSON logging&lt;/strong&gt; is essential for Logs Insights queries. Include level, message, correlation ID, timestamp, and relevant context in every log entry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch alarms&lt;/strong&gt; have three states: OK, ALARM, INSUFFICIENT_DATA. Configure &lt;code&gt;TreatMissingData&lt;/code&gt; carefully.  &lt;code&gt;notBreaching&lt;/code&gt; is usually the right choice for Lambda.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composite alarms&lt;/strong&gt; combine multiple alarms with AND/OR logic to reduce alert noise. They only fire when the combined condition is true.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;X-Ray sampling&lt;/strong&gt; defaults to 1 request/second + 5% of additional requests. Create custom rules to trace more of high-value endpoints and less of health checks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Health checks&lt;/strong&gt; should have shallow (is it running?) and deep (are dependencies healthy?) variants. Use shallow for load balancers, deep for dashboards.&lt;/li&gt;
&lt;li&gt;For Lambda, enable &lt;strong&gt;Lambda service traces&lt;/strong&gt; in the function configuration. The X-Ray daemon runs automatically no installation needed. Use the X-Ray SDK to add custom subsegments and annotations.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/xray/latest/devguide/aws-xray.html" rel="noopener noreferrer"&gt;What is AWS X-Ray?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-python.html" rel="noopener noreferrer"&gt;AWS X-Ray SDK for Python&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/AlarmThatSendsEmail.html" rel="noopener noreferrer"&gt;Using Amazon CloudWatch alarms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Create_Composite_Alarm.html" rel="noopener noreferrer"&gt;Create a composite alarm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/services-xray.html" rel="noopener noreferrer"&gt;Visualize Lambda function invocations using AWS X-Ray&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;🏗️ &lt;/p&gt;

</description>
      <category>aws</category>
      <category>certification</category>
      <category>cloud</category>
      <category>developer</category>
    </item>
    <item>
      <title>Assist In Root Cause Analysis | 🏗️ Build A Debugging Dashboard</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Mon, 13 Jul 2026 20:16:57 +0000</pubDate>
      <link>https://dev.to/aws-builders/assist-in-root-cause-analysis-build-a-debugging-dashboard-9a6</link>
      <guid>https://dev.to/aws-builders/assist-in-root-cause-analysis-build-a-debugging-dashboard-9a6</guid>
      <description>&lt;p&gt;&lt;strong&gt;Exam Guide:&lt;/strong&gt; Developer - Associate&lt;br&gt;
&lt;strong&gt;🏗️ Domain 4:&lt;/strong&gt;  &lt;strong&gt;&lt;em&gt;Troubleshooting And Optimization&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
📘 &lt;strong&gt;Task 1:&lt;/strong&gt; &lt;em&gt;Assist In A Root Cause Analysis&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;When something breaks in production, you need to find out why. &lt;em&gt;Fast.&lt;/em&gt; This task tests your ability to use CloudWatch Logs, Logs Insights, CloudWatch metrics, dashboards, and CloudTrail to debug application issues. Writing Logs Insights queries. Understanding Lambda-specific log fields. Using Embedded Metric Format for custom metrics. And recognizing common failure patterns across AWS service integrations.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  📘 Concepts
&lt;/h2&gt;
&lt;h3&gt;
  
  
  CloudWatch Logs Architecture
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Component&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;What It Is&lt;/th&gt;
&lt;th&gt;&lt;code&gt;Example&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Log Group&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A collection of log streams that share retention and access settings&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/aws/lambda/my-function&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Log Stream&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A sequence of log events from a single source&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2024/01/15/[$LATEST]abc123&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Log Event&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A single log entry with a timestamp and message&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"level":"ERROR","message":"DynamoDB timeout"}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Retention&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;How long logs are kept &lt;em&gt;(1 day to 10 years, or never expire)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;30 days&lt;/code&gt; for production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Subscription Filter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Real-time stream of log events to another destination&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Stream to Kinesis, Lambda, or OpenSearch&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Metric filter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Extract metric values from log text patterns&lt;/td&gt;
&lt;td&gt;Count &lt;code&gt;ERROR&lt;/code&gt; occurrences&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Lambda automatically creates a log group named &lt;code&gt;/aws/lambda/{function-name}&lt;/code&gt;. Each function instance creates a new log stream. Log retention defaults to "Never expire". Always set a retention policy to control costs. You need &lt;code&gt;logs:CreateLogGroup&lt;/code&gt; and &lt;code&gt;logs:PutLogEvents&lt;/code&gt; permissions in the Lambda execution role.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  CloudWatch Logs Insights Query Syntax
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Logs Insights&lt;/strong&gt; is a query language for searching and analysing log data. The exam tests specific query commands:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Command&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;&lt;code&gt;Example&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;fields&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Select which fields to display&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fields @timestamp, @message&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;filter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Filter log events by condition&lt;/td&gt;
&lt;td&gt;&lt;code&gt;filter @message like /ERROR/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;stats&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Aggregate data&lt;/td&gt;
&lt;td&gt;&lt;code&gt;stats count(*) by bin(5m)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;sort&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Order results&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sort @timestamp desc&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;limit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cap the number of results&lt;/td&gt;
&lt;td&gt;&lt;code&gt;limit 20&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;parse&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Extract fields from unstructured log text&lt;/td&gt;
&lt;td&gt;&lt;code&gt;parse @message "user=* action=*" as user, action&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;display&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Choose which fields appear in results&lt;/td&gt;
&lt;td&gt;&lt;code&gt;display @timestamp, @message&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Common Query Patterns&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Find the most recent errors&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="k"&gt;like&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ERROR&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;timestamp&lt;/span&gt; &lt;span class="k"&gt;desc&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;limit&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Count errors per 5-minute window&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="k"&gt;like&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ERROR&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;errorCount&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="n"&gt;errorCount&lt;/span&gt; &lt;span class="k"&gt;desc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Find slow Lambda invocations&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;billedDuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;memorySize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;maxMemoryUsed&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="k"&gt;desc&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;limit&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Parse custom log fields&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;parse&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="s1"&gt;'"level":"*","requestId":"*","error":"*"'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reqId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"ERROR"&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Find cold starts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"REPORT"&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;initDuration&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;initDuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;memorySize&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;initDuration&lt;/span&gt; &lt;span class="k"&gt;desc&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;limit&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡&lt;strong&gt;The &lt;code&gt;parse&lt;/code&gt; command uses glob patterns (&lt;code&gt;*&lt;/code&gt;) to extract fields from unstructured text. The &lt;code&gt;stats&lt;/code&gt; command supports &lt;code&gt;count()&lt;/code&gt;, &lt;code&gt;sum()&lt;/code&gt;, &lt;code&gt;avg()&lt;/code&gt;, &lt;code&gt;min()&lt;/code&gt;, &lt;code&gt;max()&lt;/code&gt;, &lt;code&gt;pct()&lt;/code&gt; (percentiles). Use &lt;code&gt;bin()&lt;/code&gt; to group by time intervals. The &lt;code&gt;@&lt;/code&gt; prefix denotes system-generated fields like &lt;code&gt;@timestamp&lt;/code&gt;, &lt;code&gt;@message&lt;/code&gt;, &lt;code&gt;@logStream&lt;/code&gt;, etc.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Lambda-Specific Log Fields
&lt;/h3&gt;

&lt;p&gt;Lambda automatically adds these fields to REPORT lines:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;&lt;code&gt;Field&lt;/code&gt;&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;What It Contains&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Why It Matters&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;@type&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Log line type: &lt;code&gt;START&lt;/code&gt;, &lt;code&gt;END&lt;/code&gt;, &lt;code&gt;REPORT&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Filter for REPORT to get invocation summaries&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;@requestId&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Unique invocation ID&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Correlate logs for a single invocation&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;@duration&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Actual execution time (ms)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Performance monitoring&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;@billedDuration&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rounded up to nearest ms (or 1ms minimum)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Cost tracking&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;@memorySize&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Configured memory (MB)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Right-sizing analysis&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;@maxMemoryUsed&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Peak memory used (MB)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Detect memory pressure&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;@initDuration&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cold start initialization time (ms)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Only present on cold starts&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;@xrayTraceId&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;X-Ray trace ID&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Link logs to traces&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Memory right-sizing query&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"REPORT"&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt; &lt;span class="k"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;maxMemoryUsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;avgMem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;maxMemoryUsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;peakMem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;memorySize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;configuredMem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;maxMemoryUsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;memorySize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;utilizationPct&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 If &lt;code&gt;@maxMemoryUsed&lt;/code&gt; is consistently close to &lt;code&gt;@memorySize&lt;/code&gt;, the function may be running out of memory. If it's consistently low (under 50%), you're over-provisioned and wasting money. The &lt;code&gt;@initDuration&lt;/code&gt; field only appears on cold starts. If you see it frequently, consider provisioned concurrency.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  CloudWatch Embedded Metric Format (EMF) vs PutMetricData
&lt;/h3&gt;

&lt;p&gt;There are two ways to publish custom metrics.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Aspect&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Embedded Metric Format (EMF)&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;PutMetricData API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;How It Works&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Write structured JSON to stdout:&lt;/em&gt; CloudWatch extracts metrics automatically&lt;/td&gt;
&lt;td&gt;Call the CloudWatch API directly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Async&lt;/em&gt; (written with logs)&lt;/td&gt;
&lt;td&gt;Sync &lt;em&gt;(API call blocks execution)&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Log ingestion cost only&lt;/em&gt; (no per-metric API charge)&lt;/td&gt;
&lt;td&gt;$0.01 per 1,000 PutMetricData API calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dimensions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Up to 30 dimensions&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Up to 30 dimensions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resolution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Standard (60s) or high (1s)&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Standard (60s) or high (1s)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Batching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Automatic&lt;/em&gt; (one log line = one metric data point)&lt;/td&gt;
&lt;td&gt;Manual &lt;em&gt;(up to 1,000 values per API call)&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lambda Overhead&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;None.&lt;/em&gt; Just a print statement&lt;/td&gt;
&lt;td&gt;API call adds latency to each invocation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best For&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Lambda functions, high-throughput applications&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;EC2 applications, infrequent metric publishing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;EMF format&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Your business logic here
&lt;/span&gt;    &lt;span class="n"&gt;order_total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;process_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Emit EMF metric. CloudWatch extracts this automatically
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_aws&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1234567890000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CloudWatchMetrics&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Namespace&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OrderService&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Dimensions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Environment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OrderType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Metrics&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OrderTotal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;None&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ProcessingTime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Milliseconds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;}]&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Environment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;production&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OrderType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;standard&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OrderTotal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order_total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ProcessingTime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;145&lt;/span&gt;
    &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;PutMetricData approach&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="n"&gt;cloudwatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cloudwatch&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;order_total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;process_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Sync API call — adds latency
&lt;/span&gt;    &lt;span class="n"&gt;cloudwatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_metric_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OrderService&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;MetricData&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MetricName&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OrderTotal&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order_total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Unit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;None&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Dimensions&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Environment&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;production&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OrderType&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;standard&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡&amp;gt; &lt;code&gt;EMF&lt;/code&gt; is the preferred approach for Lambda because it adds zero latency. You're just writing to &lt;code&gt;stdout&lt;/code&gt;, which Lambda already sends to CloudWatch Logs. CloudWatch automatically extracts the metrics from the structured JSON. Use &lt;code&gt;PutMetricData&lt;/code&gt; when you need to publish metrics from EC2 or when you need more control over metric timing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Integration Failure Patterns
&lt;/h3&gt;

&lt;p&gt;Scenarios to identify the root cause. Know these patterns:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;&lt;code&gt;Symptom&lt;/code&gt;&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Likely Cause&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;How to Debug&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Fix&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;502 Bad Gateway&lt;/code&gt;&lt;/strong&gt; (API GW)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Lambda returned invalid response format&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Check Lambda logs for response structure&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Return &lt;code&gt;statusCode&lt;/code&gt;, &lt;code&gt;headers&lt;/code&gt;, &lt;code&gt;body&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;504 Gateway Timeout&lt;/code&gt;&lt;/strong&gt; (API GW)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Lambda exceeded API Gateway's 29-second timeout&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Check &lt;code&gt;@duration&lt;/code&gt; in logs&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Reduce Lambda timeout, optimize code, or use async&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;429 Too Many Requests&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Lambda concurrency throttled or API GW rate limit&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Check &lt;code&gt;Throttles&lt;/code&gt; metric&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Increase reserved concurrency or request limit increase&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;Access Denied&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Missing IAM permissions&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Check CloudTrail for &lt;code&gt;AccessDenied&lt;/code&gt; events&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Update IAM policy&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;Task timed out&lt;/code&gt;&lt;/strong&gt; (Lambda)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Function exceeded its configured timeout&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Check &lt;code&gt;@duration&lt;/code&gt; vs timeout setting&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Increase timeout or optimize code&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;Out of memory&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Function exceeded configured memory&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Check &lt;code&gt;@maxMemoryUsed&lt;/code&gt; vs &lt;code&gt;@memorySize&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Increase memory allocation&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Connection timeout&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Lambda in VPC can't reach internet or service&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Check VPC config, NAT Gateway, security groups&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Add NAT Gateway or VPC endpoint&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;ResourceNotFoundException&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;DynamoDB table or resource doesn't exist in the region&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Check region configuration&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Verify resource exists in the correct region&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  502 Bad Gateway Deep Dive
&lt;/h3&gt;

&lt;p&gt;The most common Lambda + API Gateway error. API Gateway expects this exact response format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# CORRECT: API Gateway accepts this
&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;  &lt;span class="c1"&gt;# body must be a STRING
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# WRONG: causes 502
&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;# body is a dict, not a string!
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# WRONG: causes 502
&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# not the expected format at all
&lt;/span&gt;
&lt;span class="c1"&gt;# WRONG: causes 502 (unhandled exception)
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;oops&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# no try/except = 502
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;A 502 from API Gateway almost always means the Lambda function returned a response that doesn't match the expected format, or the function threw an unhandled exception. Check the Lambda logs first: the error will be there. The API Gateway execution logs (if enabled) will show "Malformed Lambda proxy response."&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  CloudTrail for Debugging Permission Issues
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;CloudTrail Feature&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;What It Does&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Event History&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Last 90 days of management events&lt;/em&gt; (free)&lt;/td&gt;
&lt;td&gt;Quick lookup of recent API calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trail&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Continuous logging to S3&lt;/em&gt; (all events)&lt;/td&gt;
&lt;td&gt;Long-term audit, compliance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Events&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;S3 object-level and Lambda invocation events&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Debug specific resource access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Insights&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Detect unusual API activity&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Identify anomalous patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Finding AccessDenied events&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; Open CloudTrail console → &lt;strong&gt;Event history&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; Filter by &lt;strong&gt;Event name&lt;/strong&gt; or &lt;strong&gt;Error code&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; Look for events with &lt;code&gt;errorCode: AccessDenied&lt;/code&gt; or &lt;code&gt;errorCode: UnauthorizedAccess&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;4.&lt;/strong&gt; The event details show:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;userIdentity:&lt;/strong&gt; who made the call (which role/user)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;eventName:&lt;/strong&gt; what API was called (e.g., &lt;code&gt;PutItem&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;requestParameters:&lt;/strong&gt; what resource was targeted&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;errorMessage:&lt;/strong&gt; the specific permission that's missing&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;CloudTrail logs every AWS API call. When you get an AccessDenied error, CloudTrail tells you exactly which principal tried to call which API on which resource and which permission was missing. This is the fastest way to debug IAM issues. Event history is free and covers the last 90 days.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Deployment Failure Troubleshooting
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Failure Type&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Where to Look&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Common Causes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CloudFormation stack failure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;CloudFormation Events tab&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;IAM permissions, resource limits, invalid template&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CodeBuild failure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;CodeBuild build logs&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Missing dependencies, test failures, buildspec syntax&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CodeDeploy failure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;CodeDeploy deployment logs&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Hook script errors, health check failures, timeout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lambda deployment failure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;CloudWatch Logs + CodeDeploy&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;New version crashes, alarm triggers rollback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SAM deploy failure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;CloudFormation Events + SAM CLI output&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Transform errors, packaging issues, capability missing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;CloudFormation failures show the specific resource and error in the Events tab. Look for &lt;code&gt;CREATE_FAILED&lt;/code&gt; or &lt;code&gt;UPDATE_FAILED&lt;/code&gt; status. The most common cause is insufficient IAM permissions because the CloudFormation role doesn't have permission to create the resource. For SAM, make sure you include &lt;code&gt;CAPABILITY_IAM&lt;/code&gt; and &lt;code&gt;CAPABILITY_AUTO_EXPAND&lt;/code&gt; when deploying.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  🏗️ Build A Debugging Dashboard
&lt;/h2&gt;

&lt;p&gt;Build a &lt;strong&gt;Debugging Dashboard&lt;/strong&gt; from scratch using the AWS Console:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Lambda function that emits structured logs and EMF custom metrics&lt;/li&gt;
&lt;li&gt;CloudWatch Logs Insights queries that find errors, slow invocations, and cold starts&lt;/li&gt;
&lt;li&gt;A CloudWatch dashboard displaying key application metrics&lt;/li&gt;
&lt;li&gt;Simulated failures (timeout, permission denied, 502) with debugging walkthroughs&lt;/li&gt;
&lt;li&gt;CloudTrail queries to find access denied events&lt;/li&gt;
&lt;/ul&gt;


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

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://ntombizakhona.medium.com/amazon-web-services-a8e57a9c6084" rel="noopener noreferrer"&gt;An AWS Account&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://ntombizakhona.medium.com/python-in-the-cloud-1462f61e3293" rel="noopener noreferrer"&gt;Python&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Part I
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Create a Lambda Function with Structured Logging and EMF
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Create the Function
&lt;/h4&gt;

&lt;p&gt;Open the &lt;strong&gt;Lambda&lt;/strong&gt; console → &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Function name:&lt;/strong&gt; &lt;code&gt;DebugDemoFunction&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; Python 3.13&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 02:&lt;/strong&gt; Add Structured Logging with EMF
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;emit_emf_metric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;metric_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dimensions&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Emit a CloudWatch metric using Embedded Metric Format.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_aws&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CloudWatchMetrics&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Namespace&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DebugDemo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Dimensions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dimensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;())],&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Metrics&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;metric_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;unit&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="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;dimensions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;metric_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="p"&gt;}))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;start_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;process&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;request_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws_request_id&lt;/span&gt;

    &lt;span class="c1"&gt;# Structured log entry
&lt;/span&gt;    &lt;span class="n"&gt;log_entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;level&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INFO&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;requestId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%Y-%m-%dT%H:%M:%SZ&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gmtime&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;functionName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;memoryLimit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;memory_limit_in_mb&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;slow&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Simulate a slow operation
&lt;/span&gt;            &lt;span class="n"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&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="n"&gt;log_entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;level&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;WARN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="n"&gt;log_entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Slow operation detected: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Simulate an error
&lt;/span&gt;            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Invalid order data: missing required field &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;customerId&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;oom&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Simulate high memory usage
&lt;/span&gt;            &lt;span class="n"&gt;log_entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Processing large dataset&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 50MB string
&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Normal processing
&lt;/span&gt;            &lt;span class="n"&gt;log_entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Order processed successfully&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_entry&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="c1"&gt;# Emit EMF metrics
&lt;/span&gt;        &lt;span class="n"&gt;processing_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
        &lt;span class="nf"&gt;emit_emf_metric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ProcessingTime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;processing_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Milliseconds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Environment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;demo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="nf"&gt;emit_emf_metric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SuccessCount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Environment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;demo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;action&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Processed successfully&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;requestId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;processingTime&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;processing_time&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;ms&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;processing_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
        &lt;span class="n"&gt;log_entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;level&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ERROR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;log_entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;log_entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;errorType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_entry&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="nf"&gt;emit_emf_metric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ErrorCount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Environment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;demo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;action&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;requestId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request_id&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;Click &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 03:&lt;/strong&gt; Generate Test Data
&lt;/h4&gt;

&lt;p&gt;Go to the &lt;strong&gt;Test&lt;/strong&gt; tab&lt;/p&gt;

&lt;p&gt;Create and run these test events multiple times:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Normal processing&lt;/strong&gt;&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="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;"process"&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;&lt;strong&gt;Slow operation&lt;/strong&gt;&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="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;"slow"&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;&lt;strong&gt;Error case&lt;/strong&gt;&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="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;"error"&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;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Run each test event 5-10 times to generate enough log data&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part II
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Write CloudWatch Logs Insights Queries
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Open Logs Insights
&lt;/h4&gt;

&lt;p&gt;Open the &lt;strong&gt;CloudWatch&lt;/strong&gt; console → ** ▼ Logs** → &lt;strong&gt;Log Management&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Select the log group: &lt;code&gt;/aws/lambda/DebugDemoFunction&lt;/code&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Set the time range to the last 1 hour&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;→ View in &lt;strong&gt;Logs Insights&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Query 01:&lt;/strong&gt; Find All Errors
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="k"&gt;like&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ERROR&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;timestamp&lt;/span&gt; &lt;span class="k"&gt;desc&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;limit&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Click &lt;strong&gt;Run query&lt;/strong&gt;. You should see the structured error logs from the error test events.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Query 02:&lt;/strong&gt; Analyze Invocation Performance
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"REPORT"&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt; &lt;span class="k"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;avgDuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;maxDuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;minDuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;invocations&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Query 03:&lt;/strong&gt; Find Cold Starts
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"REPORT"&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;initDuration&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;initDuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;maxMemoryUsed&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;initDuration&lt;/span&gt; &lt;span class="k"&gt;desc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Query 04:&lt;/strong&gt; Parse Structured Logs for Error Breakdown
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;parse&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="s1"&gt;'"level":"*","requestId":"*"'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reqId&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"ERROR"&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;errorCount&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Query 05:&lt;/strong&gt; Memory Utilization Analysis
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"REPORT"&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt; &lt;span class="k"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;maxMemoryUsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;avgMemUsed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;maxMemoryUsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;peakMemUsed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;memorySize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;configuredMem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;maxMemoryUsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;memorySize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;utilizationPct&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Query 06:&lt;/strong&gt; Slow Invocations Over Time
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"REPORT"&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;stats&lt;/span&gt; &lt;span class="k"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;avgDuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;pct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p95Duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;pct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p99Duration&lt;/span&gt;
        &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;asc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Save useful queries by clicking **Save&lt;/strong&gt; in Logs Insights. You can also add query results directly to a CloudWatch dashboard. The &lt;code&gt;pct()&lt;/code&gt; function calculates percentiles: &lt;code&gt;pct(@duration, 95)&lt;/code&gt; gives you the 95th percentile duration, which is more useful than averages for identifying tail latency.**&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part III
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create a CloudWatch Dashboard
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Build the Dashboard
&lt;/h4&gt;

&lt;p&gt;Open the &lt;strong&gt;CloudWatch&lt;/strong&gt; console → &lt;strong&gt;Dashboards&lt;/strong&gt; → &lt;strong&gt;Create dashboard&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dashboard name:&lt;/strong&gt; &lt;code&gt;DebugDemo-Dashboard&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Create dashboard&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 02:&lt;/strong&gt; Add an Invocation Count Widget
&lt;/h4&gt;

&lt;p&gt;Click &lt;strong&gt;Add widget&lt;/strong&gt; → &lt;strong&gt;Number&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;Lambda&lt;/strong&gt; → &lt;strong&gt;By Function Name&lt;/strong&gt; → &lt;code&gt;DebugDemoFunction&lt;/code&gt; → &lt;code&gt;Invocations&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Period:&lt;/strong&gt; 5 minutes&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Create widget&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 03:&lt;/strong&gt; Add an Error Rate Widget
&lt;/h4&gt;

&lt;p&gt;Click &lt;strong&gt;Add widget&lt;/strong&gt; → &lt;strong&gt;Line&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add two metrics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lambda → By Function Name → &lt;code&gt;DebugDemoFunction&lt;/code&gt; → &lt;code&gt;Errors&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Lambda → By Function Name → &lt;code&gt;DebugDemoFunction&lt;/code&gt; → &lt;code&gt;Invocations&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create widget&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 04:&lt;/strong&gt; Add a Duration Widget
&lt;/h4&gt;

&lt;p&gt;Click &lt;strong&gt;Add widget&lt;/strong&gt; → &lt;strong&gt;Line&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Select Lambda → By Function Name → &lt;code&gt;DebugDemoFunction&lt;/code&gt; → &lt;code&gt;Duration&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add statistics:&lt;/strong&gt; &lt;code&gt;Average, p99, Maximum&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Create widget&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 05:&lt;/strong&gt; Add a Custom EMF Metric Widget
&lt;/h4&gt;

&lt;p&gt;Click &lt;strong&gt;Add widget&lt;/strong&gt; → &lt;strong&gt;Number&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;DebugDemo&lt;/strong&gt; (custom namespace) → &lt;strong&gt;Environment, Action&lt;/strong&gt; → &lt;code&gt;ProcessingTime&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Create widget&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 06:&lt;/strong&gt; Add a Logs Insights Query Widget
&lt;/h4&gt;

&lt;p&gt;Click &lt;strong&gt;Add widget&lt;/strong&gt; → &lt;strong&gt;Logs table&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Select log group: &lt;code&gt;/aws/lambda/DebugDemoFunction&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Enter query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="k"&gt;like&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ERROR&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;timestamp&lt;/span&gt; &lt;span class="k"&gt;desc&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;limit&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Click &lt;strong&gt;Create&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 07:* Click **Save dashboard&lt;/strong&gt;
&lt;/h4&gt;




&lt;h2&gt;
  
  
  Part IV
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Simulate and Debug Common Failures
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Simulate a 502 Bad Gateway
&lt;/h4&gt;

&lt;p&gt;Create a new Lambda function: &lt;code&gt;BadResponseFunction&lt;/code&gt; and add this intentionally broken code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# BUG: body should be a string, not a dict
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;this will cause a 502&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;# Wrong!
&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create an API Gateway REST API pointing to this function&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Test the API endpoint. You'll get a 502&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Debug steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check Lambda logs → the function executed successfully (no error)&lt;/li&gt;
&lt;li&gt;Check API Gateway execution logs → "Malformed Lambda proxy response"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix:&lt;/strong&gt; Change &lt;code&gt;'body': {'message': '...'}&lt;/code&gt; to &lt;code&gt;'body': json.dumps({'message': '...'})&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 02:&lt;/strong&gt; Simulate a Timeout
&lt;/h4&gt;

&lt;p&gt;On &lt;code&gt;DebugDemoFunction&lt;/code&gt;, go to &lt;strong&gt;Configuration&lt;/strong&gt; → &lt;strong&gt;General configuration&lt;/strong&gt; → &lt;strong&gt;Edit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Set &lt;strong&gt;Timeout&lt;/strong&gt; to 3 seconds&lt;/p&gt;

&lt;p&gt;Run the &lt;code&gt;slow&lt;/code&gt; test event (which sleeps 2-5 seconds)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Debug steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check logs → &lt;code&gt;Task timed out after 3.00 seconds&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check the REPORT line → &lt;code&gt;@duration&lt;/code&gt; close to 3000ms&lt;/li&gt;
&lt;li&gt;Query in Logs Insights:
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;   &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="k"&gt;like&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="n"&gt;timed&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;memorySize&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Increase timeout or optimize the slow operation&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 03:&lt;/strong&gt; Simulate Permission Denied
&lt;/h4&gt;

&lt;p&gt;On &lt;code&gt;DebugDemoFunction&lt;/code&gt;, update the code to call DynamoDB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;dynamodb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dynamodb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;NonExistentTable&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;level&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ERROR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;errorType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;__name__&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)})&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡Run the test. You'll get an &lt;code&gt;AccessDeniedException&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Debug steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check Lambda logs → &lt;code&gt;AccessDeniedException&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Open &lt;strong&gt;CloudTrail&lt;/strong&gt; → &lt;strong&gt;Event history&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Filter by &lt;strong&gt;Event name:&lt;/strong&gt; &lt;code&gt;PutItem&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Find the event with &lt;code&gt;errorCode: AccessDenied&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The event shows which role tried to access which resource&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part V
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Use CloudTrail to Find Access Denied Events
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Search Event History
&lt;/h4&gt;

&lt;p&gt;Open the &lt;strong&gt;CloudTrail&lt;/strong&gt; console → &lt;strong&gt;Event history&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Change the filter to &lt;strong&gt;Error code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Type &lt;code&gt;AccessDenied&lt;/code&gt; and press Enter&lt;/p&gt;

&lt;p&gt;You should see the &lt;code&gt;PutItem&lt;/code&gt; event from the previous step&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 02:&lt;/strong&gt; Examine the Event Details
&lt;/h4&gt;

&lt;p&gt;Click on the event to expand it&lt;/p&gt;

&lt;p&gt;Key fields to examine&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;userIdentity.arn:&lt;/strong&gt; the Lambda execution role that made the call&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;eventName:&lt;/strong&gt; &lt;code&gt;PutItem&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;requestParameters:&lt;/strong&gt; the table name and item&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;errorCode:&lt;/strong&gt; &lt;code&gt;AccessDeniedException&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;errorMessage:&lt;/strong&gt; the specific permission needed&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡&lt;strong&gt;To fix:&lt;/strong&gt; add &lt;code&gt;dynamodb:PutItem&lt;/code&gt; permission to the Lambda execution role for the specific table ARN&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 03:&lt;/strong&gt; Create a Logs Insights Query for CloudTrail
&lt;/h4&gt;

&lt;p&gt;If you have a CloudTrail trail sending logs to CloudWatch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="n"&gt;eventTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;eventName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errorMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;userIdentity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;principal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;requestParameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tableName&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="n"&gt;errorCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"AccessDenied"&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sort&lt;/span&gt; &lt;span class="n"&gt;eventTime&lt;/span&gt; &lt;span class="k"&gt;desc&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;limit&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 CloudTrail Event history is free and covers the last 90 days of management events. For longer retention or data events (S3 object access, Lambda invocations), you need a trail. When debugging permission issues, CloudTrail is your first stop, it tells you exactly what permission is missing, who tried to use it, and on which resource.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🏗️ What You Built | 📘 Exam Concepts Recap
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;What You Built&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Exam Concept&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Emitted EMF metrics from structured logs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Zero-latency custom metrics via Embedded Metric Format&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Wrote CloudWatch Logs Insights queries&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;fields&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;stats&lt;/code&gt;, &lt;code&gt;parse&lt;/code&gt;, &lt;code&gt;sort&lt;/code&gt;, &lt;code&gt;pct()&lt;/code&gt; &lt;em&gt;syntax&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Queried &lt;code&gt;@initDuration&lt;/code&gt; for cold starts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Reading Lambda REPORT fields for performance&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Built a CloudWatch dashboard with mixed widgets&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Single pane of glass for application health&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reproduced and fixed a 502 Bad Gateway&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Malformed Lambda proxy response (body must be string)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reproduced a timeout and read the REPORT line&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Diagnosing &lt;code&gt;Task timed out&lt;/code&gt; from logs&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Found AccessDenied events in CloudTrail&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Debugging permission issues: principal, API, resource&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ⚠️ Clean Up Protocol
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Lambda&lt;/strong&gt; → Delete &lt;code&gt;DebugDemoFunction&lt;/code&gt; and &lt;code&gt;BadResponseFunction&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway&lt;/strong&gt; → Delete any test APIs created&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch&lt;/strong&gt; → Delete the &lt;code&gt;DebugDemo-Dashboard&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch&lt;/strong&gt; → Delete log groups for the Lambda functions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM&lt;/strong&gt; → Delete Lambda execution roles&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Key Takeaways for the Exam
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch Logs Insights&lt;/strong&gt; uses &lt;code&gt;fields&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;stats&lt;/code&gt;, &lt;code&gt;parse&lt;/code&gt;, &lt;code&gt;sort&lt;/code&gt;, and &lt;code&gt;limit&lt;/code&gt; commands. Know the syntax for each.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda REPORT lines&lt;/strong&gt; contain &lt;code&gt;@duration&lt;/code&gt;, &lt;code&gt;@billedDuration&lt;/code&gt;, &lt;code&gt;@memorySize&lt;/code&gt;, &lt;code&gt;@maxMemoryUsed&lt;/code&gt;, and &lt;code&gt;@initDuration&lt;/code&gt; (cold starts only). Use these for performance analysis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EMF&lt;/strong&gt; is preferred over &lt;code&gt;PutMetricData&lt;/code&gt; for Lambda because it adds zero latency. Metrics are extracted from structured JSON written to stdout. Use PutMetricData for EC2 or infrequent publishing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;502 Bad Gateway&lt;/strong&gt; from API Gateway almost always means the Lambda response format is wrong: &lt;code&gt;body&lt;/code&gt; must be a string, not a dict.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;504 Gateway Timeout&lt;/strong&gt; means Lambda exceeded API Gateway's 29-second limit. The Lambda timeout must be less than 29 seconds for synchronous API Gateway integrations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudTrail&lt;/strong&gt; logs every AWS API call. Use Event history to find &lt;code&gt;AccessDenied&lt;/code&gt; errors. It shows the principal, API, resource, and missing permission.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metric filters&lt;/strong&gt; extract CloudWatch metrics from log text patterns. &lt;strong&gt;Subscription filters&lt;/strong&gt; stream log events to Kinesis, Lambda, or OpenSearch in real time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch dashboards&lt;/strong&gt; can include metrics widgets, Logs Insights query results, and alarms giving you a single pane of glass for application health.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;parse&lt;/code&gt; command in Logs Insights extracts fields from unstructured text using glob patterns (&lt;code&gt;*&lt;/code&gt;). The &lt;code&gt;stats&lt;/code&gt; command supports &lt;code&gt;count()&lt;/code&gt;, &lt;code&gt;avg()&lt;/code&gt;, &lt;code&gt;sum()&lt;/code&gt;, &lt;code&gt;min()&lt;/code&gt;, &lt;code&gt;max()&lt;/code&gt;, and &lt;code&gt;pct()&lt;/code&gt; for percentiles.&lt;/li&gt;
&lt;li&gt;When debugging deployment failures, check CloudFormation Events (stack failures), CodeBuild logs (build failures), and CodeDeploy logs (deployment failures). Each service has its own log location.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CWL_QuerySyntax.html" rel="noopener noreferrer"&gt;CloudWatch Logs Insights language query syntax&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Specification.html" rel="noopener noreferrer"&gt;Specification: Embedded metric format&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-monitoring.html" rel="noopener noreferrer"&gt;Monitoring, debugging, and troubleshooting Lambda functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/awscloudtrail/latest/userguide/view-cloudtrail-events.html" rel="noopener noreferrer"&gt;Working with CloudTrail event history&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/create_dashboard.html" rel="noopener noreferrer"&gt;Creating a customized CloudWatch dashboard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-troubleshooting.html" rel="noopener noreferrer"&gt;Troubleshooting issues in Lambda&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;🏗️&lt;/p&gt;

</description>
      <category>aws</category>
      <category>certification</category>
      <category>cloud</category>
      <category>developer</category>
    </item>
    <item>
      <title>Deploy Code By Using AWS Continuous Integration And Continuous Delivery (CI/CD) Services | 🏗️ Build A Complete CI/CD Pipeline</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Wed, 08 Jul 2026 00:12:51 +0000</pubDate>
      <link>https://dev.to/aws-builders/deploy-code-by-using-aws-continuous-integration-and-continuous-delivery-cicd-services-build-5b19</link>
      <guid>https://dev.to/aws-builders/deploy-code-by-using-aws-continuous-integration-and-continuous-delivery-cicd-services-build-5b19</guid>
      <description>&lt;p&gt;&lt;strong&gt;Exam Guide:&lt;/strong&gt; Developer - Associate&lt;br&gt;
&lt;strong&gt;🏗️ Domain 3:&lt;/strong&gt;  &lt;strong&gt;&lt;em&gt;Deployment&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
📘 &lt;strong&gt;Task 4:&lt;/strong&gt; &lt;em&gt;Deploy Code By Using AWS Continuous Integration And Continuous Delivery (CI/CD) Services&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This task tests your ability to build and manage CI/CD pipelines using AWS developer tools. You need to understand how CodeCommit, CodeBuild, CodeDeploy, and CodePipeline work together, how to write buildspec and appspec files, how deployment strategies differ, and how to configure automatic rollbacks. Deployment strategies for Lambda and EC2, SAM deployment preferences, and pipeline orchestration.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  📘 Concepts
&lt;/h2&gt;
&lt;h3&gt;
  
  
  AWS CI/CD Pipeline Overview
&lt;/h3&gt;

&lt;p&gt;The four AWS developer tools form a complete CI/CD pipeline:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Service&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Role&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CodeCommit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Source control&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Git push&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Source artifact&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CodeBuild&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Build and test&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Source artifact&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Build artifact&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CodeDeploy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Deploy&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Build artifact&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Running application&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CodePipeline&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Orchestration&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Trigger (push, schedule)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Coordinated pipeline execution&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;How they connect:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CodeCommit (source) → CodeBuild (build/test) → CodeDeploy (deploy)
         ↑                                              |
         └──────── CodePipeline (orchestrates all) ─────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡CodePipeline is the orchestrator. It doesn't build or deploy anything itself. It connects stages (source, build, test, deploy) and manages transitions between them. Each stage can use different providers (GitHub instead of CodeCommit, Jenkins instead of CodeBuild, etc.).&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  CodeCommit Fundamentals
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Details&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What It Is&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Managed Git repository hosted in AWS&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Authentication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;HTTPS&lt;/em&gt; (Git credentials or credential helper) or &lt;em&gt;SSH&lt;/em&gt; (SSH keys)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Encryption&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Encrypted at rest&lt;/em&gt; (AWS managed keys) &lt;em&gt;and in transit&lt;/em&gt; (HTTPS/SSH)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Triggers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;SNS notifications or Lambda functions on repository events&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-account&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Use IAM roles with &lt;code&gt;AssumeRole&lt;/code&gt; for cross-account access&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Branching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Standard Git branching:&lt;/em&gt; main, develop, feature branches&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡CodeCommit supports triggers for push events that can invoke Lambda functions or send SNS notifications. This is different from CodePipeline's source stage: triggers are repository-level events, while CodePipeline polls or uses CloudWatch Events to detect changes.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  CodeBuild &lt;code&gt;buildspec.yml&lt;/code&gt; Structure
&lt;/h3&gt;

&lt;p&gt;The buildspec file tells CodeBuild what to do. It has four phases:&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;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.2&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;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ENV_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;production"&lt;/span&gt;
  &lt;span class="na"&gt;parameter-store&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/myapp/db-password"&lt;/span&gt;
  &lt;span class="na"&gt;secrets-manager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;myapp/api-key:API_KEY"&lt;/span&gt;

&lt;span class="na"&gt;phases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runtime-versions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;python&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3.13&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pip install -r requirements.txt&lt;/span&gt;

  &lt;span class="na"&gt;pre_build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Running tests..."&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;python -m pytest tests/ -v&lt;/span&gt;

  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Building..."&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sam build&lt;/span&gt;

  &lt;span class="na"&gt;post_build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Packaging..."&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sam package --s3-bucket my-bucket --output-template-file packaged.yaml&lt;/span&gt;

&lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;packaged.yaml&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;appspec.yml&lt;/span&gt;
  &lt;span class="na"&gt;discard-paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;

&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;paths&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;/root/.cache/pip/**/*'&lt;/span&gt;

&lt;span class="na"&gt;reports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test-reports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**/*.xml"&lt;/span&gt;
    &lt;span class="na"&gt;base-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test-results&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;Phase&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;When It Runs&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Typical Use&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;install&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;First&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Install dependencies, runtime versions&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pre_build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Before build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Run tests, log in to ECR, lint code&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Main phase&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Compile code, run SAM build, create artifacts&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;post_build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;After build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Package artifacts, push images, notifications&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;Section&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;env.variables&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Plain text environment variables&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;env.parameter-store&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Values pulled from SSM Parameter Store at build time&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;env.secrets-manager&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Values pulled from Secrets Manager at build time&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;artifacts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Files to pass to the next pipeline stage&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cache&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Paths to cache between builds (speeds up installs)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reports&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Test report files for CodeBuild report groups&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡&lt;strong&gt;The &lt;code&gt;buildspec&lt;/code&gt; file must be named &lt;code&gt;buildspec.yml&lt;/code&gt; and placed in the root of your source directory (unless you override the name in the CodeBuild project settings).&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;env&lt;/code&gt; section can pull secrets from Parameter Store and Secrets Manager. This is the secure way to use credentials in builds. Never hardcode secrets in the buildspec.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  CodeDeploy &lt;code&gt;appspec.yml&lt;/code&gt; Structure
&lt;/h3&gt;

&lt;p&gt;The appspec file tells CodeDeploy how to deploy your application. The structure differs by compute platform:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Lambda:&lt;/strong&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;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.0&lt;/span&gt;
&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;MyFunction&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::Lambda::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;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-function&lt;/span&gt;
        &lt;span class="na"&gt;Alias&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod&lt;/span&gt;
        &lt;span class="na"&gt;CurrentVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="na"&gt;TargetVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="na"&gt;Hooks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;BeforeAllowTraffic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PreTrafficCheckFunction&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AfterAllowTraffic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PostTrafficCheckFunction&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;For EC2/On-Premises:&lt;/strong&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;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.0&lt;/span&gt;
&lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;linux&lt;/span&gt;
&lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
    &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/www/html&lt;/span&gt;
&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;object&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/www/html&lt;/span&gt;
    &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;www-data&lt;/span&gt;
    &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;www-data&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;755"&lt;/span&gt;
&lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;BeforeInstall&lt;/span&gt;&lt;span class="pi"&gt;:&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;scripts/before_install.sh&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;
  &lt;span class="na"&gt;AfterInstall&lt;/span&gt;&lt;span class="pi"&gt;:&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;scripts/after_install.sh&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;
  &lt;span class="na"&gt;ApplicationStart&lt;/span&gt;&lt;span class="pi"&gt;:&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;scripts/start_server.sh&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;
  &lt;span class="na"&gt;ValidateService&lt;/span&gt;&lt;span class="pi"&gt;:&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;scripts/validate.sh&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CodeDeploy Lifecycle Hooks
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Lambda Deployment Hooks:&lt;/strong&gt;
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;Hook&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;When It Runs&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Purpose&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BeforeAllowTraffic&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Before traffic shifts to new version&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Run validation tests against the new version&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AfterAllowTraffic&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;After traffic shifts to new version&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Run integration tests, verify health&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;EC2 Deployment Hooks (&lt;em&gt;in order&lt;/em&gt;):&lt;/strong&gt;
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;Hook&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;When It Runs&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Purpose&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BeforeInstall&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Before files are copied&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Clean up old files, stop services&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AfterInstall&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;After files are copied&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Configure app, set permissions&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ApplicationStart&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;After AfterInstall&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Start services, warm up&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ValidateService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;After ApplicationStart&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Health checks, smoke tests&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BeforeBlockTraffic&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Before deregistering from ELB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Graceful connection draining&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AfterBlockTraffic&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;After deregistering from ELB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Run tasks while instance is out of service&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BeforeAllowTraffic&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Before registering with ELB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Final checks before receiving traffic&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AfterAllowTraffic&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;After registering with ELB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Verify instance is healthy in ELB&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 For Lambda deployments, the hooks are Lambda functions themselves: they run your validation code and must return &lt;code&gt;Succeeded&lt;/code&gt; or &lt;code&gt;Failed&lt;/code&gt; to CodeDeploy.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For EC2, hooks are shell scripts that run on the instance.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If a hook fails (the deployment rolls back).&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Deployment Strategies Comparison
&lt;/h3&gt;

&lt;p&gt;Know each strategy of by heart:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;Strategy&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;How It Works&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Downtime&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Rollback Speed&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Risk&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Best For&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AllAtOnce&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Deploy to all instances simultaneously&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Brief&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Redeploy previous version&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;High:&lt;/em&gt; all instances affected&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Dev/test environments&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Rolling&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Deploy in batches&lt;/strong&gt; (one batch at a time)&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;None&lt;/em&gt; (if batch size &amp;lt; total)&lt;/td&gt;
&lt;td&gt;Redeploy previous version&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Medium:&lt;/em&gt; one batch at a time&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;EC2 fleets with ELB&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Rolling with additional batch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Adds new instances before removing old ones&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;None&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Redeploy previous version&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Low:&lt;/em&gt; maintains full capacity&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Production EC2 fleets&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Blue/Green&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Create entirely new environment, switch traffic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;None&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Switch back to blue&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Low&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Production with zero downtime&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Canary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Shift X% traffic, wait, then shift 100%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;None&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Shift back to 0%&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Low:&lt;/em&gt; limited blast radius&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Lambda, API Gateway&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Linear&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Shift X% every N minutes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;None&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Shift back to 0%&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Low:&lt;/em&gt; gradual exposure&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Lambda, API Gateway&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  SAM &lt;code&gt;DeploymentPreference&lt;/code&gt; Types
&lt;/h3&gt;

&lt;p&gt;SAM automates Lambda deployment strategies through CodeDeploy:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;Type&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Traffic Shift Pattern&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Example&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AllAtOnce&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100% immediately&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Instant cutover&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Canary10Percent5Minutes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10% for 5 min, then 100%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Quick canary validation&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Canary10Percent10Minutes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10% for 10 min, then 100%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Longer canary validation&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Canary10Percent15Minutes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10% for 15 min, then 100%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Extended canary validation&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Canary10Percent30Minutes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10% for 30 min, then 100%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Conservative canary&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Linear10PercentEvery1Minute&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+10% every minute (10 min total)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Fast linear rollout&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Linear10PercentEvery2Minutes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+10% every 2 min (20 min total)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Moderate linear rollout&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Linear10PercentEvery3Minutes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+10% every 3 min (30 min total)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Slow linear rollout&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Linear10PercentEvery10Minutes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+10% every 10 min (100 min total)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Very conservative rollout&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;SAM Template Example:&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;MyFunction&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;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python3.13&lt;/span&gt;
    &lt;span class="na"&gt;AutoPublishAlias&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;live&lt;/span&gt;
    &lt;span class="na"&gt;DeploymentPreference&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;Canary10Percent5Minutes&lt;/span&gt;
      &lt;span class="na"&gt;Alarms&lt;/span&gt;&lt;span class="pi"&gt;:&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;MyFunctionErrorAlarm&lt;/span&gt;
      &lt;span class="na"&gt;Hooks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;PreTraffic&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;PreTrafficHookFunction&lt;/span&gt;
        &lt;span class="na"&gt;PostTraffic&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;PostTrafficHookFunction&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;code&gt;AutoPublishAlias&lt;/code&gt; is required for &lt;code&gt;DeploymentPreference&lt;/code&gt; to work. SAM automatically publishes a new version on each deploy and updates the alias. The &lt;code&gt;Alarms&lt;/code&gt; section triggers automatic rollback if any alarm enters ALARM state during deployment. Canary shifts traffic in two steps (X% then 100%), while Linear shifts in equal increments.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  API Gateway Stages and Custom Domains
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Concept&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;What It Is&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;A named reference to a deployment&lt;/em&gt; (dev, staging, prod)&lt;/td&gt;
&lt;td&gt;Environment separation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stage Variables&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Key-value pairs available in the stage&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Point to different Lambda aliases per stage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Canary Release&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Split traffic between current and canary deployment&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Test new API version with real traffic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Custom Domain&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Map your domain to API Gateway&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;api.example.com&lt;/code&gt; instead of &lt;code&gt;xyz.execute-api.region.amazonaws.com&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Base Path Mapping&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Map URL paths to different APIs&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/v1&lt;/code&gt; → API v1, &lt;code&gt;/v2&lt;/code&gt; → API v2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Stage Variables For Lambda Integration:&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;Stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod → stageVariables.lambdaAlias = "prod"&lt;/span&gt;
&lt;span class="py"&gt;Stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev  → stageVariables.lambdaAlias = "dev"&lt;/span&gt;

&lt;span class="err"&gt;Lambda&lt;/span&gt; &lt;span class="py"&gt;ARN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:lambda:region:account:function:name:${stageVariables.lambdaAlias}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;API Gateway Canary Settings:&lt;/strong&gt;
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Setting&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Description&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Canary Percentage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;% of traffic routed to canary deployment&lt;/em&gt; (0-100)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stage Variable Overrides&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Different stage variables for canary traffic&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Promote Canary&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Move canary deployment to production&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Delete Canary&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Remove canary and keep current production&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡&lt;strong&gt;API Gateway stages are not the same as Lambda aliases, but they work together. Use stage variables to point each stage to the corresponding Lambda alias.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;A common pattern: API Gateway &lt;code&gt;prod&lt;/code&gt; stage → stage variable &lt;code&gt;lambdaAlias=prod&lt;/code&gt; → Lambda function &lt;code&gt;prod&lt;/code&gt; alias → version 5.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Rollback Strategies
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Rollback Type&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;How It Works&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;When It Triggers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Automatic&lt;/strong&gt; (alarm-based)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;CodeDeploy monitors CloudWatch alarms during deployment&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Alarm enters ALARM state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Automatic&lt;/strong&gt; (hook failure)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;CodeDeploy rolls back if a lifecycle hook fails&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Hook returns &lt;code&gt;Failed&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Manual&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;You stop the deployment and roll back&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;You detect an issue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SAM Automatic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;SAM configures CodeDeploy with alarms from &lt;code&gt;DeploymentPreference&lt;/code&gt;&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Configured alarm triggers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;CloudWatch Alarm For Automatic Rollback:&lt;/strong&gt;
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;Metric&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Threshold&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Purpose&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Errors&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&amp;gt; 0 for 1 minute&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Catch any Lambda errors&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Duration&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&amp;gt; timeout * 0.8 for 5 minutes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Catch performance degradation&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Throttles&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&amp;gt; 0 for 1 minute&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Catch concurrency issues&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;5XXError&lt;/code&gt; (API GW)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&amp;gt; 1% for 5 minutes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Catch server errors&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡Automatic rollback with CloudWatch alarms is the recommended approach for production deployments.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SAM makes this easy: just add the &lt;code&gt;Alarms&lt;/code&gt; list to &lt;code&gt;DeploymentPreference&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CodeDeploy monitors the alarms during the deployment window. If any alarm fires, it automatically shifts all traffic back to the previous version. The rollback is fast because it just updates the alias pointer.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Pre/Post Traffic Hooks
&lt;/h3&gt;

&lt;p&gt;Traffic hooks are Lambda functions that validate your deployment:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;PreTraffic Hook Pattern:&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;codedeploy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;codedeploy&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;deployment_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DeploymentId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;lifecycle_event_hook_execution_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;LifecycleEventHookExecutionId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Run your validation tests here
&lt;/span&gt;    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Example: invoke the new version and check the response
&lt;/span&gt;        &lt;span class="n"&gt;lambda_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;lambda&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lambda_client&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="n"&gt;FunctionName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my-function:live&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# the alias
&lt;/span&gt;            &lt;span class="n"&gt;Payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Payload&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Succeeded&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Failed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Failed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

    &lt;span class="c1"&gt;# Report back to CodeDeploy
&lt;/span&gt;    &lt;span class="n"&gt;codedeploy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_lifecycle_event_hook_execution_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;deploymentId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;deployment_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;lifecycleEventHookExecutionId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;lifecycle_event_hook_execution_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;The PreTraffic hook runs before any traffic shifts to the new version:&lt;/strong&gt; use it for smoke tests and validation. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The PostTraffic hook runs after all traffic has shifted:&lt;/strong&gt; use it for integration tests. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Both hooks must call &lt;code&gt;put_lifecycle_event_hook_execution_status&lt;/code&gt; to tell CodeDeploy whether to proceed or roll back.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If a hook times out (default 1 hour), the deployment fails.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  CodePipeline Concepts
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Concept&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Description&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pipeline&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;The overall workflow definition&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;A logical group of actions&lt;/em&gt; (Source, Build, Deploy)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Action&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;A task within a stage&lt;/em&gt; (CodeBuild action, CodeDeploy action)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Artifact&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Files passed between stages&lt;/em&gt; (stored in S3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Transition&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;The link between stages&lt;/em&gt; (can be disabled to pause the pipeline)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Approval action&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Manual approval gate before proceeding&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Pipeline Feature&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Details&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trigger&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;CloudWatch Events&lt;/em&gt; (push to CodeCommit), &lt;em&gt;webhook&lt;/em&gt; (GitHub), &lt;em&gt;S3 upload&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-region&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Actions can deploy to different regions&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-account&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Use IAM roles for cross-account deployments&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Parallel Actions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Multiple actions in the same stage run in parallel&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sequential stages&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Stages run in order.&lt;/em&gt; A stage must complete before the next starts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;CodePipeline stores artifacts in an S3 bucket (created automatically or specified by you).&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Each action produces output artifacts and consumes input artifacts.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you need a manual approval before deploying to production, add an Approval action between the staging deploy and production deploy stages.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🏗️ Build A Complete CI/CD Pipeline
&lt;/h2&gt;

&lt;p&gt;Build a &lt;strong&gt;Complete CI/CD Pipeline&lt;/strong&gt; from scratch using the AWS Console.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A CodeCommit repository with application code&lt;/li&gt;
&lt;li&gt;A CodeBuild project that runs tests and packages the application&lt;/li&gt;
&lt;li&gt;A CodePipeline that orchestrates source → build → deploy&lt;/li&gt;
&lt;li&gt;A Lambda function with canary deployment configured through SAM&lt;/li&gt;
&lt;li&gt;CloudWatch alarms for automatic rollback&lt;/li&gt;
&lt;li&gt;A working pipeline that triggers on code push&lt;/li&gt;
&lt;/ul&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ntombizakhona.medium.com/amazon-web-services-a8e57a9c6084" rel="noopener noreferrer"&gt;&lt;strong&gt;An AWS Account&lt;/strong&gt; &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ntombizakhona.medium.com/python-in-the-cloud-1462f61e3293" rel="noopener noreferrer"&gt;&lt;strong&gt;Basic familiarity with Python and YAML&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ntombizakhona.medium.com/git-in-the-cloud-d8fed43331fc" rel="noopener noreferrer"&gt;&lt;strong&gt;Git&lt;/strong&gt;&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Part I
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Create A Github Repository And Add Code
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Why GitHub instead of CodeCommit?&lt;/strong&gt; AWS closed CodeCommit to new customers in July 2024. Even accounts that can create a CodeCommit repo often find it's &lt;strong&gt;not offered as a CodePipeline source provider&lt;/strong&gt;. GitHub via &lt;strong&gt;CodeConnections&lt;/strong&gt; is the reliable, current, real-world source integration. And it's fully supported by CodePipeline. CodeCommit, CodeBuild, CodeDeploy, and CodePipeline (If your account &lt;em&gt;does&lt;/em&gt; offer CodeCommit and you'd rather use it, use it)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Create the GitHub Repository
&lt;/h4&gt;

&lt;p&gt;Go to &lt;a href="https://github.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;GitHub&lt;/strong&gt;&lt;/a&gt; → &lt;strong&gt;New&lt;/strong&gt; → &lt;strong&gt;Repository name:&lt;/strong&gt; &lt;code&gt;cicd-demo-app&lt;/code&gt; → Choose &lt;strong&gt;Private&lt;/strong&gt; (or Public)&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Don't&lt;/strong&gt; initialize with a README (we'll push our own files)&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Create repository&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Prefer CodeCommit?&lt;/strong&gt; If your account offers CodeCommit &lt;em&gt;and&lt;/em&gt; it appears as a CodePipeline source provider, you can use it instead: create the repo in the CodeCommit console, generate Git credentials in IAM (&lt;strong&gt;Security credentials → API keys → Generate API key → AWS CodeCommit&lt;/strong&gt;), attach the &lt;strong&gt;&lt;code&gt;AWSCodeCommitPowerUser&lt;/code&gt;&lt;/strong&gt; policy to your user, then clone the HTTPS URL and push. HTTPS uses Git credentials. SSH uses an uploaded SSH key. Everything downstream (CodeBuild, CodePipeline, CodeDeploy) is identical. only the source provider changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step By Step Guide For Setting Up CodeCommit&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Step 1:&lt;/strong&gt; Open the &lt;strong&gt;CodeCommit&lt;/strong&gt; console → &lt;strong&gt;Create repository&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repository name:&lt;/strong&gt; &lt;code&gt;cicd-demo-app&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description - &lt;em&gt;optional&lt;/em&gt;:&lt;/strong&gt; &lt;code&gt;Demo application for CI/CD pipeline&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt;  Configure Git Credentials&lt;br&gt;
💡 &lt;strong&gt;CodeCommit HTTPS access uses Git credentials tied to your IAM user (a generated username/password, separate from your AWS access keys).&lt;/strong&gt;&lt;br&gt;
 &lt;strong&gt;Step 3:&lt;/strong&gt; Open the &lt;strong&gt;IAM&lt;/strong&gt; console → &lt;strong&gt;IAM users&lt;/strong&gt; → select your IAM user&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; Click the &lt;strong&gt;Security credentials&lt;/strong&gt; tab&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5:&lt;/strong&gt; In the &lt;strong&gt;API keys&lt;/strong&gt; section, click &lt;strong&gt;Generate API key&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6:&lt;/strong&gt; &lt;strong&gt;Service ▼:&lt;/strong&gt; &lt;code&gt;AWS CodeCommit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Generate API key&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 7:&lt;/strong&gt; &lt;strong&gt;Retrieve API key&lt;/strong&gt;&lt;br&gt;
⚠️ &lt;strong&gt;Copy or download the username and password now:&lt;/strong&gt; This is the only time the password is shown. If you lose it, you must reset it.&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Close&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Git Credentials vs Access Keys.&lt;/strong&gt; CodeCommit HTTPS uses a  dedicated username/password (Git credentials), not your &lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt;. These are IAM-managed and require a CodeCommit policy like &lt;code&gt;AWSCodeCommitPowerUser&lt;/code&gt; on your user.  HTTPS = Git credentials or the credential helper. SSH = an uploaded SSH public key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 8:&lt;/strong&gt; &lt;strong&gt;Now attach a CodeCommit permissions policy to the same IAM user so it can actually use the repository.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 9:&lt;/strong&gt; Still on the user's page, go to the &lt;strong&gt;Permissions&lt;/strong&gt; tab → &lt;strong&gt;Add permissions ▼&lt;/strong&gt; → &lt;strong&gt;Attach policies directly&lt;/strong&gt; → search for and select &lt;strong&gt;&lt;code&gt;AWSCodeCommitPowerUser&lt;/code&gt;&lt;/strong&gt; → &lt;strong&gt;Next&lt;/strong&gt; → &lt;strong&gt;Add permissions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 10:&lt;/strong&gt; Clone And Add Application Code&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://git-codecommit.us-east-1.amazonaws.com/v1/repos/cicd-demo-app
&lt;span class="nb"&gt;cd &lt;/span&gt;cicd-demo-app
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;
python&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 02:&lt;/strong&gt; Add the Application Code Locally
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;cicd-demo-app
&lt;span class="nb"&gt;cd &lt;/span&gt;cicd-demo-app
git init
git branch &lt;span class="nt"&gt;-M&lt;/span&gt; main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Step 03:&lt;/strong&gt; Create the Lambda function code &lt;code&gt;app.py&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;VERSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Order API handler with version tracking.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Order API is running&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;version&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;VERSION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;functionVersion&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function_version&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;
  
  
  &lt;strong&gt;Step 04:&lt;/strong&gt; Create the SAM template &lt;code&gt;template.yaml&lt;/code&gt;
&lt;/h4&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;Transform&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;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI/CD Demo Application&lt;/span&gt;

&lt;span class="na"&gt;Globals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Function&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Timeout&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;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python3.13&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;OrderFunction&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;app.lambda_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;.&lt;/span&gt;
      &lt;span class="na"&gt;AutoPublishAlias&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;live&lt;/span&gt;
      &lt;span class="na"&gt;DeploymentPreference&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;Canary10Percent5Minutes&lt;/span&gt;
        &lt;span class="na"&gt;Alarms&lt;/span&gt;&lt;span class="pi"&gt;:&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;OrderFunctionErrorAlarm&lt;/span&gt;

  &lt;span class="na"&gt;OrderFunctionErrorAlarm&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::CloudWatch::Alarm&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;AlarmDescription&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Errors on OrderFunction&lt;/span&gt;
      &lt;span class="na"&gt;Namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS/Lambda&lt;/span&gt;
      &lt;span class="na"&gt;MetricName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Errors&lt;/span&gt;
      &lt;span class="na"&gt;Dimensions&lt;/span&gt;&lt;span class="pi"&gt;:&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;FunctionName&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;OrderFunction&lt;/span&gt;
      &lt;span class="na"&gt;Statistic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sum&lt;/span&gt;
      &lt;span class="na"&gt;Period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;
      &lt;span class="na"&gt;EvaluationPeriods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;Threshold&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;ComparisonOperator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GreaterThanOrEqualToThreshold&lt;/span&gt;
      &lt;span class="na"&gt;TreatMissingData&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;notBreaching&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Step 05:&lt;/strong&gt; Create the buildspec &lt;code&gt;buildspec.yml&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.2&lt;/span&gt;

&lt;span class="na"&gt;phases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runtime-versions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;python&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3.13&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pip install aws-sam-cli&lt;/span&gt;

  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sam build&lt;/span&gt;

  &lt;span class="na"&gt;post_build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sam package --s3-bucket ${ARTIFACT_BUCKET} --output-template-file packaged.yaml&lt;/span&gt;

&lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;packaged.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Step 06:&lt;/strong&gt; Push the code
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial application code"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Step 07:&lt;/strong&gt; Push to Github
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote add origin &lt;span class="sb"&gt;`&lt;/span&gt;https://github.com/YOUR_USERNAME/cicd-demo-app.git&lt;span class="sb"&gt;`&lt;/span&gt;
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part II
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Set Up a CodeBuild Project
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Create an S3 Bucket for Artifacts
&lt;/h4&gt;

&lt;p&gt;Open the &lt;strong&gt;S3&lt;/strong&gt; console → &lt;strong&gt;Create bucket&lt;/strong&gt; → &lt;strong&gt;Account Regional namespace (recommended)&lt;/strong&gt; → &lt;strong&gt;Bucket name:&lt;/strong&gt; &lt;code&gt;cicd-demo-artifacts&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Keep defaults → &lt;strong&gt;Create bucket&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 02:&lt;/strong&gt; Create the CodeBuild Project
&lt;/h4&gt;

&lt;p&gt;Open the &lt;strong&gt;CodeBuild&lt;/strong&gt; console → &lt;strong&gt;Create project&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Project configuration:&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Project name:&lt;/strong&gt; &lt;code&gt;cicd-demo-build&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project type:&lt;/strong&gt; &lt;code&gt;Default project&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;▼ Source:&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Source provider ▼:&lt;/strong&gt; &lt;code&gt;GitHub&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repository:&lt;/strong&gt; &lt;code&gt;Repository in my GitHub account&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repository:&lt;/strong&gt; &lt;code&gt;cicd-demo-app&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;If you used CodeCommit instead of GitHub:&lt;/strong&gt; Set &lt;strong&gt;Source provider&lt;/strong&gt; to &lt;strong&gt;CodeCommit&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;▼ Environment:&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Provisioning model:&lt;/strong&gt; &lt;code&gt;On-demand&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment image:&lt;/strong&gt; &lt;code&gt;Managed image&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compute:&lt;/strong&gt; &lt;code&gt;EC2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Running mode:&lt;/strong&gt; &lt;code&gt;Container&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operating system:&lt;/strong&gt; &lt;code&gt;Amazon Linux&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime(s):&lt;/strong&gt; &lt;code&gt;Standard&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image:&lt;/strong&gt; &lt;code&gt;aws/codebuild/amazonlinux-x86_64-standard:5.0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image version:&lt;/strong&gt; &lt;code&gt;Always use the latest image for this runtime version&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service role:&lt;/strong&gt; &lt;code&gt;New service role&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;▶ Additional Configuration:&lt;/strong&gt; → &lt;strong&gt;Environment variables:&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;ARTIFACT_BUCKET&lt;/code&gt; → &lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;your S3 bucket name&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;▼ Buildspec:&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Build specifications:&lt;/strong&gt; &lt;code&gt;Use a buildspec file&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Buildspec name - &lt;em&gt;optional&lt;/em&gt;:&lt;/strong&gt; &lt;code&gt;buildspec.yml&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;▼ Artifacts:&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type:&lt;/strong&gt; &lt;code&gt;Amazon S3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bucket name:&lt;/strong&gt; &lt;code&gt;cicd-demo-artifacts-...&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;build-output&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create build project&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Grant the CodeBuild role access to your artifact bucket.&lt;/strong&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The auto-created CodeBuild service role can write to CodeBuild's &lt;em&gt;default&lt;/em&gt; locations, but &lt;code&gt;sam package&lt;/code&gt; uploads to your custom &lt;code&gt;ARTIFACT_BUCKET&lt;/code&gt;, and the role won't have permission by default. &lt;/p&gt;

&lt;p&gt;If the build fails at &lt;code&gt;post_build&lt;/code&gt; with an &lt;code&gt;AccessDenied&lt;/code&gt; on S3:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Go to &lt;strong&gt;IAM → Roles →&lt;/strong&gt; the &lt;code&gt;codebuild-cicd-demo-build-service-role&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Step 2:&lt;/strong&gt; &lt;strong&gt;Add permissions → Attach policies →&lt;/strong&gt; attach &lt;code&gt;AmazonS3FullAccess&lt;/code&gt; (or a scoped inline policy allowing &lt;code&gt;s3:PutObject&lt;/code&gt;, &lt;code&gt;s3:GetObject&lt;/code&gt;, &lt;code&gt;s3:GetBucketLocation&lt;/code&gt; on your bucket)&lt;/p&gt;

&lt;p&gt;💡 Also confirm your &lt;strong&gt;S3 bucket and CodeBuild project are in the same region&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 03:&lt;/strong&gt; Run a Test Build
&lt;/h4&gt;

&lt;p&gt;Click &lt;strong&gt;Start build&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Watch the build logs in real time&lt;br&gt;
Verify each phase completes: INSTALL → PRE_BUILD → BUILD → POST_BUILD&lt;br&gt;
Check the S3 bucket for the &lt;code&gt;packaged.yaml&lt;/code&gt; artifact&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;CodeBuild charges by the minute. Build environments are ephemeral. They're created fresh for each build. Use the &lt;code&gt;cache&lt;/code&gt; section in buildspec.yml to cache dependencies between builds and speed up subsequent builds. CodeBuild can also pull secrets from Parameter Store and Secrets Manager using the &lt;code&gt;env&lt;/code&gt; section.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Part III
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Create a CodePipeline
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Build the Pipeline
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01.1:&lt;/strong&gt; Open the &lt;strong&gt;CodePipeline&lt;/strong&gt; console → &lt;strong&gt;Create pipeline&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01.2:&lt;/strong&gt; &lt;strong&gt;Choose creation option&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Category:&lt;/strong&gt; &lt;code&gt;Build custom pipeline&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01.3:&lt;/strong&gt; &lt;strong&gt;Choose Pipeline settings&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pipeline name:&lt;/strong&gt; &lt;code&gt;cicd-demo-pipeline&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution mode:&lt;/strong&gt; &lt;code&gt;queued&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service role:&lt;/strong&gt; New service role&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01.4:&lt;/strong&gt; &lt;strong&gt;Add a source stage&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Source provider ▼:&lt;/strong&gt; &lt;code&gt;GitHub (via GitHub App)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection:&lt;/strong&gt; click &lt;strong&gt;Connect to GitHub&lt;/strong&gt; → authorize the &lt;strong&gt;CodeConnections&lt;/strong&gt; connection (reuse the one from CodeBuild if you already made it)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repository name:&lt;/strong&gt; &lt;code&gt;YOUR_USERNAME/cicd-demo-app&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Default branch:&lt;/strong&gt; &lt;code&gt;main&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output artifact format:&lt;/strong&gt; &lt;code&gt;CodePipeline default&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;CodeConnections.&lt;/strong&gt; A connection is a reusable, managed OAuth link between AWS and GitHub (or Bitbucket/GitLab). CodePipeline uses it to detect pushes and pull source. No Git credentials or webhooks to manage yourself. The same connection works across CodePipeline and CodeBuild.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 01.5:&lt;/strong&gt; &lt;strong&gt;Add build stage - &lt;em&gt;optional&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Build provider:&lt;/strong&gt; &lt;code&gt;Other build providers&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;▼&lt;/strong&gt; &lt;code&gt;AWS CodeBuild&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project name:&lt;/strong&gt; &lt;code&gt;cicd-demo-build&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01.6:&lt;/strong&gt; &lt;strong&gt;Add test stage &lt;em&gt;- optional&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01.7:&lt;/strong&gt; &lt;strong&gt;Add deploy stage&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deploy provider ▼:&lt;/strong&gt; &lt;code&gt;AWS CloudFormation&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Region:&lt;/strong&gt; &lt;code&gt;United States (North Virginia)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Input artifacts:&lt;/strong&gt; &lt;code&gt;BuildArtifact&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action mode:&lt;/strong&gt; &lt;code&gt;Create or update a stack&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stack name:&lt;/strong&gt; &lt;code&gt;cicd-demo-app&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Artifact name:&lt;/strong&gt; &lt;code&gt;BuildArtifact&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File name:&lt;/strong&gt; &lt;code&gt;packaged.yaml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capabilities:&lt;/strong&gt; &lt;code&gt;CAPABILITY_IAM&lt;/code&gt; &lt;code&gt;CAPABILITY_AUTO_EXPAND&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role name:&lt;/strong&gt; create/select a CloudFormation deployment role (see the warning below)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt; → &lt;strong&gt;Create pipeline&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 02:&lt;/strong&gt; Watch the Pipeline Execute
&lt;/h4&gt;

&lt;p&gt;The pipeline starts automatically after creation&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Watch each stage transition: Source → Build → Deploy &lt;/p&gt;

&lt;p&gt;Click on each stage to see details and logs&lt;/p&gt;

&lt;p&gt;The deploy stage creates a CloudFormation stack with your Lambda function&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 03:&lt;/strong&gt; Add a Manual Approval Stage
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 03.1:&lt;/strong&gt; Click &lt;strong&gt;Edit&lt;/strong&gt; on the pipeline&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03.2:&lt;/strong&gt; Click &lt;strong&gt;Add stage&lt;/strong&gt; between Build and Deploy&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03.3&lt;/strong&gt; &lt;strong&gt;Stage name:&lt;/strong&gt; &lt;code&gt;Approval&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Add stage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03.4:&lt;/strong&gt; Click &lt;strong&gt;Add action group:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Action name:&lt;/strong&gt; &lt;code&gt;ManualApproval&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action provider:&lt;/strong&gt; &lt;code&gt;Manual approval&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Click:&lt;/strong&gt; &lt;code&gt;Done&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡&lt;strong&gt;Now the pipeline pauses at the Approval stage and waits for someone to approve before deploying.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Part IV
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Configure Canary Deployment with Alarms
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Verify the Deployment Configuration
&lt;/h4&gt;

&lt;p&gt;Open the &lt;strong&gt;CodeDeploy&lt;/strong&gt; console → &lt;strong&gt;Applications&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 You should see a deployment group created by SAM (named something like &lt;code&gt;cicd-demo-app-ServerlessDeploymentApplication-*&lt;/code&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Click on it to see the deployment configuration: &lt;code&gt;CodeDeployDefault.LambdaCanary10Percent5Minutes&lt;/code&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 02:&lt;/strong&gt; View the CloudWatch Alarm
&lt;/h4&gt;

&lt;p&gt;Open the &lt;strong&gt;CloudWatch&lt;/strong&gt; console → &lt;strong&gt;Alarms&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Find the &lt;code&gt;OrderFunctionErrorAlarm&lt;/code&gt; created by the SAM template&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It should be in &lt;code&gt;OK&lt;/code&gt; state (no errors yet)&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 03:&lt;/strong&gt; Trigger a Deployment
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 03.1:&lt;/strong&gt; Update &lt;code&gt;app.py&lt;/code&gt; locally&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;VERSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Order API handler v2 — added health check.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;health&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;healthy&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;version&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;VERSION&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Order API is running&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;version&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;VERSION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;functionVersion&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;improvement&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Added health check endpoint&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 03.2:&lt;/strong&gt; Push the change&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"v2.0.0 - Add health check"&lt;/span&gt;
git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;The pipeline triggers automatically&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 03.3:&lt;/strong&gt; After the deploy stage, go to &lt;strong&gt;CodeDeploy&lt;/strong&gt; → watch the canary deployment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10% of traffic shifts to version 2&lt;/li&gt;
&lt;li&gt;CodeDeploy waits 5 minutes while monitoring the alarm&lt;/li&gt;
&lt;li&gt;If no alarm fires, 100% shifts to version 2&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 04:&lt;/strong&gt; Test Automatic Rollback
&lt;/h4&gt;

&lt;p&gt;Push a broken version that throws errors&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;VERSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.0.0-broken&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Intentionally broken to test rollback.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Something went wrong!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Push and let the pipeline deploy&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;During the canary phase, the 10% of traffic hitting the new version generates errors&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The CloudWatch alarm triggers → CodeDeploy automatically rolls back&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All traffic returns to the previous working version&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part V
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Set Up Automatic Rollback with CloudWatch Alarms
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Create Additional Alarms
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01.1:&lt;/strong&gt; Open the &lt;strong&gt;CloudWatch&lt;/strong&gt; console → &lt;strong&gt;Alarms&lt;/strong&gt; → &lt;strong&gt;Create alarm&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01.2:&lt;/strong&gt; &lt;strong&gt;Select metric:&lt;/strong&gt; Lambda → By Function Name → &lt;code&gt;OrderFunction&lt;/code&gt; → &lt;code&gt;Duration&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01.3:&lt;/strong&gt; &lt;strong&gt;Configure&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Statistic:&lt;/strong&gt; &lt;code&gt;Average&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Period:&lt;/strong&gt; &lt;code&gt;60 seconds&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Threshold:&lt;/strong&gt; &lt;code&gt;Greater than 5000&lt;/code&gt; (5 seconds)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evaluation periods:&lt;/strong&gt; &lt;code&gt;2 of 3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alarm name:&lt;/strong&gt; &lt;code&gt;OrderFunction-HighDuration&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create alarm&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01.4:&lt;/strong&gt; Create another alarm for throttles&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Metric:&lt;/strong&gt; &lt;code&gt;Throttles&lt;/code&gt; for &lt;code&gt;OrderFunction&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Threshold:&lt;/strong&gt; &lt;code&gt;Greater than 0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Period:&lt;/strong&gt; &lt;code&gt;60 seconds&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alarm name:&lt;/strong&gt; &lt;code&gt;OrderFunction-Throttles&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 02:&lt;/strong&gt; Update the SAM Template &lt;code&gt;template.yaml&lt;/code&gt;  with Multiple
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="na"&gt;DeploymentPreference&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;Canary10Percent5Minutes&lt;/span&gt;
        &lt;span class="na"&gt;Alarms&lt;/span&gt;&lt;span class="pi"&gt;:&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;OrderFunctionErrorAlarm&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;OrderFunctionDurationAlarm&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;OrderFunctionThrottleAlarm&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Push the change to trigger a pipeline run with the updated alarm configuration.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;You can attach multiple CloudWatch alarms to a deployment preference. If any single alarm fires during the deployment window, CodeDeploy rolls back.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This gives you defense in depth: catch errors, latency spikes, and throttling issues. The alarms are only monitored during the active deployment window, not permanently.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🏗️ What You Built | 📘 Exam Concepts Recap
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;What You Built&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Exam Concept&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Connected a GitHub repo via CodeConnections&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Source stage, managed source integrations&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Built a CodeBuild project with &lt;code&gt;buildspec.yml&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Build phases, environment variables, artifacts&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Created a CodePipeline&lt;/strong&gt; (source → build → deploy)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Pipeline orchestration and artifact flow&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Added a manual approval stage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Human gates before production deployment&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Configured &lt;code&gt;Canary10Percent5Minutes&lt;/code&gt; in SAM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Gradual traffic shifting with &lt;code&gt;DeploymentPreference&lt;/code&gt;&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Attached CloudWatch alarms to the deployment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Automatic rollback on alarm breach&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pushed a broken version and watched it roll back&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Defense-in-depth deployment safety&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Used &lt;code&gt;AutoPublishAlias: live&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Required for SAM safe deployments&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ⚠️ Clean Up Protocol
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;CodePipeline&lt;/strong&gt; → Delete &lt;code&gt;cicd-demo-pipeline&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CodeBuild&lt;/strong&gt; → Delete &lt;code&gt;cicd-demo-build&lt;/code&gt; project&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudFormation&lt;/strong&gt; → Delete the &lt;code&gt;cicd-demo-app&lt;/code&gt; stack (removes Lambda, alarms, CodeDeploy resources)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer Tools → Settings → Connections&lt;/strong&gt; → delete the GitHub CodeConnections connection (and optionally delete/keep the GitHub repo yourself)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3&lt;/strong&gt; → Empty and delete the artifacts bucket&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM&lt;/strong&gt; → Delete the service roles created for CodeBuild, CodePipeline, and the CloudFormation deploy role&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch&lt;/strong&gt; → Delete any remaining log groups and alarms&lt;/li&gt;
&lt;/ol&gt;




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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;CodePipeline&lt;/strong&gt; orchestrates the pipeline but doesn't build or deploy: it connects &lt;strong&gt;CodeCommit&lt;/strong&gt; (source), &lt;strong&gt;CodeBuild&lt;/strong&gt; (build), and &lt;strong&gt;CodeDeploy&lt;/strong&gt; (deploy) stages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;buildspec.yml&lt;/strong&gt; has four phases: &lt;code&gt;install&lt;/code&gt;, &lt;code&gt;pre_build&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;post_build&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;env&lt;/code&gt; section to pull secrets from Parameter Store and Secrets Manager securely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;appspec.yml&lt;/strong&gt; structure differs between Lambda (Resources + Hooks) and EC2 (files + permissions + hooks).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canary&lt;/strong&gt; shifts traffic in two steps (X% then 100%). &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linear&lt;/strong&gt; shifts in equal increments. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blue/Green&lt;/strong&gt; creates a new environment and switches all traffic at once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SAM DeploymentPreference&lt;/strong&gt; requires &lt;code&gt;AutoPublishAlias&lt;/code&gt;. It automatically creates CodeDeploy deployments with traffic shifting and alarm monitoring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PreTraffic hooks&lt;/strong&gt; run before traffic shifts: use for smoke tests. 10. &lt;strong&gt;PostTraffic hooks&lt;/strong&gt; run after: use for integration tests. Both must call &lt;code&gt;put_lifecycle_event_hook_execution_status&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic rollback&lt;/strong&gt; triggers when a CloudWatch alarm fires during deployment or when a lifecycle hook fails. This is the recommended approach for production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Gateway stage variables&lt;/strong&gt; let you point different stages to different Lambda aliases: &lt;code&gt;prod&lt;/code&gt; stage → &lt;code&gt;prod&lt;/code&gt; alias, &lt;code&gt;dev&lt;/code&gt; stage → &lt;code&gt;dev&lt;/code&gt; alias.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CodePipeline artifacts&lt;/strong&gt; are stored in S3. Each action consumes input artifacts and produces output artifacts that flow to the next stage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual approval actions&lt;/strong&gt; in CodePipeline create gates between stages: useful for requiring human sign-off before production deployments.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/codecommit/latest/userguide/setting-up-gc.html?icmpid=docs_acc_console_connect_np" rel="noopener noreferrer"&gt;Setup for HTTPS users using Git credentials&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/codepipeline/latest/userguide/welcome.html" rel="noopener noreferrer"&gt;What is AWS CodePipeline?&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html" rel="noopener noreferrer"&gt;Build specification reference for CodeBuild&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/codedeploy/latest/userguide/reference-appspec-file.html" rel="noopener noreferrer"&gt;CodeDeploy AppSpec file reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/automating-updates-to-serverless-apps.html" rel="noopener noreferrer"&gt;Deploying serverless applications gradually with AWS SAM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/canary-release.html" rel="noopener noreferrer"&gt;Set up an API Gateway canary release deployments&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/codedeploy/latest/userguide/deployment-configurations.html" rel="noopener noreferrer"&gt;Working with deployment configurations in CodeDeploy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ &lt;a href="https://aws.amazon.com/blogs/devops/aws-codecommit-returns-to-general-availability/" rel="noopener noreferrer"&gt;The Future of AWS CodeCommit&lt;/a&gt; ⚠️&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;🏗️&lt;/p&gt;

</description>
      <category>aws</category>
      <category>certification</category>
      <category>cloud</category>
      <category>developer</category>
    </item>
    <item>
      <title>Automate Deployment Testing | 🏗️ Build A Version Management System</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Sat, 04 Jul 2026 10:19:21 +0000</pubDate>
      <link>https://dev.to/aws-builders/automate-deployment-testing-build-a-version-management-system-17n2</link>
      <guid>https://dev.to/aws-builders/automate-deployment-testing-build-a-version-management-system-17n2</guid>
      <description>&lt;p&gt;&lt;strong&gt;Exam Guide:&lt;/strong&gt; Developer - Associate&lt;br&gt;
&lt;strong&gt;🏗️ Domain 3:&lt;/strong&gt;  &lt;strong&gt;&lt;em&gt;Deployment&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
📘 &lt;strong&gt;Task 3:&lt;/strong&gt; &lt;em&gt;Automate Deployment Testing&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This task is about automating tests as part of your deployment workflow: managing Lambda versions and aliases, using weighted aliases for canary deployments, deploying and reviewing IaC templates with change sets, and creating test events for automated validation. Understand version management, safe deployment strategies, and how to preview infrastructure changes before applying them.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  📘 Concepts
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Lambda Versions vs Aliases
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Concept&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;What It Is&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Mutable?&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Use Case&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;$LATEST&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;The default, mutable version of your function&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Development and testing only&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Published version&lt;/strong&gt; (v1, v2, v3...)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;An immutable snapshot of code + configuration&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Production references, rollback targets&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alias&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;A named pointer to a specific version&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Yes (can be repointed)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Environment mapping&lt;/strong&gt; (dev, staging, prod)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;code&gt;$LATEST&lt;/code&gt; is mutable:&lt;/strong&gt; every code update changes it. &lt;strong&gt;Published versions are immutable:&lt;/strong&gt; once published, the code and configuration can't change. &lt;strong&gt;Aliases are mutable pointers to immutable versions.&lt;/strong&gt; Event source mappings in production should reference aliases, never &lt;code&gt;$LATEST&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  $LATEST vs Published Versions
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Aspect&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;$LATEST&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Published Version&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code Changes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Updated on every deploy&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Frozen at publish time&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Configuration Changes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Updated immediately&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Frozen at publish time&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Concurrency Settings&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Shared across all unpublished invocations&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Can have provisioned concurrency per version&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ARN Format&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;arn:aws:lambda:region:account:function:name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;&lt;code&gt;arn:aws:lambda:region:account:function:name:1&lt;/code&gt;&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use In Production&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No (too risky)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Yes (immutable and predictable)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Weighted Aliases for Canary Deployments
&lt;/h3&gt;

&lt;p&gt;An alias can split traffic between two versions using routing configuration:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Deployment Type&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Traffic Split&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;SAM Config&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AllAtOnce&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;100% to new version immediately&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Type: AllAtOnce&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Non-critical functions, dev environments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Canary&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;X% to new, then 100% after interval&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Type: Canary10Percent5Minutes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Test with small traffic before full rollout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Linear&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Increase by X% every N minutes&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Type: Linear10PercentEvery1Minute&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gradual rollout with monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Weighted aliases route a percentage of invocations to a secondary version. If the canary version triggers a CloudWatch alarm, CodeDeploy automatically rolls back to the previous version. SAM's &lt;code&gt;AutoPublishAlias&lt;/code&gt; + &lt;code&gt;DeploymentPreference&lt;/code&gt; handles this automatically.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  IaC Templates
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;SAM vs CloudFormation&lt;/strong&gt;
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;SAM&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;CloudFormation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What It Is&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Extension of CloudFormation with serverless shortcuts&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;AWS's core IaC service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Template Format&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;YAML/JSON with &lt;code&gt;Transform: AWS::Serverless-2016-10-31&lt;/code&gt;&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;YAML/JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Serverless Resources&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;&lt;code&gt;AWS::Serverless::Function&lt;/code&gt;, &lt;code&gt;AWS::Serverless::Api&lt;/code&gt;, etc&lt;/em&gt;.&lt;/td&gt;
&lt;td&gt;Must define each resource individually (Lambda, API GW, IAM roles)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deployment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;&lt;code&gt;sam deploy&lt;/code&gt;&lt;/em&gt; (wraps CloudFormation)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws cloudformation deploy&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Local Testing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;&lt;code&gt;sam local invoke&lt;/code&gt;, &lt;code&gt;sam local start-api&lt;/code&gt;&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Not available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Policy Templates&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;&lt;code&gt;DynamoDBCrudPolicy&lt;/code&gt;, &lt;code&gt;S3ReadPolicy&lt;/code&gt;, etc.&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Write full IAM policies manually&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  CloudFormation Change Sets
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Concept&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Change Set&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A preview of what CloudFormation will add, modify, or delete before you apply it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Create&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Generates the change set without applying anything&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Review&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Shows each resource and whether it will be Added, Modified, or Removed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Execute&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Applies the changes to the stack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Delete&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Discards the change set without applying&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Replacement&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Indicates a resource will be deleted and recreated (data loss risk!)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Change sets are critical for production deployments. If a change set shows "Replacement" for a DynamoDB table or RDS instance, that means the resource will be deleted and recreated. You'll lose data. Always review change sets before executing in production. SAM's &lt;code&gt;confirm_changeset = true&lt;/code&gt; in samconfig.toml forces this review.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Amazon Q Developer for Test Generation
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Capability&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Unit Test Generation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Generates pytest/unittest tests from your function code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mock Setup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Creates moto and unittest.mock boilerplate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Edge Case Detection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Identifies missing input validation, error handling gaps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Test Coverage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Suggests tests for happy path, error cases, and boundary conditions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡&lt;strong&gt;Know that Amazon Q Developer can generate tests and suggest improvements. But, just use &lt;a href="https://ntombizakhona.medium.com/artificial-intelligence-5028f826c856" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt;. 👇&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;⚠️ &lt;a href="https://aws.amazon.com/blogs/devops/amazon-q-developer-end-of-support-announcement/" rel="noopener noreferrer"&gt;Amazon Q Developer end-of-support announcement &lt;/a&gt; ⚠️&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  🏗️ Build A Version Management System
&lt;/h2&gt;

&lt;p&gt;Build a &lt;strong&gt;Version Management System&lt;/strong&gt; that demonstrates deployment testing automation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Published Lambda versions with immutable snapshots&lt;/li&gt;
&lt;li&gt;Aliases pointing to specific versions for environment management&lt;/li&gt;
&lt;li&gt;Weighted aliases routing traffic for canary testing&lt;/li&gt;
&lt;li&gt;CloudFormation change sets reviewed and executed through the console&lt;/li&gt;
&lt;li&gt;Lambda test events created and used for automated validation&lt;/li&gt;
&lt;/ul&gt;


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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ntombizakhona.medium.com/amazon-web-services-a8e57a9c6084" rel="noopener noreferrer"&gt;&lt;strong&gt;An AWS Account&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Part I
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Publish Lambda Versions
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Create the Lambda Function
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01.1:&lt;/strong&gt; Open the &lt;strong&gt;Lambda&lt;/strong&gt; console → &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Function name:&lt;/strong&gt; &lt;code&gt;VersionedOrderAPI&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; &lt;code&gt;Python 3.13&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01.2:&lt;/strong&gt; Paste this code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;VERSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Version 1 of the Order API.
    Each published version is an immutable snapshot of this code.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Order API&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;version&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;VERSION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;functionVersion&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 01.3:&lt;/strong&gt; Click &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 02:&lt;/strong&gt; Publish Version 1
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 02.1:&lt;/strong&gt; Click &lt;strong&gt;Actions ▼&lt;/strong&gt; → &lt;strong&gt;Publish new version&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02.2:&lt;/strong&gt; &lt;strong&gt;Version description - &lt;em&gt;optional&lt;/em&gt;:&lt;/strong&gt; &lt;code&gt;v1 - Initial release&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Publish&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 You're now viewing &lt;strong&gt;Version 1:&lt;/strong&gt; notice the &lt;strong&gt;Function ARN&lt;/strong&gt; includes &lt;code&gt;:1&lt;/code&gt; at the end&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 02.3:&lt;/strong&gt; Click &lt;strong&gt;Version 1 ▼&lt;/strong&gt; dropdown → select &lt;strong&gt;Qualifier: VersionedOrderAPI&lt;/strong&gt; to go back to the editable version&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 03:&lt;/strong&gt; Update and Publish Version 2
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 03.1:&lt;/strong&gt; Back on &lt;strong&gt;VersionedOrderAPI&lt;/strong&gt;, update the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;VERSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Version 2 of the Order API — added order validation.
    This version is a new immutable snapshot.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# New in v2: input validation
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;create&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;customerId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;customerId is required&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Order API&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;version&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;VERSION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;functionVersion&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;improvement&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Added input validation&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 03.2:&lt;/strong&gt; Click &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03.3:&lt;/strong&gt; Click &lt;strong&gt;Actions ▼&lt;/strong&gt; → &lt;strong&gt;Publish new version&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03.4&lt;/strong&gt; &lt;strong&gt;Version description - &lt;em&gt;optional&lt;/em&gt;:&lt;/strong&gt; &lt;code&gt;v2 - Added input validation&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Publish&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 04:&lt;/strong&gt; Verify Versions
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 04.1&lt;/strong&gt; Click the &lt;strong&gt;Version 2 ▼&lt;/strong&gt; dropdown, you should see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Qualifier: VersionOrderAPI&lt;/strong&gt; — &lt;em&gt;the mutable working copy&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version: 2&lt;/strong&gt; — &lt;em&gt;v1 initial release&lt;/em&gt; (immutable)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version: 1&lt;/strong&gt; — &lt;em&gt;v2 with validation&lt;/em&gt; (immutable)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Each published version gets its own ARN (&lt;code&gt;function-name:1&lt;/code&gt;, &lt;code&gt;function-name:2&lt;/code&gt;). The code and configuration are frozen. You can't edit a published version. You must publish a new one. This is what makes rollbacks possible: just point the alias back to the previous version.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part II
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create Aliases Pointing to Versions
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Create the Production Alias
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01.1:&lt;/strong&gt; On the &lt;code&gt;VersionedOrderAPI&lt;/code&gt; function page, click the &lt;strong&gt;Aliases&lt;/strong&gt; tab&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01.2:&lt;/strong&gt; Click &lt;strong&gt;Create alias&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01.3:&lt;/strong&gt; &lt;strong&gt;Create alias&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;prod&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description - &lt;em&gt;optional&lt;/em&gt;:&lt;/strong&gt; &lt;code&gt;Production traffic&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version:&lt;/strong&gt; &lt;code&gt;1&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 02:&lt;/strong&gt; Create the Staging Alias
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 02.1:&lt;/strong&gt; Click &lt;strong&gt;Create alias&lt;/strong&gt; again&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02.2:&lt;/strong&gt; &lt;strong&gt;Create alias&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;staging&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description - &lt;em&gt;optional&lt;/em&gt;:&lt;/strong&gt; &lt;code&gt;Staging and QA testing&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version:&lt;/strong&gt; &lt;code&gt;2&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 03:&lt;/strong&gt; Create the Dev Alias
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 03.1:&lt;/strong&gt; Click &lt;strong&gt;Create alias&lt;/strong&gt; again&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02.2:&lt;/strong&gt; &lt;strong&gt;Create alias&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;dev&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description - &lt;em&gt;optional&lt;/em&gt;:&lt;/strong&gt; &lt;code&gt;Development and testing&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version:&lt;/strong&gt; &lt;code&gt;$LATEST&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 04:&lt;/strong&gt; Test Each Alias
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 04.1:&lt;/strong&gt; Click on the &lt;code&gt;prod&lt;/code&gt; alias&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 04.2:&lt;/strong&gt; Go to the &lt;strong&gt;Test&lt;/strong&gt; tab → create a new event:&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="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;"create"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"customerId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CUST-001"&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;&lt;strong&gt;Step 04.3:&lt;/strong&gt; Click &lt;strong&gt;Test&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡The response should show &lt;code&gt;"version": "1.0.0"&lt;/code&gt; and &lt;code&gt;"functionVersion": "1"&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 04.4:&lt;/strong&gt; Go back and click on the &lt;code&gt;staging&lt;/code&gt; alias&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 04.5:&lt;/strong&gt; Run the same test &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡The response should show &lt;code&gt;"version": "2.0.0"&lt;/code&gt; and &lt;code&gt;"functionVersion": "2"&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 05:&lt;/strong&gt; Update an Alias to Point to a New Version
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 05.1:&lt;/strong&gt; Click on the &lt;code&gt;prod&lt;/code&gt; alias → &lt;strong&gt;Configuration&lt;/strong&gt; → &lt;strong&gt;Edit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 05.2:&lt;/strong&gt; Change &lt;strong&gt;Version&lt;/strong&gt; to &lt;code&gt;2&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;⚠️ Now production traffic uses version 2. If something goes wrong, you can repoint &lt;code&gt;prod&lt;/code&gt; back to version 1 instantly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡&lt;strong&gt;Aliases decouple consumers from specific versions. Event source mappings, API Gateway integrations, and other services should reference the alias ARN (&lt;code&gt;function-name:prod&lt;/code&gt;), not a version number. This lets you promote versions by updating the alias, not reconfiguring every consumer.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part III
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Set Up Weighted Aliases for Canary Testing
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Configure Traffic Splitting
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01.1:&lt;/strong&gt; Click on the &lt;code&gt;prod&lt;/code&gt; alias → &lt;strong&gt;Configuration&lt;/strong&gt; → &lt;strong&gt;Edit&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Version:&lt;/strong&gt; &lt;code&gt;1&lt;/code&gt; &lt;em&gt;(current stable)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 01.2:&lt;/strong&gt; Under &lt;strong&gt;Weighted alias ▼&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Additional version - optional:&lt;/strong&gt; &lt;code&gt;2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weight&lt;/strong&gt; &lt;code&gt;10&lt;/code&gt; &lt;em&gt;(meaning 10% of traffic goes to v2)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Now 90% of invocations through the &lt;code&gt;prod&lt;/code&gt; alias go to version 1, and 10% go to version 2.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 02:&lt;/strong&gt; Test the Traffic Split
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 02.1:&lt;/strong&gt; Go to the &lt;strong&gt;Test&lt;/strong&gt; tab on the &lt;code&gt;prod&lt;/code&gt; alias&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02.2:&lt;/strong&gt; Run the test event multiple times &lt;em&gt;(at least 10 times)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02.3:&lt;/strong&gt; Observe the &lt;code&gt;functionVersion&lt;/code&gt; in the responses. Most will show &lt;code&gt;1&lt;/code&gt;, some will show &lt;code&gt;2&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 03:&lt;/strong&gt; Monitor the Canary
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 03.1:&lt;/strong&gt; Open the &lt;strong&gt;CloudWatch&lt;/strong&gt; console → &lt;strong&gt;Metrics&lt;/strong&gt; → &lt;strong&gt;Lambda&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03.2:&lt;/strong&gt; Look at &lt;strong&gt;By Function Name&lt;/strong&gt; metrics&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 You can see invocation counts and error rates per version&lt;br&gt;
If version 2 has higher error rates, revert by removing the weighted configuration&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 04:&lt;/strong&gt; Remove the Canary (Promote or Rollback)
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 04.1:&lt;/strong&gt; &lt;strong&gt;To promote v2 to 100%:&lt;/strong&gt;&lt;br&gt;
Edit the &lt;code&gt;prod&lt;/code&gt; alias → set &lt;strong&gt;Version&lt;/strong&gt; to &lt;code&gt;2&lt;/code&gt; → remove the weighted configuration → &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 04.2:&lt;/strong&gt; &lt;strong&gt;To rollback to v1:&lt;/strong&gt;&lt;br&gt;
Edit the &lt;code&gt;prod&lt;/code&gt; alias → set &lt;strong&gt;Version&lt;/strong&gt; to &lt;code&gt;1&lt;/code&gt; → remove the weighted configuration → &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;In SAM, weighted aliases are managed automatically with &lt;code&gt;DeploymentPreference&lt;/code&gt;. SAM creates a CodeDeploy deployment that shifts traffic gradually and monitors CloudWatch alarms. If an alarm fires, CodeDeploy rolls back automatically. For example, &lt;code&gt;Canary10Percent5Minutes&lt;/code&gt; and &lt;code&gt;Linear10PercentEvery1Minute&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Part IV
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Create and Review CloudFormation Change Sets
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Create a CloudFormation Stack
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01.1:&lt;/strong&gt; Open the &lt;strong&gt;CloudFormation&lt;/strong&gt; console&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01.2:&lt;/strong&gt; Click &lt;strong&gt;Create stack ▼&lt;/strong&gt; → &lt;strong&gt;With new resources (standard)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01.3:&lt;/strong&gt; &lt;strong&gt;Create stack&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prepare template:&lt;/strong&gt; &lt;code&gt;Choose an existing template&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Template source:&lt;/strong&gt; &lt;code&gt;Upload a template file&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 01.4:&lt;/strong&gt; Create and upload this 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;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;Version Management Demo Stack&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;Stage&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;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;
    &lt;span class="na"&gt;AllowedValues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;dev&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;staging&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;prod&lt;/span&gt;&lt;span class="pi"&gt;]&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;OrdersTable&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::DynamoDB::Table&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;TableName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${Stage}-version-demo-orders"&lt;/span&gt;
      &lt;span class="na"&gt;BillingMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PAY_PER_REQUEST&lt;/span&gt;
      &lt;span class="na"&gt;KeySchema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PK&lt;/span&gt;
          &lt;span class="na"&gt;KeyType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HASH&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SK&lt;/span&gt;
          &lt;span class="na"&gt;KeyType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RANGE&lt;/span&gt;
      &lt;span class="na"&gt;AttributeDefinitions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PK&lt;/span&gt;
          &lt;span class="na"&gt;AttributeType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;S&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SK&lt;/span&gt;
          &lt;span class="na"&gt;AttributeType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;S&lt;/span&gt;
      &lt;span class="na"&gt;Tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&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;Environment&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;Stage&lt;/span&gt;

  &lt;span class="na"&gt;ConfigTable&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::DynamoDB::Table&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;TableName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${Stage}-version-demo-config"&lt;/span&gt;
      &lt;span class="na"&gt;BillingMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PAY_PER_REQUEST&lt;/span&gt;
      &lt;span class="na"&gt;KeySchema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;configKey&lt;/span&gt;
          &lt;span class="na"&gt;KeyType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HASH&lt;/span&gt;
      &lt;span class="na"&gt;AttributeDefinitions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;configKey&lt;/span&gt;
          &lt;span class="na"&gt;AttributeType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;S&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 01.5:&lt;/strong&gt; Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01.6:&lt;/strong&gt; &lt;strong&gt;Specify stack details:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stack name:&lt;/strong&gt; &lt;code&gt;version-demo-dev&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stage:&lt;/strong&gt; &lt;code&gt;dev&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 01.7:&lt;/strong&gt; Click &lt;strong&gt;Next&lt;/strong&gt; → &lt;strong&gt;Next&lt;/strong&gt; → &lt;strong&gt;Submit&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Wait for the stack to reach &lt;code&gt;CREATE_COMPLETE&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 02:&lt;/strong&gt; Create a Change Set to Add a Resource
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 02.1:&lt;/strong&gt; Click on the &lt;code&gt;version-demo-dev&lt;/code&gt; stack&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02.2:&lt;/strong&gt; Click &lt;strong&gt;Stack actions ▼&lt;/strong&gt; → &lt;strong&gt;Create change set&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02.3:&lt;/strong&gt; &lt;strong&gt;Create change set for version-demo-dev&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Change set type:&lt;/strong&gt; &lt;code&gt;Standard change set&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prepare template:&lt;/strong&gt; &lt;code&gt;Replace existing template&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Template source:&lt;/strong&gt; &lt;code&gt;Upload a template file&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 02.4:&lt;/strong&gt; Upload an updated template that adds a new resource (add this to the Resources section):&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;AuditTable&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::DynamoDB::Table&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;TableName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${Stage}-version-demo-audit"&lt;/span&gt;
      &lt;span class="na"&gt;BillingMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PAY_PER_REQUEST&lt;/span&gt;
      &lt;span class="na"&gt;KeySchema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PK&lt;/span&gt;
          &lt;span class="na"&gt;KeyType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HASH&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SK&lt;/span&gt;
          &lt;span class="na"&gt;KeyType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RANGE&lt;/span&gt;
      &lt;span class="na"&gt;AttributeDefinitions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PK&lt;/span&gt;
          &lt;span class="na"&gt;AttributeType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;S&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SK&lt;/span&gt;
          &lt;span class="na"&gt;AttributeType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;S&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 02.5&lt;/strong&gt; Click &lt;strong&gt;Next&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02.6:&lt;/strong&gt; &lt;strong&gt;Specify change set details&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Change set name:&lt;/strong&gt; &lt;code&gt;add-audit-table&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02.7:&lt;/strong&gt; Click &lt;strong&gt;Next&lt;/strong&gt; → &lt;strong&gt;Next&lt;/strong&gt; → &lt;strong&gt;Create change set&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 03:&lt;/strong&gt; Review the Change Set
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 03.1:&lt;/strong&gt; Click on the &lt;code&gt;add-audit-table&lt;/code&gt; change set&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03.2:&lt;/strong&gt; Review the &lt;strong&gt;Changes&lt;/strong&gt; section&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AuditTable&lt;/code&gt; → &lt;strong&gt;Action&lt;/strong&gt;: &lt;code&gt;Add&lt;/code&gt; → &lt;strong&gt;Resource type:&lt;/strong&gt; &lt;code&gt;AWS::DynamoDB::Table&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;OrdersTable&lt;/code&gt; | &lt;strong&gt;no change&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ConfigTable&lt;/code&gt; | &lt;strong&gt;no change&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 This is safe. Only adding a new resource&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 04:&lt;/strong&gt; Execute the Change Set
&lt;/h4&gt;

&lt;p&gt;Click &lt;strong&gt;Execute change set&lt;/strong&gt; → Click &lt;strong&gt;Execute change set&lt;/strong&gt; to confirm&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Wait for the stack to reach &lt;code&gt;UPDATE_COMPLETE&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Go to the &lt;strong&gt;Resources&lt;/strong&gt; tab. You should now see three DynamoDB tables&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Always check for "Replacement" in change sets. If a change set shows Replacement for a DynamoDB table, that table will be deleted and recreated. All data is lost. Common causes: changing the table name, changing the key schema, or changing certain properties that require replacement. Use &lt;code&gt;DeletionPolicy: Retain&lt;/code&gt; on critical resources to prevent accidental deletion.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part V
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create Lambda Test Events and Automate Testing
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Create Shareable Test Events
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01.1:&lt;/strong&gt; Open the &lt;strong&gt;Lambda&lt;/strong&gt; console → click on &lt;code&gt;VersionedOrderAPI&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01.2:&lt;/strong&gt; Go to the &lt;strong&gt;Test&lt;/strong&gt; tab&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01.3:&lt;/strong&gt; Click &lt;strong&gt;Create new event&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Event name:&lt;/strong&gt; &lt;code&gt;CreateOrderValid&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event sharing settings:&lt;/strong&gt; &lt;code&gt;Shareable&lt;/code&gt; (visible to all IAM users)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event JSON:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&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;"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;"create"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"customerId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CUST-001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"productId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PROD-A"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&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;&lt;strong&gt;Step 01.4:&lt;/strong&gt; Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01.5:&lt;/strong&gt; Create another test event:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Event name:&lt;/strong&gt; &lt;code&gt;CreateOrderMissinCustomer&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event sharing settings:&lt;/strong&gt; &lt;code&gt;Shareable&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event JSON:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&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;"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;"create"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"productId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PROD-A"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="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;&lt;strong&gt;Step 01.6:&lt;/strong&gt; Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 01.7:&lt;/strong&gt; Create a third test event&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Event name:&lt;/strong&gt; &lt;code&gt;GetOrderStatus&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event sharing settings:&lt;/strong&gt; Shareable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event JSON:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&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;"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;"status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"orderId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-2024-001"&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;&lt;strong&gt;Step 01.8:&lt;/strong&gt; Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 02:&lt;/strong&gt; Run All Test Events
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 02.1:&lt;/strong&gt; Select &lt;code&gt;CreateOrderValid&lt;/code&gt; from the dropdown → click &lt;strong&gt;Test&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expected: 200 with version info&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 02.2:&lt;/strong&gt; Select &lt;code&gt;CreateOrderMissingCustomer&lt;/code&gt; → click &lt;strong&gt;Test&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expected: 400 with validation error (on version 2)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 02.3&lt;/strong&gt; Select &lt;code&gt;GetOrderStatus&lt;/code&gt; → click &lt;strong&gt;Test&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expected: 200 with status info&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 03:&lt;/strong&gt; Automate Testing in a CI/CD Pipeline
&lt;/h4&gt;

&lt;p&gt;In a real pipeline, you'd automate these tests in a CodeBuild buildspec:&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="c1"&gt;# buildspec.yml — automated testing in CI/CD&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.2&lt;/span&gt;
&lt;span class="na"&gt;phases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pip install pytest moto boto3&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sam build&lt;/span&gt;
  &lt;span class="na"&gt;post_build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Run unit tests&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;python -m pytest tests/unit/ -v --junitxml=test-results/unit.xml&lt;/span&gt;

      &lt;span class="c1"&gt;# Deploy to test environment&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sam deploy --config-env test --no-confirm-changeset&lt;/span&gt;

      &lt;span class="c1"&gt;# Get the deployed API URL from stack outputs&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;API_URL=$(aws cloudformation describe-stacks \&lt;/span&gt;
          &lt;span class="s"&gt;--stack-name my-app-test \&lt;/span&gt;
          &lt;span class="s"&gt;--query 'Stacks[0].Outputs[?OutputKey==`ApiUrl`].OutputValue' \&lt;/span&gt;
          &lt;span class="s"&gt;--output text)&lt;/span&gt;

      &lt;span class="c1"&gt;# Run integration tests against the deployed stack&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;API_URL=$API_URL python -m pytest tests/integration/ -v --junitxml=test-results/integration.xml&lt;/span&gt;

      &lt;span class="c1"&gt;# Invoke Lambda directly with test events&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 lambda invoke \&lt;/span&gt;
          &lt;span class="s"&gt;--function-name VersionedOrderAPI:staging \&lt;/span&gt;
          &lt;span class="s"&gt;--payload '{"action":"create","customerId":"CUST-001"}' \&lt;/span&gt;
          &lt;span class="s"&gt;response.json&lt;/span&gt;
        &lt;span class="s"&gt;cat response.json&lt;/span&gt;

&lt;span class="na"&gt;reports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test-reports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**/*.xml"&lt;/span&gt;
    &lt;span class="na"&gt;base-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test-results&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Shareable test events are visible to all IAM users with access to the function which is useful for team collaboration. Private test events are only visible to the creator. In CI/CD, use &lt;code&gt;aws lambda invoke&lt;/code&gt; with the &lt;code&gt;--payload&lt;/code&gt; flag to run automated tests against specific aliases. Use &lt;code&gt;--qualifier&lt;/code&gt; to target a specific version or alias.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🏗️ What You Built | 📘 Exam Concepts Recap
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;What You Built&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Exam Concept&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Published immutable Lambda versions&lt;/strong&gt; (v1, v2)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Versions freeze code + config. &lt;code&gt;$LATEST&lt;/code&gt; is mutable&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Created &lt;code&gt;prod&lt;/code&gt;/&lt;code&gt;staging&lt;/code&gt;/&lt;code&gt;dev&lt;/code&gt; aliases&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Aliases as mutable pointers for environment mapping&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Repointed the &lt;code&gt;prod&lt;/code&gt; alias to a new version&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Instant promotion and rollback via aliases&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Configured a weighted alias&lt;/strong&gt; (90/10 split)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Canary deployments with traffic shifting&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Created and reviewed a CloudFormation change set&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Previewing infra changes before applying&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Watched for "Replacement" in the change set&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Avoiding accidental resource deletion / data loss&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Created shareable Lambda test events&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Team-wide test standardization&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Automated tests in a CodeBuild buildspec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Test automation in CI/CD with &lt;code&gt;aws lambda invoke&lt;/code&gt;&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ⚠️ Clean Up Protocol
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Lambda&lt;/strong&gt; → Delete &lt;code&gt;VersionedOrderAPI&lt;/code&gt; (this also deletes all versions and aliases)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudFormation&lt;/strong&gt; → Delete the &lt;code&gt;version-demo-dev&lt;/code&gt; stack (this deletes all three DynamoDB tables)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM&lt;/strong&gt; → Delete Lambda execution roles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch&lt;/strong&gt; → Delete log groups&lt;/li&gt;
&lt;/ol&gt;




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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Versions&lt;/strong&gt; are immutable snapshots of code + configuration. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aliases&lt;/strong&gt; are mutable pointers to versions. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;$LATEST&lt;/strong&gt; is mutable. Don't use it in production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weighted aliases&lt;/strong&gt; split traffic between two versions for canary deployments. SAM's &lt;code&gt;DeploymentPreference&lt;/code&gt; automates this with CodeDeploy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canary10Percent5Minutes&lt;/strong&gt; sends 10% to the new version, waits 5 minutes, then shifts 100%. &lt;strong&gt;Linear10PercentEvery1Minute&lt;/strong&gt; increases by 10% each minute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Change sets&lt;/strong&gt; preview CloudFormation changes before applying. Watch for &lt;em&gt;"Replacement"&lt;/em&gt;. Tt means resource deletion and recreation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event source mappings&lt;/strong&gt; should reference aliases, not &lt;code&gt;$LATEST&lt;/code&gt; or version numbers, so you can promote versions by updating the alias.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shareable test events&lt;/strong&gt; in the Lambda console are visible to all IAM users. Use them for team-wide test standardization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Q Developer&lt;/strong&gt; can generate unit tests, mock setup, and edge case tests from your function code.
&amp;gt; ⚠️ &lt;a href="https://aws.amazon.com/blogs/devops/amazon-q-developer-end-of-support-announcement/" rel="noopener noreferrer"&gt;Amazon Q Developer end-of-support announcement &lt;/a&gt; ⚠️&lt;/li&gt;
&lt;li&gt;In CI/CD, use &lt;code&gt;aws lambda invoke --qualifier alias-name&lt;/code&gt; to test specific versions and &lt;code&gt;aws cloudformation describe-stacks&lt;/code&gt; to get deployed resource URLs for integration tests.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/configuration-versions.html" rel="noopener noreferrer"&gt;Manage Lambda function versions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html" rel="noopener noreferrer"&gt;Create an alias for a Lambda function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/automating-updates-to-serverless-apps.html" rel="noopener noreferrer"&gt;Deploying serverless applications gradually with SAM&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-changesets.html" rel="noopener noreferrer"&gt;Update CloudFormation stacks using change sets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/test-generation.html" rel="noopener noreferrer"&gt;What is Amazon Q Developer?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html" rel="noopener noreferrer"&gt;Best practices for working with AWS Lambda functions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;a href="https://aws.amazon.com/blogs/devops/amazon-q-developer-end-of-support-announcement/" rel="noopener noreferrer"&gt;&lt;strong&gt;Amazon Q Developer end-of-support announcement&lt;/strong&gt;&lt;/a&gt; ⚠️&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;🏗️&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>certification</category>
      <category>developer</category>
    </item>
    <item>
      <title>Test Applications In Development Environments | 🏗️ Build A Testing Toolkit</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Thu, 02 Jul 2026 14:14:23 +0000</pubDate>
      <link>https://dev.to/aws-builders/test-applications-in-development-environments-build-a-testing-toolkit-3ljc</link>
      <guid>https://dev.to/aws-builders/test-applications-in-development-environments-build-a-testing-toolkit-3ljc</guid>
      <description>&lt;p&gt;&lt;strong&gt;Exam Guide:&lt;/strong&gt; Developer - Associate&lt;br&gt;
&lt;strong&gt;🏗️ Domain 3:&lt;/strong&gt;  &lt;strong&gt;&lt;em&gt;Deployment&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
📘 &lt;strong&gt;Task 2:&lt;/strong&gt; &lt;em&gt;Test Applications In Development Environments&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Testing on AWS isn't just about unit tests. You need to test locally with SAM CLI, write integration tests against deployed stages, mock AWS services and external APIs, validate event-driven flows, and use API Gateway stages for environment isolation.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  📘Concepts
&lt;/h2&gt;
&lt;h3&gt;
  
  
  The Testing Pyramid
&lt;/h3&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;/th&gt;
&lt;th&gt;&lt;em&gt;Speed&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;AWS Services&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Unit Tests&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Fast&lt;/em&gt; (ms)&lt;/td&gt;
&lt;td&gt;Single function, no external calls&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Mocked&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;pytest, moto, unittest.mock&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Integration Tests&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Medium&lt;/em&gt; (seconds)&lt;/td&gt;
&lt;td&gt;Multiple services working together&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Real&lt;/strong&gt; (deployed)&lt;/td&gt;
&lt;td&gt;pytest + requests, deployed API Gateway stage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;End-to-end Tests&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Slow&lt;/em&gt; (seconds–minutes)&lt;/td&gt;
&lt;td&gt;Full user workflow&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Real&lt;/strong&gt; (deployed)&lt;/td&gt;
&lt;td&gt;Selenium, Postman, curl against live API&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Unit tests use mocks (moto for AWS, unittest.mock for external APIs). Integration tests run against real deployed resources.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  SAM Local Testing Capabilities
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;&lt;code&gt;Command&lt;/code&gt;&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;What It Does&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;When to Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sam local invoke&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Runs a single Lambda function locally in a Docker container&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Testing individual function logic with a specific event&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sam local start-api&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Starts a local API Gateway emulator&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Testing full API request/response flow locally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sam local start-lambda&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Starts a local Lambda endpoint for SDK calls&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Testing code that invokes Lambda via the SDK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sam local generate-event&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Generates sample event payloads for AWS services&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Creating test events for S3, SQS, API Gateway, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Key Details:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SAM local uses Docker to simulate the Lambda runtime environment&lt;/li&gt;
&lt;li&gt;It reads your &lt;code&gt;template.yaml&lt;/code&gt; to understand function configuration&lt;/li&gt;
&lt;li&gt;Environment variables can be overridden with &lt;code&gt;--env-vars env.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;It does &lt;strong&gt;not&lt;/strong&gt; mock AWS services, therefore if your function calls DynamoDB, it calls real DynamoDB (or you need moto)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 &lt;code&gt;sam local invoke&lt;/code&gt; runs a function once and exits. &lt;code&gt;sam local start-api&lt;/code&gt; keeps running and accepts HTTP requests. Both use Docker containers that match the Lambda runtime. Know that SAM local does NOT mock AWS services. Your function still needs real credentials or mocked services.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Mocking Strategies
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;What to Mock&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;&lt;em&gt;How It Works&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;AWS Services&lt;/strong&gt; (DynamoDB, S3, SQS, etc.)&lt;/td&gt;
&lt;td&gt;moto (&lt;code&gt;@mock_aws&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Intercepts boto3 calls and returns realistic responses&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;External HTTP APIs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;unittest.mock (&lt;code&gt;@patch&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Replaces the HTTP client with a mock that returns controlled responses&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Environment Variables&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;os.environ&lt;/code&gt; or &lt;code&gt;monkeypatch&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Set test values for TABLE_NAME, STAGE, etc.&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lambda Context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Custom object&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Create a simple object with &lt;code&gt;function_name&lt;/code&gt;, &lt;code&gt;memory_limit_in_mb&lt;/code&gt;, etc.&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  API Gateway Stages for Environment Isolation
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Stage&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;URL Pattern&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;dev&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://{api-id}.execute-api.{region}.amazonaws.com/dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Development and local integration testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;staging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://{api-id}.execute-api.{region}.amazonaws.com/staging&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;QA and pre-production testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;prod&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://{api-id}.execute-api.{region}.amazonaws.com/prod&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Production traffic&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each stage can have its own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stage Variables:&lt;/strong&gt; different table names, feature flags, log levels per stage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Throttling Settings:&lt;/strong&gt; different rate limits per stage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logging Configuration:&lt;/strong&gt; verbose logging in dev, errors only in prod&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 Stage variables are accessible in Lambda via &lt;code&gt;event['stageVariables']&lt;/code&gt;. They let you point different stages at different backends (different DynamoDB tables, different Lambda aliases) without changing code.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Event-Driven Testing Approaches
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Approach&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;What It Tests&lt;/th&gt;
&lt;th&gt;&lt;em&gt;How&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SAM Local With Generated Events&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Function logic with realistic payloads&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sam local generate-event&lt;/code&gt; → &lt;code&gt;sam local invoke&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Console Test Events&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Function logic in the deployed environment&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Create test events in the Lambda console&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EventBridge Test Events&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rule matching and target invocation&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Send test events via the EventBridge console&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SQS Test Messages&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Queue-triggered function processing&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Send messages via the SQS console&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CloudWatch Logs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Verify function executed correctly&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Check logs after sending test events&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  🏗️ Build A Testing Toolkit
&lt;/h2&gt;

&lt;p&gt;Build a &lt;strong&gt;Testing Toolkit&lt;/strong&gt; that demonstrates testing approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Local Lambda testing with SAM CLI (&lt;code&gt;invoke&lt;/code&gt; and &lt;code&gt;start-api&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Unit tests using moto to mock DynamoDB and S3&lt;/li&gt;
&lt;li&gt;Integration tests running against a deployed API Gateway stage&lt;/li&gt;
&lt;li&gt;EventBridge rule testing with test events in the console&lt;/li&gt;
&lt;li&gt;Sample event generation with &lt;code&gt;sam local generate-event&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ntombizakhona.medium.com/amazon-web-services-a8e57a9c6084" rel="noopener noreferrer"&gt;&lt;strong&gt;An AWS Account&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/engine/install/" rel="noopener noreferrer"&gt;&lt;strong&gt;Docker Installed Locally&lt;/strong&gt;&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html" rel="noopener noreferrer"&gt;&lt;strong&gt;SAM CLI Installed&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.python.org/downloads/" rel="noopener noreferrer"&gt;&lt;strong&gt;Python 3.13&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Before You Start Read This&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💡 &lt;strong&gt;SAM local does NOT mock AWS.&lt;/strong&gt; &lt;code&gt;sam local invoke&lt;/code&gt; and &lt;code&gt;sam local start-api&lt;/code&gt; run your code in a container but call &lt;strong&gt;real&lt;/strong&gt; AWS services with your real credentials. So we create the DynamoDB table &lt;strong&gt;first&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;💡 &lt;strong&gt;API Gateway routes come from the template, not your code.&lt;/strong&gt; A path only exists if it's declared in the template's &lt;code&gt;Events&lt;/code&gt;. &lt;code&gt;GET /&lt;/code&gt; will always say "Missing Authentication Token" because there's no root route.&lt;/li&gt;
&lt;li&gt;💡 &lt;strong&gt;Region must be consistent&lt;/strong&gt; across your credentials, the table, and &lt;code&gt;env.json&lt;/code&gt;. We pin &lt;code&gt;us-east-1&lt;/code&gt; everywhere so change it only if you use a different region, but change it &lt;em&gt;everywhere&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;💡 &lt;strong&gt;Windows files need UTF-8 without BOM.&lt;/strong&gt; A BOM causes &lt;code&gt;Unable to unmarshal input: Expecting value: line 1 column 1&lt;/code&gt;. Create JSON files in VS Code and check the encoding in the bottom-right status bar.&lt;/li&gt;
&lt;li&gt;💡 &lt;strong&gt;After editing &lt;code&gt;template.yaml&lt;/code&gt;, always &lt;code&gt;sam build&lt;/code&gt; then restart &lt;code&gt;start-api&lt;/code&gt;.&lt;/strong&gt; The local server serves the &lt;em&gt;built&lt;/em&gt; template in &lt;code&gt;.aws-sam/build/&lt;/code&gt;, not your edited source.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 00:&lt;/strong&gt; Set your region once for this session so every command agrees:
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&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="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;configure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;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;blockquote&gt;
&lt;p&gt;⚠️ If you already use a different region, substitute it consistently in every command and file below.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Create the DynamoDB Table First
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Because SAM local calls real DynamoDB, the table must exist before you invoke anything.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;create-table&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;--table-name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dev-orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;--attribute-definitions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AttributeName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;AttributeType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AttributeName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;AttributeType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;--key-schema&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AttributeName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;KeyType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HASH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AttributeName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;KeyType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RANGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;--billing-mode&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PAY_PER_REQUEST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;--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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 The backtick &lt;code&gt;`&lt;/code&gt; is PowerShell's line-continuation character. On macOS/Linux/CloudShell, use &lt;code&gt;\&lt;/code&gt; instead.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;⚠️ Wait until the table is &lt;code&gt;ACTIVE&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;describe-table&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--table-name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dev-orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--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;span class="nt"&gt;--query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Table.TableStatus"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ Repeat until it prints &lt;code&gt;"ACTIVE"&lt;/code&gt; (usually a few seconds).&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;The table's key schema mirrors single-table design.&lt;/strong&gt; &lt;code&gt;PK&lt;/code&gt; (partition key) + &lt;code&gt;SK&lt;/code&gt; (sort key) let one table hold many item types.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 02:&lt;/strong&gt; Create the SAM Project
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;sam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--runtime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;python3.13&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;testing-toolkit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--app-template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hello-world&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;testing-toolkit&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡This scaffolds a project with &lt;code&gt;hello_world/app.py&lt;/code&gt;, &lt;code&gt;template.yaml&lt;/code&gt;, and &lt;code&gt;tests/&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 03:&lt;/strong&gt; Replace the Function Code
&lt;/h4&gt;

&lt;p&gt;Open &lt;code&gt;hello_world/app.py&lt;/code&gt; and replace its entire contents with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="c1"&gt;# 💡 Initialize the client OUTSIDE the handler so it's reused across warm
# invocations. Creating it inside the handler would rebuild it every call.
&lt;/span&gt;&lt;span class="n"&gt;dynamodb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dynamodb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;TABLE_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TABLE_NAME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dev-orders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Order API: create an order (POST) or fetch one (GET).&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TABLE_NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;httpMethod&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# --- Create an order ---
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/orders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ORD-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;utcnow&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%Y%m%d%H%M%S&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

            &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ORDER#&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;METADATA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;orderId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customerId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customerId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]),&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;created&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;createdAt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;utcnow&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;isoformat&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="nf"&gt;_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;orderId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;created&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="c1"&gt;# --- Get a single order ---
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/orders/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pathParameters&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;orderId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ORDER#&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;METADATA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Item&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;item&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;_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Order &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; not found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&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;_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Testing Toolkit API&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                               &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;STAGE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;local&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)})&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# 💡 Always handle exceptions. An uncaught error becomes a generic
&lt;/span&gt;        &lt;span class="c1"&gt;# 500 "Internal server error" at API Gateway, hiding the real cause.
&lt;/span&gt;        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ERROR: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;detail&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)})&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;str&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;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;The &lt;code&gt;try/except&lt;/code&gt; matters for debugging.&lt;/strong&gt; Without it, a failed DynamoDB call throws an uncaught exception and API Gateway returns a bare &lt;code&gt;Internal server error&lt;/code&gt;. By catching it and returning the error name, you can see &lt;em&gt;why&lt;/em&gt; it failed in the HTTP response itself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 04:&lt;/strong&gt; Fix the Template Routes
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ The default template only maps &lt;code&gt;GET /hello&lt;/code&gt;. Open &lt;code&gt;template.yaml&lt;/code&gt; and replace the &lt;strong&gt;entire &lt;code&gt;Resources:&lt;/code&gt; section's &lt;code&gt;HelloWorldFunction&lt;/code&gt;&lt;/strong&gt; so it matches your code's routes. Your &lt;code&gt;HelloWorldFunction&lt;/code&gt; block should look exactly like this:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;HelloWorldFunction&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;hello_world/&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;app.lambda_handler&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;python3.13&lt;/span&gt;
      &lt;span class="na"&gt;Timeout&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;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;TABLE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev-orders&lt;/span&gt;
          &lt;span class="na"&gt;STAGE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&lt;/span&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;DynamoDBCrudPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev-orders&lt;/span&gt;
      &lt;span class="na"&gt;Events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;CreateOrder&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;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;Path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/orders&lt;/span&gt;
            &lt;span class="na"&gt;Method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;post&lt;/span&gt;
        &lt;span class="na"&gt;GetOrder&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;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;Path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/orders/{orderId}&lt;/span&gt;
            &lt;span class="na"&gt;Method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;get&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Routes are configuration, not code.&lt;/strong&gt; API Gateway only serves paths declared under &lt;code&gt;Events&lt;/code&gt;. Your function's internal &lt;code&gt;if path == "/orders"&lt;/code&gt; logic never runs unless the template routes that path to it. This is why unknown paths return "Missing Authentication Token."&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;&lt;code&gt;DynamoDBCrudPolicy&lt;/code&gt; is a SAM policy template.&lt;/strong&gt; It grants &lt;strong&gt;least-privilege&lt;/strong&gt; CRUD access to just the named table, instead of a broad &lt;code&gt;AmazonDynamoDBFullAccess&lt;/code&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 05:&lt;/strong&gt; Create the Environment Variable File
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ SAM local reads env vars from a JSON file. Create a file named &lt;code&gt;env.json&lt;/code&gt; in the project root (same folder as &lt;code&gt;template.yaml&lt;/code&gt;) with this content:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&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;"HelloWorldFunction"&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;"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;"dev-orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"STAGE"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"local"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"AWS_REGION"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Windows Issue:&lt;/strong&gt; Create this file in VS Code. Look at the bottom-right status bar. If it says &lt;strong&gt;"UTF-8 with BOM"&lt;/strong&gt;, click it → &lt;strong&gt;Save with Encoding&lt;/strong&gt; → &lt;strong&gt;UTF-8&lt;/strong&gt;. A BOM breaks JSON parsing with &lt;code&gt;Expecting value: line 1 column 1 (char 0)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;The top-level key is the function's logical ID.&lt;/strong&gt; &lt;code&gt;HelloWorldFunction&lt;/code&gt; matches the resource name in &lt;code&gt;template.yaml&lt;/code&gt;. Pinning &lt;code&gt;AWS_REGION&lt;/code&gt; here guarantees the function looks for the table in the same region you created it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 06:&lt;/strong&gt; Create a Test Event File
&lt;/h4&gt;

&lt;p&gt;Create a file &lt;code&gt;create_order.json&lt;/code&gt; inside the &lt;code&gt;events/&lt;/code&gt; folder :&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;"httpMethod"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"headers"&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;"Content-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;"application/json"&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;"pathParameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"body"&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="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;customerId&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;CUST-001&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;items&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: [{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;productId&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;PROD-A&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;quantity&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 2}]}"&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;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;This mimics the API Gateway proxy event.&lt;/strong&gt; When API Gateway invokes Lambda, it wraps the HTTP request in this structure. &lt;code&gt;body&lt;/code&gt; is a JSON &lt;em&gt;string&lt;/em&gt; (escaped), not an object. That's why the code does &lt;code&gt;json.loads(event["body"])&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;⚠️ Save as UTF-8 without BOM.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 07:&lt;/strong&gt; Build
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;sam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Always build after editing &lt;code&gt;template.yaml&lt;/code&gt; or your code.&lt;/strong&gt; &lt;code&gt;sam build&lt;/code&gt; copies everything into &lt;code&gt;.aws-sam/build/&lt;/code&gt;, which is what &lt;code&gt;sam local&lt;/code&gt; actually runs. Skipping this means you test stale code/routes.&lt;/p&gt;

&lt;p&gt;⚠️ If &lt;code&gt;sam build&lt;/code&gt; complains it can't find Python 3.13, run &lt;code&gt;sam build --use-container&lt;/code&gt; (needs Docker running). It builds inside a Python 3.13 image so your local Python version doesn't matter.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 08:&lt;/strong&gt; Test with &lt;code&gt;sam local invoke&lt;/code&gt; (Direct Function Test)
&lt;/h4&gt;

&lt;p&gt;This invokes the function once with your test event. No API Gateway involved.&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;sam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;invoke&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;HelloWorldFunction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-e&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;events/create_order.json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--env-vars&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;env.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected Result:&lt;/strong&gt; a &lt;code&gt;201&lt;/code&gt; response with an &lt;code&gt;orderId&lt;/code&gt;, and no errors.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;code&gt;sam local invoke&lt;/code&gt; bypasses API Gateway.&lt;/strong&gt; It calls the handler directly with the event you pass. Routing doesn't matter here, only the function logic and its (real) AWS calls. Because the &lt;code&gt;dev-orders&lt;/code&gt; table exists in &lt;code&gt;us-east-1&lt;/code&gt;, the &lt;code&gt;put_item&lt;/code&gt; succeeds.&lt;/p&gt;

&lt;p&gt;⚠️ If you see &lt;code&gt;ResourceNotFoundException&lt;/code&gt;, the table isn't in the region the function is using.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 09:&lt;/strong&gt; Verify the item actually landed in DynamoDB:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;scan&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--table-name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dev-orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--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;span class="nt"&gt;--query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Items"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the order you just created.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 10:&lt;/strong&gt; Test with &lt;code&gt;sam local start-api&lt;/code&gt; (Full HTTP Test)
&lt;/h4&gt;

&lt;p&gt;Now test the real HTTP routing. Start the local API in one terminal:&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;sam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;start-api&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;3000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--env-vars&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;env.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Leave this running. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;The function logs (including real errors) print in THIS terminal.&lt;/strong&gt; If an HTTP call returns 500, look here for the actual stack trace.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Open a &lt;strong&gt;second&lt;/strong&gt; PowerShell terminal and send requests.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 11:&lt;/strong&gt; Create an order (POST)
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ On Windows, use &lt;code&gt;Invoke-RestMethod&lt;/code&gt;. PowerShell's &lt;code&gt;curl&lt;/code&gt; is an alias for &lt;code&gt;Invoke-WebRequest&lt;/code&gt; with different syntax, and inline &lt;code&gt;curl.exe&lt;/code&gt; JSON escaping is painful.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Invoke-RestMethod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Uri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http://localhost:3000/orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ContentType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/json"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'{"customerId": "CUST-001", "items": [{"productId": "PROD-A", "quantity": 2}]}'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;macOS / Linux / CloudShell equivalents&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/orders &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"customerId": "CUST-001", "items": [{"productId": "PROD-A", "quantity": 2}]}'&lt;/span&gt;

curl http://localhost:3000/orders/ORD-20260702093000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected Result:&lt;/strong&gt; an object with &lt;code&gt;orderId&lt;/code&gt; and &lt;code&gt;status: created&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 12:&lt;/strong&gt; Get that order (GET)
&lt;/h4&gt;

&lt;p&gt;Copy the &lt;code&gt;orderId&lt;/code&gt; from the previous response and:&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;Invoke-RestMethod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Uri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http://localhost:3000/orders/ORD-20260702093000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Replace with your actual order ID.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected Result:&lt;/strong&gt; the full order item.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;orderId    : ORD-20260702112404
status     : created
createdAt  : 2026-07-02T11:24:04.213247
PK         : ORDER#ORD-20260702112404
items      : &lt;span class="o"&gt;{&lt;/span&gt;@&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;productId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;PROD-A&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;quantity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2&lt;span class="o"&gt;}}&lt;/span&gt;
customerId : CUST-001
SK         : METADATA
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Expected "errors" that are actually correct:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET http://localhost:3000/&lt;/code&gt; → "Missing Authentication Token:" there's no root route. Fine.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET http://localhost:3000/orders&lt;/code&gt; (no ID) → "Missing Authentication Token:" — &lt;code&gt;POST /orders&lt;/code&gt; and &lt;code&gt;GET /orders/{orderId}&lt;/code&gt; exist. Fine.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Stop the server with &lt;strong&gt;Ctrl+C&lt;/strong&gt; when done. If port 3000 stays stuck:&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;Get-Process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Get-NetTCPConnection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-LocalPort&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OwningProcess&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Stop-Process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Force&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Step 13:&lt;/strong&gt; Unit Tests with moto (No AWS Needed)
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 It needs no real AWS, no Docker, no network. &lt;code&gt;moto&lt;/code&gt; mocks AWS services &lt;strong&gt;in memory&lt;/strong&gt;, so tests are fast and isolated.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pytest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;moto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;boto3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Windows Path-Length&lt;/strong&gt; If you installed Python from the &lt;strong&gt;Microsoft Store&lt;/strong&gt;, its packages live under a very long base path (&lt;code&gt;AppData\Local\Packages\PythonSoftwareFoundation...\LocalCache\...&lt;/code&gt;), and moto's deeply nested files push past Windows' 260-character limit, failing with &lt;code&gt;OSError: [Errno 2] No such file or directory&lt;/code&gt;. Note that &lt;code&gt;moto[dynamodb]&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; help The extra only changes dependencies, not the files installed. Two fixes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix A (permanent, recommended):&lt;/strong&gt; Enable long paths. In an &lt;strong&gt;Administrator&lt;/strong&gt; PowerShell, run &lt;code&gt;New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force&lt;/code&gt;, then &lt;strong&gt;reboot&lt;/strong&gt; and re-run the install.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix B (no admin):&lt;/strong&gt; Use a virtual environment at a &lt;strong&gt;short&lt;/strong&gt; path so the total path stays under the limit: &lt;code&gt;python -m venv C:\venv&lt;/code&gt; then &lt;code&gt;C:\venv\Scripts\Activate.ps1&lt;/code&gt; then &lt;code&gt;pip install pytest moto boto3&lt;/code&gt;. Don't put the venv inside the deep project folder. Use &lt;code&gt;C:\venv&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 14:&lt;/strong&gt; Create &lt;code&gt;tests/unit/test_app.py&lt;/code&gt;:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;moto&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mock_aws&lt;/span&gt;


&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dynamodb_table&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;💡 moto intercepts boto3 calls and simulates DynamoDB in memory.
    We create the table inside the mock BEFORE importing the handler.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;mock_aws&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AWS_DEFAULT_REGION&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TABLE_NAME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dev-orders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

        &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dynamodb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;TableName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dev-orders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;KeySchema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AttributeName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;KeyType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HASH&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AttributeName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;KeyType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RANGE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;AttributeDefinitions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AttributeName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AttributeType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;S&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AttributeName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AttributeType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;S&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;BillingMode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PAY_PER_REQUEST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_create_order_returns_201&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dynamodb_table&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# 💡 Import the handler AFTER the mock is active so its boto3
&lt;/span&gt;    &lt;span class="c1"&gt;# client is intercepted by moto.
&lt;/span&gt;    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;hello_world.app&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;lambda_handler&lt;/span&gt;

    &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;httpMethod&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/orders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customerId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CUST-001&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;productId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PROD-A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quantity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;created&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;orderId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ORD-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_missing_order_returns_404&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dynamodb_table&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;hello_world.app&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;lambda_handler&lt;/span&gt;

    &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;httpMethod&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/orders/DOESNOTEXIST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pathParameters&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;orderId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DOESNOTEXIST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Step 15:&lt;/strong&gt; Run the tests
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pytest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tests/unit/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-v&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected Result:&lt;/strong&gt; both tests pass with no real AWS calls.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Unit vs Integration:&lt;/strong&gt; these moto tests are &lt;strong&gt;unit tests&lt;/strong&gt; (fast, isolated, mocked). The &lt;code&gt;Invoke-RestMethod&lt;/code&gt; calls against &lt;code&gt;start-api&lt;/code&gt; in Step 9 hit real DynamoDB, closer to &lt;strong&gt;integration tests&lt;/strong&gt;. The difference: mock for unit tests, real deployed resources for integration tests.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 16:&lt;/strong&gt; Deploy and Run Integration Tests Against a Real Stage
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Unit Tests&lt;/strong&gt; (moto) prove your logic. &lt;strong&gt;Integration Tests&lt;/strong&gt; prove the whole stack works (API Gateway, Lambda, and DynamoDB together) against a real deployed environment.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;sam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;deploy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--guided&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;code&gt;sam deploy&lt;/code&gt; provisions real infrastructure&lt;/strong&gt; via CloudFormation: the Lambda, the API Gateway stage, IAM roles, and the event mappings. Unlike &lt;code&gt;sam local&lt;/code&gt;, this creates resources that cost money and must be cleaned up.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 17:&lt;/strong&gt; When it finishes, copy the &lt;strong&gt;API Gateway endpoint URL&lt;/strong&gt; from the &lt;code&gt;Outputs&lt;/code&gt;. It looks like:
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;https://abc123.execute-api.us-east-1.amazonaws.com/Prod/&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Note the stage is &lt;strong&gt;&lt;code&gt;Prod&lt;/code&gt;&lt;/strong&gt; (SAM's default), so your full path is &lt;code&gt;.../Prod/orders&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 18:&lt;/strong&gt; Run integration tests against the deployed stage
&lt;/h4&gt;

&lt;p&gt;Create &lt;code&gt;tests/integration/test_api.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="c1"&gt;# 💡 Pass the deployed URL via an environment variable so the same tests
# can run against dev, staging, or prod without code changes.
&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;rstrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_create_and_get_order&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Create
&lt;/span&gt;    &lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/orders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customerId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CUST-TEST-001&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;productId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PROD-A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quantity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;
    &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;orderId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Retrieve the same order
&lt;/span&gt;    &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/orders/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;orderId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_missing_order_returns_404&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/orders/DOESNOTEXIST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Step 19:&lt;/strong&gt; Install &lt;code&gt;requests&lt;/code&gt; and run the tests, passing your real URL:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;requests&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;API_URL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://abc123.execute-api.us-east-1.amazonaws.com/Prod"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pytest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tests/integration/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-v&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected Result:&lt;/strong&gt; both tests pass against live AWS.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Testing pyramid in action.&lt;/strong&gt; Many fast unit tests (moto), fewer integration tests (deployed stage), fewest end-to-end tests. Integration tests catch problems mocks can't: IAM permissions, API Gateway mapping, real DynamoDB behaviour.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 20:&lt;/strong&gt; Test an Event-Driven Flow with EventBridge
&lt;/h4&gt;

&lt;p&gt;Not everything is an API. Let's test an event-driven path using the EventBridge console.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Create a rule and target (console)&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 20.1&lt;/strong&gt;  Open the &lt;strong&gt;EventBridge&lt;/strong&gt; console → &lt;strong&gt;Rules&lt;/strong&gt; → &lt;strong&gt;Create rule&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 20.2:&lt;/strong&gt; &lt;strong&gt;Define rule detail&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;order-placed-rule&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event bus:&lt;/strong&gt; &lt;code&gt;default&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 20.3:&lt;/strong&gt; &lt;strong&gt;Build event pattern&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Event source:&lt;/strong&gt; &lt;code&gt;Other&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creation method:&lt;/strong&gt; Custom pattern (JSON editor).&lt;/li&gt;
&lt;li&gt;Paste:
&lt;/li&gt;
&lt;/ul&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;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"orders.api"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"detail-type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"OrderPlaced"&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;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 20.4:&lt;/strong&gt; &lt;strong&gt;Select target(s)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Target:&lt;/strong&gt; AWS service → &lt;strong&gt;Lambda function&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Target location:&lt;/strong&gt; &lt;code&gt;Target in this current account&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Function:&lt;/strong&gt; &lt;code&gt;testing-toolkit-HelloWorldFunction...&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt; → &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 20.5:&lt;/strong&gt; Click &lt;strong&gt;Create rule&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 21:&lt;/strong&gt;  Send a test event
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 21.1&lt;/strong&gt; EventBridge console → &lt;strong&gt;Event buses&lt;/strong&gt; → &lt;strong&gt;default&lt;/strong&gt; → &lt;strong&gt;Send events&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 21.2&lt;/strong&gt; &lt;strong&gt;Send events&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Event source:&lt;/strong&gt; &lt;code&gt;orders.api&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detail type:&lt;/strong&gt; &lt;code&gt;OrderPlaced&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;**Event detail:
&lt;/li&gt;
&lt;/ul&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;"orderId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-EVT-001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"customerId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CUST-EVT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="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;Click &lt;strong&gt;Send&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 22:&lt;/strong&gt;  Verify it was delivered
&lt;/h4&gt;

&lt;p&gt;Open the Lambda function → &lt;strong&gt;Monitor&lt;/strong&gt; → &lt;strong&gt;View CloudWatch logs&lt;/strong&gt; → newest log stream. You'll see the event was received.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Event pattern matching is exact by default.&lt;/strong&gt; &lt;code&gt;source&lt;/code&gt; and &lt;code&gt;detail-type&lt;/code&gt; must match precisely. Content-based filtering (prefix, numeric, &lt;code&gt;exists&lt;/code&gt;) can match on &lt;code&gt;detail&lt;/code&gt; fields. Sending test events from the console is the fastest way to validate rules.&lt;/p&gt;

&lt;p&gt;⚠️  Our handler expects an API Gateway event shape, so it'll log the EventBridge event and likely return the default message. That's fine. The point here is proving the &lt;strong&gt;rule → target delivery&lt;/strong&gt;, not the handler's response.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 23:&lt;/strong&gt; Generate Realistic Test Events with SAM
&lt;/h4&gt;

&lt;p&gt;Hand-writing event JSON is error-prone. &lt;code&gt;sam local generate-event&lt;/code&gt; produces the exact structure AWS services send.&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="c"&gt;# API Gateway POST event&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;sam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;generate-event&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;apigateway&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;aws-proxy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'{\"customerId\":\"CUST-001\"}'&lt;/span&gt;&lt;span class="w"&gt; &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;events/api_post.json&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# S3 object-created event&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;sam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;generate-event&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;put&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--bucket&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;my-bucket&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;orders/data.json&lt;/span&gt;&lt;span class="w"&gt; &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;events/s3_put.json&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# SQS message event&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;sam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;generate-event&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sqs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;receive-message&lt;/span&gt;&lt;span class="w"&gt; &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;events/sqs_message.json&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# EventBridge (CloudWatch) scheduled event&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;sam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;generate-event&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cloudwatch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;scheduled-event&lt;/span&gt;&lt;span class="w"&gt; &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;events/scheduled.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List what's available:&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;sam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;generate-event&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--help&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;sam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;generate-event&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--help&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Generated events include all the metadata&lt;/strong&gt; (request IDs, ARNs, timestamps) real triggers send, so your local tests behave like production. This is far more reliable than guessing the event shape by hand.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;Symptom&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Cause&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Missing Authentication Token&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Path/method not in template &lt;code&gt;Events&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Use a declared route; &lt;code&gt;sam build&lt;/code&gt; + restart after edits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ResourceNotFoundException&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Table missing or wrong region&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Create table in the region from &lt;code&gt;env.json&lt;/code&gt; &amp;amp; keep regions consistent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Internal server error&lt;/code&gt; (500)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Uncaught exception in the function&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Read the &lt;code&gt;start-api&lt;/code&gt; terminal for the real stack trace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Expecting value: line 1 column 1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;JSON file saved with a BOM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Save &lt;code&gt;env.json&lt;/code&gt;/event files as UTF-8 &lt;strong&gt;without&lt;/strong&gt; BOM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Binary validation failed for python&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Python 3.13 not on PATH&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sam build --use-container&lt;/code&gt; or install Python 3.13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;curl&lt;/code&gt; fails in PowerShell&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;curl&lt;/code&gt; is an alias for &lt;code&gt;Invoke-WebRequest&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;Invoke-RestMethod&lt;/code&gt;, or &lt;code&gt;curl.exe&lt;/code&gt; with a body file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Port 3000 in use&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Old &lt;code&gt;start-api&lt;/code&gt; still running&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Ctrl+C&lt;/code&gt;, or kill the PID on port 3000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ⚠️ Clean Up Protocol
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;1&lt;/em&gt;&lt;/strong&gt; Delete the deployed stack&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;sam&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;delete&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--stack-name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;testing-toolkit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--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;&lt;strong&gt;&lt;em&gt;2&lt;/em&gt;&lt;/strong&gt; Delete the DynamoDB table&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;aws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;delete-table&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--table-name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;dev-orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--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;&lt;strong&gt;&lt;em&gt;3&lt;/em&gt;&lt;/strong&gt; &lt;strong&gt;EventBridge&lt;/strong&gt; → delete &lt;code&gt;order-placed-rule&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;4&lt;/em&gt;&lt;/strong&gt; &lt;strong&gt;CloudWatch&lt;/strong&gt; → delete the function's log groups&lt;br&gt;
&lt;strong&gt;&lt;em&gt;5&lt;/em&gt;&lt;/strong&gt; Remove the local &lt;code&gt;testing-toolkit&lt;/code&gt; folder if you're done&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;code&gt;sam delete&lt;/code&gt; tears down the whole CloudFormation stack&lt;/strong&gt;, so you don't have to hunt for individual resources. The manually-created DynamoDB table isn't part of the stack, so delete it separately.**&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🏗️ What You Built | 📘 Exam Concepts Recap
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;What You Built&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Exam Concept&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Created the table before invoking&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;SAM local calls &lt;strong&gt;real&lt;/strong&gt; AWS, it does not mock services&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pinned region in &lt;code&gt;env.json&lt;/code&gt; and every command&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Region consistency across credentials, resources, and config&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Declared routes in the template &lt;code&gt;Events&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;API Gateway routing is configuration, not code&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Used &lt;code&gt;DynamoDBCrudPolicy&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;SAM policy templates for least privilege&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;sam local invoke&lt;/code&gt; with an event file&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Direct function testing, bypassing API Gateway&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;sam local start-api&lt;/code&gt; + HTTP calls&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Local API Gateway emulation (integration-style)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Wrapped the handler in &lt;code&gt;try/except&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Uncaught errors surface as generic 500s&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;moto &lt;code&gt;@mock_aws&lt;/code&gt; unit tests&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Mocking AWS in memory for fast, isolated unit tests&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Imported the handler after the mock&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;moto must be active before boto3 clients are created&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;sam deploy --guided&lt;/code&gt; + integration tests&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Testing against real deployed resources&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Passed the API URL via &lt;code&gt;$env:API_URL&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Same tests run against any environment&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EventBridge rule + test event&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Validating event-driven delivery and pattern matching&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;sam local generate-event&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Realistic event payloads for S3, SQS, API GW, etc.&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;sam delete&lt;/code&gt; for teardown&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;CloudFormation stack removal&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Recognised "Missing Authentication Token"&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Generic API Gateway "no matching route" message&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-test-and-debug.html" rel="noopener noreferrer"&gt;Test your serverless application with AWS SAM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.getmoto.org/en/latest/" rel="noopener noreferrer"&gt;Moto: Mock AWS Services&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/testing-guide.html" rel="noopener noreferrer"&gt;How to test serverless functions and applications&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-policy-templates.html" rel="noopener noreferrer"&gt;AWS SAM policy templates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html" rel="noopener noreferrer"&gt;Lambda proxy integrations in API Gateway&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;🏗️&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>certification</category>
      <category>developer</category>
    </item>
    <item>
      <title>Prepare Application Artifacts To Be Deployed To AWS | 🏗️ Build A Multi-Environment Serverless App</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Wed, 01 Jul 2026 15:49:12 +0000</pubDate>
      <link>https://dev.to/aws-builders/prepare-application-artifacts-to-be-deployed-to-aws-build-a-multi-environment-serverless-app-5cj2</link>
      <guid>https://dev.to/aws-builders/prepare-application-artifacts-to-be-deployed-to-aws-build-a-multi-environment-serverless-app-5cj2</guid>
      <description>&lt;p&gt;&lt;strong&gt;Exam Guide:&lt;/strong&gt; Developer - Associate&lt;br&gt;
&lt;strong&gt;🏗️ Domain 3:&lt;/strong&gt;  &lt;strong&gt;&lt;em&gt;Deployment&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
📘 &lt;strong&gt;Task 1:&lt;/strong&gt; &lt;em&gt;Prepare Application Artifacts To Be Deployed To AWS&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Before you can deploy anything to AWS, you need to package it properly. This task covers Lambda deployment packaging (zip vs container), managing dependencies, structuring projects for multi-environment deployment, and using AWS AppConfig for runtime configuration.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  📘Concepts
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Lambda Deployment Packaging Options
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Max Size&lt;/th&gt;
&lt;th&gt;Build Complexity&lt;/th&gt;
&lt;th&gt;Cold Start&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Zip Package&lt;/strong&gt; &lt;em&gt;(inline editor)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;3 MB (editor limit)&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Fastest&lt;/td&gt;
&lt;td&gt;Simple functions, no dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Zip Package&lt;/strong&gt; &lt;em&gt;(upload)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;50 MB compressed / 250 MB uncompressed&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;td&gt;Most Lambda functions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Zip + Lambda Layers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;250 MB total (function + all layers)&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;td&gt;Shared dependencies across functions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Container Image&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;10 GB&lt;/td&gt;
&lt;td&gt;Higher&lt;/td&gt;
&lt;td&gt;Slower (first invoke)&lt;/td&gt;
&lt;td&gt;ML libraries, large dependencies, custom runtimes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡&lt;strong&gt;If a scenario is about a deployment package exceeding 250 MB, the answer is container images. If it mentions sharing dependencies across multiple functions, the answer is Lambda Layers. Zip is the default for most workloads.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Lambda Layers
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Aspect&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Detail&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What They Are&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Zip archives containing libraries, custom runtimes, or other dependencies&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Max Layers Per Function&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;5&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Size Limit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;250 MB total&lt;/em&gt; (function code + all layers uncompressed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Versioning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Each publish creates an immutable version&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sharing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Can be shared across functions, accounts, or made public&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Path&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Contents extracted to &lt;code&gt;/opt&lt;/code&gt; in the execution environment&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Dependency Management Strategies
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Strategy&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;How It Works&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Pros&lt;/th&gt;
&lt;th&gt;Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bundle In Zip&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Install deps into package directory, zip together&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Simple, self-contained&lt;/td&gt;
&lt;td&gt;Larger package, duplicated across functions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lambda Layers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Package deps as a layer, attach to functions&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Shared across functions, smaller deploys&lt;/td&gt;
&lt;td&gt;Layer version management, 5-layer limit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Container Image&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Install deps in Dockerfile&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Full control, large deps supported&lt;/td&gt;
&lt;td&gt;Slower cold starts, ECR management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;sam build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;SAM resolves deps from requirements.txt automatically&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Easiest, handles everything&lt;/td&gt;
&lt;td&gt;Requires SAM CLI&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Environment-Specific Configuration Approaches
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Approach&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;When Config Is Resolved&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SAM Parameters + samconfig.toml&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Deploy time&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Resource names, table names, stage-specific infra&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lambda Environment Variables&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Deploy time&lt;/em&gt; (baked into function config)&lt;/td&gt;
&lt;td&gt;Simple key-value config per environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSM Parameter Store&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Runtime&lt;/em&gt; (fetched by function)&lt;/td&gt;
&lt;td&gt;Config that changes without redeployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS AppConfig&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Runtime&lt;/em&gt; (with gradual rollout)&lt;/td&gt;
&lt;td&gt;Feature flags, config that needs validation and rollback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Secrets Manager&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Runtime&lt;/em&gt; (fetched by function)&lt;/td&gt;
&lt;td&gt;Credentials that rotate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;If the question is &lt;em&gt;change configuration without redeploying&lt;/em&gt;, the answer is Parameter Store or AppConfig. If it says &lt;em&gt;gradual rollout&lt;/em&gt; or &lt;em&gt;validate configuration before applying,&lt;/em&gt; the answer is AppConfig.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  AWS AppConfig Overview
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Feature&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Detail&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What It Does&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Deploy configuration changes independently from code, with validation and rollout strategies&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deployment Strategies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;AllAtOnce, Linear, Exponential&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Validators&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;JSON Schema or Lambda function to validate config before deployment&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rollback&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Automatic rollback if CloudWatch alarm triggers during deployment&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Integration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Lambda extension for caching, or direct API calls&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Free for configuration retrieval. Charges for feature flag evaluations&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Project Structure Best Practices
&lt;/h3&gt;

&lt;p&gt;A well-structured SAM project separates handlers, shared code, tests, and configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-app/
├── template.yaml              # SAM/CloudFormation template
├── samconfig.toml             # Per-environment deployment config
├── src/
│   ├── handlers/              # One file per Lambda function
│   │   ├── create_order.py
│   │   ├── get_order.py
│   │   └── process_payment.py
│   └── shared/                # Shared utilities across handlers
│       ├── models.py
│       └── utils.py
├── tests/
│   ├── unit/                  # Fast, no AWS calls
│   │   ├── test_create_order.py
│   │   └── test_get_order.py
│   └── integration/           # Against deployed resources
│       └── test_api.py
├── events/                    # Sample event payloads for testing
│   ├── create_order.json
│   └── get_order.json
└── requirements.txt           # Python dependencies
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🏗️ Build A Multi-Environment Serverless App
&lt;/h2&gt;

&lt;p&gt;Build a &lt;strong&gt;Multi-Environment Serverless App&lt;/strong&gt; that demonstrates real-world packaging and deployment patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Lambda function packaged with dependencies via zip upload in the console&lt;/li&gt;
&lt;li&gt;A container image built and pushed to ECR&lt;/li&gt;
&lt;li&gt;AWS AppConfig set up with feature flags and a deployment strategy&lt;/li&gt;
&lt;li&gt;A SAM project structured for multi-environment deployment&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;samconfig.toml&lt;/code&gt; configured for dev, staging, and prod&lt;/li&gt;
&lt;/ul&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ntombizakhona.medium.com/amazon-web-services-a8e57a9c6084" rel="noopener noreferrer"&gt;&lt;strong&gt;An AWS Account&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/engine/install/" rel="noopener noreferrer"&gt;&lt;strong&gt;Docker Installed Locally&lt;/strong&gt;&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html" rel="noopener noreferrer"&gt;&lt;strong&gt;SAM CLI Installed&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Part I
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Package a Lambda Function with Dependencies (Zip Upload)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Create the Deployment Package Locally
&lt;/h4&gt;

&lt;p&gt;Create a zip file on your local machine and upload it through the console.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Common Windows mistake:&lt;/strong&gt; Don't create the zip with Explorer (right-click → Send to → Compressed folder). That wraps everything inside a subfolder, so your handler ends up at &lt;code&gt;package/lambda_function.py&lt;/code&gt; and Lambda throws &lt;code&gt;No module named 'lambda_function'&lt;/code&gt;. Rather build the zip in CloudShell.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Create a project directory and install dependencies&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="nb"&gt;mkdir &lt;/span&gt;lambda-package &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;lambda-package

&lt;span class="c"&gt;# Create requirements.txt&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; requirements.txt &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
requests==2.31.0
boto3&amp;gt;=1.28.0
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Install dependencies into a package directory&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt &lt;span class="nt"&gt;-t&lt;/span&gt; package/

&lt;span class="c"&gt;# Create your function code&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; package/lambda_function.py &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
import json
import requests
import boto3

def lambda_handler(event, context):
    """
    Lambda function packaged with external dependencies.
    The 'requests' library is bundled in the zip package.
    boto3 is included in the Lambda runtime, but pinning
    a version in requirements.txt ensures consistency.
    """
    # Use the bundled 'requests' library
    response = requests.get('https://httpbin.org/json')

    return {
        'statusCode': 200,
        'body': json.dumps({
            'message': 'Function with bundled dependencies',
            'externalApiStatus': response.status_code,
            'requestsVersion': requests.__version__
        })
    }
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Create the zip package&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;package &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; zip &lt;span class="nt"&gt;-r&lt;/span&gt; ../deployment.zip &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Upload via the Console&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Open the &lt;strong&gt;Lambda&lt;/strong&gt; console → &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Function name:&lt;/strong&gt; &lt;code&gt;PackagedFunction&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; &lt;code&gt;Python 3.12&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; In the &lt;strong&gt;Code source&lt;/strong&gt; section, click &lt;strong&gt;Update ▼&lt;/strong&gt; → &lt;strong&gt;Update from a .zip file&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; &lt;strong&gt;Upload&lt;/strong&gt;, select your &lt;code&gt;deployment.zip&lt;/code&gt; file&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; Click &lt;strong&gt;Update&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Test the Function&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; Go to the &lt;strong&gt;Test&lt;/strong&gt; tab → create a test event with &lt;code&gt;{}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Test&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Verify the response shows the &lt;code&gt;requests&lt;/code&gt; library version and a successful API call&lt;/p&gt;

&lt;p&gt;💡&lt;strong&gt;The zip upload limit is 50 MB compressed. If your package exceeds this, upload to S3 first and reference the S3 location. The uncompressed limit is 250 MB. &lt;code&gt;sam build&lt;/code&gt; handles all of this automatically. It installs dependencies, creates the zip, and uploads to S3.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part II
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Build And Push A Container Image To ECR
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Create an ECR Repository&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;ECR&lt;/strong&gt; console &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Click &lt;strong&gt;Create repository&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repository name:&lt;/strong&gt; &lt;code&gt;lambda-container-demo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image Tag immutability:&lt;/strong&gt; &lt;code&gt;Mutable&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;▼ Image scanning settings - &lt;em&gt;deprecated&lt;/em&gt;:&lt;/strong&gt; Scan on push → &lt;code&gt;Enabled&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Click into the repository &lt;strong&gt;Summary&lt;/strong&gt; and note the &lt;strong&gt;URI&lt;/strong&gt; (e.g., &lt;code&gt;123456789012.dkr.ecr.us-east-1.amazonaws.com/lambda-container-demo&lt;/code&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Build and Push the Image Locally&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; Create a &lt;code&gt;Dockerfile&lt;/code&gt; and function code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Use the official AWS Lambda Python base image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; public.ecr.aws/lambda/python:3.12&lt;/span&gt;

&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# Copy function code&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app.py ${LAMBDA_TASK_ROOT}/&lt;/span&gt;

&lt;span class="c"&gt;# Set the handler&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["app.lambda_handler"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; Create &lt;code&gt;app.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Lambda function running as a container image.
    Container images support up to 10 GB — useful for
    ML libraries, large datasets, or custom runtimes.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hello from a container-based Lambda!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;runtime&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Container image (Python 3.12)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;maxPackageSize&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;10 GB (vs 250 MB for zip)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; Build and push&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="c"&gt;# Authenticate Docker with ECR&lt;/span&gt;
aws ecr get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 | &lt;span class="se"&gt;\&lt;/span&gt;
  docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; 123456789012.dkr.ecr.us-east-1.amazonaws.com

&lt;span class="c"&gt;# Build the image&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; lambda-container-demo &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Tag for ECR&lt;/span&gt;
docker tag lambda-container-demo:latest &lt;span class="se"&gt;\&lt;/span&gt;
  123456789012.dkr.ecr.us-east-1.amazonaws.com/lambda-container-demo:latest

&lt;span class="c"&gt;# Push to ECR&lt;/span&gt;
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/lambda-container-demo:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Alternatively&lt;/strong&gt;&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="c"&gt;# Authenticate Docker with ECR&lt;/span&gt;
aws ecr get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 | &lt;span class="se"&gt;\&lt;/span&gt;
  docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; 123456789012.dkr.ecr.us-east-1.amazonaws.com

&lt;span class="c"&gt;# Build the image — disable attestations so Lambda accepts the manifest&lt;/span&gt;
docker buildx build &lt;span class="nt"&gt;--provenance&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="nt"&gt;--sbom&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; 123456789012.dkr.ecr.us-east-1.amazonaws.com/lambda-container-demo:latest &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--push&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Manifest Format Gotcha:&lt;/strong&gt; If you build with a plain &lt;code&gt;docker build&lt;/code&gt; on a recent Docker Desktop, BuildKit adds provenance and SBOM attestations that produce an &lt;strong&gt;OCI image index&lt;/strong&gt;. Lambda rejects this with: &lt;em&gt;"The image manifest, config or layer media type for the source image ... is not supported."&lt;/em&gt; Lambda only accepts &lt;strong&gt;Docker Image Manifest V2 Schema 2&lt;/strong&gt;. The &lt;code&gt;--provenance=false --sbom=false&lt;/code&gt; flags above force the compatible format. Alternatively, set &lt;code&gt;DOCKER_BUILDKIT=0&lt;/code&gt; before building to use the legacy builder, which always produces the compatible manifest.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you prefer the separate tag-and-push steps (with BuildKit disabled):&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="c"&gt;# PowerShell: $env:DOCKER_BUILDKIT=0   |   CMD: set DOCKER_BUILDKIT=0&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; lambda-container-demo &lt;span class="nb"&gt;.&lt;/span&gt;
docker tag lambda-container-demo:latest &lt;span class="se"&gt;\&lt;/span&gt;
  123456789012.dkr.ecr.us-east-1.amazonaws.com/lambda-container-demo:latest
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/lambda-container-demo:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Verify in the Console&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; Go back to the &lt;strong&gt;ECR&lt;/strong&gt; console → click into &lt;code&gt;lambda-container-demo&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You should see your image with the &lt;code&gt;latest&lt;/code&gt; tag&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Create a Lambda Function from the Container Image&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 07:&lt;/strong&gt; Open the &lt;strong&gt;Lambda&lt;/strong&gt; console → &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 08:&lt;/strong&gt; Select &lt;strong&gt;Container image&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Function name:&lt;/strong&gt; &lt;code&gt;ContainerLambdaDemo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container image URI:&lt;/strong&gt; Click &lt;strong&gt;Browse images&lt;/strong&gt; → select &lt;code&gt;lambda-container-demo&lt;/code&gt; → select the &lt;code&gt;latest&lt;/code&gt; tag&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 09:&lt;/strong&gt; Test with an empty event &lt;code&gt;{}&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Container-based Lambda functions use ECR for image storage. The image must implement the Lambda Runtime API. AWS provides base images for all supported runtimes (&lt;code&gt;public.ecr.aws/lambda/python:3.12&lt;/code&gt;). You can also use arbitrary base images with the Runtime Interface Client.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part III
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Set Up AWS AppConfig with Feature Flags
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Create an AppConfig Application&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;AppConfig&lt;/strong&gt; console&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Click &lt;strong&gt;Get started&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; &lt;strong&gt;Select configuration type&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Configuration options:&lt;/strong&gt; &lt;code&gt;Feature flag&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration profile name:&lt;/strong&gt; &lt;code&gt;feauture-flags&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; &lt;strong&gt;Specify configuration data&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flag name:&lt;/strong&gt; &lt;code&gt;New Checkout Flow&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flag key:&lt;/strong&gt; &lt;code&gt;new-checkout-flow&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flag description - &lt;em&gt;Optional&lt;/em&gt;:&lt;/strong&gt; &lt;code&gt;Enable the redesigned checkout experience&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Variants:&lt;/strong&gt; &lt;code&gt;Basic flag&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enabled value:&lt;/strong&gt; Toggle &lt;code&gt;**ON**&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; &lt;strong&gt;Review and save&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Save to application&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Application name:&lt;/strong&gt; &lt;code&gt;orders-service&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description:&lt;/strong&gt; &lt;code&gt;Feature flags and configuration for the orders service&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Save and continue to deploy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; &lt;strong&gt;Start deployment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Environment:&lt;/strong&gt; Click &lt;code&gt;Create environment&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 07:&lt;/strong&gt; &lt;strong&gt;Create environment&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;production&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description:&lt;/strong&gt; &lt;code&gt;Production environment&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create environment&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Add A Feature Flag&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 08:&lt;/strong&gt; Click &lt;strong&gt;Add flag&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flag name:&lt;/strong&gt; &lt;code&gt;Dark Mode&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flag key:&lt;/strong&gt; &lt;code&gt;dark-mode&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description:&lt;/strong&gt; &lt;code&gt;Enable dark mode UI&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Variants:&lt;/strong&gt; &lt;code&gt;Basic flag&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enabled value:&lt;/strong&gt; Toggle &lt;code&gt;**OFF**&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Save new version&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Create a Deployment Strategy&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 09:&lt;/strong&gt; &lt;strong&gt;Deployment strategies&lt;/strong&gt; → &lt;strong&gt;Create deployment strategy&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;GradualRollout&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description:&lt;/strong&gt; &lt;code&gt;Deploy over 10 minutes with bake time&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment type:&lt;/strong&gt; &lt;code&gt;Linear ▼&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step percentage:&lt;/strong&gt; &lt;code&gt;20&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment time:&lt;/strong&gt; &lt;code&gt;10 minutes&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bake time:&lt;/strong&gt; &lt;code&gt;5 minutes&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create deployment strategy&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Deploy the Configuration&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 10:&lt;/strong&gt; Go to your &lt;code&gt;orders-service&lt;/code&gt; &lt;strong&gt;application&lt;/strong&gt; → &lt;code&gt;production&lt;/code&gt; &lt;strong&gt;environment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Start deployment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 11:&lt;/strong&gt; &lt;strong&gt;Start deployment&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Configuration profile:&lt;/strong&gt; &lt;code&gt;feature-flags&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosted configuration version:&lt;/strong&gt; &lt;code&gt;(latest)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment strategy:&lt;/strong&gt; &lt;code&gt;GradualRollout&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Start deployment&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Watch the deployment progress. It rolls out 20% at a time over 10 minutes&lt;/p&gt;

&lt;p&gt;💡&lt;strong&gt;AppConfig deployment strategies control how fast configuration changes roll out. If a CloudWatch alarm fires during deployment, AppConfig automatically rolls back. This is safer than changing a Parameter Store value directly, which takes effect immediately for all callers.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Part IV
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Structure a SAM Project for Multi-Environment Deployment
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 01:&lt;/strong&gt; Initialize the Project
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sam init &lt;span class="nt"&gt;--runtime&lt;/span&gt; python3.12 &lt;span class="nt"&gt;--name&lt;/span&gt; multi-env-app &lt;span class="nt"&gt;--app-template&lt;/span&gt; hello-world
&lt;span class="nb"&gt;cd &lt;/span&gt;multi-env-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 02:&lt;/strong&gt; Create the Handler Files
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;code&gt;sam init&lt;/code&gt; creates a &lt;code&gt;hello_world/&lt;/code&gt; folder, but our template uses a &lt;code&gt;src/handlers/&lt;/code&gt; structure with two functions. Create these files (delete the generated &lt;code&gt;hello_world/&lt;/code&gt; folder afterward. It's no longer referenced):&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 03:&lt;/strong&gt; Create &lt;code&gt;src/handlers/requirements.txt&lt;/code&gt;:
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;boto3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 04:&lt;/strong&gt; Create &lt;code&gt;src/handlers/create_order.py&lt;/code&gt;:
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;

&lt;span class="n"&gt;dynamodb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dynamodb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;TABLE_NAME&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;())[:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ORDER#&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;METADATA&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;customerId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;customerId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;created&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;orderId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ORD-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;created&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 05:&lt;/strong&gt; Create &lt;code&gt;src/handlers/get_order.py&lt;/code&gt;:
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="n"&gt;dynamodb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dynamodb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;TABLE_NAME&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pathParameters&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;orderId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ORDER#&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;METADATA&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Item&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;item&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Order not found&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;⚠️ The template's &lt;code&gt;CodeUri: src/handlers/&lt;/code&gt; and &lt;code&gt;Handler: create_order.lambda_handler&lt;/code&gt; must point to real files. If &lt;code&gt;src/handlers/&lt;/code&gt; doesn't exist or is missing &lt;code&gt;requirements.txt&lt;/code&gt;, &lt;code&gt;sam build&lt;/code&gt; fails with &lt;em&gt;"source ... does not exist"&lt;/em&gt; or &lt;em&gt;"requirements.txt file not found."&lt;/em&gt; The &lt;code&gt;CodeUri&lt;/code&gt; is the folder SAM packages; the &lt;code&gt;Handler&lt;/code&gt; is &lt;code&gt;filename.function_name&lt;/code&gt; relative to that folder.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Step 06:&lt;/strong&gt; Configure the SAM Template with Parameters
&lt;/h4&gt;

&lt;p&gt;Replace the contents of &lt;code&gt;template.yaml&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;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;Transform&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;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Multi-environment serverless application&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;Stage&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;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;
    &lt;span class="na"&gt;AllowedValues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;dev&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;staging&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;prod&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;TableName&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;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;orders&lt;/span&gt;

&lt;span class="na"&gt;Globals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Function&lt;/span&gt;&lt;span class="pi"&gt;:&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;python3.12&lt;/span&gt;
    &lt;span class="na"&gt;Timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
    &lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;STAGE&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;Stage&lt;/span&gt;
        &lt;span class="na"&gt;TABLE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${Stage}-${TableName}"&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;OrdersTable&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::DynamoDB::Table&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;TableName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${Stage}-${TableName}"&lt;/span&gt;
      &lt;span class="na"&gt;BillingMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PAY_PER_REQUEST&lt;/span&gt;
      &lt;span class="na"&gt;KeySchema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PK&lt;/span&gt;
          &lt;span class="na"&gt;KeyType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HASH&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SK&lt;/span&gt;
          &lt;span class="na"&gt;KeyType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RANGE&lt;/span&gt;
      &lt;span class="na"&gt;AttributeDefinitions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PK&lt;/span&gt;
          &lt;span class="na"&gt;AttributeType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;S&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SK&lt;/span&gt;
          &lt;span class="na"&gt;AttributeType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;S&lt;/span&gt;

  &lt;span class="na"&gt;CreateOrderFunction&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/handlers/&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;create_order.lambda_handler&lt;/span&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;DynamoDBCrudPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;TableName&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;OrdersTable&lt;/span&gt;
      &lt;span class="na"&gt;Events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;CreateOrder&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;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;Path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/orders&lt;/span&gt;
            &lt;span class="na"&gt;Method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;post&lt;/span&gt;

  &lt;span class="na"&gt;GetOrderFunction&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/handlers/&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;get_order.lambda_handler&lt;/span&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;DynamoDBReadPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;TableName&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;OrdersTable&lt;/span&gt;
      &lt;span class="na"&gt;Events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;GetOrder&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;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;Path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/orders/{orderId}&lt;/span&gt;
            &lt;span class="na"&gt;Method&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;Outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ApiUrl&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 Gateway endpoint URL&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;!Sub&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/${Stage}/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Step 07:&lt;/strong&gt; Configure samconfig.toml for Multiple Environments
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# samconfig.toml — one section per environment&lt;/span&gt;
&lt;span class="c"&gt;# The version key is REQUIRED — SAM CLI won't parse the file without it&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;

&lt;span class="nn"&gt;[default.deploy.parameters]&lt;/span&gt;
&lt;span class="py"&gt;stack_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"multi-env-app-dev"&lt;/span&gt;
&lt;span class="py"&gt;resolve_s3&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;capabilities&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"CAPABILITY_IAM"&lt;/span&gt;
&lt;span class="py"&gt;parameter_overrides&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="py"&gt;"Stage&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;dev&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="py"&gt;confirm_changeset&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

&lt;span class="nn"&gt;[staging.deploy.parameters]&lt;/span&gt;
&lt;span class="py"&gt;stack_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"multi-env-app-staging"&lt;/span&gt;
&lt;span class="py"&gt;resolve_s3&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;capabilities&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"CAPABILITY_IAM"&lt;/span&gt;
&lt;span class="py"&gt;parameter_overrides&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="py"&gt;"Stage&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;staging&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="py"&gt;confirm_changeset&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

&lt;span class="nn"&gt;[prod.deploy.parameters]&lt;/span&gt;
&lt;span class="py"&gt;stack_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"multi-env-app-prod"&lt;/span&gt;
&lt;span class="py"&gt;resolve_s3&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;capabilities&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"CAPABILITY_IAM"&lt;/span&gt;
&lt;span class="py"&gt;parameter_overrides&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="py"&gt;"Stage&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;prod&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="py"&gt;confirm_changeset&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Step 08:&lt;/strong&gt; Deploy to Different Environments
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build once — use --use-container to build inside a Lambda-compatible&lt;/span&gt;
&lt;span class="c"&gt;# Docker image (needs Docker running, but no local Python required)&lt;/span&gt;
sam build &lt;span class="nt"&gt;--use-container&lt;/span&gt;

&lt;span class="c"&gt;# Or, if you have Python 3.12 installed and on your PATH:&lt;/span&gt;
&lt;span class="c"&gt;# sam build&lt;/span&gt;

&lt;span class="c"&gt;# Deploy to dev (default config)&lt;/span&gt;
sam deploy

&lt;span class="c"&gt;# Deploy to staging&lt;/span&gt;
sam deploy &lt;span class="nt"&gt;--config-env&lt;/span&gt; staging

&lt;span class="c"&gt;# Deploy to prod (will prompt for changeset confirmation)&lt;/span&gt;
sam deploy &lt;span class="nt"&gt;--config-env&lt;/span&gt; prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;&lt;code&gt;sam build&lt;/code&gt; can't find Python?&lt;/strong&gt; If you see &lt;em&gt;"Binary validation failed for python ... did you have python for runtime: python3.12 on your PATH?"&lt;/em&gt;, it means Python 3.12 isn't installed locally (the &lt;code&gt;WindowsApps\python.EXE&lt;/code&gt; entries are Microsoft Store stubs, not a real install). Use &lt;code&gt;sam build --use-container&lt;/code&gt; to build inside a Docker container that already has the correct runtime, or install Python 3.12 from python.org and check "Add python.exe to PATH" during setup. CloudShell also has Python 3.12 and SAM pre-installed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each deployment creates isolated resources: &lt;code&gt;dev-orders&lt;/code&gt; table, &lt;code&gt;staging-orders&lt;/code&gt; table, &lt;code&gt;prod-orders&lt;/code&gt; table.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;&lt;code&gt;samconfig.toml&lt;/code&gt; uses config environments (sections like &lt;code&gt;[staging.deploy.parameters]&lt;/code&gt;) to manage per-environment settings. The &lt;code&gt;--config-env&lt;/code&gt; flag selects which section to use. Setting &lt;code&gt;confirm_changeset = true&lt;/code&gt; for production forces you to review changes before applying them.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🏗️ What You Built | 📘 Exam Concepts Recap
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;What You Built&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Exam Concept&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bundled &lt;code&gt;requests&lt;/code&gt; into a zip and uploaded via console&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Zip packaging with dependencies, 50/250 MB limits&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Built a Dockerfile and pushed to ECR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Container image packaging (up to 10 GB)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Created a Lambda from a container image&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;When to use containers vs zip&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Created an AppConfig application and environment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Runtime configuration management&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Added feature flags with enabled/disabled toggles&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Feature flag pattern for decoupling release from deploy&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Created a Linear deployment strategy with bake time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Gradual config rollout with automatic rollback&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Used SAM parameters and &lt;code&gt;!Sub&lt;/code&gt; for resource names&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Environment-specific infrastructure&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Configured &lt;code&gt;samconfig.toml&lt;/code&gt; for dev/staging/prod&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Multi-environment deployment with &lt;code&gt;--config-env&lt;/code&gt;&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Set &lt;code&gt;confirm_changeset = true&lt;/code&gt; for prod&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Forcing review before production changes&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ⚠️ Clean Up Protocol
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Lambda&lt;/strong&gt; → Delete &lt;code&gt;PackagedFunction&lt;/code&gt; and &lt;code&gt;ContainerLambdaDemo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ECR&lt;/strong&gt; → Delete the &lt;code&gt;lambda-container-demo&lt;/code&gt; repository (and all images)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AppConfig&lt;/strong&gt; → Delete the deployment, then the configuration profile, environment, and application (in that order)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudFormation&lt;/strong&gt; → Delete any SAM-deployed stacks (&lt;code&gt;multi-env-app-dev&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM&lt;/strong&gt; → Delete Lambda execution roles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch&lt;/strong&gt; → Delete log groups&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3&lt;/strong&gt; → Delete any SAM deployment buckets (prefixed with &lt;code&gt;aws-sam-cli-managed-default&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;




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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Zip packages:&lt;/strong&gt; 50 MB compressed / 250 MB uncompressed. &lt;strong&gt;Container images:&lt;/strong&gt; up to 10 GB. Use containers for large dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda Layers&lt;/strong&gt; share dependencies across functions. Up to 5 layers, 250 MB total uncompressed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sam build&lt;/strong&gt; resolves dependencies automatically. &lt;strong&gt;sam deploy&lt;/strong&gt; packages and deploys. &lt;strong&gt;sam deploy --guided&lt;/strong&gt; for first-time setup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AppConfig&lt;/strong&gt; deploys configuration independently from code, with validation, gradual rollout, and automatic rollback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;samconfig.toml&lt;/strong&gt; manages multi-environment deployment configs. Use &lt;code&gt;--config-env&lt;/code&gt; to select the environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ECR&lt;/strong&gt; stores container images. Use &lt;code&gt;public.ecr.aws/lambda/&lt;/code&gt; base images for Lambda containers.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;CloudFormation parameters&lt;/strong&gt; and &lt;code&gt;!Sub&lt;/code&gt; for environment-specific resource names.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SAM policy templates&lt;/strong&gt; (&lt;code&gt;DynamoDBCrudPolicy&lt;/code&gt;, &lt;code&gt;S3ReadPolicy&lt;/code&gt;) enforce least privilege without writing full IAM policies.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-package.html" rel="noopener noreferrer"&gt;Deploying Lambda functions as .zip file archives&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/chapter-layers.html" rel="noopener noreferrer"&gt;Managing Lambda dependencies with layers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html" rel="noopener noreferrer"&gt;What is the AWS Serverless Application Model (AWS SAM)?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/appconfig/latest/userguide/what-is-appconfig.html" rel="noopener noreferrer"&gt;What is AWS AppConfig?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/images-create.html" rel="noopener noreferrer"&gt;Create a Lambda function using a container image&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html" rel="noopener noreferrer"&gt;AWS SAM CLI configuration file&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;🏗️&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>certification</category>
      <category>developer</category>
    </item>
    <item>
      <title>Manage Sensitive Data In Application Code | 🏗️ Build A Secure Configuration Service</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Fri, 26 Jun 2026 21:20:02 +0000</pubDate>
      <link>https://dev.to/aws-builders/manage-sensitive-data-in-application-code-build-a-secure-configuration-service-3mlb</link>
      <guid>https://dev.to/aws-builders/manage-sensitive-data-in-application-code-build-a-secure-configuration-service-3mlb</guid>
      <description>&lt;p&gt;&lt;strong&gt;Exam Guide:&lt;/strong&gt; Developer - Associate&lt;br&gt;
&lt;strong&gt;🏗️ Domain 2:&lt;/strong&gt;  &lt;strong&gt;&lt;em&gt;Security&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
📘 &lt;strong&gt;Task 3:&lt;/strong&gt; &lt;em&gt;Manage Sensitive Data In Application Code&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Managing Sensitive Data In Application Code is about keeping secrets out of your code, classifying data properly, and building applications that handle sensitive data safely.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You need to know when to use Secrets Manager vs Parameter Store, how to mask PII in API responses and logs, and how to isolate data in multi-tenant applications.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The ability to choose the right secret management service, implement data sanitization, and enforce tenant-level data boundaries, is vital.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  📘Concepts
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Data Classification
&lt;/h3&gt;

&lt;p&gt;Understand data sensitivity levels and how each should be handled:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Classification&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;th&gt;Handling Requirements&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;PII&lt;/strong&gt; (Personally Identifiable Information)&lt;/td&gt;
&lt;td&gt;Name, email, SSN, phone number, address&lt;/td&gt;
&lt;td&gt;Encrypt at rest and in transit, mask in logs and API responses, restrict access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;PHI&lt;/strong&gt; (Protected Health Information)&lt;/td&gt;
&lt;td&gt;Medical records, insurance IDs, lab results&lt;/td&gt;
&lt;td&gt;HIPAA compliance, encryption required, audit trail mandatory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Financial&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Credit card numbers, bank accounts, transaction data&lt;/td&gt;
&lt;td&gt;PCI DSS compliance, tokenization, never store full card numbers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Public&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Marketing content, public API docs&lt;/td&gt;
&lt;td&gt;No special handling needed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡&lt;strong&gt;If a scenario mentions &lt;em&gt;compliance&lt;/em&gt; or &lt;em&gt;audit trail&lt;/em&gt;, think encryption with KMS (for CloudTrail logging) and Secrets Manager (for automatic rotation). If it mentions &lt;em&gt;PII in logs&lt;/em&gt;, think data masking and sanitization.&lt;/strong&gt; &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Secrets Manager vs SSM Parameter Store
&lt;/h3&gt;

&lt;p&gt;Both store configuration and secrets.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Secrets Manager&lt;/th&gt;
&lt;th&gt;SSM Parameter Store&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Automatic rotation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes (built-in for RDS, Redshift, DocumentDB)&lt;/td&gt;
&lt;td&gt;No (you build it yourself with Lambda)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$0.40/secret/month + $0.05 per 10,000 API calls&lt;/td&gt;
&lt;td&gt;Free (Standard tier), $0.05/advanced parameter/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-account access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes (via resource policy)&lt;/td&gt;
&lt;td&gt;Yes (advanced parameters only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Max size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;64 KB&lt;/td&gt;
&lt;td&gt;4 KB (Standard) / 8 KB (Advanced)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Versioning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automatic (AWSCURRENT, AWSPREVIOUS labels)&lt;/td&gt;
&lt;td&gt;Yes (version history)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Encryption&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Always encrypted (KMS required)&lt;/td&gt;
&lt;td&gt;Optional (SecureString type uses KMS)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CloudFormation resolve&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{{resolve:secretsmanager:...}}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{{resolve:ssm:...}}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Database credentials, API keys that need rotation&lt;/td&gt;
&lt;td&gt;App config, feature flags, non-rotating secrets&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡&lt;strong&gt;If it says &lt;em&gt;automatic rotation&lt;/em&gt; or &lt;em&gt;database credentials&lt;/em&gt;, the answer is Secrets Manager.&lt;/strong&gt; &lt;br&gt;
&lt;strong&gt;If it says &lt;em&gt;application configuration&lt;/em&gt;, &lt;em&gt;feature flags&lt;/em&gt;, or &lt;em&gt;free&lt;/em&gt;, the answer is Parameter Store.&lt;/strong&gt; &lt;br&gt;
&lt;strong&gt;If it says &lt;em&gt;encrypted configuration&lt;/em&gt;, Parameter Store with SecureString works and costs nothing.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Data Masking and Sanitization Patterns
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;th&gt;When to Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Field Masking&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Replace characters with asterisks (&lt;code&gt;j***e@example.com&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;API responses containing PII&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Log Sanitization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Strip or redact sensitive fields before logging&lt;/td&gt;
&lt;td&gt;Any log output that might contain secrets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tokenization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Replace sensitive data with a non-reversible token&lt;/td&gt;
&lt;td&gt;Credit card numbers, SSNs in databases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Field-Level Encryption&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Encrypt individual fields within a record&lt;/td&gt;
&lt;td&gt;When only some fields in a record are sensitive&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Multi-Tenant Data Isolation Patterns
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;How It Works&lt;/th&gt;
&lt;th&gt;Isolation Level&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Partition Key Prefix&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Prefix DynamoDB keys with tenant ID (&lt;code&gt;TENANT#123#ORDER#456&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Logical (application-enforced)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IAM Condition Keys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;dynamodb:LeadingKeys&lt;/code&gt; condition to restrict access&lt;/td&gt;
&lt;td&gt;Policy-enforced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cognito Claims&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Extract tenant ID from JWT and scope all queries&lt;/td&gt;
&lt;td&gt;Token-Enforced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Separate Tables/Accounts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Each tenant gets their own table or AWS account&lt;/td&gt;
&lt;td&gt;Physical (strongest isolation)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Partition key isolation with IAM condition keys is the most common pattern. Separate accounts is the strongest but most expensive. &lt;br&gt;
&lt;code&gt;dynamodb:LeadingKeys&lt;/code&gt; restricts which partition keys a principal can access.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  When to Use Each Secret Management Approach
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Scenario&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Why&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RDS database password that rotates every 30 days&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Secrets Manager&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Built-in rotation for RDS&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API endpoint URL that differs per environment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Parameter Store (String)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Simple config, no encryption needed, free&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Third-party API key that doesn't rotate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Parameter Store (SecureString)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Encrypted, free, no rotation needed&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OAuth client secret shared across accounts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Secrets Manager&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Cross-account resource policies&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Feature flag (enable/disable a feature)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Parameter Store (String) or AppConfig&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Simple config value&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lambda environment variable with a password&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;KMS encryption + runtime decryption&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Encrypted at rest, decrypted at cold start&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  🏗️ Build A Secure Configuration Service
&lt;/h2&gt;

&lt;p&gt;Build a &lt;strong&gt;Secure Configuration Service&lt;/strong&gt; that demonstrates secret and configuration management:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database credentials stored in Secrets Manager with console-based setup&lt;/li&gt;
&lt;li&gt;Application configuration stored in SSM Parameter Store&lt;/li&gt;
&lt;li&gt;A Lambda function that retrieves secrets and config at runtime&lt;/li&gt;
&lt;li&gt;Data masking applied to API responses containing PII&lt;/li&gt;
&lt;li&gt;A multi-tenant data isolation pattern using DynamoDB with partition key prefixes and IAM conditions&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;&lt;strong&gt;&lt;a href="https://ntombizakhona.medium.com/amazon-web-services-a8e57a9c6084" rel="noopener noreferrer"&gt;An AWS account&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Part I
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Store Database Credentials in Secrets Manager
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;Secrets Manager&lt;/strong&gt; console &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Click &lt;strong&gt;Store a new secret&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; &lt;strong&gt;Choose secret type&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Secret type:&lt;/strong&gt; &lt;code&gt;Other type of secret&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key:&lt;/strong&gt; &lt;code&gt;username&lt;/code&gt;, &lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key:&lt;/strong&gt; &lt;code&gt;password&lt;/code&gt;, &lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;SuperSecretPass123!&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key:&lt;/strong&gt; &lt;code&gt;host&lt;/code&gt;, &lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;mydb.cluster-abc123.us-east-1.rds.amazonaws.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key:&lt;/strong&gt; &lt;code&gt;port&lt;/code&gt;, &lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;3306&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key:&lt;/strong&gt; &lt;code&gt;dbname&lt;/code&gt;, &lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;orders&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; &lt;strong&gt;Encryption key:&lt;/strong&gt; &lt;code&gt;aws/secretsmanager&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; &lt;strong&gt;Configure secret&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Secret name:&lt;/strong&gt; &lt;code&gt;prod/database/credentials&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description:&lt;/strong&gt; &lt;code&gt;Production database credentials for the orders service&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 07:&lt;/strong&gt; Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 08:&lt;/strong&gt; &lt;strong&gt;Configure rotation - &lt;em&gt;optional&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Leave disabled for this tutorial (in production, you'd enable automatic rotation for RDS)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 09:&lt;/strong&gt; Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 10:&lt;/strong&gt; &lt;strong&gt;Review&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 11:&lt;/strong&gt; Click &lt;strong&gt;Store&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You now have a secret stored in Secrets Manager. &lt;br&gt;
Click into it and note the &lt;strong&gt;Secret ARN&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Explore the Secret&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 12:&lt;/strong&gt; Click on &lt;code&gt;prod/database/credentials&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In the &lt;strong&gt;Secret value&lt;/strong&gt; section, click &lt;strong&gt;Retrieve secret value&lt;/strong&gt;&lt;br&gt;
You'll see your key/value pairs displayed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 13:&lt;/strong&gt; Click the &lt;strong&gt;Versions&lt;/strong&gt; tab &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Notice the &lt;code&gt;AWSCURRENT&lt;/code&gt; staging label&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;💡Secrets Manager automatically versions secrets. When rotation happens, the new value gets &lt;code&gt;AWSCURRENT&lt;/code&gt; and the old value gets &lt;code&gt;AWSPREVIOUS&lt;/code&gt;. Your application always retrieves &lt;code&gt;AWSCURRENT&lt;/code&gt; by default, so rotation is seamless.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Part II
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Store Application Config in SSM Parameter Store
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Create String Parameters&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;Systems Manager&lt;/strong&gt; console &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; In the left sidebar &lt;strong&gt;▼ Application Tools&lt;/strong&gt;, click &lt;strong&gt;Parameter Store&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; Click &lt;strong&gt;Create parameter&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;/app/config/api-url&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description:&lt;/strong&gt; &lt;code&gt;External API endpoint URL&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tier:&lt;/strong&gt; &lt;code&gt;Standard&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type:&lt;/strong&gt; &lt;code&gt;String&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data type:&lt;/strong&gt; &lt;code&gt;text&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;https://api.example.com/v2&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; Click &lt;strong&gt;Create parameter&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Create a SecureString Parameter&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; Click &lt;strong&gt;Create parameter&lt;/strong&gt; again&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;/app/secrets/api-key&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description:&lt;/strong&gt; &lt;code&gt;Third-party API key (encrypted)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tier:&lt;/strong&gt; &lt;code&gt;Standard&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type:&lt;/strong&gt; &lt;code&gt;SecureString&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KMS key source:&lt;/strong&gt; &lt;code&gt;My current account&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KMS Key ID:&lt;/strong&gt; &lt;code&gt;alias/aws/ssm&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;sk-abc123-this-is-a-secret-key&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; Click &lt;strong&gt;Create parameter&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Create a Hierarchical Config Set&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 07:&lt;/strong&gt; Create three more parameters following the same steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;/app/config/max-items&lt;/code&gt; | &lt;strong&gt;Type:&lt;/strong&gt; String | &lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;50&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;/app/config/feature-flags/new-checkout&lt;/code&gt; | &lt;strong&gt;Type:&lt;/strong&gt; String | &lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;true&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;/app/config/feature-flags/dark-mode&lt;/code&gt; | &lt;strong&gt;Type:&lt;/strong&gt; String | &lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;false&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;You now have a parameter hierarchy. The &lt;code&gt;/app/config/&lt;/code&gt; prefix groups related configuration together.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Parameter Store supports hierarchies with path-based names. You can retrieve all parameters under a path with &lt;code&gt;GetParametersByPath&lt;/code&gt;. This is useful for loading all config for an environment at once. SecureString parameters are encrypted with KMS. Use &lt;code&gt;WithDecryption=True&lt;/code&gt; when retrieving them.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Part III
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Create a Lambda Function That Retrieves Both
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Create the Lambda Function
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;Lambda&lt;/strong&gt; console → &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Function name:&lt;/strong&gt; &lt;code&gt;SecureConfigService&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; Python 3.12&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Click &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; Paste this code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;functools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;lru_cache&lt;/span&gt;

&lt;span class="n"&gt;secrets_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;secretsmanager&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ssm_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ssm&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@lru_cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;maxsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;secret_name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Retrieve a secret from Secrets Manager.
    Cached at cold start to avoid repeated API calls.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;secrets_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_secret_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SecretId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;secret_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SecretString&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="nd"&gt;@lru_cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;maxsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;decrypt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Retrieve a parameter from SSM Parameter Store.
    Use WithDecryption=True for SecureString parameters.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssm_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WithDecryption&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;decrypt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Parameter&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_all_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Retrieve all parameters under a path hierarchy.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssm_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_parameters_by_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Recursive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;WithDecryption&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Parameters&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Demonstrates retrieving secrets and config from both services.

    Key patterns:
    - Cache secrets at cold start (lru_cache) to reduce API calls
    - Use Secrets Manager for credentials that rotate
    - Use Parameter Store for app config and non-rotating secrets
    - Use GetParametersByPath to load config hierarchies
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get_all&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get_db_credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# From Secrets Manager
&lt;/span&gt;        &lt;span class="n"&gt;creds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;prod/database/credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Database credentials retrieved from Secrets Manager&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;host&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;host&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;port&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;port&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dbname&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dbname&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;***REDACTED***&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;  &lt;span class="c1"&gt;# Never return passwords in responses!
&lt;/span&gt;            &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get_app_config&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# From Parameter Store
&lt;/span&gt;        &lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/app/config/api-url&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/app/secrets/api-key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;max_items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/app/config/max-items&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;App config retrieved from Parameter Store&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;apiUrl&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;api_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;apiKey&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;***REDACTED***&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Mask the key
&lt;/span&gt;                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;maxItems&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_items&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="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get_feature_flags&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Load all feature flags under a path
&lt;/span&gt;        &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_all_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/app/config/feature-flags&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Feature flags loaded from Parameter Store&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;flags&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&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="k"&gt;else&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Secure Config Service&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;availableActions&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get_db_credentials&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get_app_config&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get_feature_flags&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; Paste Click &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Add Permissions to the Lambda Role&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; Go to &lt;strong&gt;Configuration&lt;/strong&gt; → &lt;strong&gt;Permissions&lt;/strong&gt; → click the role name&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; Click &lt;strong&gt;Add permissions&lt;/strong&gt; → &lt;strong&gt;Attach policies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 07:&lt;/strong&gt; Search for and attach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SecretsManagerReadWrite&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AmazonSSMReadOnlyAccess&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 08:&lt;/strong&gt; Click &lt;strong&gt;Add permissions&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Test the Function&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 09:&lt;/strong&gt; Go to the &lt;strong&gt;Test&lt;/strong&gt; tab&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 10:&lt;/strong&gt; Create test events&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test event 1: Get database credentials:&lt;/strong&gt;&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="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;"get_db_credentials"&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;&lt;strong&gt;Test event 2: Get app config:&lt;/strong&gt;&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="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;"get_app_config"&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;&lt;strong&gt;Test event 3: Get feature flags:&lt;/strong&gt;&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="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;"get_feature_flags"&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;&lt;strong&gt;Step 11:&lt;/strong&gt; Run each test and verify the responses&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡Notice the caching pattern with &lt;code&gt;@lru_cache&lt;/code&gt;. Secrets Manager and Parameter Store charge per API call.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caching at cold start means you only pay for one call per Lambda instance, not one per invocation.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For secrets that rotate, use a TTL-based cache instead of &lt;code&gt;lru_cache&lt;/code&gt; so the Lambda picks up new values.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part IV
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Implement Data Masking in API Responses
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Create the Data Masking Lambda
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; &lt;strong&gt;Lambda&lt;/strong&gt; → &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Function name:&lt;/strong&gt; &lt;code&gt;DataMaskingDemo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; Python 3.12&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Click &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; Paste this code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;

&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# --- Data Masking Functions ---
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mask_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;jane.doe@example.com → j***e@example.com&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;masked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;***&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;masked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;***&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;masked&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;@&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mask_phone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;+1-555-123-4567 → ***-***-**67&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;digits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\D&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;***-***-**&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;digits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mask_ssn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ssn&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;123-45-6789 → ***-**-6789&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;***-**-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ssn&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mask_credit_card&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;4111-1111-1111-1234 → ****-****-****-1234&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;digits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\D&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;****-****-****-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;digits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sanitize_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Mask all PII fields before returning to the client.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;mask_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;phone&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;mask_phone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;phone&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ssn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;mask_ssn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ssn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;memberSince&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;memberSince&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# --- Log Sanitization ---
&lt;/span&gt;
&lt;span class="n"&gt;SENSITIVE_PATTERNS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\s*:\s*&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[^&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]*&lt;/span&gt;&lt;span class="sh"&gt;"'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ssn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\d{3}-\d{2}-\d{4}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;credit_card&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sanitize_for_logging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Remove sensitive data before logging.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;SENSITIVE_PATTERNS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="s"&gt;***REDACTED-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;***&lt;/span&gt;&lt;span class="sh"&gt;"'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Demonstrates data masking in API responses and log sanitization.

    Key patterns:
    - Mask PII fields (email, phone, SSN) before returning to clients
    - Sanitize log output to prevent sensitive data from reaching CloudWatch
    - Never log passwords, full credit card numbers, or SSNs
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# Simulated user data (in production, this comes from DynamoDB)
&lt;/span&gt;    &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;USR-001&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Jane Doe&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;jane.doe@example.com&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;phone&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;+1-555-123-4567&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ssn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;123-45-6789&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;memberSince&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2023-01-15&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;USR-002&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;John Smith&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;john.smith@example.com&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;phone&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;+1-555-987-6543&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ssn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;987-65-4321&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;memberSince&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2023-06-20&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Sanitize before logging — never log raw PII
&lt;/span&gt;    &lt;span class="n"&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Processing request: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;sanitize_for_logging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Mask PII before returning in the response
&lt;/span&gt;    &lt;span class="n"&gt;masked_users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;sanitize_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;users&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;User data with PII masked&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;masked_users&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;&lt;strong&gt;Step 04:&lt;/strong&gt; Click &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Test Data Masking&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; Go to the &lt;strong&gt;Test&lt;/strong&gt; tab → create a test event with &lt;code&gt;{}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; Run the test&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Verify the response shows masked values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Email: &lt;code&gt;j***e@example.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Phone: &lt;code&gt;***-***-**67&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;SSN: &lt;code&gt;***-**-6789&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 07:&lt;/strong&gt; Check &lt;strong&gt;CloudWatch Logs&lt;/strong&gt; to confirm the log output is also sanitized&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Data masking happens at the application layer, not the database layer. Always mask PII before returning it in API responses.&lt;/strong&gt; &lt;br&gt;
&lt;strong&gt;Always sanitize data before logging.&lt;br&gt;
CloudWatch Logs can be accessed by anyone with the right IAM permissions, so treat logs as potentially public.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Part V
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Build a Multi-Tenant Data Isolation Pattern
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Create the DynamoDB Table&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;DynamoDB&lt;/strong&gt; console → &lt;strong&gt;Create table&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Table name:&lt;/strong&gt; &lt;code&gt;MultiTenantApp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partition key:&lt;/strong&gt; &lt;code&gt;PK&lt;/code&gt; (String)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sort key:&lt;/strong&gt; &lt;code&gt;SK&lt;/code&gt; (String)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Table settings:&lt;/strong&gt; &lt;code&gt;Customize settings&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read/write capacity settings:&lt;/strong&gt; &lt;code&gt;On-demand&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Click &lt;strong&gt;Create table&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Add Sample Data&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; Click into the &lt;code&gt;MultiTenantApp&lt;/code&gt; table&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; Click &lt;strong&gt;Explore table items&lt;/strong&gt; → &lt;strong&gt;Create item&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; Switch to &lt;strong&gt;JSON view&lt;/strong&gt; and add these items one at a time:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tenant A: Order 1:&lt;/strong&gt;&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;"PK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TENANT#tenant-a#USER#user-001"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"SK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORDER#2024-001"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"orderTotal"&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="nl"&gt;"N"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"149.99"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shipped"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tenantId"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tenant-a"&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;&lt;strong&gt;Tenant A: Order 2:&lt;/strong&gt;&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;"PK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TENANT#tenant-a#USER#user-001"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"SK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORDER#2024-002"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"orderTotal"&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="nl"&gt;"N"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"89.50"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"processing"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tenantId"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tenant-a"&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;&lt;strong&gt;Tenant B: Order 1:&lt;/strong&gt;&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;"PK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TENANT#tenant-b#USER#user-002"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"SK"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORDER#2024-003"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"orderTotal"&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="nl"&gt;"N"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"299.00"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"delivered"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tenantId"&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="nl"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tenant-b"&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;h4&gt;
  
  
  &lt;strong&gt;Create the Multi-Tenant Lambda&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; &lt;strong&gt;Lambda&lt;/strong&gt; → &lt;strong&gt;Create function&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Function name:&lt;/strong&gt; &lt;code&gt;MultiTenantQuery&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; Python 3.12&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 07:&lt;/strong&gt; Paste this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;boto3.dynamodb.conditions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt;

&lt;span class="n"&gt;dynamodb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dynamodb&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MultiTenantApp&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Multi-tenant data isolation using partition key prefixes.

    The tenant ID is extracted from the request context (simulating
    a Cognito JWT claim) and used to scope all database queries.
    A user in Tenant A can never see Tenant B&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s data.

    In production, the tenant ID comes from:
    - Cognito custom claims (custom:tenantId)
    - Lambda authorizer context
    - API Gateway request context
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# In production: extract from JWT claims
&lt;/span&gt;    &lt;span class="c1"&gt;# tenant_id = event['requestContext']['authorizer']['claims']['custom:tenantId']
&lt;/span&gt;    &lt;span class="n"&gt;tenant_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tenantId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tenant-a&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;userId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;user-001&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Query is automatically scoped to this tenant
&lt;/span&gt;    &lt;span class="n"&gt;pk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TENANT#&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;#USER#&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;KeyConditionExpression&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nc"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;begins_with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ORDER#&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Items&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;orderId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SK&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ORDER#&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;total&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;orderTotal&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tenantId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;userId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;orderCount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;orders&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;orders&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;&lt;strong&gt;Step 08:&lt;/strong&gt; Click &lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 09:&lt;/strong&gt; Add &lt;strong&gt;AmazonDynamoDBReadOnlyAccess&lt;/strong&gt; to the Lambda execution role&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Test Tenant Isolation&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 10:&lt;/strong&gt; Create test events&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tenant A query:&lt;/strong&gt;&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="nl"&gt;"tenantId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tenant-a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"userId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user-001"&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;&lt;strong&gt;Tenant B query:&lt;/strong&gt;&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="nl"&gt;"tenantId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tenant-b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"userId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user-002"&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;blockquote&gt;
&lt;p&gt;⚠️ Run each test and verify that Tenant A only sees their orders and Tenant B only sees theirs&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Partition key isolation is the most common multi-tenant pattern on the exam. For stronger isolation, combine it with IAM condition keys using &lt;code&gt;dynamodb:LeadingKeys&lt;/code&gt;. This enforces isolation at the IAM policy level so even a bug in your application code can't leak data across tenants.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🏗️ What You Built | 📘 Exam Concepts Recap
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;What You Built&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Exam Concept&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stored database credentials in Secrets Manager&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Secret management for rotating credentials&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Explored the &lt;code&gt;AWSCURRENT&lt;/code&gt; staging label&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Secrets Manager versioning and seamless rotation&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Created String and SecureString parameters&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Parameter Store types. Plaintext vs KMS-encrypted&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Built a parameter hierarchy under &lt;code&gt;/app/config/&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Path-based config organization, &lt;code&gt;GetParametersByPath&lt;/code&gt;&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cached secrets with &lt;code&gt;@lru_cache&lt;/code&gt; at cold start&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Reducing API calls and cost for secret retrieval&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Retrieved SecureString with &lt;code&gt;WithDecryption=True&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;KMS decryption of encrypted parameters&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Masked email, phone, SSN in API responses&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Application-level PII data masking&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sanitized log output before writing to CloudWatch&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Preventing sensitive data leakage in logs&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Used partition key prefixes (&lt;code&gt;TENANT#...&lt;/code&gt;)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Multi-tenant data isolation pattern&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scoped queries to a tenant from request context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Token/claim-based tenant boundary enforcement&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ⚠️ Clean Up Protocol
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Secrets Manager&lt;/strong&gt; → Select &lt;code&gt;prod/database/credentials&lt;/code&gt; → &lt;strong&gt;Actions&lt;/strong&gt; → &lt;strong&gt;Delete secret&lt;/strong&gt; (set minimum 7-day waiting period)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Systems Manager&lt;/strong&gt; → &lt;strong&gt;Parameter Store&lt;/strong&gt; → Select all parameters → &lt;strong&gt;Delete&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda&lt;/strong&gt; → Delete &lt;code&gt;SecureConfigService&lt;/code&gt;, &lt;code&gt;DataMaskingDemo&lt;/code&gt;, and &lt;code&gt;MultiTenantQuery&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB&lt;/strong&gt; → Delete the &lt;code&gt;MultiTenantApp&lt;/code&gt; table&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM&lt;/strong&gt; → Delete Lambda execution roles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch&lt;/strong&gt; → Delete log groups for all three functions&lt;/li&gt;
&lt;/ol&gt;




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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Secrets Manager&lt;/strong&gt; for credentials that need automatic rotation (RDS, Redshift, DocumentDB). &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parameter Store&lt;/strong&gt; for app config and non-rotating secrets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SecureString&lt;/strong&gt; parameters are encrypted with KMS. Always use &lt;code&gt;WithDecryption=True&lt;/code&gt; to read them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't resolve secrets at deploy time&lt;/strong&gt; (&lt;code&gt;{{resolve:...}}&lt;/code&gt;) if they rotate fetch at runtime with caching instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda environment variables&lt;/strong&gt; are encrypted at rest by default. Use a CMK + in-code decryption for additional security.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mask PII&lt;/strong&gt; in API responses and sanitize logs. &lt;/li&gt;
&lt;li&gt;Never log passwords, SSNs, or credit card numbers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-tenant isolation:&lt;/strong&gt; use partition key prefixes combined with IAM condition keys (&lt;code&gt;dynamodb:LeadingKeys&lt;/code&gt;) for policy-enforced isolation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secrets Manager&lt;/strong&gt; costs $0.40/secret/month. &lt;strong&gt;Parameter Store Standard&lt;/strong&gt; is free. Choose based on rotation needs, not just cost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache secrets&lt;/strong&gt; at cold start to reduce API calls and cost. Use &lt;code&gt;lru_cache&lt;/code&gt; for static secrets, TTL-based cache for rotating secrets.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html" rel="noopener noreferrer"&gt;What is AWS Secrets Manager?&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html" rel="noopener noreferrer"&gt;AWS Systems Manager Parameter Store&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html" rel="noopener noreferrer"&gt;Working with Lambda environment variables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/blogs/security/how-to-use-aws-secrets-manager-securely-store-rotate-ssh-key-pairs/" rel="noopener noreferrer"&gt;How to use AWS Secrets Manager to securely store and rotate SSH key pairs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/agentic-ai-multitenant/enforcing-tenant-isolation.html" rel="noopener noreferrer"&gt;Enforcing tenant isolation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;🏗️&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>certification</category>
      <category>developer</category>
    </item>
    <item>
      <title>Implement Encryption By Using AWS Services | 🏗️ Create A KMS Customer Managed Key</title>
      <dc:creator>Ntombizakhona Mabaso</dc:creator>
      <pubDate>Wed, 03 Jun 2026 00:45:50 +0000</pubDate>
      <link>https://dev.to/aws-builders/implement-encryption-by-using-aws-services-create-a-kms-customer-managed-key-2eb7</link>
      <guid>https://dev.to/aws-builders/implement-encryption-by-using-aws-services-create-a-kms-customer-managed-key-2eb7</guid>
      <description>&lt;p&gt;&lt;strong&gt;Exam Guide:&lt;/strong&gt; Developer - Associate&lt;br&gt;
&lt;strong&gt;🏗️ Domain 2:&lt;/strong&gt;  &lt;strong&gt;&lt;em&gt;Security&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
📘 &lt;strong&gt;Task 2:&lt;/strong&gt; &lt;em&gt;Implement Encryption By Using AWS Services.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Encryption shows up everywhere, especially on this exam. S3, DynamoDB, SQS, Lambda environment variables, RDS, and more. You need to know the difference between client-side and server-side encryption, how KMS works, and when to use each approach.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  📘Concepts
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Encryption at Rest vs Encryption In Transit
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Encryption At Rest&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Data stored on disk: S3 Objects, DynamoDB tables, EBS volumes, RDS databases.&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Encryption In Transit&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Data moving between services or between client and server: HTTPS, TLS, VPN.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Where&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;At Rest&lt;/th&gt;
&lt;th&gt;&lt;em&gt;In Transit&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;S3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SSE-S3, SSE-KMS, SSE-C&lt;/td&gt;
&lt;td&gt;&lt;em&gt;HTTPS (enforced via bucket policy)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DynamoDB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Encrypted by default (AWS owned or KMS)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;HTTPS (always)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RDS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;KMS encryption&lt;/td&gt;
&lt;td&gt;&lt;em&gt;SSL/TLS connections&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SQS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SSE-KMS&lt;/td&gt;
&lt;td&gt;&lt;em&gt;HTTPS&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lambda env vars&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;KMS (default + optional CMK)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;HTTPS&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  KMS Key Types
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Type&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Managed By&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Use Case&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS owned keys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Default encryption (DynamoDB, S3 SSE-S3&lt;/em&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS managed keys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS (in your account)&lt;/td&gt;
&lt;td&gt;Free (per-use charges)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;&lt;code&gt;aws/s3&lt;/code&gt;, &lt;code&gt;aws/dynamodb&lt;/code&gt; (you can't manage them)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Customer managed keys (CMK)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You&lt;/td&gt;
&lt;td&gt;Monthly + per-use&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Full control: rotation, policies, cross-account&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Envelope Encryption
&lt;/h3&gt;

&lt;p&gt;KMS can only directly encrypt up to &lt;strong&gt;4 KB&lt;/strong&gt;. For larger data, it uses envelope encryption:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; KMS generates a &lt;strong&gt;data key&lt;/strong&gt; (plaintext + encrypted copy)&lt;br&gt;
&lt;strong&gt;2.&lt;/strong&gt; You encrypt your data with the &lt;strong&gt;plaintext data key&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;3.&lt;/strong&gt; You store the &lt;strong&gt;encrypted data key&lt;/strong&gt; alongside the encrypted data&lt;br&gt;
&lt;strong&gt;4.&lt;/strong&gt; You discard the plaintext data key from memory&lt;br&gt;
&lt;strong&gt;5.&lt;/strong&gt; To decrypt: KMS decrypts the data key → you decrypt the data&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The AWS Encryption SDK handles this automatically.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Server-Side Encryption Options for S3
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Option&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Key Management&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Use Case&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSE-S3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS manages everything&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Simplest, no KMS costs&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSE-KMS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You control the KMS key&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Audit trail via CloudTrail, key policies&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSE-C&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You provide the key with every request&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Full key control, AWS doesn't store the key&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Client-Side vs Server-Side Encryption
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Aspect&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;Server-Side&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Client-Side&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Who encrypts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS (after receiving data)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;You (before sending to AWS)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data in transit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Encrypted by HTTPS&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Encrypted by you + HTTPS&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Key management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS or KMS&lt;/td&gt;
&lt;td&gt;&lt;em&gt;You (via KMS or your own keys)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Complexity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple&lt;/td&gt;
&lt;td&gt;&lt;em&gt;More complex&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Most workloads&lt;/td&gt;
&lt;td&gt;&lt;em&gt;When you can't trust the storage layer&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Key Rotation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS managed keys:&lt;/strong&gt; Rotated every year automatically (can't change this)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customer managed keys:&lt;/strong&gt; Enable automatic rotation (every year)&lt;/li&gt;
&lt;li&gt;Old key material is kept, existing encrypted data still works&lt;/li&gt;
&lt;li&gt;The key ID doesn't change with automatic rotation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual rotation:&lt;/strong&gt; Create a new key, update alias to point to it&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Certificate Management
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;&lt;em&gt;What It Does&lt;/em&gt;&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ACM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Free public SSL/TLS certificates for AWS services&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS Private CA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Issue private certificates for internal services&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Paid&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡ACM certificates can't be exported. They're bound to AWS services (CloudFront, ALB, API Gateway). They auto-renew.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  🏗️ Create A KMS Customer Managed Key
&lt;/h2&gt;

&lt;p&gt;Now let's put these concepts into practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a KMS customer managed key&lt;/li&gt;
&lt;li&gt;Encrypt and decrypt data using KMS&lt;/li&gt;
&lt;li&gt;Upload objects to S3 with different encryption options (SSE-S3, SSE-KMS)&lt;/li&gt;
&lt;li&gt;Enable automatic key rotation&lt;/li&gt;
&lt;li&gt;Encrypt Lambda environment variables with a custom KMS key&lt;/li&gt;
&lt;/ul&gt;


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

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://ntombizakhona.medium.com/amazon-web-services-a8e57a9c6084" rel="noopener noreferrer"&gt;An AWS account&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An S3 bucket&lt;/strong&gt; (&lt;em&gt;we'll create one&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Part I
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Create a KMS Customer Managed Key
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;KMS&lt;/strong&gt; console &lt;br&gt;
Click &lt;strong&gt;Create key&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; &lt;strong&gt;Configure key&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key type:&lt;/strong&gt; &lt;code&gt;Symmetric&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key usage:&lt;/strong&gt; &lt;code&gt;Encrypt and decrypt&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; &lt;strong&gt;Add labels&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Alias:&lt;/strong&gt; &lt;code&gt;app-encryption-key&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description:&lt;/strong&gt; &lt;code&gt;Customer managed key for application data&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; &lt;strong&gt;Define key administrative permissions - &lt;em&gt;optional&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key administrators:&lt;/strong&gt; &lt;code&gt;Select your IAM user&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; &lt;strong&gt;Define key usage permissions - &lt;em&gt;optional&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key administrators:&lt;/strong&gt; &lt;code&gt;Select your IAM user&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; &lt;strong&gt;Edit key policy - &lt;em&gt;optional&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 07:&lt;/strong&gt; &lt;strong&gt;Review&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Finish&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Success&lt;br&gt;
Your AWS KMS key was created with alias app-encryption-key and key ID 17fx7cex-17ae-419x-x816-de16fx50x928.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 You now have a customer managed KMS key. Note the &lt;strong&gt;Key ID&lt;/strong&gt; and &lt;strong&gt;ARN&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Enable Automatic Key Rotation&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 08:&lt;/strong&gt; Click on your key (&lt;code&gt;app-encryption-key&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 09:&lt;/strong&gt; Go to the &lt;strong&gt;Key material rotations&lt;/strong&gt; tab&lt;br&gt;
Click &lt;strong&gt;Edit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 10:&lt;/strong&gt; &lt;strong&gt;Edit automatic key rotation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key rotation:&lt;/strong&gt; &lt;code&gt;Enable&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rotation period in days:&lt;/strong&gt; &lt;code&gt;365&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅Green banner:&lt;/strong&gt; Successfully enabled automatic key rotation&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 With automatic rotation, KMS keeps old key material so existing ciphertext can still be decrypted. The key ID and ARN don't change. With manual rotation (creating a new key), you get a new key ID. Use aliases to abstract this.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Part II
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Encrypt and Decrypt Data with KMS
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Using the Console&lt;/strong&gt;
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 In the KMS console, you can't directly encrypt/decrypt from the UI&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Create a Lambda function to demonstrate&lt;br&gt;
&lt;strong&gt;Lambda&lt;/strong&gt; → &lt;strong&gt;Create function:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;KMSEncryptionDemo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime:&lt;/strong&gt; &lt;code&gt;Python 3.12&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy:&lt;/strong&gt; &lt;code&gt;Code:&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;

&lt;span class="n"&gt;kms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kms&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;KEY_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;alias/app-encryption-key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Demonstrates KMS encrypt/decrypt operations.

    Key concepts:
    - KMS can directly encrypt up to 4 KB
    - For larger data, use GenerateDataKey (envelope encryption)
    - The AWS Encryption SDK handles envelope encryption automatically
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;encrypt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;plaintext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hello, this is sensitive data!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;encrypt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Encrypt data (up to 4 KB)
&lt;/span&gt;        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;KeyId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;KEY_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Plaintext&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;plaintext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ciphertext_b64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CiphertextBlob&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Data encrypted successfully&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ciphertext&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ciphertext_b64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;keyId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;KeyId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;decrypt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Decrypt data
&lt;/span&gt;        &lt;span class="n"&gt;ciphertext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ciphertext&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CiphertextBlob&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ciphertext&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Data decrypted successfully&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;plaintext&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Plaintext&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;generate_data_key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Generate a data key for envelope encryption
&lt;/span&gt;        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_data_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;KeyId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;KEY_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;KeySpec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AES_256&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Data key generated (envelope encryption)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;plaintextKey&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*** exists in memory only — use it to encrypt, then discard ***&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;encryptedKey&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CiphertextBlob&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;note&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Store the encrypted key alongside your encrypted data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; Add KMS permissions to the Lambda role:&lt;br&gt;
&lt;strong&gt;Configuration&lt;/strong&gt; → &lt;strong&gt;Permissions&lt;/strong&gt; → click role name&lt;br&gt;
&lt;strong&gt;Add permissions&lt;/strong&gt; → &lt;strong&gt;Attach policies&lt;/strong&gt; → &lt;code&gt;AWSKeyManagementServicePowerUser&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; &lt;strong&gt;Test events&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Encrypt:&lt;/strong&gt;&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="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;"encrypt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My secret credit card number"&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;&lt;strong&gt;Generate data key:&lt;/strong&gt;&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="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;"generate_data_key"&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;
  
  
  Part III
&lt;/h2&gt;

&lt;h3&gt;
  
  
  S3 Encryption Options
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Create an S3 Bucket&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open the &lt;strong&gt;S3&lt;/strong&gt; console → &lt;strong&gt;Create bucket&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; &lt;strong&gt;General configuration&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bucket type&lt;/strong&gt;: &lt;code&gt;General purpose&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bucket namespace&lt;/strong&gt;: &lt;code&gt;Account Regional namespace (recommended)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bucket name prefix&lt;/strong&gt;: &lt;code&gt;dva-encryption-demo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encryption type:&lt;/strong&gt; &lt;code&gt;Server-side encryption with Amazon S3 managed keys (SSE-S3)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; &lt;strong&gt;Upload with SSE-S3 (Default)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Upload&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add files&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Add a &lt;code&gt;file&lt;/code&gt; (any text file)&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Upload&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡&lt;code&gt;SSE-S3&lt;/code&gt; is applied automatically&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; Upload with SSE-KMS&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Upload&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add files&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Expand &lt;strong&gt;▶ Properties&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server side encryption:&lt;/strong&gt; &lt;code&gt;Specify an encryption key&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encryption settings:&lt;/strong&gt; &lt;code&gt;Override bucket settings for default encryption&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encryption type:&lt;/strong&gt; &lt;code&gt;Server-side encryption with AWS Key Management Service keys (SSE-KMS)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS KMS Key:&lt;/strong&gt; &lt;code&gt;Choose from your AWS KMS keys&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Available AWS KMS Key:&lt;/strong&gt; &lt;code&gt;app-encryption-key&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Upload&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 05:&lt;/strong&gt; &lt;strong&gt;Verify Encryption&lt;/strong&gt;&lt;br&gt;
Click on each uploaded file&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 06:&lt;/strong&gt; Go to the &lt;strong&gt;Properties&lt;/strong&gt; tab → &lt;strong&gt;Server-side encryption settings&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;You'll see which encryption method was used&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡&lt;code&gt;SSE-KMS&lt;/code&gt; gives you an audit trail in CloudTrail (every encrypt/decrypt call is logged). &lt;code&gt;SSE-S3 does not&lt;/code&gt;. If a question mentions "audit" or "compliance," &lt;code&gt;SSE-KMS&lt;/code&gt; is usually the answer.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Part IV
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Encrypt Lambda Environment Variables
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Using a Custom KMS Key&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Step 01:&lt;/strong&gt; Open your &lt;code&gt;KMSEncryptionDemo&lt;/code&gt; function&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 02:&lt;/strong&gt; &lt;strong&gt;Configuration&lt;/strong&gt; → &lt;strong&gt;Environment variables&lt;/strong&gt; → &lt;strong&gt;Edit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 03:&lt;/strong&gt; &lt;strong&gt;Edit environment variables&lt;/strong&gt;&lt;br&gt;
Click &lt;strong&gt;Add environment variable&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key:&lt;/strong&gt; &lt;code&gt;DB_PASSWORD&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;super-secret-password&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 04:&lt;/strong&gt; Expand &lt;strong&gt;▶ Encryption configuration&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Encryption in transit:&lt;/strong&gt; &lt;code&gt;✔ Check&lt;/code&gt; &lt;strong&gt;Enable helpers for encryption in transit&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS KMS key to encrypt at rest:&lt;/strong&gt; &lt;code&gt;Use a customer managed key&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;app-encryption-key&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;Encrypt&lt;/strong&gt; button next to &lt;code&gt;DB_PASSWORD&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The value is now encrypted. &lt;br&gt;
In your code, you'd decrypt it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;kms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;kms&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Decrypt at cold start, cache the result
&lt;/span&gt;&lt;span class="n"&gt;ENCRYPTED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;DECRYPTED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;CiphertextBlob&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ENCRYPTED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Plaintext&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Use DECRYPTED: already decrypted at cold start
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Password length: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DECRYPTED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Don't log the actual password!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🏗️ What You Built | 📘 Exam Concepts Recap
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;What You Built&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;em&gt;Exam Concept&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Created a Customer Managed KMS key&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Key types: &lt;em&gt;AWS owned vs AWS managed vs customer managed&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Assigned key administrators and key users&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;KMS key policies: &lt;em&gt;separate admin and usage permissions&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Enabled automatic key rotation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Old key material preserved, key ID unchanged, seamless decryption&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Encrypted data directly with &lt;code&gt;kms.encrypt()&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;KMS direct encryption: &lt;em&gt;limited to 4 KB&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Called &lt;code&gt;generate_data_key&lt;/code&gt; for AES-256&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Envelope encryption: &lt;em&gt;encrypt large data locally, protect the key with KMS&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Discarded the plaintext data key after use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Security best practice: &lt;em&gt;plaintext key lives only in memory&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Uploaded S3 objects with SSE-S3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Default encryption: &lt;em&gt;AWS manages everything, no audit trail&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Uploaded S3 objects with SSE-KMS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Audit trail in CloudTrail, key policy control, cross-account possible&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Encrypted Lambda environment variables with a CMK&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Protecting secrets at rest in Lambda configuration&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Decrypted env vars at cold start and cached the result&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Performance pattern: &lt;em&gt;avoid decrypting on every invocation&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ⚠️ Clean Up Protocol
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;S3&lt;/strong&gt; → Empty and delete the bucket&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda&lt;/strong&gt; → Delete &lt;code&gt;KMSEncryptionDemo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KMS&lt;/strong&gt; → Schedule key deletion (7-day minimum waiting period)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM&lt;/strong&gt; → Delete Lambda execution roles&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudWatch&lt;/strong&gt; → Delete log groups&lt;/li&gt;
&lt;/ol&gt;




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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;KMS encrypts up to 4 KB directly:&lt;/strong&gt; use envelope encryption (GenerateDataKey) for larger data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSE-S3:&lt;/strong&gt; simplest. &lt;strong&gt;SSE-KMS:&lt;/strong&gt; audit trail + key control. &lt;strong&gt;SSE-C:&lt;/strong&gt; you provide the key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client-side encryption:&lt;/strong&gt; you encrypt before sending to AWS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic key rotation&lt;/strong&gt; keeps old key material and existing data ia still decryptable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use aliases&lt;/strong&gt; for seamless manual key rotation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-account KMS&lt;/strong&gt; requires both a key policy AND an IAM policy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ACM&lt;/strong&gt; = free public certs (can't export). &lt;strong&gt;Private CA&lt;/strong&gt; = private certs (costs money).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Envelope encryption:&lt;/strong&gt; the AWS Encryption SDK handles this for you&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html" rel="noopener noreferrer"&gt;AWS KMS concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/serv-side-encryption.html" rel="noopener noreferrer"&gt;Protecting data using server-side encryption in S3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/kms/latest/developerguide/key-policies.html" rel="noopener noreferrer"&gt;Key policies in AWS KMS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/introduction.html" rel="noopener noreferrer"&gt;What is the AWS Encryption SDK?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/kms/latest/developerguide/rotate-keys.html" rel="noopener noreferrer"&gt;Rotate AWS KMS keys&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-encryption" rel="noopener noreferrer"&gt;Working with Lambda environment variables&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🏗️
&lt;/h2&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>certification</category>
      <category>developer</category>
    </item>
  </channel>
</rss>
