<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Markus Loupeen</title>
    <description>The latest articles on Forem by Markus Loupeen (@kronis).</description>
    <link>https://forem.com/kronis</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F502049%2F6d1caf50-03e4-469e-8faa-0c6884b8e6ec.jpeg</url>
      <title>Forem: Markus Loupeen</title>
      <link>https://forem.com/kronis</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kronis"/>
    <language>en</language>
    <item>
      <title>Managing multiple Application and Deployments in AWS CDK</title>
      <dc:creator>Markus Loupeen</dc:creator>
      <pubDate>Sun, 26 Mar 2023 11:18:43 +0000</pubDate>
      <link>https://forem.com/aws-builders/managing-multiple-application-and-deployments-in-aws-cdk-269b</link>
      <guid>https://forem.com/aws-builders/managing-multiple-application-and-deployments-in-aws-cdk-269b</guid>
      <description>&lt;p&gt;In my previous post I talked about how we manage our AWS accounts in CDK, now it is time to see how we manage applications first, and after that, we have a look at the deployments. &lt;br&gt;
Just AwsAccounts class we use a class Application. One Application is one Stack in one Git Repository.&lt;br&gt;
Here is an example for four applications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface ApplicationProps {
  name: string;
  repositoryName: string;
  stackName: string;
}

interface ApplicationObject {
  readonly name: string;
  readonly repositoryName: string;
  readonly stackName: string;
}

export default class Application {
  private static readonly applicationsArray: ApplicationObject[] = [];

  /* Account setup */
  public static readonly DNS = new Application({ name: 'dns', repositoryName: 'aws-dns', stackName: 'AwsDnsStack' });
  public static readonly SES = new Application({ name: 'ses', repositoryName: 'aws-ses', stackName: 'AwsSesPipelineStack' });

  /* Project */
  public static readonly COGNITO = new Application({ name: 'cognito', repositoryName: 'projectA-cognito-admin', stackName: 'CognitoAdminStack' });
  public static readonly API_GATEWAY = new Application({ name: 'apigateway', repositoryName: 'projectB-gateway', stackName: 'GatewayStack' });

  readonly name: string;
  readonly repositoryName: string;
  readonly stackName: string;

  private constructor({ name, repositoryName, stackName }: ApplicationProps) {
    this.name = name;
    this.repositoryName = repositoryName;
    this.stackName = stackName;

    Application.applicationsArray.push(this);
  }

  public static get applications(): ApplicationObject[] {
    return [...Application.applicationsArray];
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here you can see the four applications; DNS, SES, COGNITO and API_GATEWAY.&lt;br&gt;
Now it is time to put them into our Deployment class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Application from '../applicaitons/application';
import { AwsAccount } from '../aws-accounts/awsAccounts';

interface DeploymentProps {
  readonly name: string;
  readonly accounts: AwsAccount[];
  readonly applications: Application[];
}

interface DeploymentObject {
  readonly name: string;
  readonly accounts: AwsAccount[];
  readonly applications: Application[];
}

export default class Deployment {
  private static readonly deploymentsArray: DeploymentObject[] = [];

  public static readonly ACCOUNTS_SETUP = new Deployment({
    name: 'AccountSetup',
    accounts: [AwsAccount.PROJECT_A_TEST, AwsAccount.PROJECT_A_QA, AwsAccount.PROJECT_B_TEST, AwsAccount.PROJECT_B_QA, AwsAccount.PROJECT_B_PROD],
    applications: [Application.DNS, Application.SES],
  });

  public static readonly PROJECT_A = new Deployment({
    name: 'TheGame',
    accounts: [AwsAccount.PROJECT_A_TEST, AwsAccount.PROJECT_A_QA],
    applications: [Application.COGNITO],
  });

  public static readonly PROJECT_B = new Deployment({
    name: 'TheGame',
    accounts: [AwsAccount.PROJECT_B_TEST, AwsAccount.PROJECT_B_QA, AwsAccount.PROJECT_B_PROD],
    applications: [Application.API_GATEWAY],
  });

  readonly name: string;

  readonly accounts: AwsAccount[];

  readonly applications: Application[];

  private constructor({ name, accounts, applications }: DeploymentProps) {
    this.name = name;
    this.accounts = accounts;
    this.applications = applications;

    Deployment.deploymentsArray.push(this);
  }

  public static get deployments(): DeploymentObject[] {
    return [...Deployment.deploymentsArray];
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Still no strange stuff. &lt;br&gt;
DNS and SES should be deployed to all five accounts, while COGNITO goes to Project A´s TEST and QA accounts and API_GATEWAY will be deployed to Project B´s TEST, QA and PROD accounts. &lt;br&gt;
Now, how does our pipeline for this look like? &lt;br&gt;
This is our stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const app = new cdk.App();

new PipelinesStack(app, 'PipelinesSetup', {
  env: { account: AwsAccount.CICD.accountId, region: 'eu-north-1' },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default class PipelinesStack extends Stack {
  constructor(scope: Construct, id: string, props: StackProps) {
    super(scope, id, props);

    const deploymentsSetup = Deployment.ACCOUNT_SETUP;
    const deploymentsProjA = Deployment.PROJECT_A;
    const deploymentsProjB = Deployment.PROJECT_B;

    new DeploymentsStack(this, `Pipeline-${deploymentsSetup.name}-Pipelines`, {
      deployments: deploymentsSetup,
    });

    new DeploymentsStack(this, `Pipeline-${deploymentsGame.name}-Pipelines`, {
      deployments: deploymentsProjA,
    });

    new DeploymentsStack(this, `Pipeline-${deploymentsGame.name}-Pipelines`, {
      deployments: deploymentsProjB,
    });
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And for the DeployStack, it can be found here:&lt;br&gt;
&lt;a href="https://gist.github.com/kronis/de876999f725275f0ab92ed9f0ef5bf6"&gt;https://gist.github.com/kronis/de876999f725275f0ab92ed9f0ef5bf6&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cdk</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Managing multiple AWS Accounts and Environments with CDK.</title>
      <dc:creator>Markus Loupeen</dc:creator>
      <pubDate>Sun, 26 Feb 2023 22:19:40 +0000</pubDate>
      <link>https://forem.com/aws-builders/managing-multiple-aws-accounts-and-environments-with-cdk-54bj</link>
      <guid>https://forem.com/aws-builders/managing-multiple-aws-accounts-and-environments-with-cdk-54bj</guid>
      <description>&lt;p&gt;This is just a small post that will be followed up later with pipeline examples.&lt;/p&gt;

&lt;p&gt;We do not want our CDK applications to know much about how and where they are deployed, instead it is all defined in a small private npm package that contains all account information.&lt;/p&gt;

&lt;p&gt;Here is how we have it, so we can import it in multiple projects and pipelines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Annotations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;constructs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DeploymentRegion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;EU_NORTH_1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;eu-north-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;EU_CENTRAL_1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;eu-central-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ApplicationStack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;DEV&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dev&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;TEST&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;QA&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;qa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;PRODUCTION&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ApplicationStackType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;ApplicationStack&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;ApplicationStack&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;DeploymentRegionType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;DeploymentRegion&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;DeploymentRegion&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;AwsAccount&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AwsAccount&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="c1"&gt;// -- Applications --&lt;/span&gt;
  &lt;span class="c1"&gt;// - Production -&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;APP1_PRODUCTION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AwsAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;APP1_Production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;100000000001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ApplicationStack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PRODUCTION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;DeploymentRegion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EU_NORTH_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DeploymentRegion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EU_CENTRAL_1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// - QA -&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;APP1_QA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AwsAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;APP1_QA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;100000000002&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ApplicationStack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;QA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;DeploymentRegion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EU_NORTH_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DeploymentRegion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EU_CENTRAL_1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,);&lt;/span&gt;

  &lt;span class="c1"&gt;// - Test -&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;APP1_TEST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AwsAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;APP1_Test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;100000000003&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ApplicationStack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TEST&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// - Dev -&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;APP1_DEV&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AwsAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;APP1_Dev&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;100000000004&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ApplicationStack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TEST&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;APP2_DEV&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AwsAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;APP2_Dev&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;200000000001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ApplicationStack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TEST&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// -- Aws Core --&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;AUDIT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AwsAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Audit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;900000000010&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ApplicationStack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PRODUCTION&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;LOG_ARCHIVE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AwsAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Log archive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;900000000011&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ApplicationStack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PRODUCTION&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// -- Infrastructure --&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;OPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AwsAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OPS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;000000000001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ApplicationStack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PRODUCTION&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApplicationStackType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;deploymentRegions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DeploymentRegionType&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;DeploymentRegion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EU_NORTH_1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;approveBeforeDeploy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;AwsAccount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;fromScope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;AwsAccount&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accountNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accountId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;accountNumber&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;Annotations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;addError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Couldn't find account [&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] in AwsAccount array!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In next post I will show you how we utilize this in pipelines to make life esier.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cdk</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Using Private GitHub NPM repositories in CodeBuild</title>
      <dc:creator>Markus Loupeen</dc:creator>
      <pubDate>Tue, 13 Sep 2022 19:32:56 +0000</pubDate>
      <link>https://forem.com/aws-builders/using-private-github-npm-repositories-in-codebuild-16b3</link>
      <guid>https://forem.com/aws-builders/using-private-github-npm-repositories-in-codebuild-16b3</guid>
      <description>&lt;p&gt;I use GitHub instead of CodeCommit for many reasons, and I also use GitHub Npm Registry, for private packages. It took me some time to understand how I can use them in CodeBuild, so I want to share it with you. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer, this is maybe not the best (or even correct way) to do this, but this how I managed to solve the problem for me.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;To do this, the secure way I will have to use some magic :) I am using SecretsManager to store a GitHub Personal Token securely, for later using it in the pipelines as an environment variable. We will accomplish this in four easy steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. GitHub - Create token
&lt;/h2&gt;

&lt;p&gt;First, we have to to create a private personal token in Github, you will do that under your settings and Developer Settings. The key needs only access to read:packages.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1Kl2Ii_j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ddo62q3o7byurssnpsac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1Kl2Ii_j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ddo62q3o7byurssnpsac.png" alt="Create Token" width="700" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Save it and make sure to copy the key that is generated for next step. &lt;/p&gt;
&lt;h2&gt;
  
  
  2. AWS Secrets Manager - Add token 
&lt;/h2&gt;

&lt;p&gt;Now we have to add the key to Secrets Manager in AWS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0Uz3i4Et--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gqfejzef11nt2otph1wj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0Uz3i4Et--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gqfejzef11nt2otph1wj.png" alt="Stage 1" width="700" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kzDUKWYb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vlmnb5jlqaew2b773epd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kzDUKWYb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vlmnb5jlqaew2b773epd.png" alt="Stage 2" width="700" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FF9KfeBi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cv7208l94ncqdblvklve.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FF9KfeBi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cv7208l94ncqdblvklve.png" alt="Stage 3" width="700" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Review the details and save. It will result in a key looking like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--53KQKyTI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pwyebg6xv4logzsq1uls.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--53KQKyTI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pwyebg6xv4logzsq1uls.png" alt="Token Saved" width="700" height="762"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Configure NPM
&lt;/h2&gt;

&lt;p&gt;This was the tricky part for me, because I want to use npm login when I am developing on my personal machine, and do not want to share the key with other people. so i had to create two .npmrc files, on for local development (.npmrc) and one for CodeBuild (.npmrc_ci). &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Make sure to change @loupeen to your account/organisation.&lt;/em&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  4. Pipeline
&lt;/h2&gt;

&lt;p&gt;Last thing is to configure our pipelines synth and build step. &lt;br&gt;
First we are getting the secret at line 1, after that we are using it in synth (line 10–17) and build (line 20–27) steps, and last thing we have to do is to use the correct .npmrc file, and that is on line 32. &lt;/p&gt;

&lt;p&gt;CodePipeline example&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
And that is it, hoping it makes some sense.

</description>
      <category>aws</category>
      <category>github</category>
      <category>npm</category>
      <category>cdk</category>
    </item>
    <item>
      <title>AWS CDK Frontend Pipeline (Cross Account)</title>
      <dc:creator>Markus Loupeen</dc:creator>
      <pubDate>Tue, 16 Aug 2022 21:51:10 +0000</pubDate>
      <link>https://forem.com/aws-builders/aws-cdk-frontend-pipeline-cross-account-4na9</link>
      <guid>https://forem.com/aws-builders/aws-cdk-frontend-pipeline-cross-account-4na9</guid>
      <description>&lt;p&gt;For those who just want to see the source and get into it:&lt;br&gt;
&lt;a href="https://github.com/loupeen/aws-cdk-frontend-pipeline"&gt;https://github.com/loupeen/aws-cdk-frontend-pipeline&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ok, I am going to keep this post short. To be honest, frontend pipelines can be crazy easy or stupidly hard to achieve in AWS. One easy way is Amplify, that is easy, or CodeStar, but if you know your AWS, that is not the best way when it comes to separating your applications in AWS referring to &lt;a href="https://aws.amazon.com/organizations/getting-started/best-practices/"&gt;https://aws.amazon.com/organizations/getting-started/best-practices/&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Architecture
&lt;/h1&gt;

&lt;p&gt;So I made this little cdk-helper to easily setup and create new pipelines for multi-account setups. It is based on CodeCommit (for now), CodePipeline, S3, Lambda and CloudFront.&lt;/p&gt;

&lt;p&gt;Architecture is something like&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OH0tTOeE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w000dpnebqkyvuju8f41.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OH0tTOeE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w000dpnebqkyvuju8f41.png" alt="Architecture" width="700" height="819"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see there are two CodePipelines, one is for setting up the AWS services in all accounts you want the frontends to be deployed, and the other CodePipeline is just for building the frontend and deploying it to a S3 bucket.&lt;/p&gt;

&lt;h1&gt;
  
  
  Infrastructure CodePipeline
&lt;/h1&gt;

&lt;p&gt;A pipeline that creates the resources in all specified frontend accounts (eg. Test, QA and Production)&lt;/p&gt;

&lt;h2&gt;
  
  
  Some of the resources
&lt;/h2&gt;

&lt;p&gt;The main resources the infrastructure CodePipeline creates is following;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;S3 Bucket Frontend&lt;/strong&gt; — the bucket where the frontend pipeline publish the built frontend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lambda Copy&lt;/strong&gt; — a lambda that listens on changes in the S3 Bucket Frontend, and when something is changed (the pipeline publish new version) it copies it to the S3 Bucket CloudFront.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;S3 Bucket CloudFront&lt;/strong&gt; — a bucket where CloudFront serves files from.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lambda Cache Invalidation&lt;/strong&gt; — a lambda that listens on changes in S3 Bucket CloudFront and is for invalidating the cache in CloudFront so we can get the updates asap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CloudFront&lt;/strong&gt; — So we can have a secure frontend with HTTPS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optional resources
&lt;/h2&gt;

&lt;p&gt;If route53 configuration is specified in the configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ACM&lt;/strong&gt; — Just a SSL certificate for CloudFront.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Route53 Alias&lt;/strong&gt; — Alias pointing to the CloudFront specified.&lt;/p&gt;

&lt;p&gt;But why two buckets? I had some problems with owner of objects in buckets when only using one bucket. Tried to use a lambda to just update the permissions of all files, but it failed. So using two buckets was the fastest way to fix the permissions problem.&lt;/p&gt;

&lt;h1&gt;
  
  
  Frontend pipeline
&lt;/h1&gt;

&lt;p&gt;A pipeline that just builds the frontend for every environment and publish it to a bucket in that environments account. But why do we build the frontend multiple times? So we can configure specific environment settings per account, for example different AWS Cognito user pools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frontend app
&lt;/h2&gt;

&lt;p&gt;For this frontend I have just used Create React App to fast and easily create a frontend.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx create-react-app my-app — template typescript&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
And the build pipeline is also using the stack as parameter to REACT_APP_STAGE in the build step. There will be another blogpost about this later on.&lt;/p&gt;

&lt;p&gt;Don’t forget&lt;br&gt;
Bootstrap all accounts as well that you wish to deploy your application to.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cdk bootstrap aws://2222222222222/eu-north-1 — trust 111111111111&lt;/code&gt;&lt;br&gt;
&lt;code&gt;cdk bootstrap aws://3333333333333/eu-north-1 — trust 111111111111&lt;/code&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cdk</category>
      <category>typescript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Testing nested Stacks in AWS CDK 2</title>
      <dc:creator>Markus Loupeen</dc:creator>
      <pubDate>Tue, 16 Aug 2022 21:44:00 +0000</pubDate>
      <link>https://forem.com/aws-builders/testing-nested-stacks-in-aws-cdk-2-2klf</link>
      <guid>https://forem.com/aws-builders/testing-nested-stacks-in-aws-cdk-2-2klf</guid>
      <description>&lt;p&gt;I just wanted to mention that testing nested stacks in AWS CDK v2 is easier than you think.&lt;/p&gt;

&lt;p&gt;Check out the source over here &lt;a href="https://github.com/loupeen/aws-cdk-nested-stacks-test" rel="noopener noreferrer"&gt;https://github.com/loupeen/aws-cdk-nested-stacks-test&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just write a small stack wrapper for your nested stack.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvw8jpa0sjgcc9j3qq29t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvw8jpa0sjgcc9j3qq29t.png" alt="Wrapper"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You find more code and tests in the GitHub repository.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cdk</category>
      <category>typescript</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
