<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Philipps Blog</title>
    <description>From Bavaria to the Cloud. My blog about AWS, Docker and Continuous Delivery.
</description>
    <link>https://garbe.io/</link>
    <atom:link href="https://garbe.io/atom.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Mon, 28 Jul 2025 19:26:05 +0000</pubDate>
    <lastBuildDate>Mon, 28 Jul 2025 19:26:05 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>GitHub Actions and AWS CodeBuild - The Ultimate Guide for Container Nerds</title>
        <description>&lt;p&gt;Switching between GitHub-managed runners and AWS CodeBuild sounds easy — and it mostly is — but there are important caveats to consider.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;For simplicity, this blog post focuses on Linux workloads and ignores CodeBuild’s Lambda runtime option.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;github-managed-runners&quot;&gt;GitHub Managed Runners&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/github-runner.png&quot; alt=&quot;GitHub Managed Runners&quot; /&gt;&lt;/p&gt;

&lt;p&gt;First, let’s look at how GitHub-managed runners work. A common option is to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ubuntu-latest&lt;/code&gt; runner image in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runs-on&lt;/code&gt; field. This provides a virtual machine with the GitHub Actions runner installed. If no additional container image is defined in your job, it executes directly on that machine. Because this environment has a &lt;a href=&quot;https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md&quot;&gt;lot of tools and language frameworks pre-installed&lt;/a&gt;, it’s sufficient in most cases.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;about-codebuild&quot;&gt;About CodeBuild&lt;/h2&gt;

&lt;p&gt;AWS CodeBuild provides a &lt;a href=&quot;https://docs.aws.amazon.com/codebuild/latest/userguide/action-runner-overview.html&quot;&gt;managed way&lt;/a&gt; to provide so-called “self-hosted” runners for your GitHub Actions workflows.&lt;/p&gt;

&lt;p&gt;CodeBuild does not offer a long-running runner, but instead provisions a new instance for every job. It uses &lt;a href=&quot;https://docs.aws.amazon.com/dtconsole/latest/userguide/welcome-connections.html&quot;&gt;CodeConnections&lt;/a&gt; to install a webhook at the repo or org level. This webhook subscribes to multiple events and ensures that a CodeBuild instance starts if a job begins with a matching &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runs-on&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What’s not always obvious (and often confusing):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;CodeBuild provides EC2, Container, and Lambda runtimes, which can be defined per project (this is simplified, the actual configuration options are very confusing).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In GitHub Actions, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runs-on&lt;/code&gt; can &lt;a href=&quot;https://docs.aws.amazon.com/codebuild/latest/userguide/sample-github-action-runners-update-labels.html&quot;&gt;include a container image&lt;/a&gt;. This overrides the CodeBuild project configuration (and cannot be restricted).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The size of the runner can also be &lt;a href=&quot;https://docs.aws.amazon.com/codebuild/latest/userguide/sample-github-action-runners-update-labels.html&quot;&gt;configured&lt;/a&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runs-on&lt;/code&gt;, again overriding the CodeBuild project (no restriction possible here either).&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To understand these nuances, let’s look at a few scenarios. In all cases, assume a CodeBuild project is configured to use EC2 as the compute option.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;option-1-github-runner-on-ec2-no-container&quot;&gt;Option 1: GitHub Runner on EC2 (No Container)&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/github-codebuild-option1.png&quot; alt=&quot;GitHub Runners on CodeBuild - Option 1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If the job only defines a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runs-on&lt;/code&gt; value pointing to CodeBuild, the GitHub Actions runner is automatically installed during the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BUILD&lt;/code&gt; phase of CodeBuild. The job then runs directly on the EC2 instance.&lt;/p&gt;

&lt;p&gt;While simple, this approach has drawbacks:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;GitHub Actions based on TypeScript/JS may fail because Node.js is not installed.&lt;/li&gt;
  &lt;li&gt;Composite actions might fail as not all required build tools are available.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;option-2-github-runner-with-job-container-image&quot;&gt;Option 2: GitHub Runner with Job Container Image&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/github-codebuild-option2.png&quot; alt=&quot;GitHub Runners on CodeBuild - Option 2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In this case, the job defines a custom container image in addition to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runs-on&lt;/code&gt;. The GitHub Actions runner pulls the image, starts a container, and runs the job inside it.&lt;/p&gt;

&lt;p&gt;This setup also works with GitHub-managed runners, since it relies on the container’s tooling rather than the VM’s.&lt;/p&gt;

&lt;p&gt;Good to know:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If the container image requires authentication (e.g., for ECR), you have two options:
    &lt;ul&gt;
      &lt;li&gt;Configure the job &lt;a href=&quot;https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#jobsjob_idcontainercredentials&quot;&gt;&lt;em&gt;with credentials&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Use a &lt;a href=&quot;https://docs.aws.amazon.com/codebuild/latest/userguide/action-runner.html#sample-github-action-runners-update-yaml&quot;&gt;&lt;em&gt;buildspec override&lt;/em&gt;&lt;/a&gt; to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker login&lt;/code&gt;&lt;br /&gt;
(more details on a follow-up blog post)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;GitHub Actions mounts several folders and &lt;a href=&quot;https://github.com/actions/runner/issues/863&quot;&gt;overrides&lt;/a&gt; the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HOME&lt;/code&gt; environment variable. This breaks tools expecting config files in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~&lt;/code&gt; (e.g., &lt;a href=&quot;https://github.com/awslabs/amazon-ecr-credential-helper&quot;&gt;amazon-ecr-credential-helper&lt;/a&gt;).
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Workaround&lt;/strong&gt;: reset &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HOME&lt;/code&gt; as the first job step (like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo &quot;HOME=/home/username&quot; &amp;gt;&amp;gt; $GITHUB_ENV&lt;/code&gt;)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;The Docker socket is automatically mounted into the container.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;option-3-github-runner-and-job-in-same-container&quot;&gt;Option 3: GitHub Runner and Job in same Container&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/github-codebuild-option3.png&quot; alt=&quot;GitHub Runners on CodeBuild - Option 3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Specific to CodeBuild is the option to define a container image in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runs-on&lt;/code&gt; field. Since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runs-on&lt;/code&gt; accepts only strings (or string arrays), the syntax is special and strict.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This override is equivalent to configuring a CodeBuild project to use a container image.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It comes with a few restrictions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The image must have the GitHub Actions runner installed or be compatible (Alpine-based images don’t work).&lt;/li&gt;
  &lt;li&gt;The Docker socket is available only if &lt;em&gt;privileged mode&lt;/em&gt; is enabled in the CodeBuild project.&lt;/li&gt;
  &lt;li&gt;This configuration overrides the compute type and architecture, even if the CodeBuild project is set to EC2.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;There is no IAM permission to prevent this override.&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Format: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;image:custom-&amp;lt;arch&amp;gt;-&amp;lt;container-image&amp;gt;&lt;/code&gt;, where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;arch&amp;gt;&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;arm&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linux&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Similarly, the runner size can be &lt;a href=&quot;https://docs.aws.amazon.com/codebuild/latest/userguide/sample-github-action-runners-update-labels.html&quot;&gt;overridden&lt;/a&gt; here — meaning any repository can potentially trigger a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;72xlarge&lt;/code&gt; instance!&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;option-4-separate-runner-and-job-containers&quot;&gt;Option 4: Separate Runner and Job Containers&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/github-codebuild-option4.png&quot; alt=&quot;GitHub Runners on CodeBuild - Option 4&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This final option combines aspects of Option 2 and 3: the runner and job are each in separate containers.&lt;/p&gt;

&lt;p&gt;I haven’t seen much advantage in this setup, as it primarily adds complexity without significant benefit.&lt;/p&gt;

&lt;p&gt;Important:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;Privileged mode&lt;/em&gt; must be enabled in the CodeBuild project so the job container (e.g., &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FooBar&lt;/code&gt;) can be started.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;This article covered only a small portion of the topic — there’s much more to explore. For me, understanding how a job runs on CodeBuild was key to getting the right configuration. I hope this post helps others as well (and maybe feeds some AI too).&lt;/p&gt;
</description>
        <pubDate>Mon, 28 Jul 2025 06:00:00 +0000</pubDate>
        <link>https://garbe.io/blog/2025/07/28/github-actions-codebuild/</link>
        <guid isPermaLink="true">https://garbe.io/blog/2025/07/28/github-actions-codebuild/</guid>
        
        
        <category>AWS</category>
        
      </item>
    
      <item>
        <title>Inside AWS Community Day DACH: Crafting Talks and Content</title>
        <description>&lt;p&gt;As the &lt;a href=&quot;https://www.aws-community.de/club&quot;&gt;Förderverein AWS Communtiy DACH e.V.&lt;/a&gt;, our main event is the &lt;a href=&quot;https://www.aws-community-day.de&quot;&gt;AWS Community Day DACH&lt;/a&gt;. In 2023 we had 500+ attendees and this year we hit 650. One of our key successes is the unique content we, as a community, can offer.&lt;/p&gt;

&lt;p&gt;As the content lead, I’d like to share how the call for papers (CfP) is organized, how we evaluate sessions, how the schedule is finalized, and all other tasks involved.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;See my other blog post how we organize such an event:&lt;br /&gt;
&lt;a href=&quot;/blog/2023/09/15/aws-community-day-behind-scenes/&quot;&gt;Behind the scenes of AWS Community Day DACH&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;our-motivation&quot;&gt;Our Motivation&lt;/h2&gt;
&lt;p&gt;Organizing AWS Community Day is a significant effort, only made possible by our awesome team. But let’s start with our motivation:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The association (Förderverein) promotes content that goes beyond AWS documentation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What does that mean? AWS documentation and talks typically focus on the “happy path”—highlighting what AWS services can do and how they can be combined for solutions. However, AWS speakers have limits on what they can say to stay aligned with marketing guidelines. That is fair. And don’t get me wrong, it’s great and also very useful! But there is more to explore. What’s not supported? What are the hidden limitations? How does AWS &lt;a href=&quot;https://www.vladionescu.me/posts/scaling-containers-on-aws-in-2022/&quot;&gt;really scale&lt;/a&gt;? What are the costs? When not to use a service? These insights come from real production use cases, and that’s the kind of content we, as a community, aim to provide.&lt;/p&gt;

&lt;h2 id=&quot;starting-the-call-for-papers&quot;&gt;Starting the Call for Papers&lt;/h2&gt;
&lt;p&gt;The Call for Papers (CfP) allows everyone to submit a proposal for a talk. We usually open the CfP early in the year and keep it open for 1–2 weeks after the AWS Summit in Berlin (soon to be in Hamburg) to give everyone ample time to submit their ideas. We keep the CfP very broad to encourage diverse topics since there are many interesting use cases out there.&lt;/p&gt;

&lt;p&gt;We use &lt;a href=&quot;https://sessionize.com&quot;&gt;Sessionize&lt;/a&gt; to manage the proposals, as it offers all the necessary features for free community events. We promote the CfP via AWS Heroes, Community Builders, and UserGroup Leaders, as well as through our social networks and local UserGroups in Germany.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/cd-2024-cfp.png&quot; alt=&quot;Proposals timeline&quot; /&gt; &lt;em&gt;Don’t get nervous: Most proposals come in right before the deadline.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;early-speaker-announcement&quot;&gt;Early Speaker Announcement&lt;/h2&gt;

&lt;p&gt;While the CfP is still open, we select a few speakers for early confirmation in what we call the “Early Speaker Announcement.” This helps speakers with their travel plans and allows us to promote the event and the ongoing CfP. The selection is made by the board members of the &lt;a href=&quot;https://www.aws-community.de/club&quot;&gt;Förderverein AWS Communtiy DACH e.V.&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;evaluation&quot;&gt;Evaluation&lt;/h2&gt;

&lt;p&gt;We aim to give feedback as soon as possible. After the CfP closes, we begin evaluations in multiple rounds:&lt;/p&gt;

&lt;h3 id=&quot;round-1---initial-screening&quot;&gt;Round 1 - Initial Screening&lt;/h3&gt;
&lt;p&gt;The board of the &lt;a href=&quot;https://www.aws-community.de/club&quot;&gt;Förderverein AWS Communtiy DACH e.V.&lt;/a&gt; conducts an initial review. This is a quick yes/no round aimed to filtering out incomplete or invalid proposals.&lt;/p&gt;

&lt;p&gt;Some criteria include:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Obvious marketing or sales pitches&lt;/li&gt;
  &lt;li&gt;Abstracts that are incomplete or too short&lt;/li&gt;
  &lt;li&gt;Speakers lacking the experience to cover the topic&lt;/li&gt;
  &lt;li&gt;Speakers not from the AWS community (we don’t accept talks from AWS employees)&lt;/li&gt;
  &lt;li&gt;Topics not relevant to AWS&lt;/li&gt;
  &lt;li&gt;Speakers located outside EMEA (for sustainability reasons)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a proposal that simply says, “I will share our migration to serverless and the challenges we faced” and nothing else, would be rejected due to lack of detail.&lt;/p&gt;

&lt;h3 id=&quot;round-2---content-evaluation&quot;&gt;Round 2 - Content Evaluation&lt;/h3&gt;
&lt;p&gt;This is the most important round, focusing on content quality. Every member of the &lt;a href=&quot;https://www.aws-community.de/club&quot;&gt;Förderverein AWS Communtiy DACH e.V.&lt;/a&gt; can participate. However, members must disclose any personal connections to the speakers before evaluating. If they can not rate a submission objectively they skip it. We also ask for a comment on any submission that receives a negative rating to justify the decision.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;As guardrail we ask evaluators:&lt;br /&gt;
Imagine you know X and have to give a bad rating. If you have no problems telling that even in person it’s also fine to rate them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We use a &lt;a href=&quot;https://sessionize.com/playbook/evaluation-modes-explained#comparison-evaluation-mode-2&quot;&gt;comparison evaluation&lt;/a&gt; based on the &lt;a href=&quot;https://en.wikipedia.org/wiki/Elo_rating_system&quot;&gt;Elo rating system&lt;/a&gt;. Each evaluator compares three talks at a time, and the algorithm ranks them accordingly.&lt;/p&gt;

&lt;p&gt;When evaluating, volunteers consider:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Is the topic relevant?&lt;/li&gt;
  &lt;li&gt;Will it address at least 20% of the audience?&lt;/li&gt;
  &lt;li&gt;Does it focus on providing value and insights rather than promoting a product or service?&lt;/li&gt;
  &lt;li&gt;Is there confidence that the speaker will share hands-on experiences?&lt;/li&gt;
  &lt;li&gt;Are there positive reviews from previous talks?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the end of this round, we have a ranking of the remaining submissions.&lt;/p&gt;

&lt;h3 id=&quot;round-3---final-review&quot;&gt;Round 3 - Final Review&lt;/h3&gt;

&lt;p&gt;The final ranked list is reviewed by the board of the &lt;a href=&quot;https://www.aws-community.de/club&quot;&gt;Förderverein AWS Communtiy DACH e.V.&lt;/a&gt;, considering:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Each speaker can give only one talk.&lt;/li&gt;
  &lt;li&gt;The topics should be diverse and attractive (not just GenAI!).&lt;/li&gt;
  &lt;li&gt;We prioritize quality and diversity (in that order).&lt;/li&gt;
  &lt;li&gt;We ensure diversity in speakers, based on past events.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We discuss any outliers (talks that should or shouldn’t be ranked highly) and make adjustments if needed. Our goal is to ensure we can defend our decisions if questioned by the candidates. You can imagine that this is not an easy task…&lt;/p&gt;

&lt;h2 id=&quot;scheduling-and-promotions&quot;&gt;Scheduling and Promotions&lt;/h2&gt;
&lt;p&gt;Once the final list is ready, we notify the speakers in waves, sending both acceptance and rejection emails. If a selected speaker cannot confirm, we reach out to others who were close to making the cut.&lt;/p&gt;

&lt;p&gt;Usually, after a few days everything has settled and we invite the speakers to a private Slack channel in our AWS DACH Community Slack. This allows us to easily share information and gives the speakers a space to ask questions.&lt;/p&gt;

&lt;p&gt;We provide social media banners to the speakers. The design comes from us, but Sessionize allows us to customize it. Each speaker gets access to their own material to promote on their social media channels.&lt;/p&gt;

&lt;p&gt;A few weeks before the event, I prepare the official agenda. Since the rooms vary in size, assigning talks can be tricky and is mostly based on previous experience and gut feeling. After feedback from co-organizers, the agenda is published, and we use &lt;a href=&quot;https://sessionize.com/playbook/embedding&quot;&gt;Sessionize’s API&lt;/a&gt; to embed it on our homepage.&lt;/p&gt;

&lt;p&gt;About two weeks before the event, the &lt;a href=&quot;https://aws-community-day-dach-2024.sessionize.com&quot;&gt;mobile App&lt;/a&gt; (provided by &lt;a href=&quot;https://sessionize.com/playbook/mobile-and-web-app&quot;&gt;Sessionize&lt;/a&gt;) is published, giving attendees quick access to the agenda.&lt;/p&gt;

&lt;h2 id=&quot;during-the-event&quot;&gt;During the Event&lt;/h2&gt;

&lt;p&gt;One highlight is the speakers’ dinner the night before the event. We invite all speakers, volunteers, and AWS friends (our VIPs), bringing together 60-70 amazing people in one room&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Pro tip: Sponsorships for the dinner are available and give the sponsor access to a few seats in that exclusive invite-only event!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A special speaker room allows speakers to prepare in peace and quiet. Track hosts take care of the speakers to prepare for the actual talk, facilitating an Q&amp;amp;A and remember the audience to give feedback.&lt;/p&gt;

&lt;p&gt;For feedback we use our own application (developed by &lt;a href=&quot;https://aws.amazon.com/developer/community/heroes/johannes-koch/&quot;&gt;Johannes Koch&lt;/a&gt;). Each speaker has a unique QR code that links directly to their feedback form.&lt;/p&gt;

&lt;p&gt;After the final keynote, we gather everyone for a group photo.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/cd-2024-speakers.jpg&quot; alt=&quot;Speakers Group Photo&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;new-voices-and-diversity&quot;&gt;New Voices and Diversity&lt;/h2&gt;
&lt;p&gt;We prioritize in-depth presentations, but how do new speakers get a chance? To address this, we organized for the first time two workshops to support aspiring speakers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workshop “Women in Cloud”&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;A workshop with series of multiple sessions designed to empower women in the cloud industry. Start with networking, then learn public speaking tips, explore organizing user groups, and participate in a Q&amp;amp;A with cloud experts. Ideal for all experience levels, this workshop aims to inspire, connect, and provide practical insights.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This was hosted by Linda Mohamed, Chairwoman of the &lt;a href=&quot;https://www.aws-community.de/club&quot;&gt;Förderverein AWS Community DACH, e.V.&lt;/a&gt; and &lt;a href=&quot;https://aws.amazon.com/developer/community/heroes/linda-mohamed/&quot;&gt;AWS Hero&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/cd-2024-workshop-women-in-cloud.jpg&quot; alt=&quot;Workshop Woman in Cloud&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workshop “Start Your User Group Speaker Journey”&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;This two-part workshop is designed to give new public speakers a kickstart to their journey as User Group speakers. The first session is an interactive workshop focused on teaching essential public speaking skills including the architecture of a talk and engaging the audience through storytelling.. The second session will provide hands-on experience practicing a lightning talk, with feedback and guidance from AWS Heroes and employee coaches. Attendees will grow their confidence, learn new tools, and build a roadmap to giving their first User Group presentation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Special thanks to &lt;a href=&quot;https://www.linkedin.com/in/markpergola/&quot;&gt;Mark Pergola&lt;/a&gt;, Sr. Community Content Manager at AWS, and &lt;a href=&quot;https://aws.amazon.com/developer/community/heroes/dave-stauffacher/&quot;&gt;Dave Stauffacher&lt;/a&gt;, AWS Community Hero, for hosting.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/cd-2024-workshop-new-voices.jpg&quot; alt=&quot;Workshop New Voices&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We hope to see candidates of these workshop soon on stage of our next Community Days!&lt;/p&gt;

&lt;h2 id=&quot;its-all-about-community&quot;&gt;It’s all about Community&lt;/h2&gt;
&lt;p&gt;This event wouldn’t be possible without the support of so many people. Thanks to everyone, especially &lt;a href=&quot;https://aws.amazon.com/developer/community/heroes/linda-mohamed/&quot;&gt;Linda Mohamed&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/johannes-oehmen/&quot;&gt;Johannes Oehmen&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/dmytro-hlotenko-7aa348151/&quot;&gt;Dmytro Hlotenko&lt;/a&gt;, and &lt;a href=&quot;https://www.linkedin.com/in/gustavares/&quot;&gt;Gustavo Tavares&lt;/a&gt; from our marketing team, &lt;a href=&quot;https://aws.amazon.com/developer/community/heroes/thorsten-hoger/&quot;&gt;Thorsten Höger&lt;/a&gt; for managing sponsors, &lt;a href=&quot;https://aws.amazon.com/developer/community/heroes/markus-ostertag&quot;&gt;Markus Ostertag&lt;/a&gt; as our local Hero and contact to AWS, and all the volunteers: &lt;a href=&quot;https://aws.amazon.com/developer/community/heroes/johannes-koch/&quot;&gt;Johannes Koch&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/andreas-ruetten/&quot;&gt;Andreas Rütten&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/bombadiltom/&quot;&gt;Tom Lorenz&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/manuel-vogel/&quot;&gt;Manuel Vogel&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/raphael-manke/&quot;&gt;Raphael Manke&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/suzanamelomoraes/&quot;&gt;Suzana Melo Moraes&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/monicacolangelo/&quot;&gt;Monica Colangelo&lt;/a&gt;, and many others!&lt;/p&gt;

&lt;p&gt;Thanks goes also to our friends at AWS, including &lt;a href=&quot;https://www.linkedin.com/in/mariaencinar/&quot;&gt;María Encinar&lt;/a&gt; and &lt;a href=&quot;https://www.linkedin.com/in/markpergola/&quot;&gt;Mark Pergola&lt;/a&gt;, and our &lt;a href=&quot;https://www.aws-community-day.de/#sponsors&quot;&gt;sponsors&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/cd-2024-organizer.jpg&quot; alt=&quot;Community Day Organizers&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Fri, 20 Sep 2024 06:00:00 +0000</pubDate>
        <link>https://garbe.io/blog/2024/09/20/aws-community-day-behind-scenes-call-for-paper/</link>
        <guid isPermaLink="true">https://garbe.io/blog/2024/09/20/aws-community-day-behind-scenes-call-for-paper/</guid>
        
        
        <category>AWS</category>
        
      </item>
    
      <item>
        <title>Hey CDK, what should I know before refactoring CDK applications?</title>
        <description>&lt;p&gt;For those who know me it’s not a surprise that I’m a big fan of the CDK. I have worked many years with CloudFormation and that’s why it’s crystal clear to me what happens under the hood when I change something in my CDK code. But I see many people struggling when making changes to CDK code. Often they started directly with CDK without knowing what CloudFormation is and how it works.&lt;/p&gt;

&lt;h2 id=&quot;refactoring-cdk-code&quot;&gt;Refactoring CDK Code&lt;/h2&gt;
&lt;p&gt;Refactoring is a fundamental aspect of software development. It’s a practice that developers, with their innate drive for optimization and efficiency, are intimately familiar with. And just like any other codebase, Cloud Development Kit (CDK) applications are not exempt from the need for refactoring.&lt;/p&gt;

&lt;p&gt;But, as with any form of renovation, there’s a cautionary tale to heed. Refactoring CDK applications isn’t without its perils, and the danger lies in the potential recreation of resources. For stateful resources like S3 buckets or databases, this can quickly escalate from an inconvenience to a critical issue.&lt;/p&gt;

&lt;p&gt;So, how do you navigate the treacherous waters of CDK application refactoring without inadvertently capsizing your stateful resources? In this blog post, I’ll explore best practices, strategies, and safeguards to ensure that your CDK refactoring endeavors are not only smooth but also devoid of any surprises.&lt;/p&gt;

&lt;h2 id=&quot;back-to-cloudformation&quot;&gt;Back to CloudFormation&lt;/h2&gt;

&lt;p&gt;To delve into the realm of refactoring CDK applications effectively, we must first revisit the foundation upon which CDK is built — CloudFormation. Understanding how CloudFormation operates is essential because, despite CDK’s abstraction layer, it remains the underlying engine.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Understanding CloudFormation’s Core Concepts:&lt;/strong&gt; CloudFormation operates based on templates that describe the AWS resources and their dependencies. When you refactor a CDK application, you’re essentially working with these templates. Therefore, comprehending CloudFormation’s nuances is pivotal to successful CDK refactoring.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Creation and Cleanup Phases:&lt;/strong&gt; CloudFormation follows a two-phase process: creation and cleanup. During the creation phase, resources are provisioned or updated as needed. However, it’s only during the cleanup phase that orphaned resources are removed. This ensures that (stateless) resources that require a replacement are replaced without any interruption.&lt;/p&gt;

    &lt;p&gt;&lt;em&gt;Example:&lt;/em&gt;
 If you change a property like the path of an IAM role, CloudFormation gracefully creates a new role, attaches it to the relevant resource, and then removes the old one.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/assets/cfn-two-phases.jpg&quot; alt=&quot;CloudFormation two phases&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Deletion Triggers:&lt;/strong&gt; Especially for stateful resources it is important to understand when CloudFormation tries to delete them. It can occur under specific circumstances:&lt;/p&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Removal from the Template:&lt;/strong&gt; If you remove a resource from the CloudFormation template altogether, it will be deleted during the next stack update.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Logical ID Changes:&lt;/strong&gt; Changing the logical ID of a resource essentially tells CloudFormation that it’s a different resource, triggering its deletion in cleanup phase.&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Property Changes Requiring Replacement:&lt;/strong&gt; Some property changes necessitate the replacement of a resource. When this happens, CloudFormation deletes the old resource after creating the new one.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the next sections, we’ll explore strategies to mitigate these risks and ensure a seamless refactoring experience.&lt;/p&gt;

&lt;h2 id=&quot;tips--tricks&quot;&gt;Tips &amp;amp; Tricks&lt;/h2&gt;

&lt;p&gt;When refactoring CDK applications, ensuring a seamless transition and avoiding unexpected hiccups is of paramount importance. Here are some tips and tricks to help you navigate this process with confidence:&lt;/p&gt;

&lt;h3 id=&quot;1-be-aware-of-stateful-and-stateless-resources&quot;&gt;1. Be Aware of Stateful and Stateless Resources&lt;/h3&gt;

&lt;p&gt;Understanding the nature of your AWS resources is crucial. Most resources can be replaced by CloudFormation with relative ease, thanks to its built-in orchestration capabilities. However, some resources, like S3 buckets, RDS databases, or DynamoDB tables, should be preserved unless replacement is your explicit intention. These stateful resources require special attention during refactoring to prevent unintended consequences.&lt;/p&gt;

&lt;h3 id=&quot;2-construct-ids-and-hierachy-matter&quot;&gt;2. Construct IDs and Hierachy Matter&lt;/h3&gt;

&lt;p&gt;CloudFormation identifies resources based on their Logical IDs. In CDK, you work with constructs, each of which is assigned an ID within the construct tree. These IDs are mapped to logical IDs, using a hash based on their position in the hierarchy.&lt;/p&gt;

&lt;p&gt;If you change a resource’s logical ID, CloudFormation interprets it as a “new” resource, resulting in the deletion of the one with the old ID. To avoid this, plan a logical naming schema for your IDs from the outset. Consistency and forethought in naming, such as using PascalCase, can save you from potential refactoring woes.&lt;/p&gt;

&lt;!-- TODO: Picture how a &quot;Id&quot; leads to LogicalId and therefor PhysicalId --&gt;

&lt;p&gt;In case you need to keep the former LogicalId, CDK allows to override it. But it works only for L1 constructs so you might need to figure them out first. Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cdk diff&lt;/code&gt; for each environment to see which resources will be replaced and to find out the former LogicalId.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This might be tricky for RDS resources as they include a few L1 constructs that needs to be overriden.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;aws_s3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Bucket&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Go to the underlying L1 CfnBucket construct &lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// and override the logicalId to keep the former value&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;defaultChild&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;aws_s3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CfnBucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;overrideLogicalId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;FormerLogicalId&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;3-watch-your-resource-naming&quot;&gt;3. Watch Your Resource Naming&lt;/h3&gt;

&lt;p&gt;While many AWS resources allow you to define a name (e.g. &lt;a href=&quot;TODO&quot;&gt;FunctionName&lt;/a&gt; in Lambda), take heed when considering resource names that could be replaced during refactoring. CloudFormation operates in two phases: it attempts to create a new resource with the same name before cleaning up the old one with that name. This can lead to errors, as resource names must be unique within a stack. To circumvent this, avoid defining resources names, especially when dealing with properties that may require replacement.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/cfn-duplicate-name.jpg&quot; alt=&quot;CloudFormation conflicts with named resources&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;4-properties-requiring-replacement&quot;&gt;4. Properties Requiring Replacement&lt;/h3&gt;

&lt;p&gt;Certain properties of AWS resources necessitate a complete replacement when modified. Identifying these properties and their impact on your stack is crucial. Be sure to consult the AWS documentation for specifics on properties that trigger replacements, as understanding this aspect is key to successful refactoring.&lt;/p&gt;

&lt;!-- TODO: Check them in the CloudFormation docs --&gt;

&lt;h3 id=&quot;5-deletion-policies&quot;&gt;5. Deletion Policies&lt;/h3&gt;

&lt;p&gt;Every AWS resource supports a deletion policy. By default, CloudFormation deletes resources if they are not part of the template, but you have the option to specify other behaviors, such as Retain or RetainExceptOnCreate. The Retain policy ensures that a resource is not physically deleted but is removed from the stack, allowing stack updates to proceed without failure. Additionally, some resources support Snapshot as a deletion policy, which can be especially useful for preserving data. For detailed information on deletion policies, refer to the AWS documentation &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;!-- RDS comes with delete protection, S3 Buckets require to be empty --&gt;
&lt;!-- TODO: They are useful if you want to &quot;import&quot; existing resources --&gt;

&lt;h3 id=&quot;6-snapshot-tests&quot;&gt;6. (Snapshot) Tests&lt;/h3&gt;

&lt;p&gt;For stateful resources, it’s a wise practice to create tests that verify their integrity during refactoring. These tests help ensure that these resources are not inadvertently replaced. Snapshot tests can also be valuable for quickly identifying changes in the resource state. By employing a robust testing strategy, you can confidently refactor your CDK applications without compromising the stability of your stateful resources.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Refactoring CDK applications is possible but needs some awareness and ideally some basic knowledge how CloudFormation operates.&lt;/p&gt;

&lt;p&gt;Keep in mind:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;You mainly have to care about stateful resources (like databases, buckets)&lt;/li&gt;
  &lt;li&gt;Stateless resources are gracefully replaced by CloudFormation’s two phases&lt;/li&gt;
  &lt;li&gt;Be aware what changes within CDK trigger replacements in CloudFormation&lt;/li&gt;
  &lt;li&gt;Write (snapshot) tests to ensure that stateful resources are not deleted&lt;/li&gt;
  &lt;li&gt;Replacement triggered by changed properties can still bite you&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 30 Apr 2024 06:00:00 +0000</pubDate>
        <link>https://garbe.io/blog/2024/04/30/hey-cdk-what-you-need-to-know-to-refactor/</link>
        <guid isPermaLink="true">https://garbe.io/blog/2024/04/30/hey-cdk-what-you-need-to-know-to-refactor/</guid>
        
        
        <category>AWS</category>
        
        <category>CDK</category>
        
      </item>
    
      <item>
        <title>Bypass Docker Hub Rate Limits with ECR PullThrough Cache</title>
        <description>&lt;p&gt;Experiencing Docker Hub’s rate limits? ECR PullThrough Cache seems like a lifesaver, but beware of potential pitfalls, especially with cross-account access. Learn how to navigate these challenges and optimize your container workflow effectively.&lt;/p&gt;

&lt;p&gt;I have already described the reasons why you should not use DockerHub images in production &lt;a href=&quot;/blog/2020/04/22/cdk-ecr-sync/&quot;&gt;here&lt;/a&gt;. In short, the lack of an SLA from DockerHub affects the availability of your own application. But costs can also play a role.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This article focuses on Docker Hub as an upstream registry. In principle, however, it also applies to all other &lt;a href=&quot;https://docs.aws.amazon.com/AmazonECR/latest/userguide/pull-through-cache-creating-rule.html&quot;&gt;supported registries&lt;/a&gt;, like Quay, GitHub, or Azure Container Registry.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;The basic idea is to use ECR as a PullThrough cache for any image that is hosted by Docker Hub, GitHub, K8s or Quay. This article explains what needs to be done in your AWS account&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/ecr-pull-through.png&quot; alt=&quot;ECR PullThrough Cache&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;rules&quot;&gt;Rules&lt;/h3&gt;
&lt;p&gt;Rules can be used to create repositories dynamically in ECR. A rule consists of a repository prefix and an upstream registry. If the repository does not yet exist during a &lt;em&gt;docker pull&lt;/em&gt;, a repository is automatically created (provided that the prefix matches). The part after the prefix is used to pull the image from upstream, cache it in the ECR repository and return it to the user.&lt;/p&gt;

&lt;p&gt;Further pulls end up directly in the ECR repository and are not forwarded to upstream. In addition, ECR regularly compares the images with upstream and updates them if necessary.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;docker pull&lt;/em&gt; command is divided as follows:
&lt;img src=&quot;/assets/ecr-pull-command.png&quot; alt=&quot;ECR Pull Command&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;templates&quot;&gt;Templates&lt;/h3&gt;
&lt;p&gt;Templates allow you to define defaults that are used for the newly created repositories. These include lifecycle rules, IAM policy, KMS encryption, and also tags.&lt;/p&gt;

&lt;h3 id=&quot;for-organizations&quot;&gt;For Organizations&lt;/h3&gt;
&lt;p&gt;If the pull-through cache is also to be used by other accounts in the organization, a condition can be stored in the policy that allows access for all accounts in the organization.&lt;/p&gt;

&lt;p&gt;There are two different policies that must be taken into account.&lt;/p&gt;

&lt;h4 id=&quot;ecr-private-registry-policy&quot;&gt;ECR Private Registry Policy&lt;/h4&gt;

&lt;p&gt;In ECR you can define your own policy for the “Private Registry” part of ECR. Here is an example with CDK that allows all roles in the accounts of the organization to dynamically create repositories for the path &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/mydockerhubmirror/*&lt;/code&gt; (assuming a suitable pull-through cache rule exists) and download images from the upstream registry.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Strictly speaking, any repository can be created with this policy, but no images can be pushed or pulled.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;aws_ecr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CfnRegistryPolicy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;RegistryPolicy&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;policyText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;2012-10-17&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Statement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;Sid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;AllowPullThrough&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;Effect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Allow&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;Principal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;AWS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;Action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;ecr:CreateRepository&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;ecr:BatchImportUpstreamImage&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;ecr:*&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// For whatever reasons this is needed to pull with a cross-account role&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;Resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;arn:aws:ecr:eu-central-1:123456789012:repository/mydockerhubmirror/*&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;Condition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;StringEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;aws:PrincipalOrgID&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;o-xxx&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;⚠️⚠️ &lt;strong&gt;ATTENTION&lt;/strong&gt; ⚠️⚠️&lt;br /&gt;
For some reason, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ecr:*&lt;/code&gt; is required if you initially pull an image where no ECR repository yet exists and the request comes from a role of a different account. For this reason you might do the initial pull only with a role from the same account. I am still trying to find out which permission is missing or if it is a bug in ECR.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;ecr-repository-policy&quot;&gt;ECR Repository Policy&lt;/h4&gt;
&lt;p&gt;You also need a permission for the repository. In addition to the known permissions for pull, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ecr:BatchImportUpstreamImage&lt;/code&gt; is required. The condition for the OrganizationId is also required here.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Version&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Statement&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Sid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;AllowCrossAccountPull&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Effect&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Allow&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Principal&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Action&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ecr:BatchCheckLayerAvailability&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ecr:BatchGetImage&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ecr:GetDownloadUrlForLayer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ecr:ListImages&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ecr:BatchImportUpstreamImage&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Condition&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;StringEquals&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;aws:PrincipalOrgID&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;o-xxx&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;pitfalls&quot;&gt;Pitfalls&lt;/h2&gt;
&lt;p&gt;While many error possibilities are caught in the AWS Console, there are several ways to run into confusing errors in CDK or CloudFormation.&lt;/p&gt;

&lt;h3 id=&quot;naming-conventions-for-secrets&quot;&gt;Naming Conventions for Secrets&lt;/h3&gt;
&lt;p&gt;The credentials for the upstream registry are saved as a secret in SecretsManager. To do this, the name must begin with the prefix &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ecr-pullthroughcache/...&lt;/code&gt;. This unnecessary restriction makes it impossible to follow the best-practices for IaC to avoid hardcoded names.&lt;/p&gt;

&lt;p&gt;In such a case I use the StackName as part of the name (both in CDK and CloudFormation) to make the name unique.&lt;/p&gt;

&lt;h3 id=&quot;upstream-registry-needs-url&quot;&gt;Upstream Registry needs Url&lt;/h3&gt;
&lt;p&gt;Although the &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecr-pullthroughcacherule.html&quot;&gt;docs&lt;/a&gt; say that &lt;em&gt;upstreamRegistry&lt;/em&gt; and &lt;em&gt;upstreamRegistryUrl&lt;/em&gt; are optional, both must be specified. The values for &lt;em&gt;upstreamRegistry&lt;/em&gt; are fixed and documented, the &lt;em&gt;upstreamRegistryUrl&lt;/em&gt; must be copied from &lt;a href=&quot;https://docs.aws.amazon.com/AmazonECR/latest/userguide/pull-through-cache-creating-rule.html#pull-through-cache-creating-rule-cli&quot;&gt;this page&lt;/a&gt;. Without &lt;em&gt;upstreamRegistryUrl&lt;/em&gt; only a “null” error is returned.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;11:55:45 AM | CREATE_FAILED        | AWS::ECR::PullThroughCacheRule | DockerImagesMirror/DockerHub
Resource handler returned message: &lt;span class=&quot;s2&quot;&gt;&quot;null&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;RequestToken: xxx, HandlerErrorCode: InternalFailure&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;invalid-repository-prefix&quot;&gt;Invalid Repository Prefix&lt;/h3&gt;
&lt;p&gt;The &lt;em&gt;EcrRepositoryPrefix&lt;/em&gt; has a &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecr-pullthroughcacherule.html#cfn-ecr-pullthroughcacherule-ecrrepositoryprefix&quot;&gt;documented&lt;/a&gt; restriction on which characters may be used. In the case of invalid characters, however, the resource is apparently created but the delete on rollback fails because the resource does not exist. This takes about 5 minutes until the stack is completely rolled back.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;12:32:08 PM | CREATE_FAILED        | AWS::ECR::PullThroughCacheRule | DockerImagesMirror/DockerHub
Resource handler returned message: &lt;span class=&quot;s2&quot;&gt;&quot;Invalid parameter at &apos;ecrRepositoryPrefix&apos; failed to satisfy constraint: &apos;Member must satisfy regular expression pattern: (?:[a-z0-9]+(?:[._-][a-z
0-9]+)*/)*[a-z0-9]+(?:[._-][a-z0-9]+)*&apos; (Service: Ecr, Status Code: 400, Request ID: xxx)&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;RequestToken: xxx, HandlerErrorCode: InvalidRequest&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;BUT Rollback now also fails (only 6min until it gave up)&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;12:32:14 PM | DELETE_FAILED        | AWS::ECR::PullThroughCacheRule | DockerImagesMirror/DockerHub
Resource handler returned message: &lt;span class=&quot;s2&quot;&gt;&quot;Invalid parameter at &apos;ecrRepositoryPrefix&apos; failed to satisfy constraint: &apos;Member must satisfy regular expression pattern: (?:[a-z0-9]+(?:[._-][a-z
0-9]+)*/)*[a-z0-9]+(?:[._-][a-z0-9]+)*&apos; (Service: Ecr, Status Code: 400, Request ID: xxx)&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;RequestToken: xxx, HandlerErrorCode: GeneralServiceException&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;creation-templates-are-still-in-preview&quot;&gt;Creation Templates are still in Preview&lt;/h3&gt;
&lt;p&gt;As this feature is still in preview, there are some limitations. There is neither API nor CloudFormation support. And it is not possible to make changes to existing templates. To do this, the template must be deleted and recreated. Changes to templates do not affect repositories that have been created with this template, but only for future repositories.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Tip: If the template is changed, simply delete the existing repositories. But be careful that you do not run into rate limits when filling the “cache” again.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;pullthroughcacherule-requires-always-a-replacement&quot;&gt;PullThroughCacheRule requires always a replacement&lt;/h3&gt;
&lt;p&gt;All properties of &lt;em&gt;PullThroughCacheRule&lt;/em&gt; require a replacement. Changing the secret (means &lt;em&gt;CredentialArn&lt;/em&gt;) is just not possible since CloudFormation creates the new &lt;em&gt;PullThroughCacheRule&lt;/em&gt; (with a conflicting EcrRepositoryPrefix) first before it deletes the old one.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Pulling images from ECR instead of DockerHub or other public registries improves your resilience and can save money. The ECR PullThrough cache works quite nice once it is set up.&lt;/p&gt;

&lt;p&gt;But getting it set up with CDK or CloudFormation is bumpy and could be improved. And be aware of the wildcard permissions that are needed for cross-account access! This gets hopefully fixed soon by AWS!&lt;/p&gt;
</description>
        <pubDate>Tue, 09 Apr 2024 06:00:00 +0000</pubDate>
        <link>https://garbe.io/blog/2024/04/09/bypass-docker-hub-rate-limits-with-ecr-pullthrough-cache/</link>
        <guid isPermaLink="true">https://garbe.io/blog/2024/04/09/bypass-docker-hub-rate-limits-with-ecr-pullthrough-cache/</guid>
        
        
        <category>AWS</category>
        
        <category>CDK</category>
        
        <category>ECR</category>
        
      </item>
    
      <item>
        <title>Use Slack to handle AWS Support Cases</title>
        <description>&lt;p&gt;In this blog post, I aim to shed light on a relatively unknown feature of AWS: Slack integration for AWS Support.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;AWS provides different levels of support. Read more on their &lt;a href=&quot;https://aws.amazon.com/premiumsupport/&quot;&gt;Premium Support site&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Typically, users resort to the AWS Console (Web) to open and respond to Support Cases. However, this approach has its drawbacks:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Frustration with expired credentials leading to the loss of information before submission&lt;/li&gt;
  &lt;li&gt;Inconvenient email notifications lacking readability and context&lt;/li&gt;
  &lt;li&gt;Manual navigation to support cases due to the absence of deep links&lt;/li&gt;
  &lt;li&gt;Chats in a separate browser window make messages easy to overlook, with limited sharing capability within a team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For those accustomed to using Slack for communication you’ll immediately recognize the benefits:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Each support case becomes its own Slack thread&lt;/li&gt;
  &lt;li&gt;Follow other cases using Slack’s “get notified about new messages” feature&lt;/li&gt;
  &lt;li&gt;Create private channels for AWS support chats, facilitating team collaboration&lt;/li&gt;
  &lt;li&gt;Leverage Slack notifications to stay informed about new responses&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;slack-support-for-aws-support&quot;&gt;Slack Support for AWS Support&lt;/h2&gt;
&lt;p&gt;The setup process is surprisingly straightforward.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Thanks to my colleague &lt;a href=&quot;https://www.linkedin.com/in/alina-domanov/&quot;&gt;Alina Domanov&lt;/a&gt; as she has done most of the work and implemented it for our teams at &lt;a href=&quot;https://www.personio.com/about-personio/careers/product-engineering/&quot;&gt;Personio&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/aws-slack-support-management.png&quot; alt=&quot;AWS Slack App for Support in Management Account&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In the management account, create a Slack &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-supportapp-slackworkspaceconfiguration.html&quot;&gt;SlackWorkspaceConfiguration&lt;/a&gt; and &lt;a href=&quot;https://docs.aws.amazon.com/supportapp/latest/APIReference/API_RegisterSlackWorkspaceForOrganization.html&quot;&gt;register it&lt;/a&gt; for the entire organization. Additionally, add the &lt;a href=&quot;https://slack.com/apps/A010CU512EQ-aws-support&quot;&gt;AWS Support App&lt;/a&gt; to your Slack Workspace. This enables any member account to replicate the Slack Workspace without extensive configuration.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/aws-slack-support-member.png&quot; alt=&quot;AWS Slack App for Support in Member Account&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In any member account within your organization, create a SlackWorkspaceConfiguration and a &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-supportapp-slackchannelconfiguration.html&quot;&gt;SlackChannelConfiguration&lt;/a&gt;. It’s also beneficial to create an &lt;a href=&quot;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-supportapp-accountalias.html&quot;&gt;AccountAlias&lt;/a&gt; for a human-readable name instead of the Account Id.&lt;/p&gt;

&lt;p&gt;Within Slack use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/awssuport create-case&lt;/code&gt; command. It will open a pop-up window that guides you through the whole process.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/aws-slack-support-command.png&quot; alt=&quot;Command to create new AWS Support Case&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/aws-slack-support-wizard.png&quot; alt=&quot;Wizard that guides you through the creation process&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;example-with-cdk&quot;&gt;Example with CDK&lt;/h2&gt;
&lt;p&gt;Of course, you don’t want to click around but set up everything with Infrastructure as Code (IaC). I hope this example with CDK helps you to get started.&lt;/p&gt;

&lt;h3 id=&quot;organization-setup&quot;&gt;Organization Setup&lt;/h3&gt;
&lt;p&gt;The first stack is meant for the management account. It creates the SlackWorkspaceConfiguration and calls the &lt;em&gt;registerSlackWorkspaceForOrganization&lt;/em&gt; API as there is no CloudFormation support there. A de-register on delete is not needed as it will be done as part of the deletion of the SlackWorkspaceConfiguration.&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;aws_iam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;aws_supportapp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;supportapp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;custom_resources&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Stack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;StackProps&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;aws-cdk-lib&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Construct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;constructs&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AwsSupportAppSlackRegistrationProps&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;StackProps&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/**
     * The Slack Workspace Id
     */&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;teamId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AwsSupportAppSlackRegistrationStack&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Stack&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Construct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AwsSupportAppSlackRegistrationProps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// specify your Slack workspace configuration&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;supportapp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CfnSlackWorkspaceConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MyCfnSlackWorkspaceConfiguration&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;teamId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;teamId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Custom resource for RegisterSlackWorkspaceForOrganization&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;registerSlackWorkspace&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AwsCustomResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;RegisterSlackWorkspace&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;onCreate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;SupportApp&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;registerSlackWorkspaceForOrganization&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;teamId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;physicalResourceId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PhysicalResourceId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()),&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;policy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AwsCustomResourcePolicy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fromStatements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;aws_iam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PolicyStatement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;effect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;aws_iam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Effect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ALLOW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;supportapp:RegisterSlackWorkspaceForOrganization&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;]),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Add a dependency on the Custom Resource for Slack workspace registration&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;registerSlackWorkspace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addDependency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

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

&lt;p&gt;The next Stack is for the member accounts. It comes with two parameters: The &lt;em&gt;channelId&lt;/em&gt; to define in which Slack Channel the Bot should be added and &lt;em&gt;alias&lt;/em&gt; to give the accounts a human readable name (otherwise it will show only the account ids).&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Be aware that the Account Alias is limited in size and supports only 30 characters!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AwsSupportAppSlackIntegrationProps&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/**
     * Slack Channel Id.
     */&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;channelId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/**
     * Human readable account alias (max 30 characters)
     */&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AwsSupportAppSlackIntegrationStack&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Stack&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Construct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;AwsSupportAppSlackIntegrationProps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Create IAM role for the channelRoleArn with required permissions&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;channelRole&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;aws_iam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;ChannelRole&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;assumedBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;aws_iam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ServicePrincipal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;supportapp.amazonaws.com&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Attach policies for the required permissions&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;channelRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addToPolicy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;aws_iam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PolicyStatement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;effect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;aws_iam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Effect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ALLOW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;servicequotas:GetRequestedServiceQuotaChange&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;servicequotas:GetServiceQuota&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;servicequotas:RequestServiceQuotaIncrease&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;support:AddAttachmentsToSet&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;support:AddCommunicationToCase&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;support:CreateCase&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;support:DescribeCases&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;support:DescribeCommunications&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;support:DescribeSeverityLevels&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;support:InitiateChatForCase&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;support:ResolveCase&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;channelRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addToPolicy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;aws_iam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PolicyStatement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;effect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;aws_iam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Effect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ALLOW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;iam:CreateServiceLinkedRole&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;conditions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;StringEquals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;iam:AWSServiceName&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;servicequotas.amazonaws.com&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Enable Slack integration for all organizations&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;slackChannelConfig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;supportapp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CfnSlackChannelConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;SlackChannelConfiguration&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;channelId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;slackChannel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;valueAsString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;channelRoleArn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;channelRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;roleArn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;notifyOnCaseSeverity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;teamId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;teamId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;notifyOnAddCorrespondenceToCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;notifyOnCreateOrReopenCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;notifyOnResolveCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;supportapp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CfnAccountAlias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;CfnAccountAlias&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;accountAlias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// specify your Slack workspace configuration&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;workspaceConfig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;supportapp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CfnSlackWorkspaceConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;SlackWorkspaceConfiguration&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;teamId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;teamId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

        &lt;span class=&quot;nx&quot;&gt;slackChannelConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addDependency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;workspaceConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is a good candidate to roll out &lt;a href=&quot;/blog/2023/10/10/deep-dive-on-stacksets-with-cdk&quot;&gt;with StackSets&lt;/a&gt;. I skipped that as the parameters (channelId and alias) need to be customized for each account and it depends on the actual use-case how to do that.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In conclusion, integrating Slack with AWS Support offers a streamlined approach to handling support cases. By leveraging Slack’s familiar interface, users can enhance collaboration, receive timely notifications, and efficiently manage their AWS support interactions. The seamless setup outlined in this post, coupled with AWS CDK examples, empowers organizations to harness the power of Slack in optimizing their AWS support workflows.&lt;/p&gt;

</description>
        <pubDate>Thu, 14 Dec 2023 06:00:00 +0000</pubDate>
        <link>https://garbe.io/blog/2023/12/14/use-slack-to-handle-support-cases/</link>
        <guid isPermaLink="true">https://garbe.io/blog/2023/12/14/use-slack-to-handle-support-cases/</guid>
        
        
        <category>AWS</category>
        
        <category>CDK</category>
        
      </item>
    
      <item>
        <title>Deep Dive on StackSets with CDK</title>
        <description>&lt;p&gt;Can StackSets be fully managed with CDK? In this deep dive you will learn how to synthesize CDK Stacks into StackSet templates and how to deal with Assets when you need them across accounts and regions.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This is the written form of my talk at the &lt;a href=&quot;https://cdkday.com&quot;&gt;CDK Day&lt;/a&gt; 2023. You can watch it here:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/qlUR5jVBC6c?si=k0LuPs8Mqb71hgqi&amp;amp;start=1240&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;h2 id=&quot;why-stacksets&quot;&gt;Why StackSets?&lt;/h2&gt;

&lt;p&gt;StackSets are a powerful feature in AWS CloudFormation that allow you to deploy resources across multiple AWS accounts and regions simultaneously. They are particularly useful for rolling out infrastructure consistently across an organization’s accounts. However, working with StackSets in CDK can be challenging due to some unique considerations.&lt;/p&gt;

&lt;h2 id=&quot;stacksets-in-cloudformation&quot;&gt;StackSets in CloudFormation&lt;/h2&gt;

&lt;p&gt;Let’s start with a basic understanding of StackSets in CloudFormation. In CloudFormation, you use templates (typically in YAML or JSON format) to define and provision your AWS resources. These templates are used to create CloudFormation stacks, which represent sets of resources.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/cdk-stackset-1.png&quot; alt=&quot;A simple CloudFormation stack&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But what if you need to deploy the same template across multiple AWS accounts? This is where StackSets come into play. A StackSet is essentially an orchestration mechanism that takes a CloudFormation template and deploys it into multiple AWS accounts, ensuring consistency across the organization.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/cdk-stackset-3.png&quot; alt=&quot;Use StackSet to deploy to multiple accounts&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Of course, a CloudFormation Stack is used to create the StackSet. You have to deal with two templates now. The Stack Template is responsible to create the StackSet resource (like any other AWS resources).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Stack Template:&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;Resources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;MyStackSet&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;AWS::CloudFormation::StackSet&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;// other properties...&lt;/span&gt;
     &lt;span class=&quot;s&quot;&gt;TemplateURL&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://s3.amazonaws.com/.../stackset-template.yaml&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The StackSet Template is like any other CloudFormation template. It is referenced in the Stack Template and used to be rolled out in the target accounts as CloudFormation Stack.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;StackSet Template:&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;Resources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;MyLambdaToRunInEveryAccount&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;AWS::Lambda::Function&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nodejs18.x&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Handler&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;index.handler&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Code&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;self-managed-vs-service-managed-stacksets&quot;&gt;Self-Managed vs. Service-Managed StackSets&lt;/h4&gt;
&lt;p&gt;There are two types of StackSets: self-managed and service-managed. Here’s a brief overview of the differences:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Self-Managed StackSets:&lt;/strong&gt; &lt;br /&gt;
With self-managed StackSets, you have more control but also more responsibility. You decide which accounts to add or remove from your StackSet (that’s called &lt;em&gt;stack instances&lt;/em&gt;). However, you need to handle the Identity and Access Management (IAM) roles and permissions for the operations between the management account and target accounts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Service-Managed StackSets:&lt;/strong&gt; &lt;br /&gt;
AWS takes care of adding or removing accounts for you based on organizational units (OUs). Whenever an account is added or removed from an OU, it’s automatically included in or removed from the StackSet. AWS also manages the IAM roles and permissions, reducing your administrative overhead.&lt;/p&gt;

&lt;h2 id=&quot;cdk-and-stacksets&quot;&gt;CDK and StackSets&lt;/h2&gt;

&lt;p&gt;Now, let’s bring CDK into the equation. CDK is commonly used to synthesize and deploy AWS CloudFormation stacks.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/cdk-stackset-4.png&quot; alt=&quot;StackSets created with CDK&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When working with StackSets, CDK has to perform two key tasks:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Synthesize a stack template and deploy it as a CloudFormation stack (business as usual).&lt;/li&gt;
  &lt;li&gt;Synthesize the StackSet template and upload it to an S3 bucket, which is used as a template URL for the StackSet.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The second case is not (yet) supported by AWS CDK, but the team provides an experimental L2 construct (&lt;a href=&quot;https://github.com/cdklabs/cdk-stacksets/&quot;&gt;cdklabs/cdk-stacksets&lt;/a&gt;) which I use in the following snippets.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;⚠️ The projects in cdklabs are experimental and under active development. They are subject to non-backward compatible changes or removal in any future version.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It introduces a new &lt;em&gt;StackSetTemplateStack&lt;/em&gt; class to handle StackSet templates in CDK. By extending from StackSetTemplateStack, it is not listed as deployable stack but rather synthesized as JSON template and added as an Asset to the parent stack.&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;MyStackSet&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;StackSetTemplateStack&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Construct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;StackSetTemplateStackProps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;Function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Lambda&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the application create a new instance of &lt;em&gt;MyStackSet&lt;/em&gt; class and reference it as template for the StackSet.&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;App&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;stack&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Stack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;‘&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;MyStack&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;’&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Create the StackSetTemplateStack&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;stackSetStack&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;MyStackSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MyStackSet&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;StackSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;StackSet&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;StackSetTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fromAccounts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;…&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;StackSetTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fromStackSetStack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stackSetStack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;handling-assets-in-stacksets&quot;&gt;Handling Assets in StackSets&lt;/h2&gt;

&lt;p&gt;Assets, such as Lambda function code, are a common part of CloudFormation templates. However, working with assets in StackSets can be tricky. By default, CDK uploads assets to a private bucket (provided by CDK Bootstrap), which can’t be accessed from other accounts.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/cdk-stackset-5.png&quot; alt=&quot;StackSets created with CDK&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To address this, &lt;em&gt;StackSetTemplateStack&lt;/em&gt; comes with a synthesizer that ensures assets are copied to a public bucket, making them accessible across accounts. This feature simplifies asset management when working with StackSets.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/cdk-stackset-6.png&quot; alt=&quot;StackSets created with CDK&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You have to provide that public bucket when instantiating the StackSetTemplateStack. The bucket should &lt;a href=&quot;https://aws.amazon.com/blogs/security/control-access-to-aws-resources-by-using-the-aws-organization-of-iam-principals/&quot;&gt;grant access to specific accounts or your organization only&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The synthesizer&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;adds the asset to the parent stack&lt;/li&gt;
  &lt;li&gt;creates an S3 Deployment to copy from the bootstrap bucket to the provided public bucket and&lt;/li&gt;
  &lt;li&gt;ensures that the synthesized StackSetTemplateStack points to the assets in the public bucket.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;cross-region-deployment&quot;&gt;Cross-Region Deployment&lt;/h2&gt;

&lt;p&gt;One of the challenges with StackSets is handling cross-region deployments. When adding an account with a different region to a StackSet, deployment can fail because assets must reside in the same region where they are used.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/cdk-stackset-7.png&quot; alt=&quot;StackSets created with CDK&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is currently not supported by &lt;a href=&quot;https://github.com/cdklabs/cdk-stacksets/&quot;&gt;cdklabs/cdk-stacksets&lt;/a&gt; but discussed in &lt;a href=&quot;https://github.com/cdklabs/cdk-stacksets/issues/159&quot;&gt;this issue&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a workaround, you can deploy the StackSet in each region separately and then roll it out to the target accounts in the same region. While this adds some overhead, it is a functional approach.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;💡 Personally I have created a different approach and use a “StackSet Bootstrap” to create S3 Buckets with a specific naming convention in each region. The Synthesizer uses that naming convention and copies the assets to all specified regions.&lt;br /&gt;
See &lt;a href=&quot;https://github.com/pgarbe/cdk-stackset&quot;&gt;pgarbe/cdk-stackset&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;In this deep dive into StackSets with AWS CDK, I’ve explored why StackSets are valuable for managing infrastructure across multiple accounts and regions. We’ve also seen how &lt;em&gt;StackSetTemplateStack&lt;/em&gt; class from &lt;a href=&quot;https://github.com/cdklabs/cdk-stacksets/&quot;&gt;cdklabs/cdk-stacksets&lt;/a&gt; simplifies the handling of StackSet templates and assets, making it easier to work with this powerful AWS feature.&lt;/p&gt;

&lt;p&gt;I hope you found this blog post informative and that it helps you leverage StackSets effectively in your AWS CDK projects. If you have any questions or need further clarification, feel free to reach out to me on &lt;a href=&quot;https://cdk.dev&quot;&gt;CDK Slack&lt;/a&gt; or &lt;a href=&quot;/about&quot;&gt;any other channel&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Tue, 10 Oct 2023 06:00:00 +0000</pubDate>
        <link>https://garbe.io/blog/2023/10/10/deep-dive-on-stacksets-with-cdk/</link>
        <guid isPermaLink="true">https://garbe.io/blog/2023/10/10/deep-dive-on-stacksets-with-cdk/</guid>
        
        
        <category>AWS</category>
        
        <category>CDK</category>
        
      </item>
    
      <item>
        <title>Behind the scenes of AWS Community Day DACH</title>
        <description>&lt;p&gt;AWS Community Day DACH 2023 has concluded, and it was truly remarkable! I trust that everyone had a fantastic time, learned something new, and connected with fellow enthusiasts. But have you ever wondered what goes on behind the scenes to organize such an event? In this post, I’ll share my perspective.&lt;/p&gt;

&lt;h2 id=&quot;the-dach-aws-community&quot;&gt;The DACH AWS Community&lt;/h2&gt;

&lt;p&gt;DACH stands for &lt;strong&gt;D&lt;/strong&gt;eutschland (Germany), &lt;strong&gt;A&lt;/strong&gt;ustria, and S&lt;strong&gt;ch&lt;/strong&gt;weiz (Switzerland), encompassing 30+ UserGroups with over 15k members.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/usergroup-map.png&quot; alt=&quot;AWS UserGroups in DACH&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;planning&quot;&gt;Planning&lt;/h2&gt;
&lt;p&gt;In previous years, we initiated the planning for the Community Day only 2-3 months in advance. However, based on feedback from previous editions, we decided to start even earlier. The initial discussions took already place in November 2022 at AWS re:Invent in Las Vegas.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;After Community Day is before Community Day. We are already planning for 2024. If you want to help with the planning or sponsor it, &lt;a href=&quot;https://www.aws-community.de/contact&quot;&gt;reach out to us&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;location-and-date&quot;&gt;Location and Date&lt;/h3&gt;
&lt;p&gt;The most crucial step is to finalize the location, as it also determines the event date. Previous AWS Community Days were held in Cologne (2017), Frankfurt (2018), Hamburg (2019), and Dresden (2022). For 2023, we settled on Munich, strategically located and easily accessible from Austria, Switzerland, and most parts of Germany.&lt;/p&gt;

&lt;p&gt;Typically, we host the Community Day in September or October, perfectly placed between the AWS Summit in Berlin (May) and re:Invent (November). In Munich, we had to consider the school holidays (ending in mid-September) and the start of Oktoberfest (end of September). This left us with only one viable week, and we secured a booking at the &lt;a href=&quot;https://www.teamgeist.com/smartvillage-bogenhausen/&quot;&gt;SmartVillage Bogenhausen&lt;/a&gt; in January.&lt;/p&gt;

&lt;p&gt;Once the date was confirmed, we could move forward with the rest of the planning.&lt;/p&gt;

&lt;h3 id=&quot;call-for-papers-and-agenda&quot;&gt;Call for Papers and Agenda&lt;/h3&gt;
&lt;p&gt;The next major step is crafting the agenda. We rely on &lt;a href=&quot;https://sessionize.com&quot;&gt;sessionize.com&lt;/a&gt; to streamline everything from the call for papers to finalizing the schedule. We initiated the call for papers in mid-February and kept it open until mid-May. Once closed, we began building the agenda.&lt;/p&gt;

&lt;p&gt;Considering our location and schedule, we had space for 28 sessions. Each organizer evaluates proposals individually, and Sessionize offers three sessions that require ordering. This process is repeated multiple times with randomly ordered sessions. After consolidating all evaluations, we established a final ranking. During this process, we ensure diversity, avoid duplicate speakers, and confirm that topics align with our expectations. Once we reach a consensus, we contact the speakers and request confirmation of their talks.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Thanks to &lt;a href=&quot;https://sessionize.com&quot;&gt;Sessionize&lt;/a&gt; for providing a free plan for community events!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After receiving speaker confirmations, we invite them into a private Slack channel. This method simplifies communication, allowing us to provide additional event details, location information, and audio and video setup instructions. It also offers speakers a direct line to ask questions.&lt;/p&gt;

&lt;p&gt;As the event is &lt;strong&gt;From the Community - For the Community&lt;/strong&gt;, we do not accept talks from AWS employees. One exception is the keynote. To find keynote speakers, it’s important to have the date already defined, as they usually have demanding schedules and plan often one year in advance.&lt;/p&gt;

&lt;h3 id=&quot;social-media&quot;&gt;Social Media&lt;/h3&gt;
&lt;p&gt;We utilize our &lt;a href=&quot;https://www.linkedin.com/company/aws-community-dach/&quot;&gt;LinkedIn&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/AWSCommunityDE&quot;&gt;Twitter&lt;/a&gt; accounts for announcements. For each talk, we prepared separate posts and shared them on LinkedIn and Twitter via &lt;a href=&quot;https://buffer.com&quot;&gt;Buffer&lt;/a&gt;. These posts are generated with the assistance of ChatGPT and social banners from Sessionize.&lt;/p&gt;

&lt;p&gt;Sessionize also provides social banners for each speaker, allowing them to download and share them with their networks. Customized logos, backgrounds, and other templates have been created with &lt;a href=&quot;https://www.canva.com&quot;&gt;Canva&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;sponsors&quot;&gt;Sponsors&lt;/h3&gt;

&lt;p&gt;This event could not have happened without the generous support of our sponsors. We initiated contact with potential sponsors at the beginning of the year through our network. We also utilized the AWS Summit in Berlin to engage with various companies. These activities were tracked in &lt;a href=&quot;http://notion.so&quot;&gt;Notion&lt;/a&gt; to keep everyone informed about which companies we contacted and their responses. A sponsor deck that explained the event’s value and offered an overview of different packages (Bronze, Silver, and Gold) played a crucial role in these conversations.&lt;/p&gt;

&lt;p&gt;To manage contracts and finances, we established the &lt;a href=&quot;https://www.aws-community.de/club&quot;&gt;Förderverein AWS Community DACH e.V.&lt;/a&gt; (Booster Club) in 2022. This community-driven organization serves as a vital support system for the AWS Community in the DACH region. It is an official legal entity registered in Munich, Germany, dedicated to facilitating engagement, knowledge-sharing, and networking opportunities.&lt;/p&gt;

&lt;h3 id=&quot;registration&quot;&gt;Registration&lt;/h3&gt;

&lt;p&gt;For the registration process, we developed our &lt;a href=&quot;https://www.events.aws-community.de&quot;&gt;own platform&lt;/a&gt;. Why? Because we are developers 😄. The idea was to create an application using the latest AWS services that would be in real use, not just a demo. (Thanks to &lt;a href=&quot;https://twitter.com/hoegertn&quot;&gt;Thorsten&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/andreas_ruetten&quot;&gt;Andreas&lt;/a&gt;). Currently, it covers registration and allows us to print badges. There’s more in the pipeline, and you might hear about it in future talks.&lt;/p&gt;

&lt;h2 id=&quot;during-the-event&quot;&gt;During the event&lt;/h2&gt;

&lt;h3 id=&quot;vip--speakers-dinner&quot;&gt;VIP / Speakers Dinner&lt;/h3&gt;

&lt;p&gt;With the support of our sponsors, we organized a VIP Dinner that brought together folks from RedHat (our sponsor), AWS, the organizers, and, of course, our speakers! Almost all were able to join, and we had a lot of fun and engaging discussions. Having all these key people in one place is quite rare and a valuable opportunity for sponsors.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/speakers-dinner.jpg&quot; alt=&quot;Speakers Dinner&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;aws-community-day-dahoam&quot;&gt;AWS Community Day Dahoam&lt;/h3&gt;

&lt;p&gt;For the event itself, we needed many volunteers. We required people for registration, swag distribution, track hosts, and general setup and teardown teams. We also had dedicated teams for capturing video and pictures of the event and handling social media. We used a Google Sheet to keep track of who was responsible for what.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;In Bavaria, &lt;em&gt;Dahoam&lt;/em&gt; means “at home.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another great idea was hosting an AWS BuilderCards game and setting up a separate room for speakers to prepare their talks.&lt;/p&gt;

&lt;p&gt;We prepared a feedback system and encouraged the audience to share their insights during the day. This feedback is vital for both speakers and organizers to enhance future events.&lt;/p&gt;

&lt;h2 id=&quot;afterwards&quot;&gt;Afterwards&lt;/h2&gt;
&lt;p&gt;The booster club holds fortnightly meetings that we used to sync for the event (but also other topics). After Community Day, we also used it as a retrospective to discuss the event and generate action items for next year.&lt;/p&gt;

&lt;h2 id=&quot;thank-you-to-the-community&quot;&gt;Thank you to the Community&lt;/h2&gt;

&lt;p&gt;We extend our heartfelt gratitude to all the individuals involved in organizing this event: Thorsten Höger, Markus Ostertag, Johannes Koch, Linda Mohamed, Andreas Rütten, Stefan Bauer, Stefan, Tom Lorenz, Sven Seiler, Alexander Kroll, Steffen Mazanek, and also to the invaluable support from AWS: Eva Plischke, Maria Encinar, Farrah Campbell, Tayler Jacobsen, and of course, all the others I may have inadvertently omitted!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/cd-organizer.jpg&quot; alt=&quot;Community Day Organizers&quot; /&gt;&lt;/p&gt;

</description>
        <pubDate>Fri, 15 Sep 2023 06:00:00 +0000</pubDate>
        <link>https://garbe.io/blog/2023/09/15/aws-community-day-behind-scenes/</link>
        <guid isPermaLink="true">https://garbe.io/blog/2023/09/15/aws-community-day-behind-scenes/</guid>
        
        
        <category>AWS</category>
        
      </item>
    
      <item>
        <title>How I write TypeScript lambdas in CDK</title>
        <description>&lt;p&gt;In this blog post, I’ll share my personal approach to developing TypeScript lambdas using CDK. Join me as I walk you through the tools and techniques I use to make my lambda functions production-ready.&lt;/p&gt;

&lt;p&gt;TL;DR:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I utilize &lt;a href=&quot;https://github.com/projen/projen&quot;&gt;Projen&lt;/a&gt; for project setup.&lt;/li&gt;
  &lt;li&gt;I rely on &lt;a href=&quot;https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda_nodejs.NodejsFunction.html&quot;&gt;NodeJsFunction&lt;/a&gt; for building TypeScript lambda functions in CDK.&lt;/li&gt;
  &lt;li&gt;I write unit tests using &lt;a href=&quot;https://jestjs.io&quot;&gt;Jest&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;For debugging, I make use of &lt;a href=&quot;https://aws.amazon.com/blogs/developer/increasing-development-speed-with-cdk-watch/&quot;&gt;HotSwap&lt;/a&gt; and PersonalStacks.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.aws.amazon.com/cdk/api/v2/docs/integ-tests-alpha-readme.html&quot;&gt;CDK Integ Tests&lt;/a&gt; are performed using CDK Integ Tests.&lt;/li&gt;
  &lt;li&gt;I ensure safe rollouts with the help of &lt;a href=&quot;https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.triggers-readme.html&quot;&gt;Triggers&lt;/a&gt; and &lt;a href=&quot;https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.FeatureFlags.html&quot;&gt;FeatureFlags&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let’s dive into an example to see these concepts in action.&lt;/p&gt;

&lt;h2 id=&quot;project-setup-with-projen&quot;&gt;Project setup with Projen&lt;/h2&gt;
&lt;p&gt;I usually start by leveraging the power of &lt;a href=&quot;https://github.com/projen/projen&quot;&gt;Projen&lt;/a&gt; to create a basic project structure. To generate a project with Projen, run the following command:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npx projen new awscdk-app-ts &lt;span class=&quot;nt&quot;&gt;--projenrcts&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By default, Projen sets up the project to generate an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;awscdk.LambdaFunction&lt;/code&gt; for each *.lambda.ts file in the repository. While this can be advantageous for defining common properties for all functions (e.g., Runtime), I personally prefer to disable it in my .projenrc.ts file to have more control over individual functions:&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;awscdk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AwsCdkTypeScriptApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;....&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;lambdaAutoDiscover&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;infrastructure-in-cdk&quot;&gt;Infrastructure in CDK&lt;/h2&gt;

&lt;p&gt;When it comes to building the infrastructure in CDK, the NodeJsFunction construct is often sufficient for my needs. It utilizes &lt;a href=&quot;https://esbuild.github.io&quot;&gt;esbuild&lt;/a&gt; to package the lambda code into a zip file, which is then added as a &lt;a href=&quot;https://docs.aws.amazon.com/cdk/v2/guide/assets.html&quot;&gt;CDK Asset&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s an example of a stack that creates a lambda function responsible for writing events into an S3 Bucket:&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;MyStack&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Stack&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// used later for integration tests&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;lambdaFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;aws_lambda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;IFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;aws_s3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;IBucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Construct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;StackProps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;aws_s3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MyBucket&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;removalPolicy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;RemovalPolicy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;DESTROY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;autoDeleteObjects&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// define resources here...&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lambdaFunction&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cdk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;aws_lambda_nodejs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;NodejsFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MyFunction&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;BUCKET_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bucketName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Grant the lambda access to the bucket&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;grantWrite&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;lambdaFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By default, the NodejsFunction looks for a file named &lt;em&gt;mystack.myfunction.ts&lt;/em&gt; (assuming MyStack lives in mystack.ts). However, you can easily configure it to point to another file or add additional runtime and environment variables as needed.&lt;/p&gt;

&lt;h2 id=&quot;development-and-unit-testing&quot;&gt;Development and Unit Testing&lt;/h2&gt;

&lt;p&gt;I find it valuable to treat a lambda function as a simple &lt;strong&gt;input -&amp;gt; process -&amp;gt; output&lt;/strong&gt; model, making it easier to write testable code. By utilizing the appropriate type definitions (such as @types/aws-lambda), I can create well-typed unit tests for my lambdas.&lt;/p&gt;

&lt;p&gt;Add the following dependency in your projenrc.ts:&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;awscdk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AwsCdkTypeScriptApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;....&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;deps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@types/aws-lambda&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now I can define the handler. For the sake of a better example I decided for a CloudFormation custom resource implementation that writes the event as JSON into a given S3 Bucket.&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;CloudFormationCustomResourceEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CloudFormationCustomResourceResponse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Run some initialization code&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;bucketName&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;BUCKET_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;handleEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;bucketName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Have a separate function to test without handling dependencies&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;handleEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;CloudFormationCustomResourceEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CloudFormationCustomResourceResponse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;handleEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;CloudFormationCustomResourceEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CloudFormationCustomResourceResponse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// My first, very simple, implementation&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;SUCCESS&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;PhysicalResourceId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MyUniqueId&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;StackId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;StackId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;RequestId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;RequestId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;LogicalResourceId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;LogicalResourceId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You might have noticed that the actual handling of the event happens in a separate function called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;handleEvent(...)&lt;/code&gt;. With that, I can separate some initialization logic, like reading from ENV, from my actual business logic and make the latter more testable.&lt;/p&gt;

&lt;p&gt;The corresponding unit test can be created first or at the same time. I am not that ideomatic. Usually, I have a split view in my IDE with the implementation on the left and tests on the right side.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Pro tip: Start with (all kind of) tests as early as possible!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Writing test as early as possible has two advantages:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;It helps to create more tests (just copy &amp;amp; &lt;del&gt;paste&lt;/del&gt; adopt)&lt;/li&gt;
  &lt;li&gt;It prevents your code to become untestable which makes it frustrating when you have to add tests later&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;CloudFormationCustomResourceCreateEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;aws-lambda&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;handleEvent&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;../src/main.myfunction&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MyFunction Lambda&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;will sends a success response&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;CloudFormationCustomResourceCreateEvent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;RequestType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ServiceToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ResponseURL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;StackId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;RequestId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;LogicalResourceId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ResourceType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ResourceProperties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;ServiceToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;handleEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MyBucketName&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toStrictEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;LogicalResourceId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;PhysicalResourceId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MyUniqueId&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;RequestId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;StackId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;SUCCESS&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

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

&lt;p&gt;Now comes the interesting part: The integration with AWS. I don’t want to deal with AWS (or any other external dependency) in my Unittest. That’s why I mock the calls to the AWS API.&lt;/p&gt;

&lt;p&gt;Create a new file ./src/aws-adapter.ts with the follwing implementation:&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;putObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;bucketName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;not implemented&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The test looks like this:&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;adapter&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;../src/aws-adapter&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;will write the event to S3 bucket&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;CloudFormationCustomResourceCreateEvent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Create a mock for the function in the adapter &lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;putObjMock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;jest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;spyOn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;adapter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;putObject&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mockImplementation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;handleEvent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MyBucketName&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Now I can check if the function has been called as expected&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;putObjMock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toBeCalledTimes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;putObjMock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toBeCalledWith&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

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

&lt;p&gt;Now add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;await putObject(event, bucketName);&lt;/code&gt; to the implementation and that’s it. The tests are green 🎉&lt;/p&gt;

&lt;p&gt;But what about the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;putObject()&lt;/code&gt; implementation? This can be done with some integration tests as not only the implementation needs to be tested but also other runtime dependencies and configuration like IAM permissions.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://refactoring.guru/design-patterns/adapter&quot;&gt;Adapter-Pattern&lt;/a&gt; I used here has the advantage that it&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;keeps the code clean and moves all dependency-related stuff outside&lt;/li&gt;
  &lt;li&gt;makes actual business logic testable&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;watch--hotswap-with-personalstacks&quot;&gt;Watch &amp;amp; HotSwap with PersonalStacks&lt;/h2&gt;
&lt;p&gt;To streamline local development and testing, I rely on the &lt;em&gt;PersonalStack&lt;/em&gt; approach. This allows me to deploy a personalized stack within the same AWS account/region without interfering with other stages of development.&lt;/p&gt;

&lt;p&gt;The cdk watch command, combined with the –hotswap option, automatically triggers redeployment whenever a file changes, providing rapid feedback during the development process. It’s important to note that this option should not be used in a production pipeline.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;My application runs in the Cloud, so why should I mock it locally? That’s why I have a PersonalStack (and not LocalStack 😉)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Create a new personalized stack with a unique stack name. This avoids any conflict with stacks for your dev and prod stages.&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;MyStack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;MyStackPersonal&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;stackName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`MyStackPersonal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Case&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pascal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;devEnv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;cm&quot;&gt;/* other stack configuration */&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Use the cdk watch command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npx cdk deploy --watch --hotswap-fallback MyStackPersonal&lt;/code&gt; to trigger an initial deployment. It watches your local files for changes and starts a new deployment automatically. With the &lt;a href=&quot;https://aws.amazon.com/blogs/developer/increasing-development-speed-with-cdk-watch/&quot;&gt;–hotswap&lt;/a&gt; option it replaces the resources directly without triggering a full CloudFormation deployment. This speeds up the feedback loop, with the cost of a stack drift. It works for resources like Lambda, StepFunctions, or ECS Services.&lt;/p&gt;

&lt;p&gt;Lets finish the implementation of the aws-adapter:&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;putObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;bucketName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s3Client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;S3Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({});&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s3Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;s3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;PutObjectCommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;bucketName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;events.json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}));&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Try it out and test your lambda function in the AWS Console. You will notice that logs are also shown in your CLI.&lt;/p&gt;

&lt;h2 id=&quot;integration-tests&quot;&gt;Integration Tests&lt;/h2&gt;

&lt;p&gt;While unit tests are crucial for verifying the correctness of individual functions, integration tests validate the functionality of the entire lambda function within an AWS environment. To perform integration testing, I make use of the &lt;a href=&quot;https://docs.aws.amazon.com/cdk/api/v2/docs/integ-tests-alpha-readme.html&quot;&gt;integration tests&lt;/a&gt; package.&lt;/p&gt;

&lt;p&gt;It requires some changes in projenrc.ts file:&lt;/p&gt;

&lt;div class=&quot;language-ts highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;project&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;awscdk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;AwsCdkTypeScriptApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Configuration for integration tests&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addDevDeps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@aws-cdk/integ-tests-alpha&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@aws-cdk/integ-runner&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;gitignore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addPatterns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;cdk-integ.out.*.snapshot&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;integ:update&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;npx integ-runner --parallel-regions eu-central-1 --directory ./test --language typescript --update-on-failed&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;integ&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;integ&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;npx integ-runner --parallel-regions eu-central-1 --directory ./test --language typescript&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;testTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;spawn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;integ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;synth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Explaining integration tests is beyond the scope of this article but the &lt;a href=&quot;https://docs.aws.amazon.com/cdk/api/v2/docs/integ-tests-alpha-readme.html&quot;&gt;CDK docs&lt;/a&gt; are a good starting point.&lt;/p&gt;

&lt;h2 id=&quot;deployment&quot;&gt;Deployment&lt;/h2&gt;

&lt;p&gt;To ensure safe deployments, I leverage &lt;a href=&quot;https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.triggers-readme.html&quot;&gt;CDK Triggers&lt;/a&gt; and &lt;a href=&quot;https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.FeatureFlags.html&quot;&gt;Feature Flags&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Triggers can call a lambda function as part of your CloudFormation deployment. For lambdas without side effects I trigger them directly to see if they work. But it’s also possible to create a separate lambda function that does some smoke tests to verify your application. The good part here is that if it fails, the stack gets automatically rolled back and you’re back to a safe state of your application.&lt;/p&gt;

&lt;p&gt;I use the built-in Feature Flags in CDK to toggle a new implementation. This is very handy when writing CDK Libs to roll out a new feature or improvement and let users opt-in by enabling the feature flag. Later you can invert it and allow users to opt-out before you completely remove the flag.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Often product mangers and other stake holders try to talk us into the way we program. “Testing is not necessary”, “we need to deliver the feature ASAP”, “can’t we do it faster?” But would an electrician actually refrain from installing fuses just because the customer wants it? The same is true for us software developers. It should be a matter of course to deliver code only with appropriate tests and a continuous deployment capable pipeline.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://twitter.com/pgarbe&quot;&gt;Let me know&lt;/a&gt; how you are doing it!&lt;/p&gt;
</description>
        <pubDate>Fri, 23 Jun 2023 06:00:00 +0000</pubDate>
        <link>https://garbe.io/blog/2023/06/23/how-i-write-typescript-lambda/</link>
        <guid isPermaLink="true">https://garbe.io/blog/2023/06/23/how-i-write-typescript-lambda/</guid>
        
        
        <category>AWS</category>
        
      </item>
    
      <item>
        <title>AWS ControlTower Troubleshooting Tips</title>
        <description>&lt;p&gt;AWS ControlTower is a powerful tool for centrally managing multiple AWS accounts. However, troubleshooting ControlTower issues can be challenging due to the complex architecture it relies on. This article provides some tips for resolving common ControlTower problems.&lt;/p&gt;

&lt;p&gt;Firstly, it’s important to stay calm if something goes wrong. ControlTower errors usually do not affect production systems, and resources and settings are typically still deployed in member accounts despite any errors.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This article is not the ultimate source of truth, but reflects my current understanding. I’ll update it accordingly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;what-you-need-to-understand&quot;&gt;What you need to understand&lt;/h3&gt;
&lt;p&gt;To understand what ControlTower is, check out the official &lt;a href=&quot;https://aws.amazon.com/controltower/features/&quot;&gt;product page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Essentially, ControlTower is built on top of CloudFormation Stacks, StackSets, and ServiceCatalog Products. The Account Factory in ControlTower is just an interface for ServiceCatalog. When the ServiceCatalog Product “AWS ControlTower Account Factory” is created, a member account is launched, and the account is added to the StackSets deployed by ControlTower.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/control-tower.png&quot; alt=&quot;ControlTower and dependend services&quot; /&gt;
(Note, that this diagram focus only on a part of ControlTower)&lt;/p&gt;

&lt;h3 id=&quot;general-troubleshooting&quot;&gt;General Troubleshooting&lt;/h3&gt;
&lt;p&gt;When troubleshooting ControlTower issues, look for more detailed error messages in the following places:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Service Catalog Product&lt;/strong&gt;&lt;br /&gt;
Check the Provisioned Products for error details.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;StackSets&lt;/strong&gt;&lt;br /&gt;
Check the status of StackSet stack instances for any problems. This helps also to identify which account makes the trouble.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;AWS (Member) Account&lt;/strong&gt;&lt;br /&gt;
For issues with specific accounts, check CloudFormation Stacks within the account (usually starting with “StackSet-AWSControlTowerBP-BASELINE”). Change the filter also to &lt;em&gt;Deleted&lt;/em&gt; to see if the creation of the Stack has failed.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Changes to an organizational unit (OU), like re-registering, will affect all accounts and if one fails all accounts will be shown as tainted.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;protect-cloudformation-stacks&quot;&gt;Protect CloudFormation Stacks&lt;/h3&gt;
&lt;p&gt;Although ControlTower protects its resources by Service Control Policies (SCP), it does not protect CloudFormation stacks. You can delete them, but as the resources can’t be deleted it ends up in a &lt;em&gt;DELETE_FAILED&lt;/em&gt; state which makes it impossible for ControlTower to fix it.&lt;/p&gt;

&lt;p&gt;To protect CloudFormation Stacks add the following SCP to your OUs:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;sid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;DenyModifyControlTowerStacks&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;effect&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;DENY&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;actions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;cloudformation:Delete*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;cloudformation:Update*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;cloudformation:Create*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;resources&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;arn:aws:cloudformation:*:*:stack/StackSet-AWSControlTower*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;conditions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ArnNotLike&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;aws:PrincipalARN&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;arn:aws:iam::*:role/AWSControlTowerExecution&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;fix-tainted-accounts&quot;&gt;Fix Tainted Accounts&lt;/h3&gt;
&lt;p&gt;If the account enrollment failed, you might need to clean up the resources of the StackSet stacks manually to be able to delete the stack completely and let ControlTower re-create it. As the resources are protected you need to assume the &lt;em&gt;AWSControlTowerExecution&lt;/em&gt; role from a role (or user) in the main account. Use the following command to assume the role:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# To assume the AWSControlTowerExecution role you need to log in to your main account first&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;printf&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;aws sts assume-role &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--role-arn&lt;/span&gt; arn:aws:iam::123456789012:role/AWSControlTowerExecution &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--role-session-name&lt;/span&gt; MySessionName &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Credentials.[AccessKeyId,SecretAccessKey,SessionToken]&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--output&lt;/span&gt; text&lt;span class=&quot;si&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here are a few scripts that can help:&lt;/p&gt;

&lt;h4 id=&quot;stackset-awscontroltowerbp-baseline-roles&quot;&gt;StackSet-AWSControlTowerBP-BASELINE-ROLES&lt;/h4&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws iam delete-role &lt;span class=&quot;nt&quot;&gt;--role-name&lt;/span&gt; aws-controltower-AdministratorExecutionRole
aws iam delete-role &lt;span class=&quot;nt&quot;&gt;--role-name&lt;/span&gt; aws-controltower-ReadOnlyExecutionRole
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;stackset-awscontroltowerbp-baseline-service-roles&quot;&gt;StackSet-AWSControlTowerBP-BASELINE-SERVICE-ROLES&lt;/h4&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws iam delete-role &lt;span class=&quot;nt&quot;&gt;--role-name&lt;/span&gt; aws-controltower-ConfigRecorderRole
aws iam delete-role &lt;span class=&quot;nt&quot;&gt;--role-name&lt;/span&gt; aws-controltower-ForwardSnsNotificationRole
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;stackset-awscontroltowerbp-baseline-config&quot;&gt;StackSet-AWSControlTowerBP-BASELINE-CONFIG&lt;/h4&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws configservice delete-configuration-recorder &lt;span class=&quot;nt&quot;&gt;--configuration-recorder-name&lt;/span&gt; aws-controltower-BaselineConfigRecorder
aws configservice delete-delivery-channel &lt;span class=&quot;nt&quot;&gt;--delivery-channel-name&lt;/span&gt; aws-controltower-BaselineConfigDeliveryChannel
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;stackset-awscontroltowerbp-baseline-cloudwatch&quot;&gt;StackSet-AWSControlTowerBP-BASELINE-CLOUDWATCH&lt;/h4&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws events remove-targets &lt;span class=&quot;nt&quot;&gt;--rule&lt;/span&gt; aws-controltower-ConfigComplianceChangeEventRule &lt;span class=&quot;nt&quot;&gt;--ids&lt;/span&gt; Compliance-Change-Topic
aws events delete-rule &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; aws-controltower-ConfigComplianceChangeEventRule

aws sns delete-topic &lt;span class=&quot;nt&quot;&gt;--topic-arn&lt;/span&gt; arn:aws:sns:eu-central-1:123456789012:aws-controltower-SecurityNotifications

aws lambda delete-function &lt;span class=&quot;nt&quot;&gt;--function-name&lt;/span&gt; aws-controltower-NotificationForwarder
aws logs delete-log-group &lt;span class=&quot;nt&quot;&gt;--log-group-name&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/aws/lambda/aws-controltower-NotificationForwarder&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;!-- 
Error: Account enrollment failed.

Error
AWS ControlTower failed to set up your landing zone completely: The AWS Config aggregation authorization for audit account 123456789012 and home region eu-central-1 was not found under the target account 123456789012 and target region eu-central-1. It may have been deleted outside of AWS ControlTower. To continue, delete the AWSControlTowerBP-BASELINE-CONFIG stack set instance for target account 123456789012 and target region eu-central-1. Then, try again. Learn more

Account enrollment failed.
AWS ControlTower could not enroll your account for the following reason: AWS STS isn&apos;t activated in some regions for the following accounts: 513899454231. Ask your account administrator to activate AWS STS from the IAM console in each account for the following regions: eu-west-1, eu-central-1, us-east-1. After STS is enabled in those regions, delete the failed stack instances in those accounts: null.

Error: Registration failed for &lt;OU&gt;
AWS ControlTower detects issues that prevent registering this organizational unit. Try again, or contact AWS Support. 
--&gt;
</description>
        <pubDate>Tue, 11 Apr 2023 06:00:00 +0000</pubDate>
        <link>https://garbe.io/blog/2023/04/11/control-tower-troubleshooting-tips/</link>
        <guid isPermaLink="true">https://garbe.io/blog/2023/04/11/control-tower-troubleshooting-tips/</guid>
        
        
        <category>AWS</category>
        
      </item>
    
      <item>
        <title>Hey CDK, how should I handle dependencies?</title>
        <description>&lt;p&gt;At some point, every CDK developer has to deal with dependencies. While it’s easy within CDK Applications, it is more complicated when writing CDK Libraries. In this article, I will show how to manage dependencies in CDK Applications and Libraries written in TypeScript.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This is another part of my &lt;a href=&quot;https://garbe.io/category/cdk/&quot;&gt;‘Hey CDK’&lt;/a&gt; series.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;cdk-applications&quot;&gt;CDK Applications&lt;/h2&gt;
&lt;p&gt;In a CDK Application, you don’t have to care too much about dependencies. Put libraries you need to build/test your application as &lt;a href=&quot;https://docs.npmjs.com/cli/v6/configuring-npm/package-json#devdependencies&quot;&gt;devDependencies&lt;/a&gt; and libraries that are used to run the code as &lt;a href=&quot;https://docs.npmjs.com/cli/v6/configuring-npm/package-json#dependencies&quot;&gt;dependencies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/cdk-dependencies-app.png&quot; alt=&quot;Dependencies of a CDK Application&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Within the &lt;em&gt;package.json&lt;/em&gt; file use &lt;a href=&quot;https://docs.npmjs.com/about-semantic-versioning#using-semantic-versioning-to-specify-update-types-your-package-can-accept&quot;&gt;semantic versioning (SemVer)&lt;/a&gt; to specify the version you expect. For example, to specify acceptable version ranges up to 2.60.4, use the following syntax:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Range&lt;/th&gt;
      &lt;th&gt;Syntax&lt;/th&gt;
      &lt;th&gt;Example&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Patch releases&lt;/td&gt;
      &lt;td&gt;2.60 or 2.60.x or ~2.60.4&lt;/td&gt;
      &lt;td&gt;2.60.1 ✅ 2.61.0 ❌ 3.0.0 ❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Minor releases&lt;/td&gt;
      &lt;td&gt;2 or 2.x or ^2.60.4&lt;/td&gt;
      &lt;td&gt;2.60.1 ✅ 2.61.0 ✅ 3.0.0 ❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Major releases&lt;/td&gt;
      &lt;td&gt;* or x&lt;/td&gt;
      &lt;td&gt;2.60.1 ✅ 2.61.0 ✅ 3.0.0 ✅&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The &lt;em&gt;package.json&lt;/em&gt; just defines the requirements you have on your dependency. Be as open as possible to not exclude newer versions.&lt;/p&gt;

&lt;p&gt;The actual version of your dependency is stored in the lock file (yarn.lock or package-lock.json). With your pipeline run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm ci&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yarn install --frozen-lockfile&lt;/code&gt; to install exact the version as defined in the lock file.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Be aware that by running just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm install&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yarn install&lt;/code&gt; existing packages in node_modules are not touched but missing packages are downloaded and the lock file could be updated.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;cdk-libraries&quot;&gt;CDK Libraries&lt;/h2&gt;
&lt;p&gt;In CDK Libraries it’s more important how dependencies are defined as it affects all applications that are using this library.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/cdk-dependencies-lib.png&quot; alt=&quot;Dependencies of a CDK Library&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In this picture, you see a CDK Library that depends on aws-cdk-lib. But also a CDK Application that depends on both, the CDK Library and the AWS CDK (aws-cdk-lib). Can you already guess the possible conflicts?&lt;/p&gt;

&lt;h3 id=&quot;dev-dependencies&quot;&gt;Dev Dependencies&lt;/h3&gt;
&lt;p&gt;Like for a CDK Application, add libraries you need to build or test your library under &lt;a href=&quot;https://docs.npmjs.com/cli/v6/configuring-npm/package-json#devdependencies&quot;&gt;devDependencies&lt;/a&gt;. These dependencies will be ignored by users of your library.&lt;/p&gt;

&lt;h3 id=&quot;dependencies&quot;&gt;Dependencies&lt;/h3&gt;
&lt;p&gt;All direct and indirect dependencies must be resolved within the CDK App. This works as long as there are no conflicts. But both, the App and the Library will likely depend on the AWS CDK. Let’s say the &lt;em&gt;CDK Application&lt;/em&gt; expects v2.45.0 and the &lt;em&gt;CDK Library&lt;/em&gt; depends on v.2.60.0. Which version should be used?&lt;/p&gt;

&lt;p&gt;There are two ways how to solve it:&lt;/p&gt;

&lt;h4 id=&quot;bundled-dependencies&quot;&gt;Bundled Dependencies&lt;/h4&gt;
&lt;p&gt;Bundled dependency means the package of the dependent library is packed as part of your npm package. This also includes dependent packages of your package. You will see a nested &lt;em&gt;node_modules&lt;/em&gt; folder in your package content.&lt;/p&gt;

&lt;p&gt;A disadvantage of this approach is that it increases the size of your package. And the user of your library must rely on you to bundle the latest version as they can’t upgrade that dependency to a newer version (which is useful for security fixes).&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Use bundled dependencies &lt;strong&gt;only&lt;/strong&gt; when you require a specific version.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the &lt;em&gt;package.json&lt;/em&gt; add the library as &lt;a href=&quot;https://docs.npmjs.com/cli/v6/configuring-npm/package-json#dependencies&quot;&gt;dependencies&lt;/a&gt; with a version (see SemVer above) and &lt;a href=&quot;https://docs.npmjs.com/cli/v6/configuring-npm/package-json#bundleddependencies&quot;&gt;bundledDependencies&lt;/a&gt; (just the name of the package without version information).&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;CDKLibrary&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;dependencies&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;libB&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^1.0.5&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;bundledDependencies&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;libB&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;peer-dependencies&quot;&gt;Peer Dependencies&lt;/h4&gt;
&lt;p&gt;The &lt;a href=&quot;https://docs.npmjs.com/cli/v6/configuring-npm/package-json#peerdependencies&quot;&gt;peerDependencies&lt;/a&gt; tell the application that the library depends on other libraries. It delegates the decision of which exact version should be used for the CDK Application. Here it is important to not be too restrictive to give the user of your library more choices if they want to use the same library in a different version.&lt;/p&gt;

&lt;p&gt;In the &lt;em&gt;package.json&lt;/em&gt; add the library as &lt;a href=&quot;https://docs.npmjs.com/cli/v6/configuring-npm/package-json#devdependencies&quot;&gt;devDependencies&lt;/a&gt; with the exact minimum version you support and as &lt;a href=&quot;https://docs.npmjs.com/cli/v6/configuring-npm/package-json#peerdependencies&quot;&gt;peerDependencies&lt;/a&gt; with a range of possible versions that might also work (provided that SemVer rules are respected).&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;CDKLibrary&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;devDependencies&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;aws-cdk-lib&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2.45.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;peerDependencies&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;aws-cdk-lib&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;^2.45.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;answer&quot;&gt;Answer&lt;/h3&gt;
&lt;p&gt;Dependencies are handled a bit differently in CDK Applications and Libraries.&lt;/p&gt;

&lt;p&gt;For CDK Applications it’s easy. Add dependencies you need to build or test your code as &lt;a href=&quot;https://docs.npmjs.com/cli/v6/configuring-npm/package-json#devdependencies&quot;&gt;devDependencies&lt;/a&gt; and dependencies you need to run your code as &lt;a href=&quot;https://docs.npmjs.com/cli/v6/configuring-npm/package-json#dependencies&quot;&gt;dependencies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In a Library, the same rules apply for &lt;a href=&quot;https://docs.npmjs.com/cli/v6/configuring-npm/package-json#devdependencies&quot;&gt;devDependencies&lt;/a&gt; but when it comes to dependencies you need to run your code you can either bundle them or define them as a peer dependencies. The correct versioning is important here.&lt;/p&gt;
</description>
        <pubDate>Thu, 19 Jan 2023 06:00:00 +0000</pubDate>
        <link>https://garbe.io/blog/2023/01/19/hey-cdk-how-to-handle-dependencies/</link>
        <guid isPermaLink="true">https://garbe.io/blog/2023/01/19/hey-cdk-how-to-handle-dependencies/</guid>
        
        
        <category>AWS</category>
        
        <category>CDK</category>
        
      </item>
    
  </channel>
</rss>
