<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jeroen Reijn</title>
    <description>The latest articles on DEV Community by Jeroen Reijn (@jreijn).</description>
    <link>https://dev.to/jreijn</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%2F632650%2Fbcda97cb-92eb-4885-97b8-e4ab0e47c87d.jpg</url>
      <title>DEV Community: Jeroen Reijn</title>
      <link>https://dev.to/jreijn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jreijn"/>
    <language>en</language>
    <item>
      <title>Amazon ECS Express Mode from an IaC perspective</title>
      <dc:creator>Jeroen Reijn</dc:creator>
      <pubDate>Sat, 20 Dec 2025 21:34:14 +0000</pubDate>
      <link>https://dev.to/aws-builders/amazon-ecs-express-mode-from-an-iac-perspective-294i</link>
      <guid>https://dev.to/aws-builders/amazon-ecs-express-mode-from-an-iac-perspective-294i</guid>
      <description>&lt;p&gt;Amazon recently launched a new feature for Amazon ECS called ECS Express Mode. While reading &lt;a href="https://aws.amazon.com/blogs/aws/build-production-ready-applications-without-infrastructure-complexity-using-amazon-ecs-express-mode/" rel="noopener noreferrer"&gt;the announcement&lt;/a&gt;, I got curious as I use ECS on a regular basis. In this post, we'll look at what ECS Express Mode is, how you can use it from Infrastructure as Code by means of AWS CDK, and what the limitations are.&lt;/p&gt;

&lt;p&gt;Most posts I’ve seen look at ECS Express Mode from an AWS Console (ClickOps) or AWS CLI perspective. The AWS Console is an easy way to get going, but in my experience, most engineers in larger organizations use Infrastructure as Code (IaC) tools like AWS CDK or Terraform for creating resources in AWS. Before we dive into the IaC part, let’s first take a closer look at ECS Express Mode.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is ECS Express Mode?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html" rel="noopener noreferrer"&gt;Amazon Elastic Container Service&lt;/a&gt; (Amazon ECS) is a fully managed container orchestration service that helps you easily deploy, manage, and scale containerized applications. With ECS you can run all kinds of container based workloads. You can run container instances for data processing, web applications or spin up containers based on events.&lt;/p&gt;

&lt;p&gt;ECS Express Mode is a new feature of Amazon ECS with the promise to go from container image to a fully operational ‘production grade’ web application without thinking too much about infrastructure. The intended promise is to let application development teams focus on the application and let AWS apply the best practices around container infrastructure, scaling, and deployments. It will simplify the process of getting started with ECS and all the different components outside of ECS you will need to set up to run, for instance, a public accessible web-application.&lt;/p&gt;

&lt;p&gt;ECS Express Mode is focused on deploying web applications and APIs. It provides a fully integrated set of AWS resources out of the box. Resources that you would normally have to define, configure, and wire together manually using infrastructure as code. If you’re an experienced ECS user, you’ll immediately appreciate how much heavy lifting Express Mode handles for you.&lt;/p&gt;

&lt;p&gt;When you create your first Express Mode service, ECS automatically provisions and configures the following resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ECS Cluster&lt;/strong&gt; - A &lt;strong&gt;default&lt;/strong&gt; cluster based on Fargate is created for you if you don’t provide an existing cluster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ECS Task Definition&lt;/strong&gt; - Defines the container image, resource requirements, environment variables, and runtime configuration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ECS Service&lt;/strong&gt; - Manages task lifecycle, placement, and health checks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rollback alarm&lt;/strong&gt; - When the service starts throwing a larger amount of 40X or 50X response codes after a new deployment, it can revert the deployment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canary Deployment&lt;/strong&gt; strategy - By default, the service uses a canary deployment mechanism to deploy the service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application Load Balancer (ALB)&lt;/strong&gt; - Provides HTTP-based load balancing for your service. A single ALB will serve up to 25 ECS express services; when that limit is exceeded, Express Mode automatically provisions an additional ALB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ALB Target Groups&lt;/strong&gt; - Route traffic from the load balancer to the running tasks of your service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route 53 Records (AWS-managed)&lt;/strong&gt; - Express Mode assigns an AWS-managed domain name that is not visible in your account but is automatically wired to your load balancer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ACM Certificates&lt;/strong&gt; - An SSL/TLS certificate is created for each service to enable HTTPS. These certificates are managed in your account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Groups -&lt;/strong&gt; Security groups are created for each layer of the network stack (load balancer, tasks, etc.). They follow the principle of least privilege—you only configure the application port once, and Express Mode manages the rest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application Auto Scaling -&lt;/strong&gt; A default auto scaling policy is created along with the necessary CloudWatch alarms. By default, scaling is based on CPU utilization, but you can switch to memory utilization or request count.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudwatch Logs&lt;/strong&gt; - A new CloudWatch log group is created for your service. You can configure the log group and change the prefix if you want.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of the great benefits is that ECS deploys all these resources in &lt;strong&gt;your&lt;/strong&gt; account, which means you can see all individual components, view the configuration, and make changes to the existing configuration if required.&lt;/p&gt;

&lt;p&gt;As you can see from the above list, that’s quite a list of things to configure if you had to do this manually. If you are a user new to ECS and see a lot of unknown elements in the above list, ECS Express mode might be a great fit for you. To get started with ECS Express Mode, you only need three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;An existing container image&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An infrastructure role&lt;/strong&gt; - an IAM role that lets the ECS provision (non-ECS) resources in your account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A task execution role&lt;/strong&gt; - an IAM role that gives the container permissions to interact with other AWS services&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Creating an ECS Express Mode service in AWS CDK
&lt;/h2&gt;

&lt;p&gt;ECS Express mode has support for CloudFormation and therefor also support in AWS CDK. The ECS Express Mode service takes care of provisioning all of the resources for you, so there is only a few CloudFormation resources for the express mode service itself. Let's take a look at what using AWS CDK to provision an ECS Express Mode service looks like.&lt;/p&gt;

&lt;h3&gt;
  
  
  IAM roles and permissions
&lt;/h3&gt;

&lt;p&gt;Let's first start with creating a task executions role with the required permissions for our task. In this post, we will use a simple nginx service to show how the service works, so we don't need a lot of permissions. You can get started with the default provided managed policy for ECS tasks: &lt;strong&gt;AmazonECSTaskExecutionRolePolicy&lt;/strong&gt;.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;taskExecutionRole&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Role&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;executionRole&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="na"&gt;roleName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cdk-ecs-express-demo-execution-role&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;assumedBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ServicePrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ecs-tasks.amazonaws.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ECS Task Execution Role&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;managedPolicies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ManagedPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAwsManagedPolicyName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;service-role/AmazonECSTaskExecutionRolePolicy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next role we create will be for the ECS service to create the resources in our account. Luckily AWS, has provided a default managed policy which we can use: &lt;strong&gt;AmazonECSInfrastructureRoleforExpressGatewayServices&lt;/strong&gt;.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;infrastructureRole&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Role&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;infrastructureRole&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="na"&gt;roleName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cdk-ecs-express-demo-infrastructure-role&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;assumedBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ServicePrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ecs.amazonaws.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ECS Task Infrastructure Role&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;managedPolicies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ManagedPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAwsManagedPolicyName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;service-role/AmazonECSInfrastructureRoleforExpressGatewayServices&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Upon closer inspection, you can imagine this policy contains quite a list of permissions as it needs to cover a lot of services. It's worth inspecting the policy when your first start to use it. I won't copy it here as it contains about 280 lines or JSON at the moment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Defining the ECS Gateway Service
&lt;/h3&gt;

&lt;p&gt;Now that we've set up the roles with the correct permissions, we can move to the final step, which is to create the ECS Express Mode Service. Currently (as of the beginning of december there is a level 1 CDK construct available, which maps directly to the CloudFormation resource. The resource is named &lt;strong&gt;&lt;code&gt;CfnExpressGatewayService&lt;/code&gt;&lt;/strong&gt;.&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;cfnExpressGatewayService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CfnExpressGatewayService&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ExpressServiceNginx1&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="na"&gt;primaryContainer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nginx:latest&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="na"&gt;executionRoleArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;taskExecutionRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roleArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;infrastructureRoleArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;infrastructureRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roleArn&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 general, that's it. That's all you need to run your web app container service in ECS, which I think is pretty great. Now, how can you reach the service?&lt;/p&gt;

&lt;h3&gt;
  
  
  Accessing the service
&lt;/h3&gt;

&lt;p&gt;The ECS express mode service will handle DNS for you. The DNS records are the only thing that are not created within your own account, as far as I could tell (the ACM certificates are though). According to the &lt;a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/express-service-getting-started.html#express-service-access-application" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;, the &lt;em&gt;service name&lt;/em&gt; and &lt;em&gt;region&lt;/em&gt; should be enough to determine the URL of the service.&lt;/p&gt;

&lt;p&gt;So if we define the service as:&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;cfnExpressGatewayService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CfnExpressGatewayService&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ExpressServiceNginx1&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="na"&gt;serviceName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;demo-express-service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="na"&gt;primaryContainer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nginx:latest&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="na"&gt;executionRoleArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;taskExecutionRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roleArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;infrastructureRoleArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;infrastructureRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roleArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and we follow the pattern mentioned in the docs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;service-name&amp;gt;.ecs.&amp;lt;region&amp;gt;.on.aws/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;should have resulted in the service being accessible from &lt;a href="https://demo-express-service.ecs.eu-west-1.on.aws/" rel="noopener noreferrer"&gt;https://demo-express-service.ecs.eu-west-1.on.aws/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That made me wonder if the service name is unique, just like with an S3 bucket name, but when I tried specifying the &lt;strong&gt;serviceName&lt;/strong&gt; property of the CfnExpressGatewayService the resulting URL would return a 404. The only way I've found to determine the correct URL for the service is by using  the &lt;strong&gt;Endpoint&lt;/strong&gt; return value from the CloudFormation resource.&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;new&lt;/span&gt; &lt;span class="nc"&gt;CfnOutput&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;service1url&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="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;service1url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;cfnExpressGatewayService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAtt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Endpoint&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&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;Which results in the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;https://ng-e6840bdfebb54e28bb5c3c9bd7c1f5fa.ecs.eu-west-1.on.aws
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see there, is no mention of the service name in the generated endpoint. It does seem the first two letters of the domain are coming from the service name, but that might be a coincidence.&lt;/p&gt;

&lt;p&gt;Once the service is up and running, you can inspect all the different components.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmjg6ux2wtg3x0c1wujls.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmjg6ux2wtg3x0c1wujls.jpeg" alt="Resources overview from the AWS Console" width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring alternative settings
&lt;/h3&gt;

&lt;p&gt;We've looked at the minimum configuration. Now let's take a look as some more configuration options.&lt;/p&gt;

&lt;h3&gt;
  
  
  Specifying your own ECS cluster
&lt;/h3&gt;

&lt;p&gt;By default, the service creates an ECS cluster in your default VPC with the cluster name &lt;strong&gt;default&lt;/strong&gt;. If you don't have a default VPC or want to have more control over your cluster, you can configure your own cluster.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Cluster&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cluster&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="na"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;clusterName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cdk-ecs-express-demo-cluster&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;let&lt;/span&gt; &lt;span class="nx"&gt;cfnExpressGatewayService2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CfnExpressGatewayService&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ExpressServiceNginx2&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="na"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clusterName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;primaryContainer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nginx:latest&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="na"&gt;executionRoleArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;taskExecutionRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roleArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// other optional configuration options&lt;/span&gt;
    &lt;span class="na"&gt;healthCheckPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express-service-nginx-2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;infrastructureRoleArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;infrastructureRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roleArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// Autoscaling configuration&lt;/span&gt;
    &lt;span class="na"&gt;scalingTarget&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;minTaskCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;maxTaskCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// cpu: '512',&lt;/span&gt;
    &lt;span class="c1"&gt;// memory: '512',&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;According to the &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-expressgatewayservice.html" rel="noopener noreferrer"&gt;CloudFormation documentation&lt;/a&gt;, you can specify either the cluster name or the cluster ARN. However, when I tried the ARN, CloudFormation returned with an error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Resource handler returned message: "Invalid request provided:
Invalid identifier: Unexpected number of separators (Service: Ecs, Status Code: 400, Request ID: ee074322-e841-4256-8bb7-2b2014b9c76e) (SDK Attempt Count: 1)" (RequestToken: ec5bfa00-2c43-db0f-3b7c-6374d0a33f5a, HandlerErrorCode: InvalidRequest)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm not sure if that's a problem in the documentation or if I misconfigured something. I advise you to stick to the clusterName, which seems to work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Alternative configuration options
&lt;/h3&gt;

&lt;p&gt;The express mode service we created previously uses AWS best practices. If you have slightly different requirements, you can change specific parts of the configuration. For your container you can change for instance the cpu and memory requirements.&lt;/p&gt;

&lt;p&gt;By default the ECS server will run in the default VPC and in a public subnet. You can specify alternative network settings for your service depending on your requirements. For instance your can switch to a different VPC and subnet. Because you can change the ECS cluster, you can probably also use Fargate Spot or EC2 based compute instead. I have not tested that though, and it might be something for another blog post.&lt;/p&gt;

&lt;p&gt;Some settings can't be changed right now. Canary deployment for instance is the only supported deployment strategy. I think it's a great default, but I hope they will add linear or blue/green deployment as these strategies are now &lt;a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs_service-options.html" rel="noopener noreferrer"&gt;ECS supported deployment strategies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;ECS Express mode only allows you to configure a single container. If you’re currently using ECS and are using a sidecar container for for instance observability purposes, you won’t be able to do that during creation with ECS express mode. You might want to consider using an Open Telemetry library, which integrates with your application framework. If you really need a sidecar container you can import the task definition into your stack, after it has been created. Express Mode will keep ‘manual’ changes during additional deployments / updates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring a WAF for your ECS service
&lt;/h3&gt;

&lt;p&gt;In all cases where I use ECS with a public ALB, I always add a Web Application Firewall (WAF). I also tried this for the ECS Express mode service, but while trying this, I learned there things to consider. There is currently no out-of-the box experience for attaching the WAF to the load balancer created by the ECS service. The CloudFormation resource &lt;strong&gt;AWS::ECS::ExpressGatewayService&lt;/strong&gt; does expose some &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-expressgatewayservice.html#aws-resource-ecs-expressgatewayservice-return-values" rel="noopener noreferrer"&gt;return values&lt;/a&gt; after the resource has been created, which you can use to get the load balancer ARN.&lt;/p&gt;

&lt;p&gt;With the return value for the ALB ARN, you can assign the WebACL to the created Load balancer.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cfnWebACLAssociation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CfnWebACLAssociation&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ALBWebACLAssociation&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="na"&gt;resourceArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cfnExpressGatewayService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAtt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ECSManagedResourceArns.IngressPath.LoadBalancerArn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;webAclArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;wafV2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attrArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Don't forget to add a dependency, otherwise this association might happen before the service has been created.&lt;/span&gt;
&lt;span class="nx"&gt;cfnWebACLAssociation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addDependency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cfnExpressGatewayService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is a tricky problem I foresee, which is when you hit the 25 services limit and a new Application Load Balancer is created. ECS Express Mode handles this by itself, and if you don't have all 25 services defined in the same CDK stack, it gets hard to track if you'll need to create a new WebACL Association.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resource state management
&lt;/h2&gt;

&lt;p&gt;After playing with ECS Express Mode for a while, it made me think. Who is in control of the resources created in your account? Who is responsible for its state? According to the ECS team they are not using CloudFormation internally, but are working with the direct APIs and use their own internal state for managing all resources. If you want to change specific parts of the default generated solution, you can import resources into CloudFormation/CDK and change them, but the ECS service will always be the owner of the resources. In an enterprise context where I see a lot of infrastructure as code, I wonder about the developer experience and aspects like compliance rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  CDK L3 constructs vs ECS Express Mode
&lt;/h3&gt;

&lt;p&gt;If you're an experienced ECS and AWS CDK user you might question how Express Mode compares with, for instance, the &lt;strong&gt;ApplicationLoadBalancedFargateService&lt;/strong&gt; construct. I think it comes quite close to what ECS express mode delivers. However, express mode also adds autoscaling, deployment rollbacks, canary deployment, smart load balancer sharing, default HTTPS, and domain management out of the box. If you're an experienced CDK user, you can get very far with the CDK provided L3 construct(s). You might even consider creating one that does something similar. The advantage of that would be that you have all AWS resources defined in your in CDK/CloudFormation and you have full control over all your resources. The ExpressGatewayService is a single AWS resource, which means it's tool agnostic and delivers the same level of business value if you use CDK, CloudFormation, Pulumi, Terraform, or any other kind of IaC tool. You're not depending on Terraform modules, CDK L3 constructs, or any other kind of configuration elements when working with IaC tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;I like what the ECS team has created with ECS Express Mode. It's really great to go from a container image to a production like environment in a matter of minutes. This is going to work great for demo's and quick PoCs. Looking at this from an infrastructure as code perspective it feels a bit unnatural though as the ExpressGatewayService hides a lot of other AWS resources. If you're already a heavy CDK user, you might feel you have too little control over your resources. You can get a similar experience with creating your own L3 construct or using an existing one like the &lt;strong&gt;ApplicationLoadBalancedFargateService.&lt;/strong&gt;&lt;br&gt;
There are still some cases I want to explore further to see how ECS express mode compares to a set of L2/L3 constructs and how to handle changes to the individual components created by the express gateway service. The service is quite new, therefor I hope it will get some nice updates in the near future, like more deployment strategies, and perhaps custom domain support. If you've not looked at ECS Express Mode before I think it's definitely worth to take a look and see if it works for your specific use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=z9JUEQjpGgY" rel="noopener noreferrer"&gt;Build production-ready applications with Amazon ECS Express Mode&lt;/a&gt; (Containers from the Couch)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/blogs/aws/build-production-ready-applications-without-infrastructure-complexity-using-amazon-ecs-express-mode/" rel="noopener noreferrer"&gt;Build production-ready applications without infrastructure complexity using Amazon ECS Express Mode&lt;/a&gt; (AWS Blog)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/from-image-to-https-endpoint-in-one-step-with-ecs-express-mode-1oi2"&gt;From image to HTTPS endpoint in one step with ECS Express Mode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/jreijn/demos-aws-cdk/tree/develop/aws-cdk-ecs-express" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; containing an ECS Express Mode sample CDK application with code mentioned in this blogpost.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>ecs</category>
      <category>infrastructureascode</category>
      <category>cdk</category>
    </item>
    <item>
      <title>Improve observability for Windows EC2 instances with the CloudWatch Agent</title>
      <dc:creator>Jeroen Reijn</dc:creator>
      <pubDate>Sat, 04 Oct 2025 19:28:00 +0000</pubDate>
      <link>https://dev.to/aws-builders/improve-observability-for-windows-ec2-instances-with-the-cloudwatch-agent-4f4c</link>
      <guid>https://dev.to/aws-builders/improve-observability-for-windows-ec2-instances-with-the-cloudwatch-agent-4f4c</guid>
      <description>&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/WhatIsCloudWatch.html" rel="noopener noreferrer"&gt;Amazon CloudWatch&lt;/a&gt; monitors Windows EC2 instances out of the box, but only gives you basic host-level metrics like CPU and network usage. In this post, you’ll learn how to extend observability with the Amazon CloudWatch Agent. We will use AWS CDK in TypeScript to automatically deploy an example instance with an installed and configured CloudWatch Agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Default collected metrics
&lt;/h2&gt;

&lt;p&gt;Elastic Compute Cloud (EC2) instances send &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/viewing_metrics_with_cloudwatch.html" rel="noopener noreferrer"&gt;a set of predefined metrics&lt;/a&gt; to CloudWatch. Most of these metrics are system related and focus on CPU and Networking metrics. Under the monitoring tab in the AWS EC2 Console you can get a quick overview of some of out of the box available metrics for your EC2 instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqvs9gx1bjoydzgyhlt1u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqvs9gx1bjoydzgyhlt1u.png" alt="Monitoring Tab in the EC2 AWS Console" width="800" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s a great starting set, but what if you need more? What if you need metrics from inside of the VM?&lt;/p&gt;

&lt;h2&gt;
  
  
  Gathering additional metrics
&lt;/h2&gt;

&lt;p&gt;If you need more system or application level metrics you can make use of the Amazon &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Install-CloudWatch-Agent.html" rel="noopener noreferrer"&gt;CloudWatch Agent&lt;/a&gt;. The CloudWatch agent is service which you can run on the EC2 instance and it’s great for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Collecting system-level metrics (CPU, memory, disk, network)&lt;/li&gt;
&lt;li&gt;Gathering custom metrics from your application(s)&lt;/li&gt;
&lt;li&gt;Collecting and centralizing logs from various sources&lt;/li&gt;
&lt;li&gt;Monitoring both AWS and on-premises environments with a single tool&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a high-level overview of the CloudWatch Agent and how it interacts with an EC2 instance and the CloudWatch service is shown in the following diagram.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/blogs/infrastructure-and-automation/collect-custom-metrics-with-amazon-cloudwatch-strategic-tagging/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1drmkdflkjmrz0i7b80b.png" alt="Cloud Watch agent monitoring architecture" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can install the CloudWatch agent via EC2 user data, AWS Systems Manager, or via the AWS EC2 Console. In this post we will install the CloudWatch Agent and its configuration using the user data feature of an EC2 instance. Before we can actually install the agent, we first need to create a CloudWatch Agent configuration, which we can use later on during the installation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a CloudWatch Agent Configuration
&lt;/h2&gt;

&lt;p&gt;The CloudWatch Agent configuration file is a JSON file with four sections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;agent&lt;/strong&gt; - includes fields for the overall configuration of the agent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;metrics&lt;/strong&gt; - specifies the custom metrics for collection and publishing to CloudWatch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;logs&lt;/strong&gt; - specifies what log files are published to CloudWatch Logs. This can include events from the Windows Event Log if the server runs Windows Server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;traces&lt;/strong&gt; - specifies the sources for traces that are collected and sent to AWS X-Ray&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see there is a lot you can do with the CloudWatch agent. However, in this post we will focus only on metrics.&lt;/p&gt;

&lt;p&gt;Let’s take a look at an example configuration:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cwAgentConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;metrics_collection_interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="na"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CWAgent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;append_dimensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;InstanceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;${aws:InstanceId}&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="na"&gt;metrics_collected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;LogicalDisk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="na"&gt;measurement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
         &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;% Free Space&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Percent&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Free Megabytes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Megabytes&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resources&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&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="na"&gt;Memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="na"&gt;metrics_collection_interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;measurement&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&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="s2"&gt;% Committed Bytes in Use&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="s2"&gt;unit&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="s2"&gt;Percent&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resources&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&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="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;// Stringify the JSON&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;configJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cwAgentConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example configuration we specify that the CloudWatch Agent needs to collect metrics every 60 seconds. If you gather more than just metrics you can also specify collection intervals per section (metrics, logs, traces).&lt;/p&gt;

&lt;p&gt;For clarity the namespace is explicitly set to &lt;strong&gt;CWAgent&lt;/strong&gt; , but if you omit the namespace all metrics will be stored in the &lt;strong&gt;CWAgent&lt;/strong&gt; namespace by default. You can of course set your own custom namespace.&lt;/p&gt;

&lt;p&gt;For each collected metric an additional dimension is added for the &lt;em&gt;InstanceId&lt;/em&gt;. When collecting metrics for EC2 instances you can also think of other dimensions like InstanceType, AutoScalingGroupName or ImageId which would reflect in the JSON as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"append_dimensions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"InstanceId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${aws:InstanceId}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"InstanceType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${aws:InstanceType}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"AutoScalingGroupName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${aws:AutoScalingGroupName}"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking at the metrics &lt;strong&gt;metrics_collected&lt;/strong&gt; section you can see we want to collect metrics for the Windows Performance object &lt;em&gt;LogicalDisk&lt;/em&gt; and &lt;em&gt;Memory&lt;/em&gt;. If you’re doing this for the first time you might wonder what other performance objects and metrics are available. You can see the performance objects by running the following command on your Windows instance.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Get-Counter -ListSet *
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Command-line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TypePerf.exe –q
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running one of the above command will return quite a list of performance objects and corresponding metrics. You can expect to see all kinds of different objects. From SQL Server to Windows Events.&lt;/p&gt;

&lt;p&gt;Now let’s take a closer look at the measurement section in which the metrics are defined that we want to store. For each metrics you can explicitly specify a metric name and unit the metric needs to be stored as (Percentage, MegaBytes. etc). If you leave out the unit type the agent will try to pick the right one.&lt;/p&gt;

&lt;p&gt;Due note that all metrics collected with the CloudWatch agent are considered custom metrics and are billed separately. A custom metric costs about $0.30 per metric per month (depending on your region), so &lt;strong&gt;costs can add up quickly&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Storing the configuration in Systems Manager Parameter store
&lt;/h2&gt;

&lt;p&gt;While implementing this I ran into all kinds of issues with the JSON format of the configuration file. Creating the JSON file in typescript, passing it to Powershell, writing it to disk, and reading the file took me multiple attempts to get right. An easier approach is to store the JSON configuration in Systems Manager Parameter Store and just pass the parameter when configuring the agent.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cwAgentConfigParam&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StringParameter&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cw-agent-config&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="na"&gt;parameterName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/ec2/cw-agent-config.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;stringValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;configJson&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the instance to be able to fetch the parameter we will need to make sure we set the proper IAM permissions for the instance role. Let’s create a new IAM role and add the required permissions.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Role&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;instance-role&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="na"&gt;assumedBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ServicePrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ec2.amazonaws.com&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="c1"&gt;// required for the CloudWatch agent to send data to cloudwatch&lt;/span&gt;
&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addManagedPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ManagedPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAwsManagedPolicyName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CloudWatchAgentServerPolicy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// required for SSM to manage the instance&lt;/span&gt;
&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addManagedPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ManagedPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAwsManagedPolicyName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AmazonSSMManagedInstanceCore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Don't forget to add the permissions to read the agent configuration parameter&lt;/span&gt;
&lt;span class="nx"&gt;cwAgentConfigParam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grantRead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we’ve configured IAM and stored our agent configuration, let’s create a Windows EC2 instance to attach and monitor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Windows Server EC2 instance
&lt;/h2&gt;

&lt;p&gt;When creating a Windows instance in AWS CDK you will need to place the instance in one of the subnets of your VPC. Pick a specific Windows AMI like Windows Server 2022 and make sure to reference our IAM instance role, otherwise a default role is created without the proper permissions.&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="c1"&gt;// Create a Windows based EC2 instance&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Instance&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;windows-demo-instance&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="na"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;demoVpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;instanceType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;InstanceType&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;InstanceClass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BURSTABLE3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;InstanceSize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SMALL&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;machineImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MachineImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;latestWindows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WindowsVersion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WINDOWS_SERVER_2022_ENGLISH_FULL_BASE&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;securityGroup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;securityGroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;keyPair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;keyPair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;associatePublicIpAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;vpcSubnets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;subnetType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SubnetType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PRIVATE_WITH_EGRESS&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;detailedMonitoring&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;securityGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addIngressRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Peer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;anyIpv4&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3389&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Allow RDP Connections&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to allow remote desktop access if you want to connect with remote desktop. I usually use the Systems manager Fleet manager to connect with Remote Desktop. Keep in mind that Amazon provided Windows AMI’s have the Systems Manager Agent installed. If you use your own AMI you will need to make sure you install the Systems Manager agent yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing the CloudWatch Agent on deployment
&lt;/h2&gt;

&lt;p&gt;As mentioned before in this post we will install the CloudWatch Agent, required for gathering our metrics, during the instance deployment by means of instance userdata. The latest version of the CloudWatch Agent is published by Amazon into an S3 bucket so all we need to do is request it and run the installer.&lt;/p&gt;

&lt;p&gt;When starting the agent make sure you reference the configuration based on its SSM parameter name. We do so by specifying the &lt;code&gt;ssm:&lt;/code&gt; prefix/protocol. The CloudWatch Agent, if given the permissions to fetch the parameter, will read the parameter from parameter store during startup&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="c1"&gt;// Write config to disk and install agent&lt;/span&gt;
&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addCommands&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="c1"&gt;// Install CW Agent via MSI&lt;/span&gt;
 &lt;span class="s2"&gt;`powershell.exe -Command "Invoke-WebRequest https://s3.amazonaws.com/amazoncloudwatch-agent/windows/amd64/latest/amazon-cloudwatch-agent.msi -OutFile C:\cwagent.msi"`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;`msiexec /i C:\cwagent.msi /qn /l*v C:\cwagent-install.log`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="c1"&gt;// Wait for installation to complete&lt;/span&gt;
 &lt;span class="s2"&gt;`timeout /t 30`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="c1"&gt;// Start the agent with proper PowerShell execution&lt;/span&gt;
 &lt;span class="s2"&gt;`powershell.exe -ExecutionPolicy Bypass -Command "&amp;amp; 'C:\Program Files\Amazon\AmazonCloudWatchAgent\amazon-cloudwatch-agent-ctl.ps1' -a fetch-config -m ec2 -c ssm:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cwAgentConfigParam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parameterName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -s"`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="c1"&gt;// Wait for agent to start and load&lt;/span&gt;
 &lt;span class="s2"&gt;`timeout /t 30`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="c1"&gt;// check agent status&lt;/span&gt;
 &lt;span class="s2"&gt;`powershell.exe -ExecutionPolicy Bypass -Command "&amp;amp; 'C:\Program Files\Amazon\AmazonCloudWatchAgent\amazon-cloudwatch-agent-ctl.ps1' -a status -m ec2"`&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;Once the deployment is finished you should be able to see more metrics in the monitoring tab for the EC2 instance. By default it will check if there are metrics within the CWAgent namespace and if so add them to the monitoring screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F224a9e3eczlccsits91o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F224a9e3eczlccsits91o.png" alt="Monitoring Tab in the EC2 AWS Console with CWAgent metrics displayed" width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the agent doesn’t publish your custom metrics, login with remote desktop and check the CloudWatch Agent log file &lt;code&gt;C:\ProgramData\Amazon\AmazonCloudWatchAgent\Logs\amazon-cloudwatch-agent.log&lt;/code&gt; for errors. If the file is not there it might be the case the CloudWatch Agent isn’t running, so you will need to troubleshoot that first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating CloudWatch Alarms for proactive monitoring
&lt;/h2&gt;

&lt;p&gt;Adding metrics is great for trend analysis, but really also want to have some alarms in place. Let’s look at the scenario in case the disk is running out of space. Let’s create an alarm for the “LogicalDisk % Free Space” metric.&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="c1"&gt;// Low diskspace alarm for C drive&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lowDiskSpaceCVolumeAlarm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Alarm&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alarm&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="na"&gt;alarmDescription&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;This metric monitors the amount of free disk space on the instance. If the amount of free disk space falls below 10% for 5 minutes, the alarm will trigger&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Metric&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="na"&gt;dimensionsMap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;InstanceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instanceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;objectname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LogicalDisk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;C:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="na"&gt;statistic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Average&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CWAgent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;metricName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LogicalDisk % Free Space&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PERCENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;}),&lt;/span&gt;
 &lt;span class="na"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;evaluationPeriods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;datapointsToAlarm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;comparisonOperator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComparisonOperator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LESS_THAN_OR_EQUAL_TO_THRESHOLD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;treatMissingData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TreatMissingData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MISSING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;lowDiskSpaceCVolumeAlarm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addAlarmAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SnsAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;lowDiskSpaceCVolumeAlarm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addOkAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SnsAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should result in an alarm that’s monitoring the amount of free disk space for the C: drive of our windows instance.&lt;/p&gt;

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

&lt;p&gt;Now that the alarm is in place, CloudWatch will notify the subscribers of the SNS topic in case the alarm state is trigged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this post, we explored how to create metrics and alarms for Windows based EC2 instances by leveraging the CloudWatch Agent and CloudWatch alarms. We’ve used AWS CDK for provisioning the infrastructure. Along the way, we covered some important topics like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CloudWatch agent configuration&lt;/li&gt;
&lt;li&gt;Windows Performance objects and metrics.&lt;/li&gt;
&lt;li&gt;CloudWatch alarms for custom metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these pieces in place, you’ll have a setup that alerts your team when your windows metrics exceed their threshold. Next, you could extend this setup with other instance metrics, application-level metrics and log collection.&lt;/p&gt;

&lt;p&gt;A fully working CDK application with the code mentioned in this blogpost can be found in the &lt;a href="https://github.com/jreijn/demos-aws-cdk/tree/develop/aws-cdk-windows-diskspace-monitoring" rel="noopener noreferrer"&gt;following GitHub repo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cdk</category>
      <category>observability</category>
      <category>cloudwatch</category>
    </item>
    <item>
      <title>Enabling AWS Budget Notifications with SNS using AWS CDK</title>
      <dc:creator>Jeroen Reijn</dc:creator>
      <pubDate>Mon, 25 Aug 2025 17:28:14 +0000</pubDate>
      <link>https://dev.to/aws-builders/enabling-aws-budget-notifications-with-sns-using-aws-cdk-2pg6</link>
      <guid>https://dev.to/aws-builders/enabling-aws-budget-notifications-with-sns-using-aws-cdk-2pg6</guid>
      <description>&lt;p&gt;Keeping track of AWS spend is very important. Especially since it’s so easy to create resources. You might forget to turn off an EC2 instance or container you started, or remove a CDK stack for a specific experiment. Costs can creep up fast if you don’t put guardrails in place.&lt;/p&gt;

&lt;p&gt;Recently, I had to set up budgets across multiple AWS accounts for my team. Along the way, I learned a few gotchas (especially around SNS and KMS policies) that weren’t immediately clear to me as I started out writing AWS CDK code. In this post, we’ll go through how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create AWS Budgets with AWS CDK&lt;/li&gt;
&lt;li&gt;Send notifications via email and SNS&lt;/li&gt;
&lt;li&gt;Handle cases like encrypted topics and configuring resource policies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re setting up AWS Budgets for the first time, I hope this post will save you some trial and error.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are AWS Budgets?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/cost-management/latest/userguide/budgets-managing-costs.html" rel="noopener noreferrer"&gt;AWS Budgets&lt;/a&gt; is part of AWS Billing and Cost Management. It lets you set  &lt;strong&gt;guardrails&lt;/strong&gt;  for spend and usage limits. You can define a budget around cost, usage, or even commitment plans (like Reserved Instances and Savings Plans) and trigger alerts when you cross a threshold. You can think of Budgets as your  &lt;strong&gt;planned&lt;/strong&gt; spend tracker. Budgets are great for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Alerting when costs hit predefined thresholds (e.g., 80% of your budgeted spend)&lt;/li&gt;
&lt;li&gt;Driving team accountability by tying alerts to product or account owners&lt;/li&gt;
&lt;li&gt;Enforcing a cap on monthly spend triggering an action and shutting down compute (EC2), if you go over budget (be careful with this)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep in mind that budgets and their notifications are not instant. AWS billing data is processed multiple times a day, but you might trigger your budget a couple of hours after you’ve passed your threshold. This is clearly stated in the AWS &lt;a href="https://docs.aws.amazon.com/cost-management/latest/userguide/budgets-best-practices.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AWS billing data, which Budgets uses to monitor resources, is updated at least once per day. Keep in mind that budget information and associated alerts are updated and sent according to this data refresh cadence.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Defining Budgets with AWS CDK
&lt;/h2&gt;

&lt;p&gt;You can create different kinds of budgets, depending on your requirements. Some examples are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fixed budgets&lt;/strong&gt; : Set one amount to monitor every budget period.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Planned budgets&lt;/strong&gt; : Set different amounts to monitor each budget period.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-adjusting budgets&lt;/strong&gt; : Set a budget amount to be adjusted automatically based on the spending pattern over a time range that you specify.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll start with a simple example of how you can create a budget in the CDK. We’ll go for a &lt;strong&gt;fixed&lt;/strong&gt; budget of about $100. The AWS CDK currently only has &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/constructs.html" rel="noopener noreferrer"&gt;Level 1 constructs&lt;/a&gt; available for budgets, which means that the classes in the CDK are a 1 to 1 mapping to the CloudFormation resources. Because of this you will have to explicitly define all required properties (constructs, IAM policies, resource policies, etc), which otherwise could be taken care of by a CDK L2 construct. It also means your CDK code will be a bit more verbose. We’ll start by using the &lt;strong&gt;CfnBudget&lt;/strong&gt; construct.&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;new&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_budgets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CfnBudget&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fixed-monthly-cost-budget&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="na"&gt;budget&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;budgetType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;COST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;budgetLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="na"&gt;budgetName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Monthly Costs Budget&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;timeUnit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MONTHLY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example we’ve created a budget with a limit of &lt;strong&gt;$100 per month&lt;/strong&gt;. A budget alone isn’t very useful. You’d still have to check into the AWS console manually to see what your spend is compared to your budget. The important thing is that we want to get notified in case we reach our budget or our forecasted budget will reach our threshold, so let’s add a notification and a subscriber.&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;new&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_budgets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CfnBudget&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fixed-monthly-cost-budget&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="na"&gt;budget&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;budgetType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;COST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;budgetLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="na"&gt;budgetName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Monthly Costs Budget&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;timeUnit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MONTHLY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="na"&gt;notificationsWithSubscribers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
   &lt;span class="na"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;comparisonOperator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GREATER_THAN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;notificationType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FORECASTED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;thresholdType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PERCENTAGE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="na"&gt;subscribers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
     &lt;span class="na"&gt;subscriptionType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EMAIL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;your-email-address&amp;gt;&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="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Based on the notification settings, interested parties are notified when the spend is &lt;strong&gt;forecasted&lt;/strong&gt; to exceed 100% of our defined budget limit. You can put a notification on forecasted or actual percentages. When that happens an email is send to the designated email address. Subscribers, at the time of writing, can be either email recipients or a Simple Notification Service (SNS) topic. In the above code example we use email subscribers for which you can add up to 10 recipients.&lt;/p&gt;

&lt;p&gt;Depending on your team or organization it might be beneficial to switch to using an SNS topic. The advantage of using an SNS topic over a set of email subscribers is that you can add different kind of subscribers (email, chat, custom lambda functions) to your SNS topic. With an SNS topic you have a single place to configure subscribers and if you change your mind you can do so in one place instead of updating all budgets. Using an SNS Topic also allows you to push budget notifications to for instance a chat client like MS Teams or Slack.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fadokert2238r5zisxn6h.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fadokert2238r5zisxn6h.jpg" alt="Example architecture showing AWS Budget integration with SNS and email or AWS Chat" width="628" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this case we wil make use of SNS in combination with email subscribers. Let’s start by defining an SNS topic with the AWS CDK.&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="c1"&gt;// Create a topic for email notifications&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;topic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Topic&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;budget-notifications-topic&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="na"&gt;topicName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;budget-notifications-topic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Now let’s add an email subscriber, as this is the most simple way to receive budget notifications.&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="c1"&gt;// Add email subscription&lt;/span&gt;
&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSubscription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EmailSubscription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your-email-address&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This looks pretty straight forward and you might think you’re done, but there is one important step to take next, which I initially forgot. The AWS budgets service will need to be granted permissions to publish messages to the topic. To be able to do this, we will need to add a resource policy to the topic which allows the budgets service to call the &lt;em&gt;SNS:Publish&lt;/em&gt; action for our topic.&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="c1"&gt;// Add resource policy to allow the budgets service to publish to the SNS topic&lt;/span&gt;
&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addToResourcePolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PolicyStatement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SNS:Publish&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Effect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ALLOW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;principals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ServicePrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;budgets.amazonaws.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;topicArn&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;ArnEquals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws:SourceArn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`arn:aws:budgets::&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;of&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="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:*`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;StringEquals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws:SourceAccount&lt;/span&gt;&lt;span class="dl"&gt;'&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;of&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="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}))&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Now let’s assign the SNS topic as a subscriber in our CDK code.&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="c1"&gt;// Define a fixed budget with SNS as subscriber&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_budgets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CfnBudget&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fixed-monthly-cost-budget&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="na"&gt;budget&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;budgetType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;COST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;budgetLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="na"&gt;budgetName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Monthly Costs Budget&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;timeUnit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MONTHLY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="na"&gt;notificationsWithSubscribers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
   &lt;span class="na"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;comparisonOperator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GREATER_THAN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;notificationType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FORECASTED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;thresholdType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PERCENTAGE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="na"&gt;subscribers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
     &lt;span class="na"&gt;subscriptionType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SNS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;topicArn&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;h2&gt;
  
  
  Working with Encrypted Topics
&lt;/h2&gt;

&lt;p&gt;If you have an SNS topic with &lt;strong&gt;encryption enabled&lt;/strong&gt; (via KMS) you will need to make sure that the corresponding service has access to the KMS key. If you don’t, you will not get any messages and as far as I could tell you will see no errors (at least I could find none in CloudTrail). I actually wasted a couple of hours trying to figure this part out.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4z18q3bnwvmnoeg1oasq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4z18q3bnwvmnoeg1oasq.gif" alt="Frustrated Gru from the minions movie" width="480" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I should have &lt;a href="https://docs.aws.amazon.com/cost-management/latest/userguide/budgets-sns-policy.html#configure-kms-perm" rel="noopener noreferrer"&gt;read the documentation&lt;/a&gt; as it is explicitly stated to do so. I guess I should start with the docs instead of diving right into the AWS CDK code.&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="c1"&gt;// Create KMS key used for encryption&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Key&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sns-kms-key&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="na"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sns-kms-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Key used for SNS topic encryption&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Create topic and assign the KMS key&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;topic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Topic&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;budget-notifications-topic&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="na"&gt;topicName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;budget-notifications-topic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;masterKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Now let’s add the resource policy to the key and try to trim down the permissions as much as possible.&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="c1"&gt;// Allow access from budgets service&lt;/span&gt;
&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addToResourcePolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PolicyStatement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Effect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ALLOW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;kms:GenerateDataKey*&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="s2"&gt;kms:Decrypt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;principals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ServicePrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;budgets.amazonaws.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;StringEquals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws:SourceAccount&lt;/span&gt;&lt;span class="dl"&gt;'&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;of&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="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;ArnLike&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;aws:SourceArn&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="s2"&gt;arn:aws:budgets::&lt;/span&gt;&lt;span class="dl"&gt;"&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:*&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="p"&gt;}));&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;If you’ve configured everything correctly and deployed your stack to your target account you should be good to go. Once you cross your threshold you should be notified by email that your budget is exceeding one of your thresholds (depending on the threshold set).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F66qkwi1tiy8jvdb92np8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F66qkwi1tiy8jvdb92np8.png" alt="Image showing Google mail with an Budget alert mail" width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this post, we explored how to create AWS Budgets with AWS CDK and send notifications through email or SNS. Along the way, we covered some important topics like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Budgets alone aren’t useful until you add notifications.&lt;/li&gt;
&lt;li&gt;SNS topics need a resource policy so the Budgets service can publish.&lt;/li&gt;
&lt;li&gt;Encrypted topics require KMS permissions for the Budgets service.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these pieces in place, you’ll have a setup that alerts your team when costs exceed thresholds via email, chat, or custom integrations.&lt;/p&gt;

&lt;p&gt;A fully working CDK application with the code mentioned in this blogpost can be found in the &lt;a href="https://github.com/jreijn/demos-aws-cdk/tree/develop/aws-cdk-budget-notifications" rel="noopener noreferrer"&gt;following GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@towfiqu999999?utm_source=sveltia-cms&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Towfiqu barbhuiya&lt;/a&gt; on &lt;a href="https://unsplash.com/?utm_source=sveltia-cms&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>aws</category>
      <category>typescript</category>
      <category>awscdk</category>
      <category>finops</category>
    </item>
    <item>
      <title>Multi-account DNS with AWS CDK</title>
      <dc:creator>Jeroen Reijn</dc:creator>
      <pubDate>Mon, 10 Feb 2025 15:18:28 +0000</pubDate>
      <link>https://dev.to/aws-builders/multi-account-dns-with-aws-cdk-4dp2</link>
      <guid>https://dev.to/aws-builders/multi-account-dns-with-aws-cdk-4dp2</guid>
      <description>&lt;p&gt;I was recently tasked with setting up DNS within an AWS organization structure. The idea was to use a single domain structure that would be able to support multiple environments (development, acceptance and production). In my actual use case it was a bit more complex, but in this post I'll keep it simple and show some example code how you can implement the solution with &lt;a href="https://github.com/aws/aws-cdk" rel="noopener noreferrer"&gt;AWS CDK&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting the stage
&lt;/h2&gt;

&lt;p&gt;In this post, we'll look at setting up multi-account DNS for our AWS accounts. It starts with having a central (network) account that owns the DNS for the root domain. ( exampledomain.com ). To let teams create their applications, manage their subdomains, and request SSL certificates from ACM (AWS Certificate Manager), we wanted to let them own and manage the subdomain for a specific environment. In essence, we would have 4 AWS accounts with their own DNS management.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Creating and Managing Hosted Zones
&lt;/h2&gt;

&lt;p&gt;Implementing DNS in AWS is handled by the Amazon Route 53 service. To implement DNS in Route 53 we need to use a Route 53 hosted zone. A hosted zone is a container within Route 53 where you store and manage the DNS records for a specific domain or subdomain. There are two types of hosted zones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Public Hosted Zone&lt;/strong&gt; : Used to manage the DNS records for a domain or subdomain that is publicly accessible on the internet. Example: A website like &lt;a href="https://exampledomain.com" rel="noopener noreferrer"&gt;exampledomain.com&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Private Hosted Zone&lt;/strong&gt; : Used to manage DNS records for a domain within a Virtual Private Cloud (VPC). Example: Internal services accessed only within a private network, like &lt;a href="https://internaldomain.com" rel="noopener noreferrer"&gt;internaldomain.com&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our case, we want to create a public hosted zone for our top-level domain. To do that with AWS CDK you can use the &lt;strong&gt;PublicHostedZone&lt;/strong&gt; construct.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parentHostedZone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PublicHostedZone&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;parent-hosted-zone&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="na"&gt;zoneName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exampledomain.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code should be part of the stack deployed to the central (networking) account. Now lets look at what is being created when we created our public hosted zone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Types of DNS records in Route53
&lt;/h2&gt;

&lt;p&gt;When you create a public hosted zone, you will by default get two DNS records as part of the hosted zone.&lt;/p&gt;

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

&lt;p&gt;The hosted zone will contain both a NS record as well as a SOA record, but what do these records mean?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;NS record&lt;/strong&gt; - NS stands for 'nameserver,' and &lt;strong&gt;the nameserver record indicates which DNS server is authoritative for that domain&lt;/strong&gt; (i.e. which server contains the actual DNS records). NS records tell the Internet where to go to find out a domain's IP address.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SOA record&lt;/strong&gt; - The DNS start of authority (SOA) record stores important information about a domain or zone such as the email address of the administrator, when the domain was last updated, and how long the server should wait between refreshes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To tell the client (for instance a web browser) where to resolve the subdomain, we can explicitly define in route53 that the authoritative domain servers for the subdomain are located elsewhere by adding an explicit NS record to the hosted zone of the root/parent domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting the hosted zone in the central account with the hosted zone in the child account
&lt;/h2&gt;

&lt;p&gt;To manage the DNS for the subdomain in the environment-specific account, we will need to also create a route 53 hosted zone in the environment specific (dev, staging, prod) account. Our next would be to specify that the environment specific account is responsible for the sub-domain we will need to explicitly state the relationship in the Route53 hosted zone of the central account.&lt;/p&gt;

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

&lt;p&gt;The following table shows an example of how to define the authoritative NS servers for the subdomain dev.exampledomain.com. In the example we show a single value for the NS server, but when working with route53 you will always have four DNS servers.&lt;/p&gt;

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

&lt;p&gt;Now, when a client tries to resolve the DNS for &lt;strong&gt;dev.exampledomain.com&lt;/strong&gt; , it will first try to find the NS servers for &lt;strong&gt;.com&lt;/strong&gt; , after which it will try to resolve &lt;strong&gt;exampledomain.com,&lt;/strong&gt; followed by resolving the &lt;strong&gt;dev.exampledomain.com&lt;/strong&gt; before reaching the correct authoritative domain servers. Adding an additional NS server will cause one additional hop before it can resolve the DNS, so keep that in mind when implementing this solution.&lt;/p&gt;

&lt;p&gt;We could create the NS record(s) for the dev or staging account in the public-hosted zones of the central account via the AWS console, but ideally, you would want to automate the entire process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing Sub-zone DNS delegation
&lt;/h2&gt;

&lt;p&gt;As mentioned before we wanted teams to be able to manage sub-domain records for their accounts. This would allow them to create &lt;strong&gt;applicationname.dev.exampledomain.com&lt;/strong&gt;. Having control over the DNS for the subdomain within the corresponding account will make it easy for them to create specific domain names for applications, or create DNS-validated HTTPS certificates (big plus for automation compared to email-validated certificates). To be able to do so in AWS CDK the easiest way is to create an IAM role that the Dev account can assume when trying to insert/update the NS records for its subdomain in the hosted zone of the parent domain.&lt;/p&gt;

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

&lt;p&gt;To create such a role in the central account you can choose to do this by using an organization(al unit) id or by using an account id. Lets take a look at some examples.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Role&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ParentZoneOrganizationRole&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="na"&gt;assumedBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OrganizationPrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;o-xxxxxxx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;roleName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HostedZoneDelegationRole&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example, we specify that any account being part of a specific organization or organizational unit can assume the role to perform the changes on the hosted zone. However, if you want to be specific you can also opt for creating a specific role per account that is allowed to perform this kind of changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Role&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ParentZoneAccountRole&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="na"&gt;assumedBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AccountPrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123456789&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
  &lt;span class="na"&gt;roleName&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-HostedZoneDelegationRole&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the role name in this example Ive used the prefix part of the subdomain, but you can of course also use the AWS account id or something that shows for which subdomain the role is meant. To allow the role to change the hosted zone in the networking account a &lt;strong&gt;grant&lt;/strong&gt; can be given by means of the &lt;strong&gt;grantDelegation&lt;/strong&gt; method on the HostedZone construct.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;parentHostedZone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grantDelegation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will add the permissions to add or change records of type NS in the parent hosted zone. However, when inspecting policies I found that the IAM permissions were a bit too permissive for my liking. The following code snippet is taken from the AWS CDK v2 source code (at Feb 5th 2024):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;makeGrantDelegation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;grantee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;IGrantable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hostedZoneArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Grant&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;g1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Grant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addToPrincipal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;grantee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;route53:ChangeResourceRecordSets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;resourceArns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hostedZoneArn&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="na"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ForAllValues:StringEquals&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;route53:ChangeResourceRecordSetsRecordTypes&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NS&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;route53:ChangeResourceRecordSetsActions&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UPSERT&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;DELETE&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="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;g2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Grant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addToPrincipal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;grantee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;route53:ListHostedZonesByName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; 
    &lt;span class="na"&gt;resourceArns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&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;return&lt;/span&gt; &lt;span class="nx"&gt;g1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;g2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see the role allows all accounts (when used with an OrganizationPrincipal) to assume the role and allows these accounts to update NS records in the parent hosted zone without any strict validation on the subdomain (it does for record type). In my case, I want to limit that to a specific subdomain so that only the dev account can change the NS records in the parent zone for the &lt;em&gt;dev.exampledomain.com&lt;/em&gt; subdomain and not for instance change the NS records for the &lt;em&gt;prod.exampledomain.com&lt;/em&gt; subdomain. So how can we achieve this?&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Limiting the delegation scope
&lt;/h2&gt;

&lt;p&gt;To prevent this, a custom IAM policy needs to be created for the role that limits the scope to a specific subdomain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Validate this can only happen for the dev.exampledomain.com sub-domain&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;conditions&lt;/span&gt; &lt;span class="o"&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;ForAllValues:StringEquals&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;route53:ChangeResourceRecordSetsRecordTypes&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NS&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;route53:ChangeResourceRecordSetsActions&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UPSERT&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;DELETE&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;route53:ChangeResourceRecordSetsNormalizedRecordNames&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dev.exampledomain.com&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="p"&gt;};&lt;/span&gt; 
&lt;span class="c1"&gt;// Allow the role to perform the GetHostedZone and ChangeResourceRecordSets methods on the recordset for the subdomain. &lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recordSetPolicyStatement&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;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PolicyStatement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;route53:GetHostedZone&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="s2"&gt;route53:ChangeResourceRecordSets&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; 
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;parentDomainHostedZone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostedZoneArn&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; 
  &lt;span class="na"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;conditions&lt;/span&gt; 
&lt;span class="p"&gt;});&lt;/span&gt; 

&lt;span class="c1"&gt;// Allow the role to list hosted zones by name &lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;zoneListingPolicyStatement&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;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PolicyStatement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;  
  &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;route53:ListHostedZonesByName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; 
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&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;const&lt;/span&gt; &lt;span class="nx"&gt;policyDocument&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;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PolicyDocument&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
  &lt;span class="na"&gt;statements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;recordSetPolicyStatement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;zoneListingPolicyStatement&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we've set up our policy all we need to do is assign that the IAM role.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Set explicit inline policies for the &lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Role&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ParentZoneAccountRole&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="na"&gt;assumedBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AccountPrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123456789&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
  &lt;span class="na"&gt;roleName&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-HostedZoneDelegationRole&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;inlinePolicies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="na"&gt;delegation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;policyDocument&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;That it for the work we need to do in the central account. Now lets move on to what we need to do in the sub account.&lt;/p&gt;

&lt;h2&gt;
  
  
  DNS in the sub-account
&lt;/h2&gt;

&lt;p&gt;In the sub accounts (dev, staging, prod) we will need to create a public hosted zone so we can manage subdomains for the applications living in the accounts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subDomainHostedZone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PublicHostedZone&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;subDomainHostedZone&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="na"&gt;zoneName&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.exampledomain.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we've created the hosted zone we want to publish the NS record information into the central hosted zone, so we can perform the NS record delegation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// construct the ARN for our cross account role &lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delegationRoleArn&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;formatArn&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
  &lt;span class="na"&gt;account&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rootAccountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;role&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;resourceName&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-HostedZoneDelegationRole&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;iam&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="c1"&gt;// Get the role by ARN &lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delegationRole&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromRoleArn&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DelegationRole&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;delegationRoleArn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;span class="c1"&gt;// create a cross account hosted zone delegation record (NS) &lt;/span&gt;

&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CrossAccountZoneDelegationRecord&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DelegationRecord&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nx"&gt;delegationRole&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="nx"&gt;subDomainHostedZone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;parentHostedZoneName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exampledomain.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_route53.CrossAccountZoneDelegationRecord.html" rel="noopener noreferrer"&gt;CrossAccountZoneDelegationRecord&lt;/a&gt; is a &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudformation-customresource.html" rel="noopener noreferrer"&gt;CloudFormation CustomResource&lt;/a&gt; that will create assume the role and create the NS records in the hosted zone of the central account based on the NS servers of the hosted zone for the subdomain.&lt;/p&gt;

&lt;p&gt;Now that we have the hosted zone in place for the sub-account it should be a matter of some simple lines of CDK code for generating DNS records and certificates for applications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;certificate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Certificate&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;appSubDomainHostedZoneCert&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="na"&gt;domainName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`applicationname.dev.exampledomain.com`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;validation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CertificateValidation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromDns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subDomainHostedZone&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;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Implementing DNS management in a multi-account setup can be a bit challenging at first if youve never done this before. Using of AWS CDK you can add the required constructs that will allow you to perform sub-zone delegation. Limiting down the scope of what the cross-account role can do takes a bit more effort. While writing this post I learned there is an open issue for implementing a similar behaviour for permission limitation registered in the AWS CDK project &lt;a href="https://github.com/aws/aws-cdk/issues/28078" rel="noopener noreferrer"&gt;https://github.com/aws/aws-cdk/issues/28078&lt;/a&gt;. Lets hope it becomes part of the CDK, so it will save us some time.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cdk</category>
      <category>dns</category>
    </item>
    <item>
      <title>AWS re:Invent 2024 Day 4</title>
      <dc:creator>Jeroen Reijn</dc:creator>
      <pubDate>Mon, 09 Dec 2024 19:04:22 +0000</pubDate>
      <link>https://dev.to/aws-builders/aws-reinvent-2024-day-4-4ej4</link>
      <guid>https://dev.to/aws-builders/aws-reinvent-2024-day-4-4ej4</guid>
      <description>&lt;p&gt;Thursday, the day of the highly anticipated Dr. Werner Vogels keynote. On Wednesday I already heard about the fact that you would probably have to be in the queue around 6.30-7.00 am to be able to get into the keynote room. Because I did not sleep very well over the last couple of days I decided to take a bit of time in the morning to have breakfast in the Caesars Palace and watch the keynote from my hotel room before heading out for my first session of the day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dr. Werner Vogels keynote
&lt;/h2&gt;

&lt;p&gt;In his keynote Dr. Werner Vogels focussed on the concept of simplicity vs complexity.&lt;/p&gt;

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

&lt;p&gt;Building systems requires constant thought and with the ongoing demand from businesses, systems are always changing. Over time you need to be aware of the growing complexity of the system(s) at hand. He highlighted some great examples i’ve seen in real-life:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Declining feature velocity&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Time consuming debugging&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Frequent escalation&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The push for simplicity is important and he shared the lessons learned at Amazon.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Evolvability as a Requirement&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Break Complexity into Pieces&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Align Teams with Architecture&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Organize into Cells&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Automate Complexity&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Design Predictable Systems&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;He continued his story with principles required to build evolvable systems. In his talk there are great examples of evolving systems like S3 and CloudWatch over time. There are many lessons to learn from this keynote as an engineer/architect/CTO, so if you havent seen it yet, I highly recommend &lt;a href="https://www.youtube.com/watch?v=aim5x73crbM" rel="noopener noreferrer"&gt;watching it on Youtube&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;During the keynote there were two great customer stories by Canva and Too Good To Go (TGTG). Both shared an incredible story about the growth they had and how their systems and architecture design needed to adapt. It was nice to hear the story from TGTG about their migration from PHP to Java with Spring Framework to achieve clear boundaries and more performance by o.a. the great off the shelf features of Spring, transactions, connection pooling, etc. The story was a good step up to Aurora DSQL as TGTG wanted to scale globally into different regions where they for now had to duplicate their architecture.&lt;/p&gt;

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

&lt;p&gt;Werner continued with a deep dive on why they created Aurora DSQL (Distributed SQL) and how it can solve some of the challenges TGTG was facing during their growth phase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is your Serverless application ready for production? (SVS313)
&lt;/h2&gt;

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

&lt;p&gt;In this chalk talk by &lt;a href="https://sailes.co.uk" rel="noopener noreferrer"&gt;Mark Sailes&lt;/a&gt; and &lt;a href="https://tmmr.uk" rel="noopener noreferrer"&gt;Thomas Moore&lt;/a&gt; we looked at three different serverless architectures and evaluated the production readiness against 5 pillars of the Well Architected Framework. This was a really good session with lots of interaction with the audience. It was also great to finally meet Mark in person, after having multiple conversation on Twitter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimize Amazon DynamoDB performance using AWS SDK for Java 2.x (DAT414)
&lt;/h2&gt;

&lt;p&gt;For the final session of the day I went back to the Wynn for a code talk by &lt;a href="https://x.com/arjanschaaf" rel="noopener noreferrer"&gt;Arjan Schaaf&lt;/a&gt; and &lt;a href="https://michaelshao.com/" rel="noopener noreferrer"&gt;Michael Shao&lt;/a&gt;. This session dived deep into using the Java SDK when using DynamoDB. We looked at several different factors like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;synchronous versus asynchronous clients&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;client initialisation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;different http clients like Apache http, aws-crt and url-connection client&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;pre-initialisation of the data model&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;client side and server side metrics&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;This code talk showed some really nice and example and Arjan showed different steps with their impact on latency. Even though there are some sessions on Friday morning, this was my final re:invent session as on Friday we would be going home.&lt;/p&gt;

&lt;h2&gt;
  
  
  re:Play
&lt;/h2&gt;

&lt;p&gt;Thursdays at re:invent end with a party at the Las Vegas festival grounds. After a busy week with connecting and learning it was time for some relaxation. There was so much to do like roller disco, great food, drinks and some great headline artists like Weezer and Zedd.&lt;/p&gt;

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

&lt;p&gt;The party was great! I’m a big fan or EDM, so I really enjoyed Zedds performance!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
    </item>
    <item>
      <title>AWS re:Invent 2024 Day 3</title>
      <dc:creator>Jeroen Reijn</dc:creator>
      <pubDate>Mon, 09 Dec 2024 16:30:00 +0000</pubDate>
      <link>https://dev.to/aws-builders/aws-reinvent-2024-day-3-350m</link>
      <guid>https://dev.to/aws-builders/aws-reinvent-2024-day-3-350m</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/aws-builders/aws-reinvent-2024-day-2-747"&gt;Day 2&lt;/a&gt; ended with a great global AWS Community Builder mixer. On day 3 of re:invent I had to skip the opening keynote by Dr. Swami Sivasubramanian VP AI &amp;amp; Data, because I planned to start the day with “DAT404: Advanced data modelling with Amazon DynamoDB” by &lt;a href="https://www.alexdebrie.com" rel="noopener noreferrer"&gt;Alex DeBrie&lt;/a&gt;. Alex is a great speaker and author of the &lt;a href="https://dynamodbbook.com" rel="noopener noreferrer"&gt;DynamoDB book&lt;/a&gt;, so I really wanted to attend this session in person. When I arrived at the session I quickly learned I wasn’t the only one skipping the keynote. The queue went around the corner, but luckily I had reserved a seat so I did not have to get into the Walk-up line.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F77rfqwrzt4wxjmmpt8gt.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F77rfqwrzt4wxjmmpt8gt.jpeg" alt="Long queue for the session" width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced data modelling with Amazon DynamoDB
&lt;/h2&gt;

&lt;p&gt;Alex discussed a lot of different concerns when working with DynamoDB:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Data modelling basics (collections, denormalization, LSIs, GSIs)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DynamoDB + napkin math (always validate what working with DynamoDB will costs you)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DynamoDB Stream and how you can disconnect applications from having too many responsibilities like writhing to both DynamoDB and SQS as the same time.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Again I learned quite a few interesting lessons, so that talk was definitely worth it.&lt;/p&gt;

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

&lt;p&gt;After the session I went to the AWS Community Hub to meet some fellow builders, watch the replay of the keynote, and finish my day 2 post. The Community Hub was located inside the Venetian shopping area which is a crazy place if you’ve never been to Vegas before. It contains some elements of actual Venice like a canal with gondola’s, palazzo’s, etc.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  AI and Data keynote by Dr. Swami Sivasubramanian
&lt;/h2&gt;

&lt;p&gt;The keynote showed some interesting new releases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Amazon Bedrock:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bedrock Marketplace&lt;/strong&gt; - provides generative AI developers access to over 100 publicly available and proprietary foundation models (FMs)&lt;/li&gt;
&lt;li&gt;Text-to-image models from &lt;strong&gt;Stability AI&lt;/strong&gt; are added&lt;/li&gt;
&lt;li&gt;Generating high-quality video clips from text and images with the models from &lt;strong&gt;Luma AI&lt;/strong&gt; are added to Bedrock&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Poolside's Assistant&lt;/strong&gt; is added to Bedrock powered by its malibu and point models&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt caching&lt;/strong&gt; for supported models. Caches frequently used prompts across multiple API calls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt Routing&lt;/strong&gt; - Uses prompt matching and model understanding techniques, Intelligent Prompt Routing predicts the performance of each model for each request and dynamically routes each request to the model that it predicts is most likely to give the desired response at the lowest cost&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bedrock Knowledge Bases&lt;/strong&gt; offers an end-to-end managed workflow for customers to build custom generative AI applications that can access and incorporate contextual information from a variety of structured and unstructured data sources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bedrock Knowledge Bases&lt;/strong&gt; now supports &lt;strong&gt;GraphRAG,&lt;/strong&gt; providing more accurate and comprehensive responses to end users by using RAG techniques combined with graphs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bedrock Data Automation&lt;/strong&gt; enables developers to automate the generation of valuable insights from unstructured multimodal content such as documents, images, video, and audio to build GenAI-based applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bedrock Guardrails Multimodel toxicity detection&lt;/strong&gt; - enabling the detection and filtration of undesirable and potentially harmful image content while retaining safe and relevant visuals.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Amazon Q:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Q Developer is now available in &lt;strong&gt;SageMaker Canvas&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Is now available in &lt;strong&gt;Quicksight Scenarios&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Before heading out of to my next session, which was in the furthest away location, I decided to walk the expo a bit. The expo hall is HUGE with so many vendors promoting their products. It was great meeting many different vendors and catching up with great projects like &lt;a href="https://www.localstack.cloud" rel="noopener noreferrer"&gt;LocalStack&lt;/a&gt; and &lt;a href="https://www.elastic.co" rel="noopener noreferrer"&gt;Elastic&lt;/a&gt;, which I’ve used over the years.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Deep dive intro Amazon Aurora DSQL and it’s architecture
&lt;/h2&gt;

&lt;p&gt;After the EXPO I took the transit to the Mandalay Bay convention center to attend Marc Brooker’s deep dive session on the architecture behind &lt;a href="https://aws.amazon.com/blogs/database/introducing-amazon-aurora-dsql/" rel="noopener noreferrer"&gt;Aurora DSQL&lt;/a&gt;, the new serverless database introduced by AWS.&lt;/p&gt;

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

&lt;p&gt;This was a very interesting break-out session (recording should be up soon) about the challenges of designing and running a distributed database. It was interesting to hear how they split of a traditional monolithic database into separate services with their own responsibilities. Marc has a &lt;a href="https://brooker.co.za/blog/" rel="noopener noreferrer"&gt;series of blogs posts&lt;/a&gt; about DSQL, which I highly recommend reading if you’re interested in DSQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  EMEA networking reception
&lt;/h2&gt;

&lt;p&gt;The day ended with returning back to the Venetian before heading out to the Wynn for the EMEA networking reception in the XS night club. Finding your way around these hotels and convention centers can be a bit of a challenge, especially when it’s your first time. When I finally found the night club it was quite busy, as to be expected for a conference with about 60.000 people, and I enjoyed a nice evening with great food and nice music.&lt;/p&gt;

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

</description>
      <category>aws</category>
      <category>cloud</category>
    </item>
    <item>
      <title>AWS re:Invent 2024 Day 2</title>
      <dc:creator>Jeroen Reijn</dc:creator>
      <pubDate>Wed, 04 Dec 2024 20:00:44 +0000</pubDate>
      <link>https://dev.to/aws-builders/aws-reinvent-2024-day-2-747</link>
      <guid>https://dev.to/aws-builders/aws-reinvent-2024-day-2-747</guid>
      <description>&lt;p&gt;Today was the second day of re:invent and the day was packed with exciting events. These days are full of sessions, social events and casual conversations at the Expo, Community Hub or just in line while waiting to get into a room.&lt;/p&gt;

&lt;h2&gt;
  
  
  The CEO keynote with Matt Garman
&lt;/h2&gt;

&lt;p&gt;In the morning I kicked off the day with the keynote by AWS CEO Matt Garman. I quickly learned that walking up 30 minutes before isn't enough to get into the room. Who would have guessed right with 60.000 people attending re:invent. Luckily there are many overflow rooms available 😅 I thought Matt was killing it 🔥 during the keynote. He introduced some great new announcements and as expected the keynote contained quite some new innovations around Generative AI. However, I thought it was well positioned by talking about the use case and customer requests. It didn't feel like GenAI "because of GenAI" like last year.&lt;/p&gt;

&lt;p&gt;There are definitely some announcements I want to explore further over the next period. Some of the highlights for me were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The improvements for Amazon Q (unit test, documentation, code reviews, integration with GitLab Duo and transformations for .Net, VMWare and Mainframe).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The introduction of Amazon Aurora DSQL&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The introduction of Amazon SageMaker Unified Studio&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The introduction of Amazons own foundational models: Nova&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to see all announcements from re:invent don't forget to keep your eye on &lt;a href="https://aws.amazon.com/blogs/aws/top-announcements-of-aws-reinvent-2024/" rel="noopener noreferrer"&gt;the top re:invents announcements blog&lt;/a&gt;.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Supercharge your Lambda functions with Lambda Powertools 🚀
&lt;/h2&gt;

&lt;p&gt;Just after the keynote I attended the Dev session by &lt;a href="https://www.linkedin.com/in/raphael-manke/" rel="noopener noreferrer"&gt;Raphael Manke&lt;/a&gt; on &lt;a href="https://docs.powertools.aws.dev/lambda/typescript/latest/" rel="noopener noreferrer"&gt;Lambda Power Tools&lt;/a&gt;. Raphael gave one heck of a demo and showed how Lambda power tools can supercharge your lambda functions and help improve observability, idempotency, batch handling and much more.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  The EMEA Community Builders Mixer
&lt;/h2&gt;

&lt;p&gt;Next up was a EMEA community mixer in the Buddy V restaurant. It was great meeting many EMEA based Community Builders. I got to meet Lee Gilmore there who has an excellent newsletter called "&lt;a href="https://serverlessadvocate.substack.com" rel="noopener noreferrer"&gt;Serverless Advocate&lt;/a&gt;" (be sure to check it out at). These mixer events are great for getting to know people and learn about what other community members are doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mitigating noisy neighbour issues for multi-tenant SQS queues
&lt;/h2&gt;

&lt;p&gt;After the mixer I headed to the MGM Grand together with &lt;a href="https://www.linkedin.com/in/pubudusj/" rel="noopener noreferrer"&gt;Pubudu Jayawardana&lt;/a&gt; to attend a chalk talk about mitigating the noisy neighbours problem when having multi-tenant SQS queues.&lt;/p&gt;

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

&lt;p&gt;They showed the following strategies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Having a single queue per tenant&lt;/li&gt;
&lt;li&gt;Separate queues for noisy neighbours&lt;/li&gt;
&lt;li&gt;Sharding to different queues (for instance with a hash function)&lt;/li&gt;
&lt;li&gt;Shuffle sharding&lt;/li&gt;
&lt;li&gt;Dynamic overflow queues&lt;/li&gt;
&lt;li&gt;Using SNS Topics and queues and downstream sources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It was my first chalk talk and I'm definitely going to attend more of these as I really liked the open format and white boarding with AWS specialists.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Global AWS Community Builder mixer
&lt;/h2&gt;

&lt;p&gt;After a quick workshop session on solving Idempotency issues in distributed systems it was time to head back for a Community Builders mixer with all community builders present at the AWS summit. It was great meeting Ayman Metwally, Jacob Verhoeks , Ivan Casco Valero, Rob Van Pamel, Fernando Paz, Jocelyn Poblete, Michael Liendo, Matt Bacchi, Arijita Mitra and Paloma Lataliza.&lt;/p&gt;

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

&lt;p&gt;Jeff Bar was present and handed out some unique Builder Cards. For those of you who dont know, Jeff started the AWS blog about 20 years ago. It was also great to meet the people that bring this community together Jason Dunn, 🌤 Farrah Campbell and Thembile Martis!&lt;/p&gt;

&lt;p&gt;Day two was another great day. The jetlag and long days are starting to wear on me a bit, but Im looking forward to day 3!&lt;/p&gt;

</description>
      <category>aws</category>
    </item>
    <item>
      <title>AWS re:Invent 2024 Day 1</title>
      <dc:creator>Jeroen Reijn</dc:creator>
      <pubDate>Wed, 04 Dec 2024 06:35:29 +0000</pubDate>
      <link>https://dev.to/aws-builders/aws-reinvent-2024-day-1-3466</link>
      <guid>https://dev.to/aws-builders/aws-reinvent-2024-day-1-3466</guid>
      <description>&lt;p&gt;Day 1 of AWS re:Invent just ended and I'm now writing this update while watching the replay of Monday Nights keynote with Peter DeSantis. I couldn't attend the keynote due to a customer survey session, but I always enjoy the balance of in depth knowledge on the latest development in compute.&lt;/p&gt;

&lt;p&gt;Today was mixed with sessions and social events. For the sessions I tried to focus on non-breakout session and attended a couple of code talks which was great. Code talks focus on particular topics while diving into (example) code and are not recorded (most of the breakout sessions are) so you cant watch them after the conference. To not stick to what I know I explored some unfamiliar topics as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Sessions&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;During the day I attended several sessions which I want to share something about.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;STG351 - Automate data protection at scale with AWS Backup&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This was a great talk given by Khurram Nizami and Yangsoo(Leo) Park on AWS Backup in a Cross Account and Cross region setup. After applying tag based strategies for backup plans it introduced me to the concept of backup (test) validation. Overall very good session. You can find the git repo for the demo project on Github: &lt;a href="https://github.com/aws-samples/backup-recovery-with-aws-backup" rel="noopener noreferrer"&gt;https://github.com/aws-samples/backup-recovery-with-aws-backup&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd3nwjrvieptgjj7dl3bs.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd3nwjrvieptgjj7dl3bs.jpeg" alt="/assets/2024/reinvent/aws-backup.jpeg" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;COP405 - Coding for account customizations with AWS Control Tower with Welly Siauw and Justin P.&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Even though I dont work with AWS Control Tower in combination with the Account Factory for Terraform (AFT), this sessions was quite insightful. It gave me a good idea on the complexity of managing large organizations. You can find the sample repository here &lt;a href="https://github.com/aws-samples/aft-workshop-sample" rel="noopener noreferrer"&gt;https://github.com/aws-samples/aft-workshop-sample&lt;/a&gt;.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  &lt;strong&gt;COP318 - Streamlining incident response with AWS Systems manager with Jean Velez Torres and Raviteja Sunkavalli&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Very interesting session which introduced me to Systems Manager Automation for building automated incident response handling. By means of SSM Automation they showed how to could build a workflow similar to AWS Step functions for mitigating failures. They did this all live which was pretty great to see. You can find the code for this session at &lt;a href="https://github.com/sunkavar/reInvent2024-COP31" rel="noopener noreferrer"&gt;https://github.com/sunkavar/reInvent2024-COP318&lt;/a&gt;&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Taking a short break
&lt;/h3&gt;

&lt;p&gt;After this session I took a bit of a breather and went to the AWS Community Hub. The Community Hub is a dedicated spot for AWS Community Builders, AWS Heroes and AWS Community Leaders to take a step back and relax. At the Community Hub I got to meet &lt;a href="https://aws.amazon.com/developer/community/heroes/johannes-koch/" rel="noopener noreferrer"&gt;Johannes Koch&lt;/a&gt;, &lt;a href="https://x.com/julian_wood" rel="noopener noreferrer"&gt;Julian Wood&lt;/a&gt;, &lt;a href="https://www.ranthebuilder.cloud" rel="noopener noreferrer"&gt;Ran Isenberg&lt;/a&gt;, &lt;a href="https://x.com/raphaelmanke" rel="noopener noreferrer"&gt;Raphael Manke&lt;/a&gt; and &lt;a href="https://aws.amazon.com/developer/community/heroes/faye-ellis/" rel="noopener noreferrer"&gt;Faye Ellis&lt;/a&gt; in person. It's always great to meet people you follow or converse with online. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;SVS401 - Best practices for Serverless developers with Ran Isenberg and Julian Wood&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This breakout sessions was high on my wish list and looking at the walk-up line just before the session, I wasnt the only one. If youre into serverless or thinking about using serverless this is the session for you! The session was packed with valuable insights and I highly recommend 💯 watching this session when it becomes available (it was recorded) ! It was great watching this talk with &lt;a href="https://www.linkedin.com/in/karlrobinson/" rel="noopener noreferrer"&gt;Karl Robinson&lt;/a&gt; whose podcast I regularly consume on my daily commute.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6ueiiewzm6y55icbmqr.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6ueiiewzm6y55icbmqr.jpeg" alt="/assets/2024/reinvent/aws-lambda.jpeg" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Reflecting on the day&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;I ended my day in the Expo hall and it was great to connect with &lt;a href="https://aws.amazon.com/developer/community/heroes/martijn-van-dongen/" rel="noopener noreferrer"&gt;Martijn van Dongen&lt;/a&gt;, &lt;a href="https://pubudu.dev" rel="noopener noreferrer"&gt;Pubudu Jayawardana&lt;/a&gt;, &lt;a href="https://www.linkedin.com/in/carlovollebregt/" rel="noopener noreferrer"&gt;Carlo Vollebregt&lt;/a&gt; and &lt;a href="https://www.youtube.com/@studywithnova" rel="noopener noreferrer"&gt;Nova Lailatul Rizkiyah&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While doing some mid night snack shopping on the way to the hotel I ended up having a lovely conversation with &lt;a href="https://www.linkedin.com/in/camille-nigon/" rel="noopener noreferrer"&gt;Camille Nigon&lt;/a&gt; about what it feels like to attend re:invent for the first time. A lot of people had warned me about how massive and impressive it is, but you only know what it feels like when you actually attend.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>reinvent2024</category>
    </item>
    <item>
      <title>My Journey with AWS CDK and Java: What You Need to Know</title>
      <dc:creator>Jeroen Reijn</dc:creator>
      <pubDate>Mon, 02 Sep 2024 09:25:25 +0000</pubDate>
      <link>https://dev.to/aws-builders/my-journey-with-aws-cdk-and-java-what-you-need-to-know-456j</link>
      <guid>https://dev.to/aws-builders/my-journey-with-aws-cdk-and-java-what-you-need-to-know-456j</guid>
      <description>&lt;p&gt;One of the first decisions you’ll need to make when working with the &lt;a href="https://aws.amazon.com/cdk/" rel="noopener noreferrer"&gt;AWS Cloud Development Kit (CDK)&lt;/a&gt; is choosing the language for writing your Infrastructure as Code (IaC). The CDK currently supports TypeScript, JavaScript, Python, Java, C#, and Go. Over the past few years, I’ve worked with the CDK in Typescript, Python and Java. While there is ample information available online for TypeScript and Python, this post aims to share my experience using Java as the language of choice for the AWS CDK.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;em&gt;Wait…. what? Using Java with the AWS CDK?&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Some may say that Typescript is the most obvious language to use while working with the AWS CDK. The CDK itself is written in Typescript and it’s also the most used language &lt;a href="https://matthewbonig.com/posts/community-survey-2023/" rel="noopener noreferrer"&gt;according to the 2023 CDK Community Survey&lt;/a&gt;. Java is coming in 3rd place with a small percentage of use.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiynrv17hjg0902m8ppd5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiynrv17hjg0902m8ppd5.png" alt="Languages used by users of the AWS CDK. Image source https://matthewbonig.com/posts/community-survey-2023/" width="800" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Image source &lt;a href="https://matthewbonig.com/posts/community-survey-2023/" rel="noopener noreferrer"&gt;https://matthewbonig.com/posts/community-survey-2023/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I do wonder if this still holds true given the number of responses to the survey. I’ve worked with small businesses and large enterprise organizations over the last years, and I see more and more Java oriented teams move their workloads to AWS while adopting AWS CDK as their Infrastructure as Code tool. Depending on the type of service(s) being built by these teams they may or may not have any experience with either Python or Typescript and the NodeJs ecosystem, which makes sticking to Java an easy choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  General observations
&lt;/h2&gt;

&lt;p&gt;From what I’ve seen, adopting the CDK in Java is relatively easy for most of these teams as they already understand the language and the ecosystem. Integrating the CDK with their existing build tools like &lt;a href="https://maven.apache.org" rel="noopener noreferrer"&gt;Maven&lt;/a&gt; and &lt;a href="https://gradle.org" rel="noopener noreferrer"&gt;Gradle&lt;/a&gt; is well documented, which leaves them with the learning curve of understanding how to work with infrastructure as code, how to structure a CDK project and when to use &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/constructs.html" rel="noopener noreferrer"&gt;L1, L2 and L3 constructs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Compared to Typescript the CDK stacks and constructs written in Java contain a bit more boilerplate code and therefor might feel a bit more bloated if you come from a different language. I personally don’t feel this makes the code less readable and with modern IDE’s and coding assistants I don’t feel I’m less productive.&lt;/p&gt;

&lt;p&gt;The CDK also seems to become more widely adopted in the Java community with more recent Java frameworks like &lt;a href="https://micronaut.io" rel="noopener noreferrer"&gt;Micronaut&lt;/a&gt; even having built-in support for AWS CDK in the framework.&lt;/p&gt;

&lt;p&gt;See for instance the following Micronaut launch configurations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://micronaut.io/launch?type=DEFAULT&amp;amp;javaVersion=JDK_11&amp;amp;features=aws-lambda&amp;amp;features=aws-cdk&amp;amp;features=amazon-api-gateway" rel="noopener noreferrer"&gt;&lt;strong&gt;Micronaut Application with API Gateway and CDK for Java runtime&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://micronaut.io/launch?type=FUNCTION&amp;amp;javaVersion=JDK_11&amp;amp;features=aws-lambda&amp;amp;features=aws-cdk&amp;amp;features=amazon-api-gateway" rel="noopener noreferrer"&gt;&lt;strong&gt;Micronaut Function with API Gateway and CDK for Java runtime&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of the advantages of Java is that it’s a statically-typed language, which means it will catch most CDK coding errors during compile-time. There are still some errors which you will only see during an actual &lt;code&gt;cdk synth&lt;/code&gt; or &lt;code&gt;cdk deploy&lt;/code&gt;. For instance, some constructs have required properties which will only become visible if you try to synthesize the stack, but in my experience, you will have that in other languages as well.&lt;/p&gt;

&lt;p&gt;Performance wise it feels like the CDK in Java is a bit slower compared to using it Typescript or any other interpreted language. I’ve not measured this, but it’s more of a gut feeling. This might have to do with the static nature of Java and its corresponding build tools and compile phase. On the other hand it might be that the &lt;a href="https://aws.github.io/jsii/overview/runtime-architecture/" rel="noopener noreferrer"&gt;JSII runtime architecture&lt;/a&gt; also has an effect and how Java is interacting with a Javascript environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Java Builders
&lt;/h2&gt;

&lt;p&gt;One of the biggest differences when using the AWS CDK with Java is the use of Builders. When creating constructs with Typescript you’re mainly using the &lt;em&gt;props&lt;/em&gt; argument (map of configuration properties) while creating a construct. Let’s take a look at an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bucket&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;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Bucket&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MyBucket&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="na"&gt;versioned&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;encryption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BucketEncryption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;KMS_MANAGED&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Java version of the above snippet uses a Builder class that follows the builder pattern for constructing the properties. If you’re unfamiliar with the Builder pattern in Java I recommend to checkout &lt;a href="https://blogs.oracle.com/javamagazine/post/exploring-joshua-blochs-builder-design-pattern-in-java" rel="noopener noreferrer"&gt;this blog post&lt;/a&gt; about using the Builder pattern. Depending on the CDK construct you might be able to define a CDK resource in two different ways.&lt;/p&gt;

&lt;p&gt;In the first example you use the Builder for the Bucket properties.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Bucket&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"MyBucket"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BucketProps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                           &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;versioned&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                           &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encryption&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BucketEncryption&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;KMS_MANAGED&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                           &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The alternative is that constructs can have their own builder class, which makes it a little less verbose and easier to read.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Bucket&lt;/span&gt; &lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt;
                           &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"MyBucket"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                           &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;versioned&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                           &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encryption&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BucketEncryption&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;KMS_MANAGED&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                           &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  IDE support
&lt;/h2&gt;

&lt;p&gt;Overall IDE support is really great when working with CDK in Java. I use IntelliJ IDEA on a daily basis and auto completion really helps when using the Builder objects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4zl07r4vg9xruwopshpt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4zl07r4vg9xruwopshpt.png" alt="Image displaying auto completion in IntelliJ IDEA for CDK constructs" width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As the CDK documentation is also inside the CDKs Java source code, looking up documentation is really easy. It’s similar to how you would do it with any kind of other object or library.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu33ux6m19y8iqhoc9q2r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu33ux6m19y8iqhoc9q2r.png" alt="Image displaying inline Java docs for constructs" width="800" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Third party construct support
&lt;/h2&gt;

&lt;p&gt;The CDK itself is written in Typescript and for each supported programming language a specific binding is generated. This means that when a new resource or feature for an AWS service is added in the Typescript variant of the CDK it’s also available to developers using a Java based CDK.&lt;/p&gt;

&lt;p&gt;Besides the default CDK constructs there are also a lot of community generated constructs. &lt;a href="https://constructs.dev" rel="noopener noreferrer"&gt;Construct Hub&lt;/a&gt; is a great place to find them.&lt;/p&gt;

&lt;p&gt;From what I’ve seen most constructs coming out of AWS will support Java as one of the default languages. Community supported constructs however might not. There are several popular constructs that only support Typescript and Python. Filtering on Construct Hub for AWS CDK v2 based constructs, sorted by programming languages results in the following data.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Language&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Number of constructs libraries&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Typescript&lt;/td&gt;
&lt;td&gt;1164&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;781&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.Net&lt;/td&gt;
&lt;td&gt;511&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;td&gt;455&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;132&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Depending on the type of infrastructure or third-party services you’re planning to use, you might not be able to use all available constructs. For instance, the constructs maintained by &lt;a href="https://constructs.dev/packages/datadog-cdk-constructs-v2/" rel="noopener noreferrer"&gt;DataDog&lt;/a&gt; are only available in Typescript, Python and Go. In my personal experience though, most construct developers are open to support Java. Third party constructs are based on &lt;a href="https://projen.io" rel="noopener noreferrer"&gt;projen&lt;/a&gt; and &lt;a href="https://aws.github.io/jsii/" rel="noopener noreferrer"&gt;jsii&lt;/a&gt;, which means that adding a Java based version is most of the time a matter of configuration in the package.json file of the project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"jsii"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outdir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"targets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"java"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"package"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"io.github.cdklabs.cdknag"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"maven"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"groupId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"io.github.cdklabs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"artifactId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cdknag"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"python"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"distName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cdk-nag"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cdk_nag"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"dotnet"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"namespace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cdklabs.CdkNag"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"packageId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cdklabs.CdkNag"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"go"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"moduleName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"github.com/cdklabs/cdk-nag-go"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tsc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lib"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"rootDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;( An example of how JSII is configured for the &lt;a href="https://github.com/cdklabs/cdk-nag" rel="noopener noreferrer"&gt;CDK NAG project&lt;/a&gt; )&lt;/p&gt;

&lt;p&gt;Once the configuration is in place and the artifacts have been pushed to for instance Maven Central, you’re good to go.&lt;/p&gt;

&lt;p&gt;When thinking about it, I once had a 3rd party construct I wanted to use that did not support Java (yet). It got added quite quickly and there was also an alternative solution for it, so I can't remember having issues with the lower number of available constructs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples, tutorials and documentation
&lt;/h2&gt;

&lt;p&gt;I think it’s good to reflect on the fact that there are more CDK examples and tutorials available in Typescript and Python compared to Java. This reflects the findings in the usage chart from the CDK Community Survey. However, reading Typescript as a Java programmer is relatively easy (my personal opinion). If you’re new to the AWS CDK there is a ton of example code available on Github, Youtube, and in numerous blog posts and tutorials. If you’re already using the CDK in combination with Java, be sure to write some blog posts or tutorials, so others can see that and benefit from your knowledge!&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Java is a very viable option when working with the AWS CDK, especially for workload teams already familiar with the language and its ecosystem. IDE support for the CDK is excellent with features like auto-completion and easy access to source code documentation.&lt;/p&gt;

&lt;p&gt;All in all, the experience is really good. Keep in mind that picking Java for your infrastructure as code all depends on the context and the environment you’re in. I would suggest picking the language which is most applicable in your specific situation. If you still need to make the choice and are already working with Java, I definitely recommend trying it out!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cdk</category>
      <category>java</category>
      <category>infrastructureascode</category>
    </item>
    <item>
      <title>Analyze and debug Quarkus based AWS Lambda functions with X-Ray</title>
      <dc:creator>Jeroen Reijn</dc:creator>
      <pubDate>Tue, 06 Feb 2024 21:30:00 +0000</pubDate>
      <link>https://dev.to/aws-builders/analyze-and-debug-quarkus-based-aws-lambda-functions-with-x-ray-3ke7</link>
      <guid>https://dev.to/aws-builders/analyze-and-debug-quarkus-based-aws-lambda-functions-with-x-ray-3ke7</guid>
      <description>&lt;p&gt;Serverless architectures have emerged as a paradigm-shifting approach to building, fast, scalable and cost efficient applications. While Serverless architectures provide unparalleled flexibility, they also introduce new challenges in terms of monitoring and troubleshooting.&lt;/p&gt;

&lt;p&gt;In this blog post, we'll explore how Quarkus integrates with &lt;a href="https://aws.amazon.com/xray/" rel="noopener noreferrer"&gt;AWS X-Ray&lt;/a&gt; and how using a Jakarta CDI Interceptor can keep your code clean while adding custom instrumentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quarkus and AWS Lambda
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://quarkus.io" rel="noopener noreferrer"&gt;Quarkus&lt;/a&gt; is a Java based framework tailored for GraalVM and HotSpot, which results in an amazingly fast boot time while having an incredibly low memory footprint. It offers near instant scale up and high density memory utilization which can be very useful for container orchestration platforms like Kubernetes or Serverless runtimes like AWS Lambda.&lt;/p&gt;

&lt;p&gt;Building AWS Lambda Functions can be as easy as &lt;a href="https://code.quarkus.io" rel="noopener noreferrer"&gt;starting a Quarkus project&lt;/a&gt;, adding the &lt;code&gt;quarkus-amazon-lambda&lt;/code&gt; dependency, and defining your AWS Lambda Handler function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.quarkus&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;quarkus-amazon-lambda&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An extensive guide on how to develop AWS Lambda Functions with Quarkus can be found in the official &lt;a href="https://quarkus.io/guides/aws-lambda" rel="noopener noreferrer"&gt;Quarkus AWS Lambda Guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enabling X-Ray for your Lambda functions
&lt;/h2&gt;

&lt;p&gt;Quarkus provides out of the box support for X-Ray, but you will need to add a dependency to your project and configure some setting to make it work with GraalVM / native compiled Quarkus applications. Let's first start with adding the &lt;code&gt;quarkus-amazon-lambda-xray&lt;/code&gt; dependency.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- adds dependency on required x-ray classes and adds support for graalvm native --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.quarkus&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;quarkus-amazon-lambda-xray&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't forget to enable tracing for your Lambda function otherwise it won't work. An example of doing that is by setting the tracing argument to &lt;strong&gt;active&lt;/strong&gt; within your AWS CDK code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"feed-parsing-function"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;...&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;memorySize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tracing&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Tracing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ACTIVE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Runtime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PROVIDED_AL2023&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logRetention&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RetentionDays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ONE_WEEK&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the deployment of your function, and a function invocation, you should be able to see the X-Ray traces from within the Cloudwatch interface. By default it will show you some basic timing information for your function like the initialization and the invocation duration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxillarjw171bc9hsk3rs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxillarjw171bc9hsk3rs.png" alt="Image displaying the X-Ray traces with minimal information" width="800" height="89"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding more instrumentation
&lt;/h2&gt;

&lt;p&gt;Now that the dependencies are in place and tracing is enabled for our function we can enrich the traces in X-Ray by leveraging the X-Ray SDKs &lt;code&gt;TracingIntercepter&lt;/code&gt; . For instance for the SQS and DynamoDB client you can explicitly set the intercepter inside the &lt;strong&gt;application.properties&lt;/strong&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;quarkus.dynamodb.async-client.type=aws-crt
quarkus.dynamodb.interceptors=com.amazonaws.xray.interceptors.TracingInterceptor
quarkus.sqs.async-client.type=aws-crt
quarkus.sqs.interceptors=com.amazonaws.xray.interceptors.TracingInterceptor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After putting these properties in place, redeploying and executing the function, the &lt;code&gt;TracingIntercepter&lt;/code&gt; will wrap around each API call to SQS and DynamoDB and store the actual trace information along side the trace.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4l1napu63wdlxvqznz1x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4l1napu63wdlxvqznz1x.png" alt="Image displaying the X-Ray traces with DynamoDB information" width="800" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is very useful for debugging purposes as it will allow you to validate your code and check for any mistakes. Requests to AWS Services are part of the pricing model, so if you make a mistake in your code and you make too many calls it can become quite costly.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1635244161778737152-958" src="https://platform.twitter.com/embed/Tweet.html?id=1635244161778737152"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1635244161778737152-958');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1635244161778737152&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom subsegments
&lt;/h2&gt;

&lt;p&gt;With the AWS SDK &lt;code&gt;TracingInterceptor&lt;/code&gt; configured we get information about the calls to the AWS APIs, but what if we want to see information about our own code or remote calls to services outside of AWS?&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java.html" rel="noopener noreferrer"&gt;Java SDK for X-Ray&lt;/a&gt; supports the concept of adding custom subsegments to your traces. You can add subsegments to a trace by adding a few lines of code to your own business logic as you can see in the following code snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;someMethod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// wrap in subsegment&lt;/span&gt;
  &lt;span class="nc"&gt;Subsegment&lt;/span&gt; &lt;span class="n"&gt;subsegment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AWSXRay&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;beginSubsegment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"someMethod"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Your business logic&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
     &lt;span class="n"&gt;subsegment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
     &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
     &lt;span class="nc"&gt;AWSXRay&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;endSubsegment&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although this is trivial to do, it will become quit messy if you have a lot of methods you want to apply tracing to. This isn't ideal and it would be better of we don't have to mix our own code with the X-Ray instrumentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quarkus and Jakarta CDI Interceptors
&lt;/h2&gt;

&lt;p&gt;The Quarkus programming model is based on the Lite version of the Jakarta Contexts and Dependency Injection 4.0 specification. Besides dependency injection the specification also describes other features like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lifecycle Callbacks&lt;/strong&gt; - A bean class may declare lifecycle &lt;code&gt;@PostConstruct&lt;/code&gt; and &lt;code&gt;@PreDestroy&lt;/code&gt; callbacks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Interceptors&lt;/strong&gt; - used to separate cross-cutting concerns from business logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Decorators&lt;/strong&gt; - similar to interceptors, but because they implement interfaces with business semantics, they are able to implement business logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Events and Observers&lt;/strong&gt; - Beans may also produce and consume events to interact in a completely decoupled fashion.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As mentioned, CDI Interceptors are used to separate cross-cutting concerns from business logic. As tracing is a cross-cutting concern this sounds like a great fit. Let's take a look at how we can create an interceptor for our AWS X-Ray instrumentation.&lt;/p&gt;

&lt;p&gt;We start with defining our interceptor binding which we will call &lt;code&gt;XRayTracing&lt;/code&gt;. Interceptor bindings are intermediate annotations that may be used to associate interceptors with target beans.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.jeroenreijn.aws.quarkus.xray&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.annotation.Priority&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.interceptor.InterceptorBinding&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.lang.annotation.Retention&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;annotation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RetentionPolicy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RUNTIME&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@InterceptorBinding&lt;/span&gt;
&lt;span class="nd"&gt;@Retention&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;RUNTIME&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Priority&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nd"&gt;@interface&lt;/span&gt; &lt;span class="nc"&gt;XRayTracing&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is to define the actual Interceptor logic, the code that will add the additional X-Ray instructions for creating the subsegment and wrapping it around our business logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.jeroenreijn.aws.quarkus.xray&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.amazonaws.xray.AWSXRay&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.interceptor.AroundInvoke&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.interceptor.Interceptor&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.interceptor.InvocationContext&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Interceptor&lt;/span&gt;
&lt;span class="nd"&gt;@XRayTracing&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;XRayTracingInterceptor&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@AroundInvoke&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="nf"&gt;tracingMethod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;InvocationContext&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;AWSXRay&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;beginSubsegment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"## "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMethod&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;proceed&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;AWSXRay&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCurrentSubsegment&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;addException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;AWSXRay&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;endSubsegment&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An important part of the interceptor is the &lt;code&gt;@AroundInvoke&lt;/code&gt; annotation, which means that this interceptor code will be wrapped around the invocation of our own business logic.&lt;/p&gt;

&lt;p&gt;Now that we've defined both our interceptor binding and our interceptor it's time to start using it. Every method that we want to create a subsegment for, can now be annotated with the &lt;code&gt;@XRayTracing&lt;/code&gt; annotation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@XRayTracing&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;SyndFeed&lt;/span&gt; &lt;span class="nf"&gt;getLatestFeed&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;InputStream&lt;/span&gt; &lt;span class="n"&gt;feedContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getFeedContent&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;getSyndFeed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;feedContent&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@XRayTracing&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;SyndFeed&lt;/span&gt; &lt;span class="nf"&gt;getSyndFeed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;InputStream&lt;/span&gt; &lt;span class="n"&gt;feedContent&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;SyndFeedInput&lt;/span&gt; &lt;span class="n"&gt;feedInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SyndFeedInput&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;feedInput&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;XmlReader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;feedContent&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;FeedException&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RuntimeException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That looks much better. Pretty clean, if I say so myself.&lt;/p&gt;

&lt;p&gt;Based on the hierarchy of subsegments for a trace, X-Ray will be able to show a nested tree structure with the timing information.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgyebckiyjofi8t123nsh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgyebckiyjofi8t123nsh.png" alt="Image displaying the X-Ray traces with custom segments" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;The integration between Quarkus and X-Ray is quite simple to enable. The developer experience is really good out of the box with defining the interceptors on a per client basis. With the help of CDI interceptors you can keep your code clean without worrying too much about X-Ray specific code inside your business logic.&lt;/p&gt;

&lt;p&gt;An alternative to building your own Interceptor might be to start using &lt;a href="https://docs.powertools.aws.dev/lambda/java/" rel="noopener noreferrer"&gt;AWS PowerTools for Lambda (Java)&lt;/a&gt;. Powertools for Java is a great way to boost your developer productivity, but it can be used for more than X-Ray, so Ill save it for another post.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>quarkus</category>
      <category>serverless</category>
      <category>observability</category>
    </item>
    <item>
      <title>The Evolution of the AWS Community Builders Blogs Twitter Bot Architecture</title>
      <dc:creator>Jeroen Reijn</dc:creator>
      <pubDate>Mon, 22 Jan 2024 15:11:15 +0000</pubDate>
      <link>https://dev.to/aws-builders/the-evolution-of-the-aws-community-builders-blogs-twitter-bot-architecture-2nb0</link>
      <guid>https://dev.to/aws-builders/the-evolution-of-the-aws-community-builders-blogs-twitter-bot-architecture-2nb0</guid>
      <description>&lt;p&gt;In 2022, when I was accepted into the AWS Community Builders program, I was amazed by the abundance of high-quality content generated by the broader AWS community. While most community builders publish their blog content on personal websites, &lt;a href="https://medium.com/" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;, or &lt;a href="https://hashnode.com/" rel="noopener noreferrer"&gt;Hashnode&lt;/a&gt;, it is common for this content to be (re)published on the &lt;a href="http://dev.to/"&gt;dev.to&lt;/a&gt; platform within the &lt;a href="https://dev.to/aws-builders"&gt;AWS Community Builders organization&lt;/a&gt;. It currently acts as a hub for blog content created by AWS community builders.&lt;/p&gt;

&lt;p&gt;I didn't frequently visit the &lt;a href="https://dev.to/"&gt;dev.to&lt;/a&gt; platform and also was not really using an RSS reader anymore, so I began exploring alternative solutions. Most of the interesting articles I usually find on Twitter/X. I sought to find an existing Twitter account sharing the community builders content but was unsuccessful. Being a builder myself, I decided to create a fresh Twitter account (&lt;a href="https://twitter.com/aws_cb_blogs" rel="noopener noreferrer"&gt;@aws_cb_blogs&lt;/a&gt;) and wanted to create a fully Serverless architecture. In this post I'll explain how the architecture has evolved over time and what are some of the things to come.&lt;/p&gt;

&lt;h2&gt;
  
  
  The initial idea
&lt;/h2&gt;

&lt;p&gt;As a first iteration I decided to put something together rather quickly. I needed three component:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;a component to read and parse the rss feed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a component to turn posts into tweets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a schedule to make sure new items are being picked up.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To initiate the entire process I needed to have a solution similar to a &lt;a href="https://en.wikipedia.org/wiki/Cron" rel="noopener noreferrer"&gt;cron&lt;/a&gt; job (scheduled job). AWS offers this in the form of &lt;a href="https://aws.amazon.com/blogs/compute/introducing-amazon-eventbridge-scheduler/" rel="noopener noreferrer"&gt;Amazon EventBridge Scheduler&lt;/a&gt;. Its a pretty straightforward service which allows you to easily create a scheduled call to more than 270 AWS services and over 6,000 API operations.&lt;/p&gt;

&lt;p&gt;I decided to split the business logic of the application by responsibilities into different lambda functions and I ended up with the following architecture.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fczp5zxxhr19s51mcs392.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fczp5zxxhr19s51mcs392.jpeg" alt="Image displaying the initial architecture with Eventbridge scheduler, two lambda functions and the Twitter API" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Twitter API requires authentication with keys and I needed to store them in a secure place. Its better to not store them with(in) the Lambda, so I decided to put them into Systems Manager Parameter Store. For each cold start invocation of the Tweet function a call is made to parameter store to fetch the API credentials.&lt;/p&gt;

&lt;p&gt;While building, I quickly realized I needed some persistent storage as the RSS feed could contain the same items during a consecutive fetch. I did not want the bot to repeat an already tweeted post so I needed some sort of storage for storing the tweetid for a specific post. I needed a simple persistent database and decided to use DynamoDB as the database for keeping track of the posts. The great thing about DynamoDB is that its a fully managed serverless databases and it offers a pay as you go model. Its really cheap for most cases and I only pay $0.01 a month for using it for this application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fczb81qasmn5qslwcdhho.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fczb81qasmn5qslwcdhho.jpeg" alt="Image displaying the initial architecture with Eventbridge scheduler, two lambda functions, DynamoDB and the Twitter API" width="800" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Improving the design
&lt;/h2&gt;

&lt;p&gt;A few weeks later I decided that I wanted to improve the architecture a bit and decided to decouple the two Lambda components by means of an SQS queue. The advantage of using the SQS queue is that when the function fails the item(s) being processed is not removed from the queue and will be retried at a later moment in time. For consecutive failures I added a dead letter queue (DLQ), so in case the messages on the queue can't be processed, they will be moved to the DLQ. If there is an error in the component or at the Twitter API I will be able to inspect the messages and re-drive them. You might think this is not necessary, but once Twitter announced their new &lt;a href="https://www.theverge.com/2023/3/30/23662832/twitter-api-tiers-free-bot-novelty-accounts-basic-enterprice-monthly-price" rel="noopener noreferrer"&gt;API plans in March 2023&lt;/a&gt; , the function that was responsible for tweeting was unable to send out tweets until I subscribed to the new usage plan. Because of the DLQ, it was a matter of re-driving the messages instead of trying to get the changes to propagate again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw035nwibddko45z67fyx.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw035nwibddko45z67fyx.jpeg" alt="Image displaying the initial architecture with Eventbridge scheduler, two lambda functions, SQS and the Twitter API" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  New feature imposed more changes
&lt;/h2&gt;

&lt;p&gt;Some authors on the &lt;a href="http://dev.to"&gt;dev.to&lt;/a&gt; platform have added their Twitter handle to their profile. I thought it would be great to be able to mention the author in the tweet when its send out. It gives the tweet a bit more body and the author is notified when the post hits the twitter platform.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1749107205444444636-737" src="https://platform.twitter.com/embed/Tweet.html?id=1749107205444444636"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1749107205444444636-737');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1749107205444444636&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;From the RSS feed only the &lt;a href="https://dev.to"&gt;dev.to&lt;/a&gt; username of the author. The twitter handle is only available on the post itself, so I decided to add a third component to get the Twitter handle for the author.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flqc9u010v7gwsvr1ma8n.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flqc9u010v7gwsvr1ma8n.jpeg" alt="Image displaying the initial architecture with Eventbridge scheduler, three lambda functions, SQS, DynamoDB and the Twitter API" width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Whats next?
&lt;/h2&gt;

&lt;p&gt;This architecture has been running fine for quite some time now and Ive even deployed the same stack for a bot for the posts by the AWS Heroes, but I still have some improvements in mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Replacing the SQS queue for the posts by DynamoDB Streams. The author enrichment function would only have react to events in the stream. That would remove a bit of complexity and responsibility from the feed parser function.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If I look at the flow it looks a bit like a workflow. I'm considering to replace the entire thing and with a step functions workflow.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Integrate with Amazon Bedrock for generating summaries of the posts the bot is posting to Twitter. It would improve the Twitter posts a bit instead of just mentioning the author and the title of the post.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Extend the bot to also publish to other platforms. Ive been looking into Threads, but it does not seem to have a public API yet. Not sure how big the AWS community is on other social platforms. Feel free to reach out if you prefer to follow all the great content on your social media platform of choice.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In a follow up post I will dive into the technical details of how the functions work and will take a look at some of the best practices for building Lambda functions. If you have some comments/ideas/feedback, feel free to reach out!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>architecture</category>
      <category>twitter</category>
    </item>
    <item>
      <title>Develop and Test your Github Actions workflow locally with "act"</title>
      <dc:creator>Jeroen Reijn</dc:creator>
      <pubDate>Mon, 03 Jul 2023 10:11:37 +0000</pubDate>
      <link>https://dev.to/jreijn/develop-and-test-your-github-actions-workflow-locally-with-act-jij</link>
      <guid>https://dev.to/jreijn/develop-and-test-your-github-actions-workflow-locally-with-act-jij</guid>
      <description>&lt;p&gt;At work, I regularly train people on the subject of Continuous Integration and Continuous Delivery, where I predominantly utilize GitHub Actions for the workshop assignments. This choice is motivated by GitHub's extensive adoption within the developer community and the generous offering of approximately 2000 minutes or 33 hours of free build time per month.&lt;/p&gt;

&lt;p&gt;During one of my recent workshops, a participant raised a question regarding the possibility of locally testing workflows before pushing them to GitHub. They pointed out the inconvenience of waiting for a runner to pick up their pipeline or workflow, which negatively impacts the developer experience. At that time, I was unaware of any local options for GitHub Actions. However, I have since come across a solution called &lt;a href="https://github.com/nektos/act" rel="noopener noreferrer"&gt;"act"&lt;/a&gt; that addresses this issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is "act"?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/nektos/act" rel="noopener noreferrer"&gt;"act"&lt;/a&gt; is a command-line utility that emulates a Github Actions environment and allows you to test your Github Actions workflows on your developer laptop instead of in a Github Actions environment. You can install "act" by using for instance &lt;code&gt;brew&lt;/code&gt; on the Mac.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;act
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running Workflows Locally
&lt;/h2&gt;

&lt;p&gt;"act" enables you to execute and debug GitHub Actions workflows locally, providing a faster feedback loop during development. Running the "act" command line will pick up the workflows in your &lt;code&gt;.github/workflows&lt;/code&gt; folder and try to execute them. Using "act" can be as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;act
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"act" uses Docker to create an isolated environment that closely resembles the GitHub Actions execution environment. This ensures consistency in the execution of actions and workflows. If you don't have Docker installed you can use &lt;a href="https://www.docker.com/products/docker-desktop/" rel="noopener noreferrer"&gt;Docker Desktop&lt;/a&gt; or use &lt;a href="https://github.com/abiosoft/colima" rel="noopener noreferrer"&gt;Colima&lt;/a&gt;, an easy way to run container runtimes on macOS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Runners
&lt;/h2&gt;

&lt;p&gt;When defining the workflow you can specify a runner based on a specific virtual machine/environment when performing your steps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, "act" has a mapping to a specific docker image when you specify the &lt;code&gt;ubuntu-latest&lt;/code&gt; runner. When running "act" for the first time it will ask you to pick a default image for &lt;code&gt;ubuntu-latest&lt;/code&gt;. You can choose from 3 types of base images that can be mapped to &lt;code&gt;ubuntu-latest&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Micro Docker Image (&lt;a href="https://hub.docker.com/_/buildpack-deps" rel="noopener noreferrer"&gt;&lt;code&gt;node:16-buster-slim&lt;/code&gt;&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Medium Docker Image (&lt;a href="https://github.com/catthehacker/docker_images" rel="noopener noreferrer"&gt;&lt;code&gt;catthehacker/ubuntu:act-latest&lt;/code&gt;&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Large Docker Image (&lt;a href="https://github.com/catthehacker/docker_images" rel="noopener noreferrer"&gt;&lt;code&gt;catthehacker/ubuntu:full-latest&lt;/code&gt;&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don't worry if you're not happy with the one you selected, you can always change the default selection by changing the following file in your users home directory &lt;code&gt;~/.actrc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The large docker image is around &lt;strong&gt;18GB!!&lt;/strong&gt; , so I initially picked the medium-sized image as it should contain most of the commonly used system dependencies. I soon learned that it contains quite some libraries, but when I tried to run a Java + Maven-based project I learned that it did not contain Apache Maven, while the normal &lt;code&gt;ubuntu-latest&lt;/code&gt; on GitHub does have that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[CI/Build] Run Main Build
[CI/Build] 🐳 docker exec cmd=[bash --noprofile --norc -e -o pipefail /var/run/act/workflow/2] user= workdir=
| /var/run/act/workflow/2: line 2: mvn: command not found
[CI/Build] Failure - Main Build
[CI/Build] exitcode '127': command not found, please refer to https://github.com/nektos/act/issues/107 for more information
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I didn't want to switch to an 18GB docker image to be able to just run Maven, so I ended up finding an existing image by &lt;a href="https://github.com/jamezp" rel="noopener noreferrer"&gt;Jamez Perkins&lt;/a&gt;. It simply takes the original "act" image &lt;a href="http://ghcr.io/catthehacker/ubuntu:act-latest" rel="noopener noreferrer"&gt;ghcr.io/catthehacker/ubuntu:act-latest&lt;/a&gt; and adds Maven version 3.x to it. You can easily specify running your workflow with custom images by providing the platform parameter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;act &lt;span class="nt"&gt;-P&lt;/span&gt; ubuntu-latest&lt;span class="o"&gt;=&lt;/span&gt;quay.io/jamezp/act-maven
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After using that image my workflow ran without any errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with multiple jobs/stages
&lt;/h2&gt;

&lt;p&gt;Your GitHub actions workflow usually consists of one or more jobs that separate different stages of your workflow. You might for instance have a Build, Test and Deploy stage.&lt;/p&gt;

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

&lt;p&gt;Usually, you build your application in the build job and use the resulting artifact in the deploy job. Jobs can run on different runners, so in a GitHub Actions environment, you will probably be using the up/download artifact action which will use centralized storage for sharing the artifacts between different runners. When using "act" and sharing artifacts you will need to be specific about where the artifacts need to be stored. You can do so by providing a specific parameter named &lt;code&gt;--artifact-server-path&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;act &lt;span class="nt"&gt;-P&lt;/span&gt; ubuntu-latest&lt;span class="o"&gt;=&lt;/span&gt;quay.io/jamezp/act-maven &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--artifact-server-path&lt;/span&gt; /tmp/act-artifacts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Working with secrets
&lt;/h2&gt;

&lt;p&gt;It's a good practice to always separate your secrets from your workflow definition and only reference them from a specific secret store. When using GitHub Actions you can store your secrets in the built-in secret management functionality.&lt;/p&gt;

&lt;p&gt;To provide an action with a secret, you can use the &lt;code&gt;secrets&lt;/code&gt; context to access secrets you've created in your repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;staticanalysis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Disabling shallow clone is recommended for improving relevancy of reporting&lt;/span&gt;
        &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SonarQube Scan&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sonarsource/sonarqube-scan-action@master&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;SONAR_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SONAR_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;SONAR_HOST_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SONAR_HOST_URL }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"act" does not have a UI in which you can specify secrets, so you will need to provide those values explicitly from the command line or store them in a .env formatted file when testing your workflow. If you only have a few secrets you can easily add them by just providing the secret from the command line by using the &lt;code&gt;-s&lt;/code&gt; option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;act &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nv"&gt;SONAR_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;somevalue
&lt;span class="nv"&gt;$ &lt;/span&gt;act &lt;span class="nt"&gt;--secret-file&lt;/span&gt; my.secrets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Working with environment variables
&lt;/h2&gt;

&lt;p&gt;Similar to secrets you sometimes make use of environment variables inside your workflow. For a single environment variable you can use &lt;code&gt;--env myenv=foo&lt;/code&gt; or if you have a set of environment variables you can create a &lt;code&gt;dotenv&lt;/code&gt; file and provide a reference to the file from the CLI by providing the &lt;code&gt;--env-file&lt;/code&gt; parameter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;act &lt;span class="nt"&gt;--env-file&lt;/span&gt; my.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;.env&lt;/code&gt; file is based on a simple standard file format which contains a set of key-value pairs divided by new lines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;MY_ENV_VAR&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;MY_ENV_VAR_VALUE&lt;/span&gt;
&lt;span class="py"&gt;MY_2ND_ENV_VAR&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"my 2nd env var value"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Event simulation
&lt;/h2&gt;

&lt;p&gt;Events are a fundamental part of workflows. Workflows will start due to some specific event happening within Github like a push, creation of a pull request, etc. With "act" you can simulate such an event to trigger your workflow(s). You can provide the event as an argument.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;act pull_request
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Events are usually more complex than just a simple string so if you want to be specific you can provide a reference to an event payload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;act&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;--eventpath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;pull_request.json&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pull_request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"head"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sample-head-ref"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"base"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sample-base-ref"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By providing your events from the command line you can test different scenarios and observe how your workflows respond to those events.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Using "act" is straightforward and can significantly help in the initial phase of developing your workflow. "act" offers a significant advantage in terms of a swift feedback loop. It enables developers to perform tests locally and iterate rapidly until they achieve the desired outcome, eliminating the need to wait for GitHub's runners to finish the workflow.&lt;/p&gt;

&lt;p&gt;"act" additionally aids developers in avoiding resource wastage on GitHub's runners. By conducting local tests, developers can ensure the proper functioning of their workflows before pushing code changes to the repository and initiating a workflow on GitHub's runners.&lt;/p&gt;

&lt;p&gt;If you're working with GitHub Actions I would recommend to asses "act" as a tool for your development team.&lt;/p&gt;

</description>
      <category>github</category>
      <category>cicd</category>
      <category>development</category>
      <category>act</category>
    </item>
  </channel>
</rss>
