
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>CI-CD on Bastab C - Notes to Self</title>
    <link>https://bastabc.com/</link>
    <description>Recent content in CI-CD on Bastab C - Notes to Self</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Sun, 27 Jul 2025 00:00:00 +0000</lastBuildDate>
    
	<atom:link href="https://bastabc.com/tags/ci-cd/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>DevSecOps Guardrails - Series Overview</title>
      <link>https://bastabc.com/posts/aws-cdk-devsecops/</link>
      <pubDate>Thu, 18 Sep 2025 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/aws-cdk-devsecops/</guid>
      <description>&lt;p&gt;Running CDK in production, one thing becomes clear: a passing &lt;code&gt;cdk synth&lt;/code&gt; is not the same as a safe deploy. There are four categories of risk that a standard CI/CD pipeline leaves unchecked: IaC policy violations, CloudFormation template errors, application code quality issues, and vulnerable dependencies. Each one has a tool that catches it at build time - and together they form a pipeline where &amp;ldquo;it deployed&amp;rdquo; also means &amp;ldquo;it deployed safely.&amp;rdquo;&lt;/p&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  &lt;ul&gt;
&lt;li&gt;&lt;strong&gt;cdk-nag&lt;/strong&gt; - CDK-specific. Runs against the CDK construct tree; no direct equivalent outside a CDK project.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;cfn-lint&lt;/strong&gt; - works anywhere you synthesise a CloudFormation template.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SonarQube, OWASP&lt;/strong&gt; - work with any CI/CD pipeline: GitHub Actions, Jenkins, GitLab CI, or anything else.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The examples use CDK Pipelines but the tools are portable.&lt;/p&gt;
&lt;/div&gt;

&lt;h2 id=&#34;the-series&#34;&gt;The series&lt;/h2&gt;
&lt;h3 id=&#34;chapter-1---policy-checks-with-cdk-nag&#34;&gt;&lt;a href=&#34;https://bastabc.com/posts/aws-cdk-devsecops-cdknag/&#34;&gt;Chapter 1 - Policy Checks with cdk-nag&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;cdk-nag runs NagPacks (AwsSolutions, HIPAA, NIST, PCI DSS) against the synthesised CDK app at synth time - violations block the build before any CloudFormation changeset is created.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;chapter-2---template-linting-with-cfn-lint-coming-soon&#34;&gt;Chapter 2 - Template Linting with cfn-lint (coming soon)&lt;/h3&gt;
&lt;p&gt;cfn-lint validates the synthesised CloudFormation template for invalid properties, deprecated fields, and best practice violations.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;chapter-3---static-analysis-with-sonarqube-coming-soon&#34;&gt;Chapter 3 - Static Analysis with SonarQube (coming soon)&lt;/h3&gt;
&lt;p&gt;SonarQube runs static analysis on the application code and gates the pipeline on quality thresholds - bugs, security hotspots, code smells.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;chapter-4---dependency-scanning-with-owasp-coming-soon&#34;&gt;Chapter 4 - Dependency Scanning with OWASP (coming soon)&lt;/h3&gt;
&lt;p&gt;OWASP Dependency-Check scans Python and Node dependencies against the CVE database and fails the build on any HIGH or above finding.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  &lt;ol&gt;
&lt;li&gt;The series assumes CDK familiarity. The &lt;a href=&#34;https://bastabc.com/posts/aws-cdk-infrastructure-as-code/&#34;&gt;Infrastructure as Code with AWS CDK&lt;/a&gt; series covers project setup, constructs, configuration, testing, and CI/CD pipelines.&lt;/li&gt;
&lt;li&gt;Each guardrail is independent - add cdk-nag without cfn-lint, wire in OWASP without SonarQube. No requirement to implement all four at once.&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>AWS Well-Architected Framework - Series Overview</title>
      <link>https://bastabc.com/posts/aws-well-architected/</link>
      <pubDate>Thu, 21 Aug 2025 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/aws-well-architected/</guid>
      <description>&lt;p&gt;The Well-Architected Framework is one of the more useful things to have internalised for serious AWS work. It comes up in architecture trade-off discussions, CDK design decisions, cost justifications, security reviews - not as a formal checklist but as a consistent vocabulary for reasoning through decisions. I started writing notes to keep the structure clear and they grew into this series.&lt;/p&gt;
&lt;p&gt;The framework is AWS&amp;rsquo;s documented approach to evaluating cloud workloads against a set of architectural best practices. It is structured around six pillars, supported by a tool for running formal reviews against your own workloads, and extended by Lenses that apply the same thinking to specific domains like serverless or SaaS. This series works through each component.&lt;/p&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  The &lt;a href=&#34;https://docs.aws.amazon.com/wellarchitected/latest/framework/wellarchitected-framework.pdf&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Well-Architected Framework whitepaper&lt;/a&gt; is the primary source behind all of it. First published in 2015 and updated regularly since, it formalised what AWS Solutions Architects were doing in customer workload reviews into a consistent, documented system. Every revision to the pillars - including the addition of sustainability in 2021 - and the full question set in the Well-Architected Tool originate here. If you work with the framework seriously, this is the document to read.
&lt;/div&gt;

&lt;hr&gt;
&lt;h2 id=&#34;the-six-pillars&#34;&gt;The six pillars&lt;/h2&gt;
&lt;p&gt;The pillars are the core of the framework. Each defines a set of design principles and best practices. AWS lists them in this order consistently across the whitepaper, documentation, and the Well-Architected Tool.&lt;/p&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  &lt;p&gt;&lt;strong&gt;O&lt;/strong&gt;ur &lt;strong&gt;S&lt;/strong&gt;ystems &lt;strong&gt;R&lt;/strong&gt;un &lt;strong&gt;P&lt;/strong&gt;erfectly, &lt;strong&gt;C&lt;/strong&gt;heaply, &lt;strong&gt;S&lt;/strong&gt;ustainably&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Operational Excellence&lt;/li&gt;
&lt;li&gt;Security&lt;/li&gt;
&lt;li&gt;Reliability&lt;/li&gt;
&lt;li&gt;Performance Efficiency&lt;/li&gt;
&lt;li&gt;Cost Optimization&lt;/li&gt;
&lt;li&gt;Sustainability&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;

&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Pillar&lt;/th&gt;
          &lt;th&gt;What it addresses&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Operational Excellence&lt;/td&gt;
          &lt;td&gt;Running and monitoring workloads, improving processes over time&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Security&lt;/td&gt;
          &lt;td&gt;Protecting data, systems, and assets&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Reliability&lt;/td&gt;
          &lt;td&gt;Recovering from failures and meeting demand&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Performance Efficiency&lt;/td&gt;
          &lt;td&gt;Using resources efficiently as demand changes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Cost Optimization&lt;/td&gt;
          &lt;td&gt;Avoiding unnecessary spend&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Sustainability&lt;/td&gt;
          &lt;td&gt;Minimising the environmental impact of cloud workloads&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;No pillar operates in isolation - a decision that improves reliability (multi-AZ) has cost implications; a decision that improves performance (larger instance) has sustainability implications. The framework acknowledges these trade-offs rather than pretending they do not exist.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;the-series&#34;&gt;The series&lt;/h2&gt;
&lt;h3 id=&#34;chapter-1---the-well-architected-tool&#34;&gt;Chapter 1 - The Well-Architected Tool&lt;/h3&gt;
&lt;p&gt;What the Tool is, how to run a workload review, how to interpret findings, and how the Tool differs from ad hoc pillar checklists.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;chapter-2---the-six-pillars&#34;&gt;Chapter 2 - The Six Pillars&lt;/h3&gt;
&lt;p&gt;An overview of all six pillars - design principles, key questions, and the trade-offs each one introduces. The map before the deep dives.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;chapter-3---operational-excellence-pillar&#34;&gt;Chapter 3 - Operational Excellence Pillar&lt;/h3&gt;
&lt;p&gt;Operations as code, observability, deployment automation, and continuous improvement of operational processes.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;chapter-4---security-pillar&#34;&gt;Chapter 4 - Security Pillar&lt;/h3&gt;
&lt;p&gt;Identity, detection, infrastructure protection, data protection, and incident response - the security pillar in detail.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;chapter-5---reliability-pillar&#34;&gt;Chapter 5 - Reliability Pillar&lt;/h3&gt;
&lt;p&gt;Foundations, workload architecture, change management, and failure management - what reliability means in the context of AWS workloads.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;chapter-6---performance-efficiency-pillar&#34;&gt;Chapter 6 - Performance Efficiency Pillar&lt;/h3&gt;
&lt;p&gt;Selecting and right-sizing resources, monitoring performance over time, and maintaining efficiency as demand evolves.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;chapter-7---cost-optimization-pillar&#34;&gt;Chapter 7 - Cost Optimization Pillar&lt;/h3&gt;
&lt;p&gt;Expenditure awareness, cost-effective resources, matching supply to demand, and optimising over time.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;chapter-8---sustainability-pillar&#34;&gt;Chapter 8 - Sustainability Pillar&lt;/h3&gt;
&lt;p&gt;Measuring and reducing the environmental impact of cloud workloads - right-sizing, Graviton, efficient storage tiers, and sustainability metrics.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;chapter-9---lenses&#34;&gt;Chapter 9 - Lenses&lt;/h3&gt;
&lt;p&gt;How Lenses extend the framework for specific domains - Serverless, SaaS, Machine Learning, and others - and when to apply them.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  &lt;ol&gt;
&lt;li&gt;Content is based on the public AWS Well-Architected Framework documentation, not any specific workload or employer environment.&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>Infrastructure as Code with AWS CDK - Series Overview</title>
      <link>https://bastabc.com/posts/aws-cdk-infrastructure-as-code/</link>
      <pubDate>Fri, 21 Feb 2025 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/aws-cdk-infrastructure-as-code/</guid>
      <description>&lt;p&gt;AWS CDK lets you define cloud infrastructure in familiar programming languages - Python, TypeScript, Java - and synthesise it into CloudFormation. Compared to writing raw CloudFormation or Terraform HCL, CDK gives you loops, conditionals, type safety, and reusable constructs.&lt;/p&gt;
&lt;p&gt;This series covers practical CDK patterns using Python, with a consistent use case across all parts so the trade-offs are easy to compare.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;why-cdk-over-cloudformation-for-aws-native-workloads&#34;&gt;Why CDK over CloudFormation for AWS-native workloads?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;CloudFormation is YAML or JSON - no loops, no conditionals, no abstraction. Any shared pattern gets copy-pasted.&lt;/li&gt;
&lt;li&gt;CDK uses a real programming language - loops, functions, classes, and type checks all apply to infrastructure the same way they apply to application code.&lt;/li&gt;
&lt;li&gt;L2 constructs handle boilerplate. &lt;code&gt;bucket.grant_read(fn)&lt;/code&gt; generates the IAM policy, role attachment, and resource reference in one call. The CloudFormation equivalent is four resources wired together manually.&lt;/li&gt;
&lt;li&gt;The compiler catches mistakes before CloudFormation ever sees the template.&lt;/li&gt;
&lt;li&gt;CDK still synthesises to CloudFormation under the hood - rollbacks, drift detection, and stack history are unchanged.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;why-cdk-over-terraform-for-aws-native-workloads&#34;&gt;Why CDK over Terraform for AWS-native workloads?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Terraform&amp;rsquo;s strength is multi-cloud. For AWS-only workloads, that comes with overhead that doesn&amp;rsquo;t pay off - a state backend, a provider version to pin, and HCL alongside the application code.&lt;/li&gt;
&lt;li&gt;CDK uses CloudFormation as the deployment engine - AWS manages state natively. No S3 bucket for state, no DynamoDB table for locking, no &lt;code&gt;terraform init&lt;/code&gt; in the pipeline.&lt;/li&gt;
&lt;li&gt;New AWS services appear in CDK constructs faster.&lt;/li&gt;
&lt;li&gt;IAM is easier. Terraform requires writing policy JSON by hand and threading ARNs between resources. CDK&amp;rsquo;s &lt;code&gt;grant_*&lt;/code&gt; methods generate least-privilege policies from the resource graph.&lt;/li&gt;
&lt;li&gt;Terraform is the right call when infrastructure spans multiple cloud providers, or when the team already has a mature Terraform codebase.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;use-case&#34;&gt;Use case&lt;/h2&gt;
&lt;!-- 
&lt;div class=&#34;callout callout-border&#34;&gt;
  Deploy an AWS Lambda function that processes files uploaded to an S3 bucket. Configuration - bucket name, log level, Lambda timeout - varies per environment (dev, staging, prod).
&lt;/div&gt;
 --&gt;
&lt;!-- 
&lt;div class=&#34;callout callout-card&#34;&gt;
  Deploy an AWS Lambda function that processes files uploaded to an S3 bucket. Configuration - bucket name, log level, Lambda timeout - varies per environment (dev, staging, prod).
&lt;/div&gt;
 --&gt;
&lt;!-- 
&lt;div class=&#34;callout callout-indigo&#34;&gt;
  Deploy an AWS Lambda function that processes files uploaded to an S3 bucket. Configuration - bucket name, log level, Lambda timeout - varies per environment (dev, staging, prod).
&lt;/div&gt;
 --&gt;
&lt;!-- 
&lt;div class=&#34;callout callout-teal&#34;&gt;
  Deploy an AWS Lambda function that processes files uploaded to an S3 bucket. Configuration - bucket name, log level, Lambda timeout - varies per environment (dev, staging, prod).
&lt;/div&gt;
 --&gt;
&lt;!-- 
&lt;div class=&#34;callout callout-slate&#34;&gt;
  Deploy an AWS Lambda function that processes files uploaded to an S3 bucket. Configuration - bucket name, log level, Lambda timeout - varies per environment (dev, staging, prod).
&lt;/div&gt;
 --&gt;
&lt;!-- 
&lt;div class=&#34;callout callout-amber&#34;&gt;
  Deploy an AWS Lambda function that processes files uploaded to an S3 bucket. Configuration - bucket name, log level, Lambda timeout - varies per environment (dev, staging, prod).
&lt;/div&gt;
 --&gt;
&lt;!-- 
&lt;div class=&#34;callout callout-aws1&#34;&gt;
  Deploy an AWS Lambda function that processes files uploaded to an S3 bucket. Configuration - bucket name, log level, Lambda timeout - varies per environment (dev, staging, prod).
&lt;/div&gt;



&lt;div class=&#34;callout callout-aws2&#34;&gt;
  Deploy an AWS Lambda function that processes files uploaded to an S3 bucket. Configuration - bucket name, log level, Lambda timeout - varies per environment (dev, staging, prod).
&lt;/div&gt;



&lt;div class=&#34;callout callout-aws3&#34;&gt;
  Deploy an AWS Lambda function that processes files uploaded to an S3 bucket. Configuration - bucket name, log level, Lambda timeout - varies per environment (dev, staging, prod).
&lt;/div&gt;
 --&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  Deploy an AWS Lambda function that processes files uploaded to an S3 bucket. Configuration - bucket name, log level, Lambda timeout - varies per environment (dev, staging, prod).
&lt;/div&gt;

&lt;!-- 
&lt;div class=&#34;callout callout-aws5&#34;&gt;
  Deploy an AWS Lambda function that processes files uploaded to an S3 bucket. Configuration - bucket name, log level, Lambda timeout - varies per environment (dev, staging, prod).
&lt;/div&gt;
 --&gt;
&lt;p&gt;Simple infrastructure by design - the CDK patterns are the point, not the resources.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;the-series&#34;&gt;The series&lt;/h2&gt;
&lt;h3 id=&#34;chapter-1---project-setup-and-bootstrapping&#34;&gt;&lt;a href=&#34;https://bastabc.com/posts/aws-cdk-project-setup-and-bootstrapping/&#34;&gt;Chapter 1 - Project Setup and Bootstrapping&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Getting a CDK project off the ground - directory structure, virtual environments, bootstrapping an AWS account, and understanding the synth/deploy cycle.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;chapter-2---managing-configuration-and-context&#34;&gt;&lt;a href=&#34;https://bastabc.com/posts/aws-cdk-managing-configuration-and-context/&#34;&gt;Chapter 2 - Managing Configuration and Context&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;How environment-specific values flow into CDK stacks - four approaches covering local static config, local dynamic config, SSM Parameter Store, and Secrets Manager, with trade-offs and code examples for each.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;chapter-3---writing-and-sharing-constructs&#34;&gt;&lt;a href=&#34;https://bastabc.com/posts/aws-cdk-writing-constructs/&#34;&gt;Chapter 3 - Writing and Sharing Constructs&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Building L3 constructs - extracting resources into reusable units, the keyword-only props pattern, and sharing constructs across stacks.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;chapter-4---testing-cdk-stacks&#34;&gt;&lt;a href=&#34;https://bastabc.com/posts/aws-cdk-testing-stacks/&#34;&gt;Chapter 4 - Testing CDK Stacks&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Unit testing constructs and stacks with &lt;code&gt;aws_cdk.assertions&lt;/code&gt; - fine-grained assertions, snapshot testing, and when each applies.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;chapter-5---cicd-pipelines&#34;&gt;&lt;a href=&#34;https://bastabc.com/posts/aws-cdk-cicd/&#34;&gt;Chapter 5 - CI/CD Pipelines&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Automating CDK deployments with GitHub Actions, Jenkins, and Azure DevOps - auth setup, multi-environment promotion, and approval gates.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  &lt;ol&gt;
&lt;li&gt;All examples use Python and CDK v2.&lt;/li&gt;
&lt;li&gt;Steps are tested on WSL2/Ubuntu on Windows and native macOS terminal.&lt;/li&gt;
&lt;li&gt;More chapters added as I work through these patterns.&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>AWS Certified AI Practitioner (AIF-C01) Study Notes</title>
      <link>https://bastabc.com/posts/aws-aif-c01/</link>
      <pubDate>Mon, 15 Jun 2026 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/aws-aif-c01/</guid>
      <description>&lt;p&gt;I sat the AWS Certified AI Practitioner (AIF-C01) and passed. This is the retrospective I wish I had read before starting - what the exam actually tests, the services and concepts to know well, and which prep resources earned their place.&lt;/p&gt;
&lt;p&gt;AIF-C01 covers more ground than the name suggests: classical ML, generative AI, foundation models, and responsible AI. Domains 2 and 3 carry the most weight and the vocabulary is denser than it looks. Your starting point will shape where you need to spend time, so calibrate accordingly.&lt;/p&gt;
&lt;p&gt;For more content on other relevant certifications, check &lt;a href=&#34;https://bastabc.com/certifications/&#34;&gt;Certifications&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;the-exam-at-a-glance&#34;&gt;The exam at a glance&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Questions&lt;/td&gt;
          &lt;td&gt;65 (50 scored, 15 unscored)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Time&lt;/td&gt;
          &lt;td&gt;90 minutes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Format&lt;/td&gt;
          &lt;td&gt;Multiple choice and multiple response&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Passing score&lt;/td&gt;
          &lt;td&gt;700 out of 1000 (scaled)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Cost&lt;/td&gt;
          &lt;td&gt;100 USD&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Validity&lt;/td&gt;
          &lt;td&gt;3 years&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Confirm these against the live AWS certification page before your exam - costs and question counts occasionally change.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;where-i-wrote-this-up&#34;&gt;Where I wrote this up&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve written about the concepts and service distinctions that come up across this exam in &lt;a href=&#34;https://bastabc.com/posts/aws-ai-ml-fundamentals/&#34;&gt;Fundamentals of AWS AI and ML&lt;/a&gt; - algorithm types, performance metrics, inference options, Bedrock vs SageMaker, customization approaches, and prompt engineering techniques, all in one place.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;the-five-domains&#34;&gt;The five domains&lt;/h2&gt;
&lt;p&gt;AIF-C01 has five domains. The percentages are from the official exam guide - weight your study time accordingly.&lt;/p&gt;
&lt;h3 id=&#34;domain-1---fundamentals-of-ai-and-ml-20&#34;&gt;Domain 1 - Fundamentals of AI and ML (20%)&lt;/h3&gt;

&lt;div class=&#34;callout callout-aws2&#34;&gt;
  The conceptual foundation the rest of the exam builds on. Know the core ML concepts - types of learning, how models are trained and evaluated, and what the lifecycle of an ML project looks like. This domain tests breadth more than depth.
&lt;/div&gt;

&lt;p&gt;Focus areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Types of ML: supervised, unsupervised, reinforcement learning - and when each applies&lt;/li&gt;
&lt;li&gt;Core concepts: training vs inference, features, labels, overfitting, underfitting&lt;/li&gt;
&lt;li&gt;Model evaluation metrics: accuracy, precision, recall, F1, AUC-ROC&lt;/li&gt;
&lt;li&gt;The ML pipeline: data preparation, training, evaluation, deployment, monitoring&lt;/li&gt;
&lt;li&gt;AWS ML services overview: SageMaker, Rekognition, Comprehend, Polly, Transcribe, Translate&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;domain-2---fundamentals-of-generative-ai-24&#34;&gt;Domain 2 - Fundamentals of Generative AI (24%)&lt;/h3&gt;

&lt;div class=&#34;callout callout-aws2&#34;&gt;
  Heavier than the name implies. Know what foundation models are, how LLMs work at a conceptual level, and the vocabulary of generative AI - tokens, embeddings, context windows, temperature. This is the domain where terminology trips people up most.
&lt;/div&gt;

&lt;p&gt;Focus areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Foundation models vs traditional ML models - pre-trained, general-purpose, fine-tunable&lt;/li&gt;
&lt;li&gt;Large language models: tokenization, embeddings, context windows, attention mechanisms (conceptual)&lt;/li&gt;
&lt;li&gt;Generative AI output types: text, image, code, audio&lt;/li&gt;
&lt;li&gt;Key inference parameters: temperature, top-p, max tokens - what they control&lt;/li&gt;
&lt;li&gt;Amazon Bedrock: managed access to foundation models; providers and model IDs&lt;/li&gt;
&lt;li&gt;Prompt engineering basics: zero-shot, few-shot, chain-of-thought&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;domain-3---applications-of-foundation-models-28&#34;&gt;Domain 3 - Applications of Foundation Models (28%)&lt;/h3&gt;

&lt;div class=&#34;callout callout-aws2&#34;&gt;
  The largest domain and the most practical. It covers how you actually use foundation models - retrieval-augmented generation, fine-tuning, agents, and the AWS services that enable them. Know when to use each approach and the trade-offs each carries.
&lt;/div&gt;

&lt;p&gt;Focus areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Retrieval-Augmented Generation (RAG): how it works, why it reduces hallucinations, when to prefer it over fine-tuning&lt;/li&gt;
&lt;li&gt;Fine-tuning vs RAG vs prompt engineering - the trade-off matrix: cost, data needs, latency, freshness&lt;/li&gt;
&lt;li&gt;Amazon Bedrock Knowledge Bases - managed RAG on AWS&lt;/li&gt;
&lt;li&gt;Amazon Bedrock Agents - multi-step task orchestration with foundation models&lt;/li&gt;
&lt;li&gt;Vector databases and embeddings: how semantic search enables RAG&lt;/li&gt;
&lt;li&gt;Evaluating generative AI outputs: human review, automated metrics, hallucination detection&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;domain-4---guidelines-for-responsible-ai-14&#34;&gt;Domain 4 - Guidelines for Responsible AI (14%)&lt;/h3&gt;

&lt;div class=&#34;callout callout-aws2&#34;&gt;
  Lighter on services, heavier on principles. Know the AWS responsible AI pillars and what each means in practice. The exam tests whether you can identify responsible vs irresponsible AI design decisions, not just recite definitions.
&lt;/div&gt;

&lt;p&gt;Focus areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AWS responsible AI pillars: fairness, explainability, transparency, privacy, robustness, safety, controllability&lt;/li&gt;
&lt;li&gt;Bias in AI: types of bias, where it enters the pipeline, how to detect and mitigate it&lt;/li&gt;
&lt;li&gt;Explainability: what it means, why it matters for regulated use cases&lt;/li&gt;
&lt;li&gt;Amazon SageMaker Clarify - bias detection and explainability tooling&lt;/li&gt;
&lt;li&gt;Human oversight: when to require human review of AI outputs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;domain-5---security-compliance-and-governance-for-ai-solutions-14&#34;&gt;Domain 5 - Security, Compliance, and Governance for AI Solutions (14%)&lt;/h3&gt;

&lt;div class=&#34;callout callout-aws2&#34;&gt;
  Overlaps with general AWS security but scoped to AI workloads. Know the shared responsibility model as it applies to managed AI services, and what guardrails exist to control model behaviour.
&lt;/div&gt;

&lt;p&gt;Focus areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Shared responsibility for managed AI services vs self-hosted models&lt;/li&gt;
&lt;li&gt;Amazon Bedrock Guardrails - content filtering, denied topics, PII redaction&lt;/li&gt;
&lt;li&gt;Data privacy for training and inference: where data goes, what AWS controls&lt;/li&gt;
&lt;li&gt;IAM for Bedrock and SageMaker - least privilege for ML workloads&lt;/li&gt;
&lt;li&gt;Compliance considerations: data residency, model provenance, audit logging&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;services-to-know-well&#34;&gt;Services to know well&lt;/h2&gt;
&lt;p&gt;The exam tests AWS services at a conceptual level, not deep implementation detail. Know what each does, which use case it fits, and what it is commonly confused with:&lt;/p&gt;
&lt;table class=&#34;bordered&#34;&gt;
  &lt;thead&gt;
    &lt;tr&gt;&lt;th&gt;Service / Concept&lt;/th&gt;&lt;th&gt;Know this about it&lt;/th&gt;&lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;&lt;td&gt;Amazon Bedrock&lt;/td&gt;&lt;td&gt;Managed access to foundation models; providers, model IDs, inference parameters&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Amazon SageMaker&lt;/td&gt;&lt;td&gt;Full ML lifecycle - build, train, deploy custom models; contrast with Bedrock&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Bedrock Knowledge Bases&lt;/td&gt;&lt;td&gt;Managed RAG - connects foundation models to your data via vector search&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Bedrock Agents&lt;/td&gt;&lt;td&gt;Orchestrates multi-step tasks using foundation models and external tools&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Bedrock Guardrails&lt;/td&gt;&lt;td&gt;Content filtering, topic denial, PII handling for model responses&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;SageMaker Clarify&lt;/td&gt;&lt;td&gt;Bias detection and explainability across training data and model predictions&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Amazon Rekognition&lt;/td&gt;&lt;td&gt;Image and video analysis - object detection, facial analysis, content moderation&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Amazon Comprehend&lt;/td&gt;&lt;td&gt;NLP service - entity recognition, sentiment, key phrase extraction&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Amazon Transcribe&lt;/td&gt;&lt;td&gt;Speech-to-text; Transcribe Medical for clinical audio&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Amazon Polly&lt;/td&gt;&lt;td&gt;Text-to-speech with multiple voices and languages&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Amazon Lex&lt;/td&gt;&lt;td&gt;Conversational AI for chatbots - same technology as Alexa&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Amazon Kendra&lt;/td&gt;&lt;td&gt;Intelligent enterprise search; often confused with Bedrock Knowledge Bases&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Amazon Personalize&lt;/td&gt;&lt;td&gt;Real-time personalisation and recommendations without ML expertise&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;RAG&lt;/td&gt;&lt;td&gt;Grounds model responses in your data at inference time to reduce hallucination&lt;/td&gt;&lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&#34;easy-things-to-mix-up&#34;&gt;Easy things to mix up&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Bedrock vs SageMaker&lt;/strong&gt; - Bedrock is for consuming pre-built foundation models via API; SageMaker is for building, training, and deploying your own models. The exam constructs scenarios specifically to distinguish them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RAG vs fine-tuning&lt;/strong&gt; - RAG retrieves relevant context at inference time (no retraining, data stays fresh); fine-tuning bakes new knowledge into model weights (requires retraining, more expensive, knowledge becomes stale). Fine-tuning suits tone and style; RAG suits factual, updatable knowledge.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bedrock Knowledge Bases vs Kendra&lt;/strong&gt; - both do search, but Bedrock Knowledge Bases is semantic vector search to feed a generative model; Kendra is keyword and ML-powered enterprise search without a GenAI layer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Temperature vs top-p&lt;/strong&gt; - temperature scales the probability distribution across all tokens (higher = more random, more creative output; lower = more deterministic and conservative); top-p limits the token pool to the smallest set whose cumulative probability meets a threshold. Both control randomness but from different angles.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Supervised vs unsupervised vs reinforcement learning&lt;/strong&gt; - supervised learns from labelled examples; unsupervised finds structure in unlabelled data; reinforcement learns from reward signals. Clustering = unsupervised, classification = supervised.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Overfitting vs underfitting&lt;/strong&gt; - overfitting is when a model memorises training data and fails to generalise (high variance: performs well on training data, poorly on new data); underfitting is when the model is too simple to capture the pattern (high bias: performs poorly on both). The bias-variance trade-off is the tension between them - more model complexity reduces bias but increases variance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Precision vs recall&lt;/strong&gt; - precision is &amp;ldquo;of what I predicted positive, how many were actually positive&amp;rdquo; (minimise false positives); recall is &amp;ldquo;of all actual positives, how many did I catch&amp;rdquo; (minimise false negatives). The trade-off matters in content moderation and medical diagnosis scenarios.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hallucination&lt;/strong&gt; - model produces confident but factually wrong output. RAG is the primary mitigation the exam points to; scenario questions will describe it and ask how to address it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Foundation model vs LLM&lt;/strong&gt; - foundation model is the broader term for large pre-trained general-purpose models (includes image and multimodal); LLM is specifically language-based. All LLMs are foundation models; not all foundation models are LLMs.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;resources&#34;&gt;Resources&lt;/h2&gt;
&lt;p&gt;More so than most AWS certs, the official Skill Builder material is worth starting with here - it&amp;rsquo;s closely aligned to what the exam actually tests.&lt;/p&gt;
&lt;h3 id=&#34;essential&#34;&gt;Essential&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Official Exam Guide (AIF-C01)&lt;/strong&gt; - read this first. The five domains, their weights, and the in-scope services list are the syllabus. Available on the AWS certification page.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://explore.skillbuilder.aws/learn/courses/19554/exam-prep-standard-course-aws-certified-ai-practitioner-aif-c01&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Exam Prep Standard Course: AWS Certified AI Practitioner (AIF-C01)&lt;/a&gt; - the AWS-authored prep course. More directly exam-relevant here than third-party alternatives.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://explore.skillbuilder.aws/learn/learning-plans/2193/standard-exam-prep-plan-aws-certified-ai-practitioner-aif-c01&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Standard Exam Prep Plan: AWS Certified AI Practitioner (AIF-C01)&lt;/a&gt; - a structured plan that sequences all the prep material.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://explore.skillbuilder.aws/learn/courses/19790/exam-prep-official-practice-question-set-aws-certified-ai-practitioner-aif-c01-english&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Official Practice Question Set&lt;/a&gt; - AWS-authored practice questions. The style matches the real exam closely.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Official Practice Exam&lt;/strong&gt; - the full-length timed version; available via AWS Skill Builder. Do one complete timed run before sitting the real exam.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;useful&#34;&gt;Useful&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Amazon Bedrock documentation&lt;/strong&gt; - the Bedrock service is central to Domains 2 and 3. The concepts overview and Guardrails docs are high-yield reading.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AWS Responsible AI resources&lt;/strong&gt; - AWS publishes a responsible AI FAQ and whitepaper; Domain 4 pulls from this framing directly.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;skip-if-you-are-tight-on-time&#34;&gt;Skip if you are tight on time&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;General ML textbooks and courses&lt;/strong&gt; - this exam tests conceptual awareness, not implementation depth. Andrew Ng&amp;rsquo;s ML course is excellent but goes far deeper than required. Save it for after.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SageMaker deep-dives&lt;/strong&gt; - SageMaker appears in the exam but at a high level. A full SageMaker course is overkill for AIF-C01 specifically.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  &lt;ol&gt;
&lt;li&gt;The Skill Builder prep plan and practice question set cover most of what the exam tests - more so than the equivalent material for other AWS certs I&amp;rsquo;ve sat. Start there.&lt;/li&gt;
&lt;li&gt;Domain 3 (Applications of Foundation Models, 28%) carries the most weight. Know RAG vs fine-tuning vs prompt engineering well enough to pick the right approach in a scenario question - not just as definitions.&lt;/li&gt;
&lt;li&gt;Domain 4 (Responsible AI) is lighter on services and heavier on principles. Read through the AWS responsible AI pillars and be able to identify violations in scenario questions.&lt;/li&gt;
&lt;li&gt;If you have CLF-C02, the cloud fundamentals carry over - the AI/ML concepts, generative AI vocabulary, and responsible AI principles are what&amp;rsquo;s new.&lt;/li&gt;
&lt;li&gt;Verify question count, time, and cost on the live AWS certification page before your exam date.&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>AWS Kiro CLI - Manage AWS Infrastructure from the Terminal</title>
      <link>https://bastabc.com/posts/aws-kiro-cli/</link>
      <pubDate>Fri, 15 May 2026 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/aws-kiro-cli/</guid>
      <description>&lt;p&gt;Kiro CLI is an AI-assisted terminal tool for AWS, rebranded from Amazon Q Developer CLI in November 2025. It sits on top of your existing AWS credentials and tooling. The &lt;code&gt;q&lt;/code&gt; and &lt;code&gt;q chat&lt;/code&gt; shortcuts from Q CLI still work but &lt;code&gt;kiro-cli&lt;/code&gt; is the current entry point.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;install&#34;&gt;Install&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;macOS&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;brew install --cask kiro-cli
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or via script:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl -fsSL https://cli.kiro.dev/install | bash
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Windows (PowerShell)&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;irm &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://cli.kiro.dev/install.ps1&amp;#39;&lt;/span&gt; | iex
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Linux / WSL2 (Ubuntu / Debian) - .deb&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wget https://desktop-release.q.us-east-1.amazonaws.com/latest/kiro-cli.deb
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo dpkg -i kiro-cli.deb
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt-get install -f
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Linux - ZIP (x86-64, non-Debian distros)&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;curl --proto &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;=https&amp;#39;&lt;/span&gt; --tlsv1.2 -sSf &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://desktop-release.q.us-east-1.amazonaws.com/latest/kirocli-x86_64-linux.zip&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -o kirocli.zip
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;unzip kirocli.zip
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./kirocli/install.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Verify:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kiro-cli version
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kiro-cli doctor   &lt;span style=&#34;color:#75715e&#34;&gt;# diagnoses config and auth issues&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;authenticate&#34;&gt;Authenticate&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kiro-cli login     &lt;span style=&#34;color:#75715e&#34;&gt;# opens browser to complete sign-in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kiro-cli whoami    &lt;span style=&#34;color:#75715e&#34;&gt;# shows current user and auth method&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kiro-cli logout
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;!-- If using IAM Identity Center, confirm which account the credentials resolve to:

```bash
aws sts get-caller-identity --profile my-prod
```

This does not apply to Builder ID - Builder ID is an AWS identity for accessing Kiro services and is not tied to an IAM account or AWS credentials. --&gt;
&lt;p&gt;&lt;strong&gt;AWS Builder ID&lt;/strong&gt; - free individual account at builder.aws. No existing AWS account required. Enough to get started with 50 free credits/month (500 bonus credits on first sign-in).&lt;/p&gt;
&lt;!-- **IAM Identity Center** - for org-managed access via SSO:

```bash
kiro-cli login --license pro \
  --identity-provider https://your-org.awsapps.com/start \
  --region us-east-1
```

**Device flow** - for remote machines, EC2, containers, or WSL2 without a browser. Supported for Builder ID, IAM Identity Center, GitHub, and Google. The CLI prints a URL and one-time code - enter them in any browser on any device:

```bash
kiro-cli login   # prints device code + URL if no browser detected
```

**API key** - for headless/CI use (Pro plan and above):

```bash
export KIRO_API_KEY=ksk_xxxxxxxx
kiro-cli chat --no-interactive &#34;your prompt here&#34;
``` --&gt;
&lt;p&gt;&lt;strong&gt;Builder ID with AWS accounts&lt;/strong&gt; - You can log in to Kiro with a personal Builder ID for the AI features, and connect to your AWS account using AWS SSO profile. Kiro picks up AWS credentials are active in the environment:&lt;/p&gt;
&lt;p&gt;Set the profile for the session and run commands targeting the profile:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export AWS_PROFILE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;my-prod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aws sts get-caller-identity              &lt;span style=&#34;color:#75715e&#34;&gt;# confirm which account is active&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aws sts get-caller-identity --profile my-prod
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kiro-cli chat &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;get Compute Optimizer recommendations&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;key-commands&#34;&gt;Key commands&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;kiro-cli&lt;/code&gt; with no arguments opens an interactive session - type prompts directly without any prefix. &lt;code&gt;kiro-cli chat &amp;quot;prompt&amp;quot;&lt;/code&gt; is for scripted or one-shot use.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kiro-cli                          &lt;span style=&#34;color:#75715e&#34;&gt;# interactive session - type prompts directly&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kiro-cli chat &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;your prompt&amp;#34;&lt;/span&gt;       &lt;span style=&#34;color:#75715e&#34;&gt;# start session with an initial prompt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kiro-cli chat --resume            &lt;span style=&#34;color:#75715e&#34;&gt;# continue previous session&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kiro-cli chat --no-interactive &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;prompt&amp;#34;&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# one-shot, no interaction - useful in scripts&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kiro-cli translate &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;list S3 buckets&amp;#34;&lt;/span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# natural language to shell command&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kiro-cli agent list               &lt;span style=&#34;color:#75715e&#34;&gt;# list available agents&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kiro-cli mcp add                  &lt;span style=&#34;color:#75715e&#34;&gt;# add an MCP server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kiro-cli mcp list                 &lt;span style=&#34;color:#75715e&#34;&gt;# list configured MCP servers&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kiro-cli mcp status               &lt;span style=&#34;color:#75715e&#34;&gt;# check MCP server health&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kiro-cli settings list            &lt;span style=&#34;color:#75715e&#34;&gt;# view current settings&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kiro-cli update                   &lt;span style=&#34;color:#75715e&#34;&gt;# upgrade to latest version&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;infrastructure-workflows&#34;&gt;Infrastructure workflows&lt;/h2&gt;
&lt;p&gt;Kiro CLI does not call AWS APIs directly - it generates and optionally runs commands using your existing AWS credentials. A few patterns that work well:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Generate and run AWS CLI commands from natural language:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kiro-cli translate &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;list all EC2 instances in us-east-1 with their state&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# outputs: aws ec2 describe-instances --region us-east-1 --query ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Pipe AWS CLI output into chat for analysis:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aws cloudwatch describe-alarms --state-value ALARM | &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  kiro-cli chat --no-interactive &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;summarise these alarms and suggest remediation&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Generate Terraform or CloudFormation:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kiro-cli chat &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;write a Terraform module for a private S3 bucket with versioning and a 90-day lifecycle rule to Glacier&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Use in CI without interaction:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;KIRO_API_KEY&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;ksk_xxxxxxxx kiro-cli chat --no-interactive &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;explain this deployment error: &lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;cat deploy.log | tail -50&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Run commands with auto-approval in trusted environments:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kiro-cli chat --trust-all-tools &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;get Compute Optimizer recommendations for my account&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;!-- ---

## Pricing

| Tier | Monthly credits | Price |
|---|---|---|
| Free | 50 (+ 500 first-sign-in bonus) | $0 |
| Pro | 1,000 | $20/month |
| Pro+ | 2,000 | $40/month |
| Power | 10,000 | $200/month |

Credits do not roll over. IDE and CLI share the same pool. API key access requires Pro or above. --&gt;
&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  &lt;ol&gt;
&lt;li&gt;If migrating from Q CLI, config migrates automatically from &lt;code&gt;~/.aws/amazonq&lt;/code&gt; to &lt;code&gt;~/.kiro&lt;/code&gt; on first run.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;kiro-cli doctor&lt;/code&gt; is the first thing to run if auth or config behaves unexpectedly.&lt;/li&gt;
&lt;li&gt;The CLI is not open source - the license changed from Apache to AWS Intellectual Property License with the Kiro rebrand.&lt;/li&gt;
&lt;li&gt;For the IDE-side equivalent of this kind of AI-assisted tooling, &lt;a href=&#34;https://bastabc.com/posts/github-copilot-custom-instructions/&#34;&gt;Configure GitHub Copilot Custom Instructions&lt;/a&gt; covers the same ground for Copilot.&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>AWS Certified Developer Associate (DVA-C02) Study Notes</title>
      <link>https://bastabc.com/posts/aws-dva-c02/</link>
      <pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/aws-dva-c02/</guid>
      <description>&lt;p&gt;I sat the AWS Certified Developer Associate (DVA-C02) recently and passed. This is the retrospective I wish I had read before starting - what the exam actually tests, the services you need to know well, the concepts that trip people up, and which resources earned their place.&lt;/p&gt;
&lt;p&gt;I came in comfortable across most of the exam&amp;rsquo;s services from day-to-day work, so for me this was more about confirming and formalising what I already knew than learning it fresh. Your starting point will shape where you spend time, so calibrate the advice accordingly.&lt;/p&gt;
&lt;p&gt;For more content on other relevant certifications, check &lt;a href=&#34;https://bastabc.com/certifications/&#34;&gt;Certifications&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;the-exam-at-a-glance&#34;&gt;The exam at a glance&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Questions&lt;/td&gt;
          &lt;td&gt;65 (50 scored, 15 unscored)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Time&lt;/td&gt;
          &lt;td&gt;130 minutes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Format&lt;/td&gt;
          &lt;td&gt;Multiple choice and multiple response&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Passing score&lt;/td&gt;
          &lt;td&gt;720 out of 1000 (scaled)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Cost&lt;/td&gt;
          &lt;td&gt;150 USD&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Validity&lt;/td&gt;
          &lt;td&gt;3 years&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The scaled score means you do not need 72% of questions right - scoring is normalised across question difficulty. Aim to be comfortably clear of the line rather than counting questions.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;the-four-domains&#34;&gt;The four domains&lt;/h2&gt;
&lt;p&gt;DVA-C02 has four domains. The percentages are the share of scored content, straight from the exam guide - the bigger the share, the more of your study time it deserves.&lt;/p&gt;
&lt;h3 id=&#34;domain-1---development-with-aws-services-32&#34;&gt;Domain 1 - Development with AWS Services (32%)&lt;/h3&gt;

&lt;div class=&#34;callout callout-aws2&#34;&gt;
  The largest domain and the heart of a &lt;em&gt;developer&lt;/em&gt; exam. Lambda, DynamoDB, and API Gateway show up everywhere. Know event-driven patterns, how services pass data, and the SDK behaviours - pagination, retries, and error handling. This is where hands-on time pays off the most.
&lt;/div&gt;

&lt;p&gt;Focus areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lambda - triggers, environment variables, layers, versions and aliases, concurrency&lt;/li&gt;
&lt;li&gt;DynamoDB - data modelling, partition vs sort keys, indexes (LSI vs GSI), capacity modes, streams&lt;/li&gt;
&lt;li&gt;API Gateway - REST vs HTTP APIs, integration types, stages, caching, throttling&lt;/li&gt;
&lt;li&gt;S3 - storage classes, lifecycle, presigned URLs, multipart upload, event notifications&lt;/li&gt;
&lt;li&gt;SQS and SNS - fan-out, FIFO vs standard, dead-letter queues&lt;/li&gt;
&lt;li&gt;Step Functions - standard vs express, when orchestration beats chaining Lambdas&lt;/li&gt;
&lt;li&gt;Caching - ElastiCache vs DAX, and where each fits&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;domain-2---security-26&#34;&gt;Domain 2 - Security (26%)&lt;/h3&gt;

&lt;div class=&#34;callout callout-aws2&#34;&gt;
  Bigger than people expect, and an easy domain to under-prepare for. It covers the practical IAM, encryption, and auth mechanics a developer wires into an application every day, not abstract security theory.
&lt;/div&gt;

&lt;p&gt;Focus areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;IAM - roles vs users, policy evaluation, assume-role, least privilege&lt;/li&gt;
&lt;li&gt;Cognito - user pools vs identity pools, and which problem each solves&lt;/li&gt;
&lt;li&gt;KMS - envelope encryption, customer-managed vs AWS-managed keys, key policies&lt;/li&gt;
&lt;li&gt;Secrets Manager vs SSM Parameter Store - rotation, cost, when to use which&lt;/li&gt;
&lt;li&gt;Encryption in transit and at rest across S3, DynamoDB, and the rest&lt;/li&gt;
&lt;li&gt;STS and temporary credentials&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;domain-3---deployment-24&#34;&gt;Domain 3 - Deployment (24%)&lt;/h3&gt;

&lt;div class=&#34;callout callout-aws2&#34;&gt;
  The CI/CD and packaging domain. If you have shipped anything through a pipeline this is familiar ground, but the exam wants the specific AWS service behaviours, not generic DevOps intuition.
&lt;/div&gt;

&lt;p&gt;Focus areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Code suite - CodeCommit, CodeBuild, CodeDeploy, CodePipeline and how they connect&lt;/li&gt;
&lt;li&gt;CodeDeploy deployment configs - all-at-once, linear, canary, and what each means for Lambda vs EC2&lt;/li&gt;
&lt;li&gt;SAM and CloudFormation - templates, change sets, nested stacks, &lt;code&gt;sam local&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Elastic Beanstalk - deployment policies (rolling, immutable, blue/green)&lt;/li&gt;
&lt;li&gt;Environment configuration and artifact management&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;domain-4---troubleshooting-and-optimization-18&#34;&gt;Domain 4 - Troubleshooting and Optimization (18%)&lt;/h3&gt;

&lt;div class=&#34;callout callout-aws2&#34;&gt;
  The smallest domain, but do not skip it - it leans on observability and the small operational details that the other domains gloss over. A handful of focused hours here is good value.
&lt;/div&gt;

&lt;p&gt;Focus areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CloudWatch - metrics, custom metrics, logs, alarms, Logs Insights&lt;/li&gt;
&lt;li&gt;X-Ray - tracing, segments and subsegments, annotations vs metadata&lt;/li&gt;
&lt;li&gt;Lambda performance - cold starts, memory-to-CPU relationship, timeouts&lt;/li&gt;
&lt;li&gt;Exponential backoff with jitter, and handling throttling&lt;/li&gt;
&lt;li&gt;Reading and reacting to API error codes&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;services-to-know-well&#34;&gt;Services to know well&lt;/h2&gt;
&lt;p&gt;If you can explain what each of these does, when to reach for it, and one thing it is commonly confused with, you are in good shape:&lt;/p&gt;
&lt;table class=&#34;bordered&#34;&gt;
  &lt;thead&gt;
    &lt;tr&gt;&lt;th&gt;Service&lt;/th&gt;&lt;th&gt;Know this about it&lt;/th&gt;&lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;&lt;td&gt;Lambda&lt;/td&gt;&lt;td&gt;Concurrency models, versions/aliases, layers, env vars&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;DynamoDB&lt;/td&gt;&lt;td&gt;Key design, LSI vs GSI, on-demand vs provisioned, streams&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;API Gateway&lt;/td&gt;&lt;td&gt;REST vs HTTP, integration types, throttling, caching&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;S3&lt;/td&gt;&lt;td&gt;Storage classes, presigned URLs, event notifications&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;IAM&lt;/td&gt;&lt;td&gt;Policy evaluation logic, roles vs users, assume-role&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Cognito&lt;/td&gt;&lt;td&gt;User pools (authn) vs identity pools (AWS access)&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;KMS&lt;/td&gt;&lt;td&gt;Envelope encryption, key policies vs IAM&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;SQS / SNS&lt;/td&gt;&lt;td&gt;FIFO vs standard, fan-out, DLQs, visibility timeout&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Step Functions&lt;/td&gt;&lt;td&gt;Standard vs express, orchestration over chaining&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;SAM&lt;/td&gt;&lt;td&gt;Local testing, &lt;code&gt;template.yaml&lt;/code&gt;, transform&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;CodeDeploy&lt;/td&gt;&lt;td&gt;Deployment configs and rollback triggers&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;X-Ray&lt;/td&gt;&lt;td&gt;Tracing model, annotations vs metadata&lt;/td&gt;&lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&#34;easy-things-to-mix-up&#34;&gt;Easy things to mix up&lt;/h2&gt;
&lt;p&gt;These are the pairs and details that the exam likes to probe. Knowing the distinction cleanly is worth more than memorising any single service:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cognito user pools vs identity pools&lt;/strong&gt; - user pools authenticate users (who are you), identity pools hand out AWS credentials (what can you access). Questions blur the two on purpose.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DynamoDB on-demand vs provisioned&lt;/strong&gt; - on-demand for unpredictable traffic, provisioned (with auto scaling) for steady, forecastable load. Watch for cost-driven wording.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LSI vs GSI&lt;/strong&gt; - LSI shares the partition key and must be created at table creation; GSI has its own keys and can be added later.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lambda reserved vs provisioned concurrency&lt;/strong&gt; - reserved caps and guarantees a slice of the account limit; provisioned keeps instances warm to kill cold starts. They solve different problems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CodeDeploy configs&lt;/strong&gt; - all-at-once, linear, and canary differ in blast radius and rollout speed. Know which suits a low-risk vs high-risk deploy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secrets Manager vs Parameter Store&lt;/strong&gt; - Secrets Manager has built-in rotation and costs per secret; Parameter Store is cheaper and fine for non-rotating config.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SQS visibility timeout vs message retention&lt;/strong&gt; - visibility timeout hides an in-flight message; retention is how long an unconsumed message survives.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exponential backoff with jitter&lt;/strong&gt; - the standard fix for throttling and transient errors. If a question describes retry storms, this is usually what it is after.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;STS / assume-role&lt;/strong&gt; - reach for temporary credentials whenever cross-account access comes up, not long-lived keys.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;resources&#34;&gt;Resources&lt;/h2&gt;
&lt;p&gt;There is no shortage of DVA-C02 material. These are the ones worth your time, ranked by how much they actually helped.&lt;/p&gt;
&lt;h3 id=&#34;essential&#34;&gt;Essential&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://d1.awsstatic.com/training-and-certification/docs-dev-associate/AWS-Certified-Developer-Associate_Exam-Guide.pdf&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Exam Guide (DVA-C02)&lt;/a&gt; - read the source. The domain breakdown and in-scope services list are the syllabus.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://explore.skillbuilder.aws/learn/course/external/view/elearning/13757/exam-prep-official-practice-question-set-aws-certified-developer-associate-dva-c02-english&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Official Practice Question Set&lt;/a&gt; and &lt;a href=&#34;https://explore.skillbuilder.aws/learn/course/external/view/elearning/14196/aws-certified-developer-associate-official-practice-exam-dva-c02-english&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Official Practice Exam&lt;/a&gt; - calibrate against AWS-authored questions before the real thing.&lt;/li&gt;
&lt;li&gt;A real AWS account on the free tier - build a Lambda, wire it to DynamoDB and API Gateway, deploy it through CodePipeline. The hands-on sticks far better than reading.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;useful&#34;&gt;Useful&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://digitalcloud.training/category/aws-cheat-sheets/aws-developer-associate/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;DigitalCloud cheat sheets&lt;/a&gt; - good for last-week revision once the concepts are already in place.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Per-service FAQ pages&lt;/strong&gt; (Lambda FAQs, DynamoDB FAQs, and so on) - AWS exam questions lean heavily on FAQ-level detail. High yield for the time spent.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://community.aws/content/2j7IVZZ6uUphQiQrm897xodx14m/aws-certified-developer---associate&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Resources to prepare (community.aws)&lt;/a&gt; - a decent jumping-off point to the official material.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;skip-if-you-are-tight-on-time&#34;&gt;Skip if you are tight on time&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://towardsthecloud.com/blog/aws-developer-associate-exam-guide&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Towards the Cloud study guide&lt;/a&gt; and the dev.to write-ups (&lt;a href=&#34;https://dev.to/aws-builders/my-aws-developer-associate-study-guide-298l&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;study guide&lt;/a&gt;, &lt;a href=&#34;https://dev.to/aws-builders/how-i-passed-the-aws-certified-developer-associate-exam-1336&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;how I passed&lt;/a&gt;) - useful for orientation and motivation, but they overlap heavily with the guide and your own practice. Read one, skim the rest.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://rishabkumar7.github.io/CloudNotes/cloud/AWS-CDA.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Rishab Kumar&amp;rsquo;s CloudNotes&lt;/a&gt; - handy as a quick reference, not a primary study source.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  &lt;ol&gt;
&lt;li&gt;Do a timed practice exam early, even before you feel ready - it surfaces the gaps while there is still time to close them.&lt;/li&gt;
&lt;li&gt;The exam rewards the services you do not touch day to day as much as the ones you do. The practice exams are what flush out those blind spots.&lt;/li&gt;
&lt;li&gt;If you write infrastructure as code already, lean on it - building these services is the fastest way to internalise them. My &lt;a href=&#34;https://bastabc.com/posts/aws-cdk-infrastructure-as-code/&#34;&gt;AWS CDK series&lt;/a&gt; covers a lot of the same services from the building side.&lt;/li&gt;
&lt;li&gt;Treat the exam guide as the syllabus and the practice exams as the gap finder. Everything else is supporting material.&lt;/li&gt;
&lt;li&gt;Score is scaled - aim to clear 720 comfortably rather than chasing a number on practice tests.&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>GitHub PR Fix: &#34;Commits must have verified signatures&#34;</title>
      <link>https://bastabc.com/posts/fix-gpg-signature-verification-blocking-pr-merge/</link>
      <pubDate>Sat, 25 Apr 2026 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/fix-gpg-signature-verification-blocking-pr-merge/</guid>
      <description>&lt;h2 id=&#34;problem&#34;&gt;Problem&lt;/h2&gt;
&lt;p&gt;GitHub branch protection requires all commits to have verified GPG signatures. Two commits at the base of the branch (made before GPG signing was configured) were unsigned, blocking the merge.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;root-cause&#34;&gt;Root cause&lt;/h2&gt;
&lt;p&gt;Commits made before &lt;code&gt;commit.gpgsign=true&lt;/code&gt; was configured in git have no GPG signature. GitHub requires all commits in a PR to be verified - one unsigned commit blocks the merge regardless of approvals.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;steps-to-fix&#34;&gt;Steps to fix&lt;/h2&gt;
&lt;h3 id=&#34;1-identify-unsigned-commits&#34;&gt;1. Identify unsigned commits&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git log --format&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%G? %ad %s&amp;#34;&lt;/span&gt; --date&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;short origin/main..HEAD
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Look for lines starting with &lt;code&gt;N&lt;/code&gt; (no signature). Note the hash of the oldest unsigned commit&amp;rsquo;s parent on main.&lt;/p&gt;
&lt;h3 id=&#34;2-re-sign-all-branch-commits-using-filter-branch&#34;&gt;2. Re-sign all branch commits using filter-branch&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git filter-branch -f --commit-filter &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;git commit-tree -S &amp;#34;$@&amp;#34;&amp;#39;&lt;/span&gt; -- origin/main..HEAD
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Rewrites all branch commits adding a GPG signature - no content changes, no conflicts.&lt;/p&gt;
&lt;h3 id=&#34;3-verify-no-unsigned-commits-remain&#34;&gt;3. Verify no unsigned commits remain&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git log --format&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%G?&amp;#34;&lt;/span&gt; origin/main..HEAD | Select-String &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;^N&lt;/span&gt;$&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Should return nothing.&lt;/p&gt;
&lt;h3 id=&#34;4-force-push&#34;&gt;4. Force push&lt;/h3&gt;
&lt;p&gt;Attempt force push via terminal using the configured remote:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git push --force origin &amp;lt;branch&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If this fails with &amp;ldquo;repository not found&amp;rdquo; (HTTPS remote, no credentials) or &amp;ldquo;Could not read from remote repository&amp;rdquo; (SSH not configured), use a classic PAT with repo scope directly in the URL:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git push --force https://&amp;lt;username&amp;gt;:&amp;lt;PAT&amp;gt;@github.com/&amp;lt;org&amp;gt;/&amp;lt;repo&amp;gt;.git &amp;lt;branch&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;5-update-local-tracking-ref-if-github-desktop-shows-stale-state&#34;&gt;5. Update local tracking ref (if GitHub Desktop shows stale state)&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git update-ref refs/remotes/origin/&amp;lt;branch&amp;gt; &amp;lt;tip-commit-hash&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  &lt;ol&gt;
&lt;li&gt;Avoid &lt;code&gt;git rebase --exec &amp;quot;git commit --amend -S&amp;quot;&lt;/code&gt; if the branch has merge commits - it drops them and causes conflicts.&lt;/li&gt;
&lt;li&gt;Avoid &lt;code&gt;git rebase --rebase-merges&lt;/code&gt; - re-applying old merge commits causes conflicts from the original merges.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;filter-branch&lt;/code&gt; rewrites commit objects without replaying merges, which is what&amp;rsquo;s needed here.&lt;/li&gt;
&lt;li&gt;Fine-grained PATs do not work for org repositories - use classic PAT only.&lt;/li&gt;
&lt;li&gt;After force pushing, avoid &lt;code&gt;git pull&lt;/code&gt; / IDE auto-sync until the PR is merged - it will re-introduce the old unsigned commits via a new merge.&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>Configure GitHub Copilot Custom Instructions</title>
      <link>https://bastabc.com/posts/github-copilot-custom-instructions/</link>
      <pubDate>Sun, 05 Apr 2026 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/github-copilot-custom-instructions/</guid>
      <description>&lt;p&gt;I set up Copilot instructions on a CDK project and went looking for confirmation that the structure actually matched GitHub&amp;rsquo;s model, rather than carrying over an assumption from Claude Code. Claude Code works by walking the directory tree and picking up any &lt;code&gt;CLAUDE.md&lt;/code&gt; it finds in the current or parent folder - drop a file next to the code it should govern, and Claude finds it. Copilot has no equivalent &amp;ldquo;drop a file in the folder&amp;rdquo; convention. It centralises everything under &lt;code&gt;.github/&lt;/code&gt; instead, and scopes by path through frontmatter rather than by file location.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the order I&amp;rsquo;d actually set it up in, in VS Code, from a blank repo.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;1-create-the-repo-wide-file&#34;&gt;1. Create the repo-wide file&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;.github/copilot-instructions.md&lt;/code&gt; is the one file every Copilot Chat request sees, regardless of which file is open. VS Code picks it up automatically from the workspace on disk - no setting to enable, no reference needed in chat.&lt;/p&gt;
&lt;p&gt;A minimal version covers project overview, conventions, how to add something new, the deploy command, and comment style:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-markdown&#34; data-lang=&#34;markdown&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Project name
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;AWS CDK v2 (Python) infrastructure. &lt;span style=&#34;color:#e6db74&#34;&gt;`app.py`&lt;/span&gt; registers all stacks.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;## Conventions
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;-&lt;/span&gt; Stack and construct IDs use PascalCase; file names use snake_case
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;-&lt;/span&gt; Every stack takes &lt;span style=&#34;color:#e6db74&#34;&gt;`env`&lt;/span&gt; and &lt;span style=&#34;color:#e6db74&#34;&gt;`config`&lt;/span&gt; as constructor arguments
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;-&lt;/span&gt; Tag every stack with &lt;span style=&#34;color:#e6db74&#34;&gt;`Environment`&lt;/span&gt; and &lt;span style=&#34;color:#e6db74&#34;&gt;`Owner`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;## Adding a new stack
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;1.&lt;/span&gt; Create &lt;span style=&#34;color:#e6db74&#34;&gt;`cdk_app/&amp;lt;area&amp;gt;/&amp;lt;name&amp;gt;_stack.py`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;2.&lt;/span&gt; Register it in &lt;span style=&#34;color:#e6db74&#34;&gt;`app.py`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;3.&lt;/span&gt; Add a README entry under &lt;span style=&#34;color:#e6db74&#34;&gt;`cdk_app/&amp;lt;area&amp;gt;/`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;## Deploying
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Run &lt;span style=&#34;color:#e6db74&#34;&gt;`cdk deploy --require-approval never -c env=dev`&lt;/span&gt; from the project root.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;## Comments
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Explain why, not what - skip a comment that just restates the line below it.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Anything that would otherwise repeat across every path-scoped file belongs here instead - it&amp;rsquo;s worth getting right before anything else, since on its own it already covers most of what a project needs.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;2-add-path-scoped-files-where-conventions-diverge&#34;&gt;2. Add path-scoped files where conventions diverge&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;.github/instructions/*.instructions.md&lt;/code&gt; is for subtrees where the repo-wide file falls short:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A module with hard architectural constraints that don&amp;rsquo;t apply anywhere else&lt;/li&gt;
&lt;li&gt;A CI pipeline format every file in &lt;code&gt;ci/&lt;/code&gt; has to follow exactly&lt;/li&gt;
&lt;li&gt;A subsystem mid-redesign, where the assistant needs to know what&amp;rsquo;s actually built versus just planned&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each file starts with frontmatter declaring where it applies:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;applyTo&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;src/payments/**&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The glob syntax:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Pattern&lt;/th&gt;
          &lt;th&gt;Matches&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;*&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;current directory&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;**&lt;/code&gt; or &lt;code&gt;**/*&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;recursive, all directories&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;src/**/*.py&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;a specific subtree and extension&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;&amp;quot;**/*.ts,**/*.tsx&amp;quot;&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;more than one pattern, comma-separated&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;When the glob matches the file currently open or being edited, this file loads alongside the repo-wide one - not instead of it.&lt;/p&gt;
&lt;p&gt;Reach for a path-scoped file only when the guidance would be wrong outside that subtree - not just because it&amp;rsquo;s specific to it.&lt;/p&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  If you&amp;rsquo;re coming from Claude Code and reach for a per-folder file instinctively, stop - &lt;code&gt;.github/instructions/&lt;/code&gt; with &lt;code&gt;applyTo&lt;/code&gt; globs is the actual recommended structure here, not a workaround for the lack of one.
&lt;/div&gt;

&lt;p&gt;You can exclude a path-scoped file from a particular Copilot surface with &lt;code&gt;excludeAgent&lt;/code&gt;, useful if a file is tuned for Chat suggestions but shouldn&amp;rsquo;t influence Copilot code review:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;applyTo&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;src/payments/**&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;excludeAgent&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;code-review&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;3-decide-on-agentsmd---probably-not-yet&#34;&gt;3. Decide on AGENTS.md - probably not yet&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;AGENTS.md&lt;/code&gt; is a cross-tool standard - read by Copilot, Codex, and other AI coding tools, not just Copilot. Support varies by surface, and all of it coexists with &lt;code&gt;copilot-instructions.md&lt;/code&gt; and &lt;code&gt;instructions/*.instructions.md&lt;/code&gt; rather than replacing them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Copilot coding agent&lt;/strong&gt; - reads a root &lt;code&gt;AGENTS.md&lt;/code&gt; plus nested ones per subtree&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Copilot code review&lt;/strong&gt; - reads a root-level &lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VS Code&lt;/strong&gt; - lists it alongside &lt;code&gt;copilot-instructions.md&lt;/code&gt; and &lt;code&gt;CLAUDE.md&lt;/code&gt; as a recognised source&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What&amp;rsquo;s undocumented is precedence - when &lt;code&gt;CLAUDE.md&lt;/code&gt;, &lt;code&gt;AGENTS.md&lt;/code&gt;, and &lt;code&gt;copilot-instructions.md&lt;/code&gt; all exist at once, whether they&amp;rsquo;re merged, layered, or something else. That&amp;rsquo;s reason enough to leave an established &lt;code&gt;.github/instructions/&lt;/code&gt; setup alone for now rather than migrate it onto &lt;code&gt;AGENTS.md&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;resources&#34;&gt;Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Adding custom instructions for GitHub Copilot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.github.com/en/copilot/tutorials/customization-library/custom-instructions/your-first-custom-instructions&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Your first custom instructions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.blog/changelog/2025-08-28-copilot-coding-agent-now-supports-agents-md-custom-instructions/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Copilot coding agent now supports AGENTS.md custom instructions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.blog/changelog/2025-09-03-copilot-code-review-path-scoped-custom-instruction-file-support/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Copilot code review: path-scoped custom instruction file support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.blog/changelog/2026-06-18-copilot-code-review-agents-md-support-and-ui-improvements/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Copilot code review: AGENTS.md support and UI improvements&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  &lt;p&gt;This is the area most likely to change. Worth revisiting once GitHub documents how &lt;code&gt;copilot-instructions.md&lt;/code&gt;, &lt;code&gt;instructions/*.instructions.md&lt;/code&gt;, &lt;code&gt;AGENTS.md&lt;/code&gt;, and &lt;code&gt;CLAUDE.md&lt;/code&gt; actually interact when more than one is present in the same repo - that&amp;rsquo;s the gap driving the &amp;ldquo;not yet&amp;rdquo; on &lt;code&gt;AGENTS.md&lt;/code&gt; above, and it&amp;rsquo;s the kind of thing that gets settled in a changelog post with no fanfare.&lt;/p&gt;
&lt;p&gt;On the terminal side rather than the IDE, &lt;a href=&#34;https://bastabc.com/posts/aws-kiro-cli/&#34;&gt;AWS Kiro CLI&lt;/a&gt; is the AI-assisted equivalent for AWS work.&lt;/p&gt;

&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>Kubernetes and Cloud Native Associate (KCNA) Study Notes</title>
      <link>https://bastabc.com/posts/kcna/</link>
      <pubDate>Thu, 19 Mar 2026 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/kcna/</guid>
      <description>&lt;p&gt;I sat the Kubernetes and Cloud Native Associate (KCNA) and passed. KCNA is the entry-level CNCF certification - it covers Kubernetes fundamentals and the wider cloud native landscape at a conceptual level, and it is the natural first step before the hands-on CKA, CKAD, and CKS exams.&lt;/p&gt;
&lt;p&gt;It is a multiple-choice exam, so it rewards knowing what the pieces are and how they relate, not hands-on kubectl skill. If you already work with Kubernetes, a lot of this will be familiar, so calibrate the prep to what you already know.&lt;/p&gt;
&lt;p&gt;For more content on other relevant certifications, check &lt;a href=&#34;https://bastabc.com/certifications/&#34;&gt;Certifications&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;the-exam-at-a-glance&#34;&gt;The exam at a glance&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Questions&lt;/td&gt;
          &lt;td&gt;60&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Time&lt;/td&gt;
          &lt;td&gt;90 minutes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Format&lt;/td&gt;
          &lt;td&gt;Multiple choice, online proctored&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Passing score&lt;/td&gt;
          &lt;td&gt;75%&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Cost&lt;/td&gt;
          &lt;td&gt;250 USD (two attempts included)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Validity&lt;/td&gt;
          &lt;td&gt;2 years&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The pass mark is a flat 75%, not a scaled score, so the smaller domains still count - do not leave them to chance.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;the-four-domains&#34;&gt;The four domains&lt;/h2&gt;
&lt;p&gt;KCNA has four domains. The percentages are the share of scored content, straight from the official curriculum - the bigger the share, the more of your study time it deserves. Note the curriculum was restructured: older guides reference a five-domain version, so calibrate against the current one below.&lt;/p&gt;
&lt;h3 id=&#34;kubernetes-fundamentals-44&#34;&gt;Kubernetes Fundamentals (44%)&lt;/h3&gt;

&lt;div class=&#34;callout callout-aws2&#34;&gt;
  By far the largest domain, and the core of the exam. The Kubernetes architecture, the main resources, and how you interact with a cluster. If you only have time for one area, this is it.
&lt;/div&gt;

&lt;p&gt;Focus areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Kubernetes architecture - control plane vs node components&lt;/li&gt;
&lt;li&gt;Core resources - Pods, Deployments, ReplicaSets, Services, namespaces&lt;/li&gt;
&lt;li&gt;Configuration - ConfigMaps and Secrets&lt;/li&gt;
&lt;li&gt;&lt;code&gt;kubectl&lt;/code&gt; basics and the API-driven, declarative model&lt;/li&gt;
&lt;li&gt;Containers and the container runtime (CRI)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;container-orchestration-28&#34;&gt;Container Orchestration (28%)&lt;/h3&gt;

&lt;div class=&#34;callout callout-aws2&#34;&gt;
  The second-largest domain. Why orchestration exists and how Kubernetes does it - scheduling, networking, storage, and security at a conceptual level.
&lt;/div&gt;

&lt;p&gt;Focus areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Scheduling and how Pods land on nodes&lt;/li&gt;
&lt;li&gt;Networking model - Services, the CNI, Pod-to-Pod communication&lt;/li&gt;
&lt;li&gt;Storage - volumes, persistent volumes, the CSI&lt;/li&gt;
&lt;li&gt;Container security and the runtime ecosystem&lt;/li&gt;
&lt;li&gt;Service mesh and the role it plays&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;cloud-native-application-delivery-16&#34;&gt;Cloud Native Application Delivery (16%)&lt;/h3&gt;

&lt;div class=&#34;callout callout-aws2&#34;&gt;
  How cloud native software gets built and shipped. CI/CD, GitOps, and the packaging tools you would meet around Kubernetes.
&lt;/div&gt;

&lt;p&gt;Focus areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CI/CD fundamentals in a cloud native context&lt;/li&gt;
&lt;li&gt;GitOps and declarative delivery&lt;/li&gt;
&lt;li&gt;Packaging - Helm and templating&lt;/li&gt;
&lt;li&gt;Application delivery patterns and rollout strategies&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;cloud-native-architecture-12&#34;&gt;Cloud Native Architecture (12%)&lt;/h3&gt;

&lt;div class=&#34;callout callout-aws2&#34;&gt;
  The wider landscape around Kubernetes. Autoscaling, serverless, open standards, and the roles and personas in the cloud native world. Conceptual, breadth-first.
&lt;/div&gt;

&lt;p&gt;Focus areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Autoscaling - horizontal vs vertical, cluster autoscaling&lt;/li&gt;
&lt;li&gt;Serverless and event-driven concepts&lt;/li&gt;
&lt;li&gt;Open standards and the role of the CNCF&lt;/li&gt;
&lt;li&gt;Observability concepts - metrics, logs, traces (Prometheus, OpenTelemetry)&lt;/li&gt;
&lt;li&gt;Community, governance, and personas&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;concepts-to-know-well&#34;&gt;Concepts to know well&lt;/h2&gt;
&lt;p&gt;For KCNA you need to recognise what each piece is and what it is for, not how to configure it:&lt;/p&gt;
&lt;table class=&#34;bordered&#34;&gt;
  &lt;thead&gt;
    &lt;tr&gt;&lt;th&gt;Concept&lt;/th&gt;&lt;th&gt;Know this about it&lt;/th&gt;&lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;&lt;td&gt;Pod&lt;/td&gt;&lt;td&gt;Smallest deployable unit; one or more containers sharing network and storage&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Deployment&lt;/td&gt;&lt;td&gt;Declarative rollouts and scaling of stateless Pods via ReplicaSets&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Service&lt;/td&gt;&lt;td&gt;Stable networking endpoint for a set of Pods; ClusterIP, NodePort, LoadBalancer&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;kube-apiserver&lt;/td&gt;&lt;td&gt;The control plane front door; everything goes through the API&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;etcd&lt;/td&gt;&lt;td&gt;The cluster&#39;s key-value store of record&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;kubelet&lt;/td&gt;&lt;td&gt;Node agent that runs and reports on Pods&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;kube-scheduler&lt;/td&gt;&lt;td&gt;Decides which node a Pod runs on&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;ConfigMap / Secret&lt;/td&gt;&lt;td&gt;Non-sensitive vs sensitive configuration injected into Pods&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;CNI&lt;/td&gt;&lt;td&gt;Pluggable networking layer for Pod connectivity&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Helm&lt;/td&gt;&lt;td&gt;Package manager for Kubernetes applications&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Prometheus&lt;/td&gt;&lt;td&gt;The default cloud native metrics and monitoring project&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;kubectl&lt;/td&gt;&lt;td&gt;The CLI you use to talk to the API server&lt;/td&gt;&lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&#34;easy-things-to-mix-up&#34;&gt;Easy things to mix up&lt;/h2&gt;
&lt;p&gt;These are the distinctions the exam likes to probe. Knowing the boundary between a pair is usually the whole question:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Deployment vs StatefulSet vs DaemonSet&lt;/strong&gt; - Deployments for stateless replicas, StatefulSets for stable identity and storage, DaemonSets for one Pod per node.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service types&lt;/strong&gt; - ClusterIP is internal only, NodePort exposes a port on every node, LoadBalancer provisions an external load balancer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ConfigMap vs Secret&lt;/strong&gt; - both inject configuration, but Secrets are for sensitive data and are base64-encoded (not encrypted by default).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Liveness vs readiness vs startup probes&lt;/strong&gt; - liveness restarts a stuck container, readiness gates traffic, startup protects slow-starting apps.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Requests vs limits&lt;/strong&gt; - requests are what the scheduler reserves, limits are the hard ceiling the runtime enforces.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Labels vs annotations&lt;/strong&gt; - labels are for selecting and grouping objects, annotations are for non-identifying metadata.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Control plane vs node components&lt;/strong&gt; - api-server, scheduler, etcd, and controllers run the cluster; kubelet, kube-proxy, and the runtime run the workloads.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Container vs container runtime&lt;/strong&gt; - the container is the running unit, the runtime (containerd, CRI-O) is what runs it via the CRI.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;resources&#34;&gt;Resources&lt;/h2&gt;
&lt;p&gt;KCNA is conceptual, so a structured course plus practice questions covers most of it. You do not need to spend much.&lt;/p&gt;
&lt;h3 id=&#34;essential&#34;&gt;Essential&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/cncf/curriculum/blob/master/KCNA_Curriculum.pdf&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;KCNA Curriculum (CNCF)&lt;/a&gt; - the syllabus. The domains and their weights tell you exactly what is in scope.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LFS250: Kubernetes and Cloud Native Essentials&lt;/strong&gt; - the Linux Foundation&amp;rsquo;s own course, written to the KCNA curriculum. The closest thing to an official study path.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;KodeKloud KCNA course&lt;/strong&gt; - the most recommended third-party prep, with hands-on labs and practice questions. Pick this or LFS250 if you want one guided path.&lt;/li&gt;
&lt;li&gt;A throwaway cluster to poke at - minikube, kind, or a free Killercoda sandbox - plus the &lt;a href=&#34;https://kubernetes.io/docs/reference/kubectl/quick-reference/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;kubectl Quick Reference&lt;/a&gt;. Even on a conceptual exam, touching the objects makes them stick.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;useful&#34;&gt;Useful&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href=&#34;https://training.linuxfoundation.org/wp-content/uploads/2024/10/KCNA_CurriculumPath.pdf&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;KCNA Sample Curriculum Path&lt;/a&gt; - the Linux Foundation&amp;rsquo;s suggested learning order.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;skip-if-you-are-tight-on-time&#34;&gt;Skip if you are tight on time&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Community write-ups like &lt;a href=&#34;https://www.linkedin.com/pulse/how-ace-kcna-kubernetes-cloud-native-associate-exam-keratishvili-mwhwf&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;How to Ace the KCNA&lt;/a&gt; and &lt;a href=&#34;https://medium.com/faun/kcna-exam-prep-%EF%B8%8F-bac0495f8c4c&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Pass the KCNA Exam&lt;/a&gt; - good for orientation and a second pass over the topics, but they overlap with the course and curriculum. Pick one, skim the rest.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  &lt;ol&gt;
&lt;li&gt;Verify the curriculum version. KCNA was restructured to four domains - plenty of older guides still teach the five-domain layout, and the weightings have shifted.&lt;/li&gt;
&lt;li&gt;This is a breadth exam. Know what each piece is for and how the control plane and nodes fit together; do not go deep on any single object.&lt;/li&gt;
&lt;li&gt;Even though it is multiple choice, a few hours with &lt;code&gt;kubectl&lt;/code&gt; on a throwaway cluster makes the concepts far more concrete than reading.&lt;/li&gt;
&lt;li&gt;The cloud native landscape questions reward knowing the flagship CNCF projects by what they do - Prometheus for metrics, Helm for packaging, etcd for state.&lt;/li&gt;
&lt;li&gt;KCNA is the on-ramp to the hands-on CNCF exams. If CKA or CKAD is next, treat this as the groundwork for them.&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>DevSecOps Guardrails - Policy Checks with cdk-nag</title>
      <link>https://bastabc.com/posts/aws-cdk-devsecops-cdknag/</link>
      <pubDate>Sun, 12 Oct 2025 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/aws-cdk-devsecops-cdknag/</guid>
      <description>&lt;p&gt;This is Chapter 1 of the &lt;a href=&#34;https://bastabc.com/posts/aws-cdk-devsecops/&#34;&gt;DevSecOps Guardrails&lt;/a&gt; series.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;cdk-nag runs against the CDK construct tree at synth time and blocks the build on any rule violation - before a changeset is created, before any AWS API call is made. I wired it in as the first guardrail because it is CDK-native: it sees the construct level, not just the synthesised template, which means it can catch things cfn-lint cannot.&lt;/p&gt;
&lt;p&gt;Adding it is a five-line change to &lt;code&gt;app.py&lt;/code&gt;. The part that matters more than setup is suppressions: knowing when to suppress versus fix, and writing a reason string that makes the decision visible in a code review. A synth failure maps directly to a blocked pipeline stage in Jenkins, GitHub Actions, and Azure DevOps with no extra configuration needed.&lt;/p&gt;
&lt;p&gt;The examples use the S3 + Lambda stack from the &lt;a href=&#34;https://bastabc.com/posts/aws-cdk-infrastructure-as-code/&#34;&gt;Infrastructure as Code with AWS CDK&lt;/a&gt; series.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;what-cdk-nag-checks&#34;&gt;What cdk-nag checks&lt;/h2&gt;
&lt;p&gt;cdk-nag is an open-source library from cdklabs that runs rule sets (called NagPacks) against a synthesised CDK app. It traverses the CDK construct tree and raises a finding for any resource that violates a rule.&lt;/p&gt;
&lt;p&gt;Two severity levels:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;NagError&lt;/strong&gt; - blocks synthesis. The &lt;code&gt;cdk synth&lt;/code&gt; command fails and no CloudFormation template is written until the violation is resolved or suppressed with a justification.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NagWarning&lt;/strong&gt; - does not block synthesis. Worth reviewing but will not stop a deploy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;NagPacks that ship out of the box:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AwsSolutionsChecks&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HIPAASecurityChecks&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NIST80053R5Checks&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PCIDSS321Checks&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For most CDK projects, &lt;code&gt;AwsSolutionsChecks&lt;/code&gt; is the right starting point - it maps to AWS best practice guidance and covers the most common misconfiguration patterns.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;installation&#34;&gt;Installation&lt;/h2&gt;
&lt;p&gt;Run this from the project root - the same folder as &lt;code&gt;app.py&lt;/code&gt;, &lt;code&gt;cdk.json&lt;/code&gt;, and &lt;code&gt;requirements.txt&lt;/code&gt; - with the virtual environment from &lt;a href=&#34;https://bastabc.com/posts/aws-cdk-project-setup-and-bootstrapping/&#34;&gt;Chapter 1&lt;/a&gt; activated. Outside the project, or without the venv active, it installs somewhere &lt;code&gt;pip freeze&lt;/code&gt; below won&amp;rsquo;t see:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install cdk-nag
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Add &lt;code&gt;cdk-nag&lt;/code&gt; to &lt;code&gt;requirements.txt&lt;/code&gt; so it is installed in CI - pin it with &lt;code&gt;pip freeze&lt;/code&gt; rather than a bare &lt;code&gt;cdk-nag&lt;/code&gt; line, matching the rest of the file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip freeze | grep cdk-nag &amp;gt;&amp;gt; requirements.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;adding-cdk-nag-to-the-app&#34;&gt;Adding cdk-nag to the app&lt;/h2&gt;
&lt;p&gt;cdk-nag v3 dropped the Aspect-based wiring - NagPacks are now validation plugins registered through CDK&amp;rsquo;s native &lt;code&gt;Validations&lt;/code&gt; API, not &lt;code&gt;Aspects.of(app).add()&lt;/code&gt;. Register cdk-nag in &lt;code&gt;app.py&lt;/code&gt;, after the stacks are instantiated:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; aws_cdk &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; cdk
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_cdk &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Validations
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; cdk_nag &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; AwsSolutionsChecks
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; my_cdk_app.my_stack &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; MyStack
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; cdk&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;App()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;env &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; app&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;node&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;try_get_context(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;env&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;or&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dev&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;config &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; app&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;node&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;try_get_context(env)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;MyStack(app, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;MyStack&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    env&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;cdk&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Environment(account&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;123456789012&amp;#34;&lt;/span&gt;, region&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;us-east-1&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    config&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;config
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Validations&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;of(app)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_plugins(AwsSolutionsChecks(app))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;synth()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Every subsequent &lt;code&gt;cdk synth&lt;/code&gt; run - locally or in CI - will check the synthesised template against the AwsSolutions rules.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;using-additional-nagpacks&#34;&gt;Using additional NagPacks&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;add_plugins()&lt;/code&gt; takes more than one pack in a single call. In &lt;code&gt;app.py&lt;/code&gt;, extend the same call from the previous section - add the others after &lt;code&gt;AwsSolutionsChecks&lt;/code&gt; once those findings are clean; layering in a compliance-specific pack at that point is low friction.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; cdk_nag &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; AwsSolutionsChecks, HIPAASecurityChecks, NIST80053R5Checks, PCIDSS321Checks
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Validations&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;of(app)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_plugins(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    AwsSolutionsChecks(app),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    HIPAASecurityChecks(app),   &lt;span style=&#34;color:#75715e&#34;&gt;# HIPAA Security Rule&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    NIST80053R5Checks(app),     &lt;span style=&#34;color:#75715e&#34;&gt;# NIST 800-53 Rev 5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    PCIDSS321Checks(app),       &lt;span style=&#34;color:#75715e&#34;&gt;# PCI DSS 3.2.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each pack runs independently and has its own rule IDs (e.g. &lt;code&gt;HIPAA.Security-S3BucketLoggingEnabled&lt;/code&gt;, &lt;code&gt;NIST.800.53.R5-S3BucketLoggingEnabled&lt;/code&gt;). A suppression for &lt;code&gt;AwsSolutions-S1&lt;/code&gt; does not suppress the equivalent rule in HIPAA or NIST - if the same resource violates rules across multiple packs, each needs its own suppression with a justification.&lt;/p&gt;
&lt;p&gt;Start with &lt;code&gt;AwsSolutionsChecks&lt;/code&gt; on its own. Running all four packs simultaneously on a codebase that has not been through any nag checks yet produces a lot of noise and makes it harder to prioritise.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;how-violations-look&#34;&gt;How violations look&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bucket &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; s3&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Bucket(self, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;FilesBucket&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    bucket_name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;config[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bucket_name&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;No &lt;code&gt;server_access_logs_bucket&lt;/code&gt; - this triggers &lt;code&gt;AwsSolutions-S1&lt;/code&gt;, a &lt;code&gt;NagError&lt;/code&gt;, at construct path &lt;code&gt;/MyStack/FilesBucket/Resource&lt;/code&gt;. cdk-nag writes it to &lt;code&gt;cdk.out/policy-validation-report.json&lt;/code&gt;, with a summary printed to the terminal.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;suppressions&#34;&gt;Suppressions&lt;/h2&gt;
&lt;p&gt;Sometimes a rule genuinely does not apply - a development bucket that intentionally has no access logging, a Lambda that has a pinned runtime for a documented reason. v3 dropped &lt;code&gt;NagSuppressions&lt;/code&gt; entirely; suppressing is now &amp;ldquo;acknowledging&amp;rdquo; a finding through the same &lt;code&gt;Validations&lt;/code&gt; API used to register the NagPacks:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_cdk &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Validations, Acknowledgment
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Validations&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;of(fn)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;acknowledge(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Acknowledgment(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        id&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AwsSolutions-L1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        reason&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Runtime pinned to 3.12; upgrade tracked in backlog&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The acknowledgment lives in the same stack code as the resource it covers. That means it is code-reviewed alongside the change that triggered it - not buried in a config file or applied globally.&lt;/p&gt;
&lt;p&gt;For a stack-wide acknowledgment (use sparingly):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Validations&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;of(self)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;acknowledge(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Acknowledgment(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        id&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AwsSolutions-IAM4&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        reason&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AWSLambdaBasicExecutionRole is an AWS-managed policy; risk accepted&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;v3 also dropped bulk suppression by rule ID prefix - a wildcard finding like &lt;code&gt;AwsSolutions-IAM5&lt;/code&gt; on an auto-generated policy now needs its own specific ID per finding (e.g. &lt;code&gt;AwsSolutions-IAM5[Resource::*]&lt;/code&gt;), taken from the actual finding text rather than guessed.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;cdk-nag-in-jenkins-github-actions-and-azure-devops&#34;&gt;cdk-nag in Jenkins, GitHub Actions, and Azure DevOps&lt;/h2&gt;
&lt;p&gt;Because cdk-nag is registered as a validation plugin in &lt;code&gt;app.py&lt;/code&gt;, it runs automatically on every &lt;code&gt;cdk synth&lt;/code&gt; call - wherever that call happens. The synth step already exists in the Jenkins and GitHub Actions pipelines from &lt;a href=&#34;https://bastabc.com/posts/aws-cdk-cicd/&#34;&gt;Chapter 5 of the CDK series&lt;/a&gt;; Azure DevOps follows the same pattern. Adding &lt;code&gt;cdk-nag&lt;/code&gt; to &lt;code&gt;requirements.txt&lt;/code&gt; is all the pipeline change needed - no new pipeline code.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;NagError&lt;/code&gt; causes &lt;code&gt;cdk synth&lt;/code&gt; to exit non-zero, and each platform blocks the deploy the same way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Jenkins&lt;/strong&gt; - the Synth stage is marked failed; the Deploy stage never runs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub Actions&lt;/strong&gt; - the Synth step fails the job, which blocks the deploy job downstream via &lt;code&gt;needs: deploy-dev&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Azure DevOps&lt;/strong&gt; - the Synth step is marked failed and the pipeline stops; the Deploy step never runs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nothing ships until the violation is fixed or acknowledged.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;generating-a-report&#34;&gt;Generating a report&lt;/h2&gt;
&lt;p&gt;Pass &lt;code&gt;verbose&lt;/code&gt; to a NagPack&amp;rsquo;s constructor for more detail in that report:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Validations&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;of(app)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;add_plugins(AwsSolutionsChecks(app, verbose&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If existing tooling depends on the v2-style &lt;code&gt;cdk_nag&lt;/code&gt; metadata block in the synthesised CloudFormation template, cdk-nag has a flag to restore it - check the current docs for the exact option name before relying on it for an audit pipeline.&lt;/p&gt;
&lt;h3 id=&#34;surfacing-the-report-in-ci&#34;&gt;Surfacing the report in CI&lt;/h3&gt;
&lt;p&gt;The report is just a JSON file in &lt;code&gt;cdk.out/&lt;/code&gt; - archive it as a build artifact after the Synth stage so it&amp;rsquo;s visible without re-running the pipeline, including on a failed build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Jenkins&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;stage&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Synth&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    steps &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cdk synth -c env=dev&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    post &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        always &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            archiveArtifacts artifacts: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cdk.out/policy-validation-report.json&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt; allowEmptyArchive: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;allowEmptyArchive: true&lt;/code&gt; stops the archive step itself from failing the build on a run where the file doesn&amp;rsquo;t exist (e.g. synth failed before writing it). The report shows up as a downloadable artifact on the build page.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GitHub Actions&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Synth&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cdk synth -c env=dev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Upload cdk-nag report&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;if&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;always()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/upload-artifact@v4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;policy-validation-report&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;path&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cdk.out/policy-validation-report.json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;if: always()&lt;/code&gt; runs the upload step even if the Synth step above it failed - the report is attached to the workflow run.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Azure DevOps&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cdk synth -c env=dev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;displayName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Synth&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;task&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;PublishBuildArtifacts@1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;condition&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;always()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;inputs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;pathToPublish&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cdk.out/policy-validation-report.json&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;artifactName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;policy-validation-report&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;condition: always()&lt;/code&gt; does the same job - the report attaches to the pipeline run regardless of whether the Synth step passed.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  &lt;ol&gt;
&lt;li&gt;Register cdk-nag in &lt;code&gt;app.py&lt;/code&gt;, not inside a stack. Registered at the app level it covers every stack in the app. Registered inside a stack it only covers that stack.&lt;/li&gt;
&lt;li&gt;Start with &lt;code&gt;AwsSolutionsChecks&lt;/code&gt; and add other NagPacks once the AwsSolutions findings are clean - running all packs at once on a new codebase produces a lot of noise.&lt;/li&gt;
&lt;li&gt;An acknowledgment without a &lt;code&gt;reason&lt;/code&gt; string will not be accepted by cdk-nag. The reason field is required and shows up in the audit report.&lt;/li&gt;
&lt;li&gt;cdk-nag does not check runtime behaviour - it checks the synthesised CloudFormation template. It catches misconfiguration, not application bugs.&lt;/li&gt;
&lt;li&gt;This post targets cdk-nag v3 (3.0.0+, released June 2026). Pin your version in &lt;code&gt;requirements.txt&lt;/code&gt; - a major version bump like this will break a pipeline that was passing the day before.&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>AWS CDK - CI/CD Pipelines</title>
      <link>https://bastabc.com/posts/aws-cdk-cicd/</link>
      <pubDate>Sun, 27 Jul 2025 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/aws-cdk-cicd/</guid>
      <description>&lt;p&gt;This is Chapter 5 of the &lt;a href=&#34;https://bastabc.com/posts/aws-cdk-infrastructure-as-code/&#34;&gt;Infrastructure as Code with AWS CDK&lt;/a&gt; series. The same &lt;code&gt;cdk deploy&lt;/code&gt; command that runs locally works in any pipeline - the differences are auth and orchestration.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;common-flags-for-ci&#34;&gt;Common flags for CI&lt;/h2&gt;
&lt;p&gt;These apply regardless of which CI system you use:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cdk synth                          &lt;span style=&#34;color:#75715e&#34;&gt;# generate CloudFormation template&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cdk deploy --require-approval never -c env&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;prod   &lt;span style=&#34;color:#75715e&#34;&gt;# deploy without interactive prompts&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cdk deploy --outputs-file outputs.json            &lt;span style=&#34;color:#75715e&#34;&gt;# write stack outputs to file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;--require-approval never&lt;/code&gt; skips the IAM change confirmation that CDK shows interactively. Required in CI.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-c env=prod&lt;/code&gt; passes the environment context from &lt;a href=&#34;https://bastabc.com/posts/aws-cdk-managing-configuration-and-context/&#34;&gt;Chapter 2&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;github-actions&#34;&gt;GitHub Actions&lt;/h2&gt;
&lt;h3 id=&#34;auth&#34;&gt;Auth&lt;/h3&gt;
&lt;p&gt;OIDC is the recommended auth method - no long-lived credentials stored in GitHub. The pipeline assumes an IAM role directly via GitHub&amp;rsquo;s OIDC provider.&lt;/p&gt;
&lt;p&gt;Create the OIDC provider in AWS once per account:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aws iam create-open-id-connect-provider &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  --url https://token.actions.githubusercontent.com &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  --client-id-list sts.amazonaws.com &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  --thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Create an IAM role with a trust policy scoped to your repository:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2012-10-17&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Statement&amp;#34;&lt;/span&gt;: [{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Effect&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Allow&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Principal&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Federated&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Action&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sts:AssumeRoleWithWebIdentity&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;Condition&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;StringLike&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;token.actions.githubusercontent.com:sub&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;repo:my-org/my-repo:*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Attach whatever IAM policies the CDK deploy needs (CloudFormation, S3, Lambda, IAM) to this role.&lt;/p&gt;
&lt;h3 id=&#34;workflow&#34;&gt;Workflow&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# .github/workflows/deploy.yml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;on&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;push&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;branches&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;main]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;permissions&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;id-token&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;write&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;contents&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;read&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;jobs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;deploy-dev&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;runs-on&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ubuntu-latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;steps&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/checkout@v4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/setup-python@v5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;python-version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;3.12&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/setup-node@v4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;node-version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;20&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Install CDK CLI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;npm install -g aws-cdk&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Install dependencies&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;pip install -r requirements.txt -r requirements-dev.txt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Configure AWS credentials&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;aws-actions/configure-aws-credentials@v4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;role-to-assume&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;arn:aws:iam::123456789012:role/GitHubActionsRole&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;aws-region&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;us-east-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Synth&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cdk synth -c env=dev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;pytest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy dev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cdk deploy --require-approval never -c env=dev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;deploy-prod&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;runs-on&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ubuntu-latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;needs&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;deploy-dev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;environment&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;production&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;steps&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/checkout@v4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/setup-python@v5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;python-version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;3.12&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/setup-node@v4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;node-version&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;20&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Install CDK CLI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;npm install -g aws-cdk&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Install dependencies&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;pip install -r requirements.txt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Configure AWS credentials&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;aws-actions/configure-aws-credentials@v4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;role-to-assume&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;arn:aws:iam::123456789012:role/GitHubActionsRole&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;aws-region&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;us-east-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cdk deploy --require-approval never -c env=prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;environment: production&lt;/code&gt; job pauses for a manual approval before running. Configure required reviewers under Settings &amp;gt; Environments &amp;gt; production in the GitHub repository.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;jenkins&#34;&gt;Jenkins&lt;/h2&gt;
&lt;h3 id=&#34;auth-1&#34;&gt;Auth&lt;/h3&gt;
&lt;p&gt;The simplest setup: run the Jenkins agent on an EC2 instance with an IAM instance role. The CDK CLI picks up credentials automatically via the instance metadata service - no credentials to manage.&lt;/p&gt;
&lt;p&gt;If the agent is not on EC2, store AWS credentials as a Jenkins credential (Credentials &amp;gt; Add &amp;gt; AWS Credentials) and inject them into the pipeline with the &lt;code&gt;withCredentials&lt;/code&gt; block.&lt;/p&gt;
&lt;h3 id=&#34;jenkinsfile&#34;&gt;Jenkinsfile&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pipeline &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    agent any
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    environment &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        AWS_DEFAULT_REGION &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;us-east-1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stages &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        stage&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Install&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            steps &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pip install -r requirements.txt -r requirements-dev.txt&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;npm install -g aws-cdk&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        stage&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Synth&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            steps &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cdk synth -c env=dev&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        stage&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Test&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            steps &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;pytest&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        stage&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Deploy dev&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            steps &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cdk deploy --require-approval never -c env=dev&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        stage&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Approve prod&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            steps &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                input message: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Deploy to production?&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt; ok: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Deploy&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        stage&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Deploy prod&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            steps &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cdk deploy --require-approval never -c env=prod&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    post &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        failure &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Pipeline failed - check CloudFormation events for rollback details&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;input&lt;/code&gt; step pauses the pipeline until someone clicks Deploy in the Jenkins UI.&lt;/p&gt;
&lt;p&gt;If credentials are stored in Jenkins rather than on an instance role, wrap the deploy steps:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;withCredentials&lt;span style=&#34;color:#f92672&#34;&gt;([[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $class&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;AmazonWebServicesCredentialsBinding&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    credentialsId: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;aws-deploy-credentials&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;]])&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cdk deploy --require-approval never -c env=prod&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;azure-devops&#34;&gt;Azure DevOps&lt;/h2&gt;
&lt;h3 id=&#34;auth-2&#34;&gt;Auth&lt;/h3&gt;
&lt;p&gt;Store AWS credentials as secret pipeline variables (&lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt;, &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt;) or use the AWS Toolkit for Azure DevOps extension, which adds an &lt;code&gt;AWSShellScript&lt;/code&gt; task with a configured service connection.&lt;/p&gt;
&lt;p&gt;The example below uses secret variables directly, which works without the extension.&lt;/p&gt;
&lt;h3 id=&#34;pipeline&#34;&gt;Pipeline&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# azure-pipelines.yml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;trigger&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;branches&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;include&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ae81ff&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;variables&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;pythonVersion&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;3.12&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;awsRegion&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;us-east-1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;stages&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;stage&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;DeployDev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;displayName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy to dev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;jobs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;job&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;pool&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;vmImage&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ubuntu-latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;steps&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;task&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;UsePythonVersion@0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;inputs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;versionSpec&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;$(pythonVersion)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              pip install -r requirements.txt -r requirements-dev.txt
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              npm install -g aws-cdk&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;displayName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Install dependencies&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cdk synth -c env=dev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;displayName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Synth&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;env&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;$(AWS_ACCESS_KEY_ID)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;$(AWS_SECRET_ACCESS_KEY)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;AWS_DEFAULT_REGION&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;$(awsRegion)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;pytest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;displayName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cdk deploy --require-approval never -c env=dev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;displayName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy dev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;env&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;$(AWS_ACCESS_KEY_ID)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;$(AWS_SECRET_ACCESS_KEY)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;AWS_DEFAULT_REGION&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;$(awsRegion)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;stage&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;DeployProd&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;displayName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy to prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;dependsOn&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;DeployDev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;jobs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;deployment&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;environment&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;production&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;pool&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;vmImage&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ubuntu-latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;strategy&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;runOnce&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;deploy&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#f92672&#34;&gt;steps&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#f92672&#34;&gt;checkout&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;self&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#f92672&#34;&gt;task&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;UsePythonVersion@0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;inputs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;versionSpec&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;$(pythonVersion)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;: |&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                    pip install -r requirements.txt
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                    npm install -g aws-cdk&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;displayName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Install dependencies&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                - &lt;span style=&#34;color:#f92672&#34;&gt;script&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cdk deploy --require-approval never -c env=prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;displayName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                  &lt;span style=&#34;color:#f92672&#34;&gt;env&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;$(AWS_ACCESS_KEY_ID)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;$(AWS_SECRET_ACCESS_KEY)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#f92672&#34;&gt;AWS_DEFAULT_REGION&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;$(awsRegion)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;deployment&lt;/code&gt; job type with &lt;code&gt;environment: production&lt;/code&gt; enables approval gates. Configure approvers under Pipelines &amp;gt; Environments &amp;gt; production in Azure DevOps.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;aws-codepipeline&#34;&gt;AWS CodePipeline&lt;/h2&gt;
&lt;p&gt;CDK ships a &lt;code&gt;pipelines&lt;/code&gt; module that creates a CodePipeline pipeline directly from CDK code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_cdk &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pipelines
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pipeline &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; pipelines&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;CodePipeline(self, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Pipeline&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    synth&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;pipelines&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;ShellStep(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Synth&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        input&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;pipelines&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;CodePipelineSource&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;git_hub(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-org/my-repo&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;main&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        commands&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;[
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pip install -r requirements.txt&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;npm install -g aws-cdk&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cdk synth&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The pipeline is self-mutating - when CDK code changes, the pipeline updates itself before deploying application changes. It is tightly coupled to CodePipeline and requires more upfront setup than the external CI approaches above, but is the right fit when the deployment needs to stay fully within AWS.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  &lt;ol&gt;
&lt;li&gt;&lt;code&gt;--require-approval never&lt;/code&gt; skips the IAM diff prompt. Review IAM changes in &lt;code&gt;cdk diff&lt;/code&gt; output or pull request checks before merging rather than at deploy time.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;cdk.context.json&lt;/code&gt; file should be committed - pipelines need it for SSM lookups cached in Chapter 2 to avoid live SSM calls on every run.&lt;/li&gt;
&lt;li&gt;CloudFormation automatically rolls back a failed deployment to the previous stable state. No extra rollback configuration is needed in the pipeline.&lt;/li&gt;
&lt;li&gt;For OIDC in GitHub Actions, scope the trust condition to a specific branch (&lt;code&gt;repo:my-org/my-repo:ref:refs/heads/main&lt;/code&gt;) rather than &lt;code&gt;*&lt;/code&gt; to limit which workflows can assume the role.&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>AWS CDK - Testing CDK Stacks</title>
      <link>https://bastabc.com/posts/aws-cdk-testing-stacks/</link>
      <pubDate>Thu, 05 Jun 2025 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/aws-cdk-testing-stacks/</guid>
      <description>&lt;p&gt;This is Chapter 4 of the &lt;a href=&#34;https://bastabc.com/posts/aws-cdk-infrastructure-as-code/&#34;&gt;Infrastructure as Code with AWS CDK&lt;/a&gt; series. Chapter 3 built &lt;code&gt;FileProcessorConstruct&lt;/code&gt; - the tests here cover that construct and the stack it lives in.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;how-cdk-testing-works&#34;&gt;How CDK testing works&lt;/h2&gt;
&lt;p&gt;CDK unit tests don&amp;rsquo;t deploy anything. They synthesize a stack into a CloudFormation template and run assertions against that template. No AWS credentials needed, and the suite runs in CI without any special setup.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;aws_cdk.assertions&lt;/code&gt; module is already part of &lt;code&gt;aws-cdk-lib&lt;/code&gt; - no extra install required.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;setup&#34;&gt;Setup&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;cdk init&lt;/code&gt; adds pytest to &lt;code&gt;requirements-dev.txt&lt;/code&gt; automatically. Install it if not already active:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install -r requirements-dev.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Create the test files:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;my-cdk-app/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── tests/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── __init__.py
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── test_file_processor.py
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── test_stack.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;testing-the-construct&#34;&gt;Testing the construct&lt;/h2&gt;
&lt;p&gt;Each test builds a minimal stack, instantiates the construct inside it, and asserts against the synthesized template:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# tests/test_file_processor.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; pytest
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_cdk &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; App, Stack
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_cdk.assertions &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Template, Match
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; my_cdk_app.file_processor &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; FileProcessorConstruct
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;TEST_PROPS &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dict(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    bucket_name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;test-bucket&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    log_level&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DEBUG&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    lambda_timeout&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@pytest.fixture&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;template&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    app &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; App()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stack &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Stack(app, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;TestStack&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    FileProcessorConstruct(stack, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;TestConstruct&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;**&lt;/span&gt;TEST_PROPS)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; Template&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;from_stack(stack)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test_bucket_created&lt;/span&gt;(template):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    template&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;has_resource_properties(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AWS::S3::Bucket&amp;#34;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;BucketName&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;test-bucket&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test_lambda_runtime_and_timeout&lt;/span&gt;(template):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    template&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;has_resource_properties(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AWS::Lambda::Function&amp;#34;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Runtime&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;python3.12&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Timeout&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Environment&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Variables&amp;#34;&lt;/span&gt;: {&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;LOG_LEVEL&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DEBUG&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test_lambda_has_read_access&lt;/span&gt;(template):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    template&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;has_resource_properties(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AWS::IAM::Policy&amp;#34;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;PolicyDocument&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Statement&amp;#34;&lt;/span&gt;: Match&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;array_with([
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                Match&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;object_like({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Action&amp;#34;&lt;/span&gt;: Match&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;array_with([&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;s3:GetObject*&amp;#34;&lt;/span&gt;]),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Effect&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Allow&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            ])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;has_resource_properties&lt;/code&gt; does a subset match - the resource only needs to contain the specified properties, not match them exactly. &lt;code&gt;Match.array_with&lt;/code&gt; and &lt;code&gt;Match.object_like&lt;/code&gt; apply the same subset logic to nested arrays and objects.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;testing-the-stack&#34;&gt;Testing the stack&lt;/h2&gt;
&lt;p&gt;Test &lt;code&gt;MyStack&lt;/code&gt; the same way, using a config dict that matches what &lt;code&gt;app.py&lt;/code&gt; passes:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# tests/test_stack.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_cdk &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; App
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_cdk.assertions &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Template
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; my_cdk_app.my_cdk_app_stack &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; MyStack
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;TEST_CONFIG &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bucket_name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;test-bucket&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log_level&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;INFO&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lambda_timeout&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test_stack_resource_count&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    app &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; App()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stack &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; MyStack(app, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;TestStack&amp;#34;&lt;/span&gt;, config&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;TEST_CONFIG)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    template &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Template&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;from_stack(stack)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    template&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;resource_count_is(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AWS::S3::Bucket&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    template&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;resource_count_is(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AWS::Lambda::Function&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;snapshot-testing&#34;&gt;Snapshot testing&lt;/h2&gt;
&lt;p&gt;Snapshot tests capture the full synthesized template and fail if anything changes. Write the template to a file on first run, then compare on subsequent runs:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; pathlib &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Path
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_cdk &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; App
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_cdk.assertions &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Template
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; my_cdk_app.my_cdk_app_stack &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; MyStack
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SNAPSHOT_PATH &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Path(__file__)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parent &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;snapshots&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;stack.json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;TEST_CONFIG &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bucket_name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;test-bucket&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log_level&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;INFO&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lambda_timeout&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test_stack_snapshot&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    app &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; App()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stack &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; MyStack(app, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;TestStack&amp;#34;&lt;/span&gt;, config&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;TEST_CONFIG)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    template &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Template&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;from_stack(stack)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;to_json()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; SNAPSHOT_PATH&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;exists():
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        SNAPSHOT_PATH&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;parent&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;mkdir(exist_ok&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        SNAPSHOT_PATH&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;write_text(json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;dumps(template, indent&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    expected &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loads(SNAPSHOT_PATH&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;read_text())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;assert&lt;/span&gt; template &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; expected
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Commit the snapshot file. To update it after an intentional change, delete the file and run the test once to regenerate.&lt;/p&gt;
&lt;p&gt;The downside: CDK version bumps routinely touch metadata in the template - &lt;code&gt;aws:cdk:path&lt;/code&gt;, asset hashes, checksums. Any of these invalidates the snapshot even when nothing meaningful changed. They work well for catching unintended IAM or resource config changes, but generate noise as a general regression check.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;running-tests&#34;&gt;Running tests&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pytest
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pytest -v                             &lt;span style=&#34;color:#75715e&#34;&gt;# verbose output&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pytest tests/test_file_processor.py  &lt;span style=&#34;color:#75715e&#34;&gt;# single file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  &lt;ol&gt;
&lt;li&gt;&lt;code&gt;has_resource_properties&lt;/code&gt; only checks the &lt;code&gt;Properties&lt;/code&gt; block. To assert on the full resource including &lt;code&gt;DeletionPolicy&lt;/code&gt;, &lt;code&gt;DependsOn&lt;/code&gt;, or &lt;code&gt;Metadata&lt;/code&gt;, use &lt;code&gt;has_resource&lt;/code&gt; instead.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Template.from_stack&lt;/code&gt; calls &lt;code&gt;app.synth()&lt;/code&gt; implicitly - no need to call it manually.&lt;/li&gt;
&lt;li&gt;CDK assigns logical IDs based on the construct path. Changing a construct&amp;rsquo;s &lt;code&gt;id&lt;/code&gt; argument changes its logical ID, which CloudFormation treats as a delete and recreate. Snapshot tests catch this; &lt;code&gt;has_resource_properties&lt;/code&gt; tests do not.&lt;/li&gt;
&lt;li&gt;For TypeScript CDK projects, Jest has built-in snapshot support via &lt;code&gt;toMatchSnapshot()&lt;/code&gt;. Python has no equivalent - the manual file approach above is the workaround.&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>AWS CDK - Writing and Sharing Constructs</title>
      <link>https://bastabc.com/posts/aws-cdk-writing-constructs/</link>
      <pubDate>Wed, 14 May 2025 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/aws-cdk-writing-constructs/</guid>
      <description>&lt;p&gt;This is Chapter 3 of the &lt;a href=&#34;https://bastabc.com/posts/aws-cdk-infrastructure-as-code/&#34;&gt;Infrastructure as Code with AWS CDK&lt;/a&gt; series. Chapters 1 and 2 put the S3 bucket and Lambda directly in &lt;code&gt;MyStack&lt;/code&gt;. Both get extracted into a reusable construct here.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;l1-l2-l3&#34;&gt;L1, L2, L3&lt;/h2&gt;
&lt;p&gt;CDK organises constructs into three levels:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;L1&lt;/strong&gt; - raw CloudFormation resource wrappers, prefixed with &lt;code&gt;Cfn&lt;/code&gt;. &lt;code&gt;CfnBucket&lt;/code&gt;, &lt;code&gt;CfnFunction&lt;/code&gt;. No defaults added - every property must be set explicitly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;L2&lt;/strong&gt; - CDK&amp;rsquo;s built-in higher-level constructs. &lt;code&gt;s3.Bucket&lt;/code&gt;, &lt;code&gt;_lambda.Function&lt;/code&gt;. These set sensible defaults, expose helper methods, and handle IAM via &lt;code&gt;grant_*&lt;/code&gt; methods.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;L3&lt;/strong&gt; - composite constructs you write that bundle multiple resources into a single unit. The previous chapters used L2 constructs directly inside a stack. This chapter builds an L3.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;the-problem&#34;&gt;The problem&lt;/h2&gt;
&lt;p&gt;The current &lt;code&gt;MyStack&lt;/code&gt; creates the bucket and Lambda directly:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyStack&lt;/span&gt;(Stack):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(self, scope, id, config, &lt;span style=&#34;color:#f92672&#34;&gt;**&lt;/span&gt;kwargs):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        super()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(scope, id, &lt;span style=&#34;color:#f92672&#34;&gt;**&lt;/span&gt;kwargs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        bucket &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; s3&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Bucket(self, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;FilesBucket&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            bucket_name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;config[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bucket_name&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fn &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; _lambda&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Function(self, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ProcessorFunction&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            runtime&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;_lambda&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Runtime&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;PYTHON_3_12,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            handler&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;handler.handler&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            code&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;_lambda&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Code&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;from_asset(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lambda&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            timeout&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;Duration&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;seconds(config[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lambda_timeout&amp;#34;&lt;/span&gt;]),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            environment&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;LOG_LEVEL&amp;#34;&lt;/span&gt;: config[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log_level&amp;#34;&lt;/span&gt;]}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        bucket&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;grant_read(fn)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you need a second processor - say one for raw uploads and one for processed files - all of this gets duplicated. That&amp;rsquo;s what a construct solves.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;writing-the-construct&#34;&gt;Writing the construct&lt;/h2&gt;
&lt;p&gt;Add &lt;code&gt;file_processor.py&lt;/code&gt; inside the existing app module:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;my-cdk-app/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── app.py
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── my_cdk_app/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── __init__.py
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── my_cdk_app_stack.py
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── file_processor.py     ← new
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# my_cdk_app/file_processor.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_cdk &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Duration,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    aws_s3 &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; s3,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    aws_lambda &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; _lambda,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; constructs &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Construct
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;FileProcessorConstruct&lt;/span&gt;(Construct):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        scope: Construct,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        id: str,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        bucket_name: str,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        log_level: str,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        lambda_timeout: int,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        super()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(scope, id)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;bucket &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; s3&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Bucket(self, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Bucket&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            bucket_name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;bucket_name
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fn &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; _lambda&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Function(self, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Function&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            runtime&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;_lambda&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Runtime&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;PYTHON_3_12,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            handler&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;handler.handler&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            code&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;_lambda&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Code&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;from_asset(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lambda&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            timeout&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;Duration&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;seconds(lambda_timeout),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            environment&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;LOG_LEVEL&amp;#34;&lt;/span&gt;: log_level}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;bucket&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;grant_read(self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;fn)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;*&lt;/code&gt; before the keyword arguments makes them keyword-only - callers must pass them by name, not by position. &lt;code&gt;self.bucket&lt;/code&gt; and &lt;code&gt;self.fn&lt;/code&gt; are public so callers can reach in and extend if needed.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;using-it-in-a-stack&#34;&gt;Using it in a stack&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# my_cdk_app/my_cdk_app_stack.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_cdk &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Stack
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; constructs &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Construct
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; my_cdk_app.file_processor &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; FileProcessorConstruct
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyStack&lt;/span&gt;(Stack):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(self, scope: Construct, id: str, config: dict, &lt;span style=&#34;color:#f92672&#34;&gt;**&lt;/span&gt;kwargs):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        super()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(scope, id, &lt;span style=&#34;color:#f92672&#34;&gt;**&lt;/span&gt;kwargs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        FileProcessorConstruct(self, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;FileProcessor&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            bucket_name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;config[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bucket_name&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            log_level&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;config[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log_level&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            lambda_timeout&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;config[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lambda_timeout&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;reusing-the-construct&#34;&gt;Reusing the construct&lt;/h2&gt;
&lt;p&gt;Two processors in the same stack, no duplication:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyStack&lt;/span&gt;(Stack):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(self, scope: Construct, id: str, config: dict, &lt;span style=&#34;color:#f92672&#34;&gt;**&lt;/span&gt;kwargs):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        super()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(scope, id, &lt;span style=&#34;color:#f92672&#34;&gt;**&lt;/span&gt;kwargs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        FileProcessorConstruct(self, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;RawProcessor&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            bucket_name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;config[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;bucket_name&amp;#39;&lt;/span&gt;]&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;-raw&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            log_level&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;config[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log_level&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            lambda_timeout&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;config[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lambda_timeout&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        FileProcessorConstruct(self, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ProcessedProcessor&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            bucket_name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;config[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;bucket_name&amp;#39;&lt;/span&gt;]&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;-processed&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            log_level&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;config[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log_level&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            lambda_timeout&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;config[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lambda_timeout&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;CDK uses the &lt;code&gt;id&lt;/code&gt; argument (&lt;code&gt;RawProcessor&lt;/code&gt;, &lt;code&gt;ProcessedProcessor&lt;/code&gt;) to make all resource logical IDs unique within the stack. Both constructs deploy without naming conflicts.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;using-exposed-attributes&#34;&gt;Using exposed attributes&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;self.bucket&lt;/code&gt; and &lt;code&gt;self.fn&lt;/code&gt; when something needs to be added at the call site:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;processor &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; FileProcessorConstruct(self, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;FileProcessor&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    bucket_name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;config[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bucket_name&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    log_level&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;config[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;log_level&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    lambda_timeout&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;config[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lambda_timeout&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Grant a second function read access to the same bucket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;processor&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;bucket&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;grant_read(reporting_fn)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If it&amp;rsquo;s specific to one stack, keep it there rather than baking it into the construct.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  &lt;ol&gt;
&lt;li&gt;Do not name the constructs directory &lt;code&gt;constructs/&lt;/code&gt; - it conflicts with the CDK &lt;code&gt;constructs&lt;/code&gt; package import. Putting files inside the app module (&lt;code&gt;my_cdk_app/&lt;/code&gt;) sidesteps this entirely.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;*&lt;/code&gt; in the &lt;code&gt;__init__&lt;/code&gt; signature enforces keyword arguments. Without it, callers can pass values positionally, which gets confusing with three or more parameters.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;self.bucket&lt;/code&gt; and &lt;code&gt;self.fn&lt;/code&gt; being public is a choice. If the construct is fully self-contained and callers should never reach in, keep them private with a leading underscore (&lt;code&gt;self._bucket&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;To use a construct across multiple CDK projects, move it into a standalone Python package and install it as a dependency. Cross-language support (TypeScript, Java) requires &lt;code&gt;jsii&lt;/code&gt; - a separate topic.&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>AWS CDK - Managing Configuration and Context</title>
      <link>https://bastabc.com/posts/aws-cdk-managing-configuration-and-context/</link>
      <pubDate>Mon, 28 Apr 2025 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/aws-cdk-managing-configuration-and-context/</guid>
      <description>&lt;p&gt;This is Chapter 2 of the &lt;a href=&#34;https://bastabc.com/posts/aws-cdk-infrastructure-as-code/&#34;&gt;Infrastructure as Code with AWS CDK&lt;/a&gt; series. Four approaches to environment-specific configuration in CDK stacks - same use case throughout so the trade-offs sit side by side.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use case&lt;/strong&gt;: An S3 bucket and Lambda function where bucket name, log level, and Lambda timeout vary per environment (dev, staging, prod).&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;quick-comparison&#34;&gt;Quick comparison&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Approach&lt;/th&gt;
          &lt;th&gt;Version controlled&lt;/th&gt;
          &lt;th&gt;Supports secrets&lt;/th&gt;
          &lt;th&gt;Change without redeploy&lt;/th&gt;
          &lt;th&gt;Shared across stacks&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Static config (&lt;code&gt;cdk.json&lt;/code&gt;)&lt;/td&gt;
          &lt;td&gt;Yes&lt;/td&gt;
          &lt;td&gt;No&lt;/td&gt;
          &lt;td&gt;No&lt;/td&gt;
          &lt;td&gt;No&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Dynamic config&lt;/td&gt;
          &lt;td&gt;No&lt;/td&gt;
          &lt;td&gt;Partial&lt;/td&gt;
          &lt;td&gt;Yes&lt;/td&gt;
          &lt;td&gt;No&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Secrets Manager&lt;/td&gt;
          &lt;td&gt;No&lt;/td&gt;
          &lt;td&gt;Yes&lt;/td&gt;
          &lt;td&gt;Yes&lt;/td&gt;
          &lt;td&gt;Yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;CI/CD context injection&lt;/td&gt;
          &lt;td&gt;No&lt;/td&gt;
          &lt;td&gt;No&lt;/td&gt;
          &lt;td&gt;Yes&lt;/td&gt;
          &lt;td&gt;No&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&#34;local-dev-patterns&#34;&gt;Local dev patterns&lt;/h2&gt;
&lt;p&gt;Suitable for personal projects or local development. Neither is a good fit for CI/CD pipelines or shared team environments.&lt;/p&gt;
&lt;h3 id=&#34;static-config-cdkjson&#34;&gt;Static config (&lt;code&gt;cdk.json&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;Store environment-specific config in &lt;code&gt;cdk.json&lt;/code&gt; under the &lt;code&gt;context&lt;/code&gt; key. CDK reads this file automatically at synth time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When to use&lt;/strong&gt;: Stable, non-sensitive values that are the same for everyone on the team.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;app&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;python3 app.py&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;context&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;dev&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;bucket_name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-bucket-dev&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;log_level&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DEBUG&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;lambda_timeout&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;prod&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;bucket_name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-bucket-prod&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;log_level&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;INFO&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;lambda_timeout&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Read context in &lt;code&gt;app.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; cdk&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;App()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;env_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; app&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;node&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;try_get_context(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;env&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;or&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dev&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;config &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; app&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;node&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;try_get_context(env_name)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;MyStack(app, &lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;MyStack-&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;env_name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, config&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;config)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Deploy with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cdk deploy -c env&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;dev
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cdk deploy -c env&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;prod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Trade-off&lt;/strong&gt;: Any config change requires a commit and redeploy. Never put secrets here - &lt;code&gt;cdk.json&lt;/code&gt; is committed to source control.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;dynamic-config&#34;&gt;Dynamic config&lt;/h3&gt;
&lt;p&gt;Load config at synth time from a local file or environment variables, outside the CDK project. Useful when values differ per developer or machine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When to use&lt;/strong&gt;: Values that differ between developers - e.g. personal AWS account IDs, local endpoint overrides.&lt;/p&gt;
&lt;p&gt;Create a &lt;code&gt;config.dev.json&lt;/code&gt; outside source control:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;bucket_name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-bucket-dev&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;log_level&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;DEBUG&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;lambda_timeout&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;30&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Load it in &lt;code&gt;app.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; os
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; cdk&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;App()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;env_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;environ&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ENV&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dev&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;with&lt;/span&gt; open(&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;config.&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;env_name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;.json&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; f:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    config &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;load(f)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;MyStack(app, &lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;MyStack-&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;env_name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, config&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;config)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Add config files to &lt;code&gt;.gitignore&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;config.*.json
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Deploy with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ENV&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;dev cdk deploy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ENV&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;prod cdk deploy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Trade-off&lt;/strong&gt;: Config lives outside source control - harder to audit and reproduce. Not suitable for team environments where config needs to be consistent.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;production-patterns&#34;&gt;Production patterns&lt;/h2&gt;
&lt;p&gt;Config lives in AWS or is injected by the pipeline - not in the codebase. Values can change without a code commit.&lt;/p&gt;
&lt;h3 id=&#34;secrets-manager&#34;&gt;Secrets Manager&lt;/h3&gt;
&lt;p&gt;Store sensitive config in Secrets Manager. Values are fetched at deploy time and injected into the Lambda environment - they are never visible in the CloudFormation template.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When to use&lt;/strong&gt;: API keys, database passwords, tokens. Secrets should never go in &lt;code&gt;cdk.json&lt;/code&gt; or plain environment variables.&lt;/p&gt;
&lt;p&gt;Create a secret (run once per environment):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aws secretsmanager create-secret &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  --name &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/my-app/dev/config&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  --secret-string &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;{&amp;#34;api_key&amp;#34;:&amp;#34;abc123&amp;#34;,&amp;#34;db_password&amp;#34;:&amp;#34;s3cr3t&amp;#34;}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Reference the secret in the stack:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_cdk &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; aws_secretsmanager &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; secretsmanager
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyStack&lt;/span&gt;(Stack):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(self, scope, id, env_name, &lt;span style=&#34;color:#f92672&#34;&gt;**&lt;/span&gt;kwargs):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        super()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(scope, id, &lt;span style=&#34;color:#f92672&#34;&gt;**&lt;/span&gt;kwargs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        secret &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; secretsmanager&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Secret&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;from_secret_name_v2(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            self, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AppSecret&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/my-app/&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;env_name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/config&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        fn &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; _lambda&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Function(self, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;MyFunction&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            environment&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;SECRET_ARN&amp;#34;&lt;/span&gt;: secret&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;secret_arn
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        secret&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;grant_read(fn)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The Lambda reads the secret at runtime using the ARN:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; boto3&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt; os
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;handler&lt;/span&gt;(event, context):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    client &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; boto3&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;client(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;secretsmanager&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    secret &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; json&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;loads(client&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_secret_value(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        SecretId&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;os&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;environ[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;SECRET_ARN&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;SecretString&amp;#34;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    api_key &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; secret[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;api_key&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Trade-off&lt;/strong&gt;: Cost per secret per month. Values are only available at runtime, not synth time. Rotation adds operational overhead but is worth setting up for credentials.&lt;/p&gt;
&lt;p&gt;For non-sensitive shared config that needs to change without a code commit - bucket names, log levels, timeouts - SSM Parameter Store is an alternative. Parameters are fetched at synth time via &lt;code&gt;value_from_lookup&lt;/code&gt; and cached in &lt;code&gt;cdk.context.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_cdk &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; aws_ssm &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; ssm
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bucket_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ssm&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;StringParameter&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;value_from_lookup(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    self, &lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/my-app/&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;env_name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/bucket_name&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Commit &lt;code&gt;cdk.context.json&lt;/code&gt; so CI doesn&amp;rsquo;t need live SSM access on every run. SSM has no secrets support - keep anything sensitive in Secrets Manager.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;cicd-context-injection&#34;&gt;CI/CD context injection&lt;/h3&gt;
&lt;p&gt;Pass values into the stack directly from the pipeline via CDK context flags. The stack reads them at synth time - no external config files or AWS lookups required.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When to use&lt;/strong&gt;: Values that are known to the pipeline at deploy time - environment name, account ID, deploy target region, feature flags. Works well when the pipeline is the source of truth for what gets deployed where.&lt;/p&gt;
&lt;p&gt;The stack reads context with &lt;code&gt;try_get_context&lt;/code&gt; and uses &lt;code&gt;Stack.of(self).account&lt;/code&gt; for the resolved AWS account:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; aws_cdk &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Stack
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; constructs &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; Construct
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyStack&lt;/span&gt;(Stack):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(self, scope: Construct, id: str, &lt;span style=&#34;color:#f92672&#34;&gt;**&lt;/span&gt;kwargs):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        super()&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;__init__&lt;/span&gt;(scope, id, &lt;span style=&#34;color:#f92672&#34;&gt;**&lt;/span&gt;kwargs)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        env_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;node&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;try_get_context(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;env&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        profile &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;node&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;try_get_context(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;profile&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        account_id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Stack&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;of(self)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;account
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        bucket &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; s3&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;Bucket(self, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;FilesBucket&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            bucket_name&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-bucket-&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;env_name&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{&lt;/span&gt;account_id&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The pipeline passes context at deploy time:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# GitHub Actions, CodePipeline, or any CI runner&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cdk deploy -c env&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;prod -c profile&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;my-prod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A GitHub Actions step looks like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Deploy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cdk deploy --require-approval never -c env=prod -c profile=my-prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;env&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ secrets.AWS_ACCESS_KEY_ID }}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ secrets.AWS_SECRET_ACCESS_KEY }}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;try_get_context&lt;/code&gt; returns &lt;code&gt;None&lt;/code&gt; if the key is not passed. Add a guard if the value is required:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;env_name &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; self&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;node&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;try_get_context(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;env&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; env_name:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;raise&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ValueError&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;env context is required - pass with -c env=&amp;lt;name&amp;gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Trade-off&lt;/strong&gt;: Context values are visible in the pipeline logs and the synthesized CloudFormation template. Keep secrets in &lt;a href=&#34;#secrets-manager&#34;&gt;Secrets Manager&lt;/a&gt; and pass only non-sensitive identifiers this way.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  &lt;ol&gt;
&lt;li&gt;These approaches combine well - SSM for non-sensitive shared config, Secrets Manager for secrets, CI/CD context injection for environment identifiers.&lt;/li&gt;
&lt;li&gt;Secrets Manager values are never embedded in CloudFormation templates - they are resolved at runtime by the Lambda itself.&lt;/li&gt;
&lt;li&gt;On Windows with PowerShell, set the env var with &lt;code&gt;$env:ENV=&amp;quot;dev&amp;quot;; cdk deploy&lt;/code&gt; instead of the bash syntax.&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>AWS CDK - Project Setup and Bootstrapping</title>
      <link>https://bastabc.com/posts/aws-cdk-project-setup-and-bootstrapping/</link>
      <pubDate>Tue, 11 Mar 2025 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/aws-cdk-project-setup-and-bootstrapping/</guid>
      <description>&lt;p&gt;This is Chapter 1 of the &lt;a href=&#34;https://bastabc.com/posts/aws-cdk-infrastructure-as-code/&#34;&gt;Infrastructure as Code with AWS CDK&lt;/a&gt; series. Two things come before writing any stack code: initialising the CDK project and bootstrapping the AWS account.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Python 3.9+&lt;/li&gt;
&lt;li&gt;Node.js 18+ (CDK CLI is a Node package)&lt;/li&gt;
&lt;li&gt;AWS CLI v2 configured with valid credentials - see &lt;a href=&#34;https://bastabc.com/posts/connect-to-aws-sso-ssh-into-ec2-instance/&#34;&gt;Connect to AWS SSO and SSH into EC2 Instance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;An AWS account with permissions to create IAM roles, S3 buckets, and CloudFormation stacks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Install the CDK CLI globally:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm install -g aws-cdk
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cdk --version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;initialise-project&#34;&gt;Initialise project&lt;/h2&gt;
&lt;p&gt;Create a new directory and initialise a CDK Python project inside it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir my-cdk-app &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; cd my-cdk-app
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cdk init app --language python
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This scaffolds the project with a virtual environment, a sample stack, and a &lt;code&gt;cdk.json&lt;/code&gt; config file.&lt;/p&gt;
&lt;p&gt;Activate the virtual environment and install dependencies:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# macOS / Linux&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;source .venv/bin/activate
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Windows (PowerShell)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.venv&lt;span style=&#34;color:#ae81ff&#34;&gt;\S&lt;/span&gt;cripts&lt;span style=&#34;color:#ae81ff&#34;&gt;\a&lt;/span&gt;ctivate.ps1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install -r requirements.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;directory-structure&#34;&gt;Directory structure&lt;/h2&gt;
&lt;p&gt;After initialisation the project looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;my-cdk-app/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── app.py                  # CDK app entry point
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── cdk.json                # CDK config and context
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── requirements.txt        # Python dependencies
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── requirements-dev.txt    # Dev dependencies (e.g. pytest)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── .venv/                  # Python virtual environment
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── my_cdk_app/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├── __init__.py
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    └── my_cdk_app_stack.py # Default stack
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;app.py&lt;/code&gt; is where the CDK app is defined and stacks are instantiated. &lt;code&gt;my_cdk_app_stack.py&lt;/code&gt; is where resources are defined. The folder and class names match the project name passed to &lt;code&gt;cdk init&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;bootstrap-aws-account&#34;&gt;Bootstrap AWS account&lt;/h2&gt;
&lt;p&gt;CDK needs a one-time setup in each AWS account and region it deploys to. Bootstrapping creates an S3 bucket (for assets) and IAM roles (for deployments) in the account:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cdk bootstrap aws://123456789012/us-east-1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Replace &lt;code&gt;123456789012&lt;/code&gt; with your account ID and &lt;code&gt;us-east-1&lt;/code&gt; with your target region. If credentials are already configured via AWS CLI or SSO, the account ID can be omitted:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cdk bootstrap
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This only needs to run once per account/region combination. Re-running it is safe - it updates the bootstrap stack if needed.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;synth-and-deploy&#34;&gt;Synth and deploy&lt;/h2&gt;
&lt;p&gt;CDK compiles Python into CloudFormation before deploying. Run &lt;code&gt;synth&lt;/code&gt; to see the generated CloudFormation template:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cdk synth
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This outputs a CloudFormation template to &lt;code&gt;cdk.out/&lt;/code&gt;. Use it to verify what CDK will create before deploying.&lt;/p&gt;
&lt;p&gt;Deploy the stack:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cdk deploy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;CDK shows a diff of changes and prompts for confirmation before creating or modifying any IAM resources.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;verify&#34;&gt;Verify&lt;/h2&gt;
&lt;p&gt;After deploying, the stack appears in CloudFormation in the AWS console. Check:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CloudFormation &amp;gt; Stacks &amp;gt; &lt;code&gt;MyCdkAppStack&lt;/code&gt; - status should be &lt;code&gt;CREATE_COMPLETE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;S3 &amp;gt; Buckets - the CDK bootstrap bucket should be visible (&lt;code&gt;cdk-hnb659fds-assets-...&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To tear down the stack:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cdk destroy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  &lt;ol&gt;
&lt;li&gt;The bootstrap stack itself is a CloudFormation stack - it can be viewed and managed in the console under &lt;code&gt;CDKToolkit&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Each AWS account + region combination needs its own bootstrap - e.g. deploying to both &lt;code&gt;us-east-1&lt;/code&gt; and &lt;code&gt;ap-southeast-2&lt;/code&gt; requires bootstrapping both.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cdk synth&lt;/code&gt; is useful for debugging - the generated CloudFormation is in &lt;code&gt;cdk.out/&lt;/code&gt; and can be inspected directly.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;.venv/&lt;/code&gt; folder and &lt;code&gt;cdk.out/&lt;/code&gt; should be in &lt;code&gt;.gitignore&lt;/code&gt; - they are added automatically by &lt;code&gt;cdk init&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>Install CloudWatch Agent in EC2 Instance</title>
      <link>https://bastabc.com/posts/install-cloudwatch-agent-in-ec2/</link>
      <pubDate>Tue, 03 Dec 2024 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/install-cloudwatch-agent-in-ec2/</guid>
      <description>&lt;p&gt;EC2 does not send memory or disk metrics to CloudWatch by default - only CPU, network and status checks. The CloudWatch agent runs inside the instance and collects system-level metrics and logs directly.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;EC2 instance running (Amazon Linux 2 or Ubuntu)&lt;/li&gt;
&lt;li&gt;SSH access to the instance - see &lt;a href=&#34;https://bastabc.com/posts/connect-to-aws-sso-ssh-into-ec2-instance/&#34;&gt;Connect to AWS SSO and SSH into EC2 Instance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Terminal&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;attach-iam-role-to-the-instance&#34;&gt;Attach IAM role to the instance&lt;/h2&gt;
&lt;p&gt;The agent needs permission to write metrics and logs to CloudWatch. In the AWS console:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;EC2 &amp;gt; Instances &amp;gt; select your instance&lt;/li&gt;
&lt;li&gt;Actions &amp;gt; Security &amp;gt; Modify IAM role&lt;/li&gt;
&lt;li&gt;Attach a role that has &lt;code&gt;CloudWatchAgentServerPolicy&lt;/code&gt; managed policy&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If no such role exists, create one:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;IAM &amp;gt; Roles &amp;gt; Create role&lt;/li&gt;
&lt;li&gt;Trusted entity: AWS service &amp;gt; EC2&lt;/li&gt;
&lt;li&gt;Add permission: &lt;code&gt;CloudWatchAgentServerPolicy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Name it (e.g. &lt;code&gt;ec2-cloudwatch-agent-role&lt;/code&gt;) and create&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;install-the-agent&#34;&gt;Install the agent&lt;/h2&gt;
&lt;p&gt;SSH into the instance, then run the appropriate command for your OS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Amazon Linux 2:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo yum install -y amazon-cloudwatch-agent
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Ubuntu:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt-get install -y amazon-cloudwatch-agent
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;configure-the-agent&#34;&gt;Configure the agent&lt;/h2&gt;
&lt;p&gt;Run the configuration wizard - it steps through metrics and log collection interactively:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Alternatively, create a config file manually at &lt;code&gt;/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;agent&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;metrics_collection_interval&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;run_as_user&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;root&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;metrics&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;namespace&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;CWAgent&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;metrics_collected&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;mem&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;measurement&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mem_used_percent&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;disk&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;measurement&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;disk_used_percent&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;resources&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Collects memory and disk usage every 60 seconds under the &lt;code&gt;CWAgent&lt;/code&gt; namespace.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;start-the-agent&#34;&gt;Start the agent&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -a fetch-config &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -m ec2 &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;&lt;/span&gt;  -s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;verify&#34;&gt;Verify&lt;/h2&gt;
&lt;p&gt;Check the agent is running:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -m ec2 -a status
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Expected output:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;running&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;starttime&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;...&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the AWS console, go to CloudWatch &amp;gt; Metrics &amp;gt; All metrics &amp;gt; CWAgent to confirm metrics are coming in. Give it 2-3 minutes after starting for metrics to show up.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  &lt;ol&gt;
&lt;li&gt;If the instance had no IAM role before, restart the agent after attaching the role - it picks up credentials on start.&lt;/li&gt;
&lt;li&gt;The wizard config is saved to &lt;code&gt;/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json&lt;/code&gt; by default.&lt;/li&gt;
&lt;li&gt;To collect logs, add a &lt;code&gt;logs&lt;/code&gt; block to the config pointing to the log file paths.&lt;/li&gt;
&lt;li&gt;Agent logs are at &lt;code&gt;/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log&lt;/code&gt; - check here if metrics are not appearing.&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>AWS Certified Cloud Practitioner (CLF-C02) Study Notes</title>
      <link>https://bastabc.com/posts/aws-clf-c02/</link>
      <pubDate>Fri, 15 Nov 2024 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/aws-clf-c02/</guid>
      <description>&lt;p&gt;I sat the AWS Certified Cloud Practitioner (CLF-C02) back in 2024 and passed. CLF-C02 is AWS&amp;rsquo;s foundational certification - broad rather than deep, covering cloud concepts, security, core services, and billing at a level anyone working around AWS should recognise.&lt;/p&gt;
&lt;p&gt;It is the most accessible AWS exam, so if you already work with AWS day to day, a lot of this will be familiar. Treat it as a breadth check rather than a deep technical exam, and calibrate the prep to what you already know.&lt;/p&gt;
&lt;p&gt;For more content on other relevant certifications, check &lt;a href=&#34;https://bastabc.com/certifications/&#34;&gt;Certifications&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;the-exam-at-a-glance&#34;&gt;The exam at a glance&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;/th&gt;
          &lt;th&gt;&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Questions&lt;/td&gt;
          &lt;td&gt;65 (50 scored, 15 unscored)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Time&lt;/td&gt;
          &lt;td&gt;90 minutes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Format&lt;/td&gt;
          &lt;td&gt;Multiple choice and multiple response&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Passing score&lt;/td&gt;
          &lt;td&gt;700 out of 1000 (scaled)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Cost&lt;/td&gt;
          &lt;td&gt;100 USD&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Validity&lt;/td&gt;
          &lt;td&gt;3 years&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The score is scaled, so you do not need 70% of questions right - it is normalised across question difficulty. The exam is conceptual: it tests whether you understand what AWS services do and when they apply, not whether you can build with them.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;the-four-domains&#34;&gt;The four domains&lt;/h2&gt;
&lt;p&gt;CLF-C02 has four domains. The percentages are the share of scored content, straight from the exam guide - the bigger the share, the more of your study time it deserves.&lt;/p&gt;
&lt;h3 id=&#34;domain-1---cloud-concepts-24&#34;&gt;Domain 1 - Cloud Concepts (24%)&lt;/h3&gt;

&lt;div class=&#34;callout callout-aws2&#34;&gt;
  The &amp;ldquo;why cloud&amp;rdquo; domain. Value proposition of the cloud, the AWS global infrastructure, and the basics of how cloud economics differ from running your own hardware. Conceptual, not technical.
&lt;/div&gt;

&lt;p&gt;Focus areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Benefits of cloud - elasticity, agility, pay-as-you-go, economies of scale&lt;/li&gt;
&lt;li&gt;AWS global infrastructure - Regions, Availability Zones, edge locations&lt;/li&gt;
&lt;li&gt;Cloud economics - CapEx vs OpEx, total cost of ownership&lt;/li&gt;
&lt;li&gt;The AWS Well-Architected Framework at a high level&lt;/li&gt;
&lt;li&gt;Migration and the cloud adoption basics&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;domain-2---security-and-compliance-30&#34;&gt;Domain 2 - Security and Compliance (30%)&lt;/h3&gt;

&lt;div class=&#34;callout callout-aws2&#34;&gt;
  The second-largest domain. The shared responsibility model is the spine of it - know exactly where AWS&amp;rsquo;s responsibility ends and yours begins. The rest is IAM basics and where to find compliance information.
&lt;/div&gt;

&lt;p&gt;Focus areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Shared responsibility model - what AWS secures vs what you secure&lt;/li&gt;
&lt;li&gt;IAM - users, groups, roles, policies, MFA, root account protection&lt;/li&gt;
&lt;li&gt;Security services - Shield, WAF, GuardDuty, Inspector, KMS at a high level&lt;/li&gt;
&lt;li&gt;Compliance - AWS Artifact, where audit reports come from&lt;/li&gt;
&lt;li&gt;Encryption in transit and at rest as concepts&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;domain-3---cloud-technology-and-services-34&#34;&gt;Domain 3 - Cloud Technology and Services (34%)&lt;/h3&gt;

&lt;div class=&#34;callout callout-aws2&#34;&gt;
  The largest domain and the widest. You need recognition-level knowledge of a long list of services - what each one is for, not how to configure it. Breadth beats depth here.
&lt;/div&gt;

&lt;p&gt;Focus areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Compute - EC2, Lambda, Elastic Beanstalk, ECS/EKS at a glance&lt;/li&gt;
&lt;li&gt;Storage - S3, EBS, EFS, Glacier and when each fits&lt;/li&gt;
&lt;li&gt;Databases - RDS, DynamoDB, Aurora, ElastiCache at a glance&lt;/li&gt;
&lt;li&gt;Networking - VPC, Route 53, CloudFront, the basics&lt;/li&gt;
&lt;li&gt;Management and monitoring - CloudWatch, CloudTrail, Organizations, Trusted Advisor&lt;/li&gt;
&lt;li&gt;Ways to access AWS - console, CLI, SDKs, infrastructure as code&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;domain-4---billing-pricing-and-support-12&#34;&gt;Domain 4 - Billing, Pricing, and Support (12%)&lt;/h3&gt;

&lt;div class=&#34;callout callout-aws2&#34;&gt;
  The smallest domain, but easy marks if you know the tools. Pricing models, the billing and cost tools, and the support plan tiers. Worth a focused hour - the distinctions are clean and frequently tested.
&lt;/div&gt;

&lt;p&gt;Focus areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pricing models - On-Demand, Reserved, Spot, Savings Plans&lt;/li&gt;
&lt;li&gt;Cost tools - Cost Explorer, Budgets, Cost and Usage Report, Billing Conductor&lt;/li&gt;
&lt;li&gt;Support plans - Basic, Developer, Business, Enterprise and what each includes&lt;/li&gt;
&lt;li&gt;AWS Organizations and consolidated billing&lt;/li&gt;
&lt;li&gt;Trusted Advisor cost checks&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;services-to-know-well&#34;&gt;Services to know well&lt;/h2&gt;
&lt;p&gt;For CLF-C02 you need recognition-level knowledge - what each service is for and when you would reach for it, not how to configure it:&lt;/p&gt;
&lt;table class=&#34;bordered&#34;&gt;
  &lt;thead&gt;
    &lt;tr&gt;&lt;th&gt;Service&lt;/th&gt;&lt;th&gt;Know this about it&lt;/th&gt;&lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;&lt;td&gt;EC2&lt;/td&gt;&lt;td&gt;Virtual servers; the pricing models attach here&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;S3&lt;/td&gt;&lt;td&gt;Object storage, storage classes, durability&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;RDS&lt;/td&gt;&lt;td&gt;Managed relational databases vs running your own&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Lambda&lt;/td&gt;&lt;td&gt;Serverless compute, pay per execution&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;VPC&lt;/td&gt;&lt;td&gt;Your private network boundary in AWS&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;IAM&lt;/td&gt;&lt;td&gt;Users, groups, roles, policies, MFA&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;CloudWatch&lt;/td&gt;&lt;td&gt;Monitoring, metrics, alarms, logs&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;CloudTrail&lt;/td&gt;&lt;td&gt;API activity auditing (who did what)&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Organizations&lt;/td&gt;&lt;td&gt;Multi-account management, consolidated billing&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Trusted Advisor&lt;/td&gt;&lt;td&gt;Best-practice checks across cost, security, performance&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Cost Explorer / Budgets&lt;/td&gt;&lt;td&gt;Viewing spend vs alerting on spend&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;Well-Architected Tool&lt;/td&gt;&lt;td&gt;Reviewing workloads against the six pillars&lt;/td&gt;&lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&#34;easy-things-to-mix-up&#34;&gt;Easy things to mix up&lt;/h2&gt;
&lt;p&gt;These are the distinctions the exam likes to probe. At this level, knowing the boundary between a pair is usually the whole question:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Shared responsibility model&lt;/strong&gt; - AWS secures the cloud (hardware, global infrastructure); you secure what you put in it (data, IAM, configuration). Managed services shift more onto AWS.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Regions vs Availability Zones vs edge locations&lt;/strong&gt; - a Region is a geographic area, an AZ is one or more data centres within it, an edge location serves CloudFront content closer to users.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security groups vs network ACLs&lt;/strong&gt; - security groups are stateful and act at the instance; NACLs are stateless and act at the subnet.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;On-Demand vs Reserved vs Spot&lt;/strong&gt; - On-Demand for flexibility, Reserved/Savings Plans for steady long-term workloads at a discount, Spot for interruptible workloads at the biggest discount.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost Explorer vs Budgets vs Cost and Usage Report&lt;/strong&gt; - Explorer visualises past spend, Budgets alerts on thresholds, the CUR is the detailed line-item export.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IAM users vs roles&lt;/strong&gt; - users are long-lived identities, roles are assumed temporarily and avoid long-lived credentials.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Support plans&lt;/strong&gt; - Basic is free, Developer adds business-hours email support, Business adds 24/7 and a fuller Trusted Advisor, Enterprise adds a TAM.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Trusted Advisor vs Well-Architected Tool&lt;/strong&gt; - Trusted Advisor runs automated best-practice checks; the Well-Architected Tool is a guided self-review against the framework.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;resources&#34;&gt;Resources&lt;/h2&gt;
&lt;p&gt;CLF-C02 is well served by free, AWS-authored material. You do not need to spend much here.&lt;/p&gt;
&lt;h3 id=&#34;essential&#34;&gt;Essential&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://d1.awsstatic.com/training-and-certification/docs-cloud-practitioner/AWS-Certified-Cloud-Practitioner_Exam-Guide.pdf&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Exam Guide (CLF-C02)&lt;/a&gt; - the syllabus. The domain breakdown and in-scope service list tell you exactly what is fair game.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://explore.skillbuilder.aws/learn/course/external/view/elearning/16485/exam-prep-enhanced-course-aws-certified-cloud-practitioner-clf-c02-english&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Exam Prep Enhanced Course (Skill Builder)&lt;/a&gt; - AWS&amp;rsquo;s own guided prep, free, and enough to carry most people through on its own.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://explore.skillbuilder.aws/learn/course/external/view/elearning/14637/aws-certified-cloud-practitioner-official-practice-exam-clf-c02-english&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Official Practice Exam (Skill Builder)&lt;/a&gt; - calibrate against AWS-authored questions before booking.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;useful&#34;&gt;Useful&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://explore.skillbuilder.aws/learn/course/external/view/elearning/16434/exam-prep-standard-course-aws-certified-cloud-practitioner-clf-c02-english&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Exam Prep Standard Course (Skill Builder)&lt;/a&gt; - a lighter path than the enhanced course if you only need a refresher.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://digitalcloud.training/category/aws-cheat-sheets/aws-cloud-practitioner/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;DigitalCloud cheat sheets&lt;/a&gt; - good for last-week revision once the concepts are in place.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;skip-if-you-are-tight-on-time&#34;&gt;Skip if you are tight on time&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://digitalcloud.training/aws-cloud-practitioner-resources/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;DigitalCloud resources page&lt;/a&gt; and &lt;a href=&#34;https://rishabkumar7.github.io/CloudNotes/cloud/AWS-CCP.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Rishab Kumar&amp;rsquo;s CloudNotes&lt;/a&gt; - handy as references, but they overlap with the official course and your practice exams. Skim, do not study cover to cover.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  &lt;ol&gt;
&lt;li&gt;This is a breadth exam. Recognising what a service is for matters far more than any configuration detail - do not over-study any single service.&lt;/li&gt;
&lt;li&gt;The free Skill Builder enhanced course plus one set of practice exams is enough for most people. There is no need to buy multiple courses.&lt;/li&gt;
&lt;li&gt;Learn the shared responsibility model and the support plan tiers cold - they are reliable marks and the distinctions are clean.&lt;/li&gt;
&lt;li&gt;The score is scaled, so aim to clear 700 comfortably on practice exams rather than chasing a specific number.&lt;/li&gt;
&lt;li&gt;If you want the developer-level depth that this exam only touches, the &lt;a href=&#34;https://bastabc.com/posts/aws-dva-c02/&#34;&gt;DVA-C02 study notes&lt;/a&gt; are the natural next step up.&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>Setting Up WSL2 on Windows</title>
      <link>https://bastabc.com/posts/wsl-setup-on-windows/</link>
      <pubDate>Fri, 27 Sep 2024 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/wsl-setup-on-windows/</guid>
      <description>&lt;p&gt;WSL (Windows Subsystem for Linux) lets you run a Linux environment directly on Windows - no VM, no dual boot. WSL2 runs a real Linux kernel in a lightweight managed VM, giving you full syscall compatibility and much better performance than WSL1.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Windows 10 version 2004+ or Windows 11&lt;/li&gt;
&lt;li&gt;Admin access to your machine&lt;/li&gt;
&lt;li&gt;PowerShell or Windows Terminal&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;install-wsl&#34;&gt;Install WSL&lt;/h2&gt;
&lt;p&gt;From an elevated PowerShell or Command Prompt:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wsl --install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This installs WSL2 and Ubuntu (the default distro) in one step. Restart when prompted.&lt;/p&gt;
&lt;p&gt;To install a different distro, list what&amp;rsquo;s available first:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wsl --list --online
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then install by name:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wsl --install -d Debian
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To check what&amp;rsquo;s installed and running:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wsl --list --verbose
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;first-run&#34;&gt;First run&lt;/h2&gt;
&lt;p&gt;Launch the distro from the Start menu or by typing its name in a terminal. The first launch prompts you to create a UNIX username and password - this is local to the distro and doesn&amp;rsquo;t need to match your Windows credentials.&lt;/p&gt;
&lt;p&gt;Set WSL2 as the default version if it isn&amp;rsquo;t already:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wsl --set-default-version &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;update-the-distro&#34;&gt;Update the distro&lt;/h2&gt;
&lt;p&gt;After first launch, update the package list and upgrade installed packages:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt update &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo apt upgrade -y
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Install any essentials you need:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt install -y curl git build-essential unzip
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;configuration&#34;&gt;Configuration&lt;/h2&gt;
&lt;p&gt;WSL has two config files that control different things.&lt;/p&gt;
&lt;h3 id=&#34;wslconfig---windows-side-limits&#34;&gt;&lt;code&gt;.wslconfig&lt;/code&gt; - Windows-side limits&lt;/h3&gt;
&lt;p&gt;Controls resources WSL2 allocates from Windows. Create or edit &lt;code&gt;C:\Users\&amp;lt;your-username&amp;gt;\.wslconfig&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[wsl2]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;memory&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;8GB&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;processors&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;swap&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;2GB&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By default WSL2 can use up to 50% of system RAM and all CPU cores. On a machine shared with other workloads, it&amp;rsquo;s worth capping this.&lt;/p&gt;
&lt;p&gt;After editing, restart WSL for changes to take effect:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wsl --shutdown
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;wslconf---distro-side-settings&#34;&gt;&lt;code&gt;wsl.conf&lt;/code&gt; - distro-side settings&lt;/h3&gt;
&lt;p&gt;Controls behaviour inside the distro. Create or edit &lt;code&gt;/etc/wsl.conf&lt;/code&gt; inside WSL:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo nano /etc/wsl.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Useful options:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[boot]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;systemd&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[automount]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;enabled&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;options&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;metadata&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[network]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;hostname&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;my-dev-box&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;generateResolvConf&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[user]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;default&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;myusername&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;systemd=true&lt;/code&gt; - enables systemd, needed for Docker Engine, services, etc.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;automount options=&amp;quot;metadata&amp;quot;&lt;/code&gt; - lets you set Linux file permissions on Windows-mounted drives&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hostname&lt;/code&gt; - sets the hostname inside WSL (useful to distinguish from the Windows host)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;user.default&lt;/code&gt; - the user WSL drops you into on launch&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Restart WSL after saving:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wsl --shutdown
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;useful-wsl-commands&#34;&gt;Useful WSL commands&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# List installed distros and their state&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wsl --list --verbose
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Stop all running distros&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wsl --shutdown
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Stop a specific distro&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wsl --terminate Ubuntu
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Set a different distro as default&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wsl --set-default Debian
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Run a command directly without entering the shell&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wsl ls -la /home/myusername
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Export a distro to a backup file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wsl --export Ubuntu ubuntu-backup.tar
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Import from backup&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wsl --import Ubuntu C:\WSL\Ubuntu ubuntu-backup.tar --version &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;corporate-proxy&#34;&gt;Corporate proxy&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re on a corporate network, WSL2&amp;rsquo;s outbound connections go through a virtual network adapter that often doesn&amp;rsquo;t inherit Windows proxy settings. Here&amp;rsquo;s what to configure.&lt;/p&gt;
&lt;h3 id=&#34;shell-environment&#34;&gt;Shell environment&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;~/.bashrc&lt;/code&gt; (or &lt;code&gt;~/.zshrc&lt;/code&gt; if you use zsh):&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;nano ~/.bashrc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;Add the proxy variables:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export HTTP_PROXY&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://proxy.your-company.com:8080&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export HTTPS_PROXY&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;http://proxy.your-company.com:8080&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export NO_PROXY&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;localhost,127.0.0.1,169.254.0.0/16,10.0.0.0/8&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export http_proxy&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$HTTP_PROXY&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export https_proxy&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$HTTPS_PROXY&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;export no_proxy&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$NO_PROXY&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;Reload the shell:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;source ~/.bashrc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;apt&#34;&gt;apt&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Create &lt;code&gt;/etc/apt/apt.conf.d/proxy.conf&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo nano /etc/apt/apt.conf.d/proxy.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;Add the proxy config:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Acquire::http::Proxy &amp;#34;http://proxy.your-company.com:8080&amp;#34;;
Acquire::https::Proxy &amp;#34;http://proxy.your-company.com:8080&amp;#34;;
&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;Test:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;curl-and-wget&#34;&gt;curl and wget&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;curl&lt;/code&gt; and &lt;code&gt;wget&lt;/code&gt; pick up &lt;code&gt;HTTP_PROXY&lt;/code&gt;/&lt;code&gt;HTTPS_PROXY&lt;/code&gt; from the environment automatically once the shell variables are set above. No extra config needed.&lt;/p&gt;
&lt;h3 id=&#34;git&#34;&gt;git&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git config --global http.proxy http://proxy.your-company.com:8080
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git config --global https.proxy http://proxy.your-company.com:8080
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To unset if you move off the corporate network:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git config --global --unset http.proxy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git config --global --unset https.proxy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;!-- ### Docker Engine (not Docker Desktop)

If you&#39;re running Docker Engine inside WSL2 directly, it needs proxy config at the service level.

1. Create the systemd override directory:

```bash
sudo mkdir -p /etc/systemd/system/docker.service.d
```

2. Create the proxy config file:

```bash
sudo nano /etc/systemd/system/docker.service.d/proxy.conf
```

3. Add the proxy environment variables:

```ini
[Service]
Environment=&#34;HTTP_PROXY=http://proxy.your-company.com:8080&#34;
Environment=&#34;HTTPS_PROXY=http://proxy.your-company.com:8080&#34;
Environment=&#34;NO_PROXY=localhost,127.0.0.1&#34;
```

4. Reload and restart Docker:

```bash
sudo systemctl daemon-reload
sudo systemctl restart docker
```

The shell environment variables don&#39;t apply to the Docker daemon - only the systemd service config affects image pulls.

5. Configure the Docker client for build-time proxy args. Add to `~/.docker/config.json`:

```json
{
  &#34;proxies&#34;: {
    &#34;default&#34;: {
      &#34;httpProxy&#34;: &#34;http://proxy.your-company.com:8080&#34;,
      &#34;httpsProxy&#34;: &#34;http://proxy.your-company.com:8080&#34;,
      &#34;noProxy&#34;: &#34;localhost,127.0.0.1&#34;
    }
  }
}
```

### Corporate CA certificate

If the proxy does SSL inspection, `curl`, `apt`, and Docker pulls will fail with certificate errors. Install the corporate root CA into the distro&#39;s trust store.

1. Copy the `.crt` file into the distro&#39;s CA directory:

```bash
sudo cp my-corp-root-ca.crt /usr/local/share/ca-certificates/
```

2. Update the trust store:

```bash
sudo update-ca-certificates
```

This covers `curl`, `wget`, `apt`, and most tools that use the system trust store. For Python, Node, and Java you may need to configure the CA separately as they use their own bundled stores. --&gt;
&lt;hr&gt;
&lt;h2 id=&#34;keeping-wsl-updated&#34;&gt;Keeping WSL updated&lt;/h2&gt;
&lt;p&gt;The WSL platform itself (separate from the distro) updates via Windows Update or manually:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wsl --update
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The distro packages update via &lt;code&gt;apt&lt;/code&gt; inside WSL as usual. There&amp;rsquo;s no automatic update mechanism for the distro - run &lt;code&gt;sudo apt update &amp;amp;&amp;amp; sudo apt upgrade&lt;/code&gt; periodically.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;notes&#34;&gt;Notes&lt;/h2&gt;

&lt;div class=&#34;callout callout-aws4&#34;&gt;
  &lt;ol&gt;
&lt;li&gt;WSL2 uses a real Linux kernel running in a lightweight VM - this is why it needs a virtual network adapter, which is the root cause of most proxy and connectivity issues on corporate networks.&lt;/li&gt;
&lt;li&gt;Windows Firewall rules apply to WSL2 traffic. If a connection is blocked for no obvious reason, check Windows Defender Firewall inbound/outbound rules.&lt;/li&gt;
&lt;li&gt;VPNs are a common cause of WSL2 connectivity failures. Many corporate VPN clients replace or restrict the virtual network adapter WSL2 uses. Disconnecting the VPN is usually the fastest way to confirm this is the cause.&lt;/li&gt;
&lt;li&gt;File I/O is significantly faster when working inside the Linux filesystem (&lt;code&gt;/home/...&lt;/code&gt;) vs on the Windows mount (&lt;code&gt;/mnt/c/...&lt;/code&gt;). Keep project files in the Linux filesystem if performance matters.&lt;/li&gt;
&lt;li&gt;Once WSL2 is set up, &lt;a href=&#34;https://bastabc.com/posts/aws-kiro-cli/&#34;&gt;AWS Kiro CLI&lt;/a&gt; installs directly via its Linux/WSL2 package - no separate Windows setup needed.&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>Connect to AWS SSO and SSH into EC2 Instance</title>
      <link>https://bastabc.com/posts/connect-to-aws-sso-ssh-into-ec2-instance/</link>
      <pubDate>Mon, 15 Jul 2024 00:00:00 +0000</pubDate>
      
      <guid>https://bastabc.com/posts/connect-to-aws-sso-ssh-into-ec2-instance/</guid>
      <description>&lt;p&gt;Two paths depending on your setup - follow one:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Without SSO&lt;/strong&gt;: personal AWS account, direct IAM credentials&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;With SSO&lt;/strong&gt;: enterprise/team setup with an SSO start URL (e.g. &lt;code&gt;https://company.awsapps.com/start&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Without SSO:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AWS CLI v2&lt;/li&gt;
&lt;li&gt;AWS IAM user credentials (Access Key ID and Secret Access Key)&lt;/li&gt;
&lt;li&gt;Terminal&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With SSO:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AWS CLI v2&lt;/li&gt;
&lt;li&gt;AWS SSO start URL and access&lt;/li&gt;
&lt;li&gt;Terminal&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;files-you-will-need&#34;&gt;Files you will need&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;~/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── .aws/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── config
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── credentials
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── .ssh/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├── ec2-key.pem
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    └── config
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;path-a-without-sso&#34;&gt;Path A: Without SSO&lt;/h2&gt;
&lt;p&gt;Create &lt;code&gt;.aws&lt;/code&gt; folder if it doesn&amp;rsquo;t exist. Run from your home directory (&lt;code&gt;~&lt;/code&gt;).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p ~/.aws
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Create &lt;code&gt;~/.aws/credentials&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[default]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;aws_access_key_id&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;YOUR_ACCESS_KEY_ID&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;aws_secret_access_key&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;YOUR_SECRET_ACCESS_KEY&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Create &lt;code&gt;~/.aws/config&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[default]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;region&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;ap-southeast-2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;output&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Test your config.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aws sts get-caller-identity
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;path-b-with-sso&#34;&gt;Path B: With SSO&lt;/h2&gt;
&lt;p&gt;Add a profile block to &lt;code&gt;~/.aws/config&lt;/code&gt; for each environment (e.g. &lt;code&gt;aws-dev&lt;/code&gt;, &lt;code&gt;aws-prod&lt;/code&gt;):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ini&#34; data-lang=&#34;ini&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;[profile aws-prod]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;sso_session&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;aws-prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;sso_account_id&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;123456789123&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;sso_role_name&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;aws-prod-SystemAdmin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;region&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;ap-southeast-4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;output&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;credential_process&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;aws configure export-credentials --profile aws-prod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sample &lt;code&gt;.aws&lt;/code&gt; folder structure:
&lt;img loading=&#34;lazy&#34; src=&#34;sso.png&#34; alt=&#34;aws-folder&#34;  /&gt;
&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;setup-ssh-config&#34;&gt;Setup SSH config&lt;/h2&gt;
&lt;p&gt;Download the &lt;code&gt;.pem&lt;/code&gt; keypair when launching the EC2 instance and move it here.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p ~/.ssh
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mv ~/Downloads/ec2-prod.pem ~/.ssh/ec2-prod.pem
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;chmod &lt;span style=&#34;color:#ae81ff&#34;&gt;400&lt;/span&gt; ~/.ssh/ec2-prod.pem
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Basic SSH config (no SSO)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Add to &lt;code&gt;~/.ssh/config&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Host ec2-prod
    HostName 1.2.3.4             # Replace with your EC2&amp;#39;s public IP
    User ec2-user                # Use &amp;#39;ubuntu&amp;#39; for Ubuntu instances
    IdentityFile ~/.ssh/ec2-prod.pem
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;SSO ProxyCommand config&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Use this if connecting via AWS SSM Session Manager with an SSO profile. Add to &lt;code&gt;~/.ssh/config&lt;/code&gt; (or a separate file under &lt;code&gt;~/.ssh/config.d/&lt;/code&gt;):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Host ec2-prod
  User ec2-user
  IdentityFile ~/.ssh/ec2-prod.pem
  ProxyCommand aws ssm start-session --target i-01xyz7659824a123q --profile aws-prod --document-name AWS-StartSSHSession --parameters portNumber=22
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sample &lt;code&gt;.ssh&lt;/code&gt; folder structure:
&lt;img loading=&#34;lazy&#34; src=&#34;ssh.png&#34; alt=&#34;ssh-folder&#34;  /&gt;
&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;connect&#34;&gt;Connect&lt;/h2&gt;
&lt;p&gt;If using SSO, log in first:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;aws sso login --profile aws-prod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then SSH in:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ssh ec2-prod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img loading=&#34;lazy&#34; src=&#34;ec2.png&#34; alt=&#34;ec2-connect&#34;  /&gt;
&lt;/p&gt;
&lt;!--
## Original version

This how-to article explains steps required to configure AWS account with and without need of AWS SSO and then SSH into an EC2 instance. The steps are generic in nature and can be customized to apply to any environment or any project.

---

## Prerequisites
- AWS CLI v2
- AWS SSO access
- Terminal
- AWS IAM user credentials (Access Key ID and Secret Access Key)

---

## Directory structure
```text
~/
├── .aws/
│   ├── config
│   └── credentials
└── .ssh/
    ├── ec2-key.pem
    └── config
```

---

## Setup .aws config without SSO
Create .aws folder if it doesn&#39;t exist.
```
mkdir -p ~/.aws
```

Create credentials file.
```
# ~/.aws/credentials
[default]
aws_access_key_id = YOUR_ACCESS_KEY_ID
aws_secret_access_key = YOUR_SECRET_ACCESS_KEY
```

Create config file.
```
# ~/.aws/config
[default]
region = ap-southeast-2
output = json
```

Test your config.
```
aws sts get-caller-identity
```

---

## Setup .aws config with SSO
This is more applicable for enterprise setup rather than individual setup. Prerequisite is have a valid and working AWS SSO Start URL (e.g. https://company.awsapps.com/start) which allows to complete authentication in the browser.
Add AWS config entry (~/.aws/config) in your OS user root location. Add section per required spec e.g. new profiles each for aws-dev, aws-prod.

Sample .aws folder structure is below. Ignore &#39;amplify&#39; and &#39;cli&#39; folders to start with.
![aws-folder](sso.png)

```ssh
[profile aws-prod]
sso_session = aws-prod
sso_account_id = 123456789123
sso_role_name = aws-prod-SystemAdmin
region = ap-southeast-4
output = json
credential_process = aws configure export-credentials --profile aws-prod
```

Connect by SSO by running following command.

```ssh
aws sso login --profile aws-prod
```

---

## Setup .ssh config
The .pem files are keypairs created while launching ec2 instance ec2-prod.

Create .ssh folder if it doesn&#39;t exist.
```
mkdir -p ~/.ssh
```

Move downloaded .pem file to .ssh folder.
```
mv ~/Downloads/ec2-prod.pem ~/.ssh/ec2-prod.pem
```

Provide necessary permissions to key pair.
```
chmod 400 ~/.ssh/ec2-prod.pem
```

Add an entry to SSH config.
```
nano ~/.ssh/config
```

Add following to the conf file.

```
Host ec2-prod
    HostName 1.2.3.4             # Replace with your EC2&#39;s public IP
    User ec2-user                # Use &#39;ubuntu&#39; for Ubuntu instances
    IdentityFile ~/.ssh/ec2-prod.pem
```

Sample .ssh folder structure is below.
![ssh-folder](ssh.png)

Alternatively, add SSH config entry (~/.ssh/.sshd_config.d/profile.conf) in your OS user root location. Add conf files per required profile e.g. new conf files each for aws-dev, aws-prod.

Add following to aws-prod.conf file.

```
Host ec2-prod
  User ec2-user
  IdentityFile ~/.ssh/ec2-prod.pem
  ProxyCommand aws ssm start-session --target i-01xyz7659824a123q --profile aws-prod --document-name AWS-StartSSHSession --parameters portNumber=22
```

This creates separate config files for each profile. It&#39;s more applicable if you&#39;re using AWS profile with SSO.

---

## Connect to ec2 instance
SSH into ec2 instance.

```ssh
ssh ec2-prod
```

Sample output indicating you&#39;re connected to the ec2 instance.
![ec2-connect](ec2.png)
--&gt;
</description>
    </item>
    
  </channel>
</rss>