<?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: Steven Smiley</title>
    <description>The latest articles on DEV Community by Steven Smiley (@stevensmiley).</description>
    <link>https://dev.to/stevensmiley</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%2F804732%2F3771f820-216f-481f-8c5c-198699c36d2a.jpeg</url>
      <title>DEV Community: Steven Smiley</title>
      <link>https://dev.to/stevensmiley</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/stevensmiley"/>
    <language>en</language>
    <item>
      <title>Deploying and Tagging ECR Container Images in AWS CodePipeline</title>
      <dc:creator>Steven Smiley</dc:creator>
      <pubDate>Fri, 14 Feb 2025 21:57:22 +0000</pubDate>
      <link>https://dev.to/stevensmiley/deploying-and-tagging-ecr-container-images-in-aws-codepipeline-44dl</link>
      <guid>https://dev.to/stevensmiley/deploying-and-tagging-ecr-container-images-in-aws-codepipeline-44dl</guid>
      <description>&lt;p&gt;AWS CodePipeline is not the most popular or robust pipeline tool out there, but it's conveniently available in AWS and works decently well for most scenarios. I have, however, often had to create custom steps where I would have expected there to be pre-built integrations. Fortunately, CodePipeline is flexible enough to support building your own steps through both AWS CodeBuild and AWS Lambda, and I have two examples that I expect would be useful to many others I'd like to share.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pipeline overview
&lt;/h2&gt;

&lt;p&gt;For this scenario, let's imagine we have container images that are already built and pushed to Amazon ECR. We want to (1) deploy them to Amazon ECS and then (2) tag the image to denote the environment the image is running in.&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%2F0umjkw7gn6nwh4rkf74v.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%2F0umjkw7gn6nwh4rkf74v.png" alt="Image description" width="800" height="939"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying from ECR to ECS
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-ECS.html" rel="noopener noreferrer"&gt;CodePipeline action to deploy a container to ECS&lt;/a&gt; requires an input &lt;code&gt;FileName&lt;/code&gt; of a JSON file with the target service's container name and the image to be deployed. Unfortunately, the &lt;a href="https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-ECR.html" rel="noopener noreferrer"&gt;ECR source action&lt;/a&gt; does not output this file in the correct format.&lt;/p&gt;

&lt;p&gt;In this case, we need to add a CodeBuild step between the Source action and the Deploy action to output the required file in the correct format. The BuildSpec for the CodeBuild project includes a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: 0.2
phases:
  build:
    commands:
      - printf '[{"name":"%s","imageUri":"%s:latest"}]' "$TASK_FAMILY" "$REPOSITORY_URI" &amp;gt; imagedefinitions.json
artifacts:
  files: imagedefinitions.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will write the required JSON file and output it as an artifact that can be referenced during the Deploy action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tagging ECR Images in CodePipeline
&lt;/h2&gt;

&lt;p&gt;It can be valuable to indicate which images are actually deployed in your environment, particularly which have been promoted to production. This allows vulnerability management tooling and personnel to assess the risk of vulnerabilities identified in an image -- if the image is deployed to production that's a real risk compared to an old image that just stale in ECR.&lt;/p&gt;

&lt;p&gt;We can build an AWS Lambda function to tag images when triggered by CodePipeline. First, the CodePipeline action configuration needs to pass the function the required parameters in the &lt;code&gt;UserParameters&lt;/code&gt; field.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Name: TagImageWithProd
              RunOrder: 3
              ActionTypeId:
                Category: Invoke
                Owner: AWS
                Provider: Lambda
                Version: "1"
              Configuration:
                FunctionName: 
                  Fn::ImportValue: !Sub ${PipelineTagStack}-FunctionName
                UserParameters: "{\"RepositoryName\": \"#{ImageVariables.RepositoryName}\", \"ImageTag\": \"#{ImageVariables.ImageTag}\" , \"NewImageTag\": \"prod\", \"ImageDigest\": \"#{ImageVariables.ImageDigest}\", \"ImageURI\": \"#{ImageVariables.ImageURI}\"}"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the Lambda function, we can parse the required parameters from the &lt;a href="https://docs.aws.amazon.com/codepipeline/latest/userguide/actions-invoke-lambda-function.html#actions-invoke-lambda-function-json-event-example" rel="noopener noreferrer"&gt;CodePipeline event&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;codepipeline_job_id = event["CodePipeline.job"]["id"]
    user_parameters = json.loads(
        event["CodePipeline.job"]["data"]["actionConfiguration"]["configuration"][
            "UserParameters"
        ]
    )

image_uri = user_parameters["ImageURI"]
repository_name = user_parameters["RepositoryName"]
image_tag = user_parameters["ImageTag"]
new_image_tag = user_parameters["NewImageTag"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To tag the image using the &lt;a href="https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_PutImage.html" rel="noopener noreferrer"&gt;ECR PutImage API&lt;/a&gt;, however, we will also need the image's manifest. So first we retrieve that using ECR Batch Get Image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;image_manifest = ecr_client.batch_get_image(
            repositoryName=repository_name,
            imageIds=[
                {
                    "imageTag": image_tag,
                }
            ],
        )["images"][0]["imageManifest"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have everything we need, and can tag the image in ECR.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ecr_client.put_image(
            repositoryName=repository_name,
            imageTag=new_image_tag,
            imageManifest=image_manifest,
        )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, don't forget to tell CodePipeline that your action has succeeded (or failed), or the pipeline will wait around for about 15 minutes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;codepipeline_client.put_job_success_result(jobId=codepipeline_job_id)
codepipeline_client.put_job_failure_result(
            jobId=codepipeline_job_id,
            failureDetails={
                "type": "JobFailed",
                "message": "Error tagging image",
            },
        )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although I would have preferred these actions to be available without extra work, the AWS's flexibility again makes it work without too much trouble.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>pipeline</category>
    </item>
    <item>
      <title>CTF Walkthrough: pentesting.cloud "Aurora Borealis"</title>
      <dc:creator>Steven Smiley</dc:creator>
      <pubDate>Sun, 22 Jan 2023 01:16:14 +0000</pubDate>
      <link>https://dev.to/stevensmiley/ctf-walkthrough-pentestingcloud-aurora-borealis-4o77</link>
      <guid>https://dev.to/stevensmiley/ctf-walkthrough-pentestingcloud-aurora-borealis-4o77</guid>
      <description>&lt;p&gt;The &lt;a href="//pentesting.cloud"&gt;&lt;code&gt;pentesting.cloud&lt;/code&gt;&lt;/a&gt; challenge &lt;a href="https://pentesting.cloud/challenge/aurora-borealis/" rel="noopener noreferrer"&gt;Aurora Borealis&lt;/a&gt; asks us to understand the permissions and processes to connect to Aurora databases with AWS IAM authentication. It creates an Amazon Aurora DB based on a snapshot with unknown contents and configuration, an EC2 instance, and limited user permissions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding a starting point
&lt;/h2&gt;

&lt;p&gt;We examine the IAM roles and policies in the environment, and see that there's an EC2 instance role that can &lt;code&gt;rds-db:*&lt;/code&gt; on &lt;code&gt;arn:aws:rds-db:us-west-2:*:dbuser:*/us-west-2&lt;/code&gt;. The &lt;code&gt;rds-db:*&lt;/code&gt; includes all RDS IAM actions, including connecting to the database. It's important to read that ARN carefully because it reveals the database username we'll need. The &lt;a href="https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonrdsiamauthentication.html" rel="noopener noreferrer"&gt;ARN format of a db-user&lt;/a&gt; is &lt;code&gt;arn:${Partition}:rds-db:${Region}:${Account}:dbuser:${DbiResourceId}/${DbUserName}&lt;/code&gt;. That's right, the &lt;em&gt;username&lt;/em&gt; is &lt;code&gt;us-west-2&lt;/code&gt;, it's not referring to the region.&lt;/p&gt;

&lt;p&gt;Since the permissions belong to the EC2 instance, we need to connect from there. Conveniently, &lt;code&gt;pentesting-user&lt;/code&gt; can &lt;code&gt;ssm:StartSession&lt;/code&gt; on that instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting to the DB
&lt;/h2&gt;

&lt;p&gt;We start an SSM session on the EC2 instance so we can use its permissions to connect to the &lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.Connecting.AWSCLI.html" rel="noopener noreferrer"&gt;RDS database using IAM authentication&lt;/a&gt;. We'll first need to install the mysql client, download the SSL certificate, and generate an authentication token for the user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sh-4.2$ sudo yum install mysql
sh-4.2$ wget https://truststore.pki.rds.amazonaws.com/us-west-2/us-west-2-bundle.pem
sh-4.2$ RDSHOST="aurora-dbcluster-yjt22bb5xqez.cluster-cmugjtcpbuo6.us-west-2.rds.amazonaws.com"
sh-4.2$ TOKEN="$(aws rds generate-db-auth-token --hostname $RDSHOST --port 1337 --region us-west-2 --username us-west-2)"
sh-4.2$ mysql --host=$RDSHOST --port=1337 --ssl-ca=us-west-2-bundle.pem --user=us-west-2 --password=$TOKEN

Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 24
Server version: 5.7.12 MySQL Community Server (GPL)
MySQL [(none)]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the MySQL connection, let's explore the database to find the flag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MySQL [(none)]&amp;gt; show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| flags              |
+--------------------+

MySQL [(none)]&amp;gt; use flags;
Database changed

MySQL [flags]&amp;gt; show tables;
+-----------------+
| Tables_in_flags |
+-----------------+
| flag            |
+-----------------+

MySQL [flags]&amp;gt; describe flag;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| flag  | varchar(100) | YES  |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+

MySQL [flags]&amp;gt; select * from flag;
+----------------------------------+
| flag                             |
+----------------------------------+
| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
+----------------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Improving AWS data protection
&lt;/h2&gt;

&lt;p&gt;This challenge didn't involve many steps, but required understanding AWS IAM authentication to RDS. To improve data protection with RDS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Be cognizant of the enabled authentication mechanisms&lt;/li&gt;
&lt;li&gt;Grant users the minimum permissions to perform their duties&lt;/li&gt;
&lt;li&gt;As much as possible, keep people away from data, limiting potential access vectors&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>motivation</category>
      <category>productivity</category>
      <category>career</category>
    </item>
    <item>
      <title>AWS Step Functions: Handling Paginated API Responses</title>
      <dc:creator>Steven Smiley</dc:creator>
      <pubDate>Fri, 18 Mar 2022 19:27:17 +0000</pubDate>
      <link>https://dev.to/stevensmiley/handling-paginated-api-responses-in-aws-step-functions-1emf</link>
      <guid>https://dev.to/stevensmiley/handling-paginated-api-responses-in-aws-step-functions-1emf</guid>
      <description>&lt;p&gt;Since AWS Step Functions &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/09/aws-step-functions-200-aws-sdk-integration/" rel="noopener noreferrer"&gt;added support for AWS SDK integration&lt;/a&gt;, it has become very powerful for serverless integration of AWS services. Previously, the go-to approach would be a simple Lambda function, but Step Functions involves even less maintenance. However, Step Functions has (mostly deliberate) limitations that we need to work with/around.&lt;/p&gt;

&lt;p&gt;Some AWS API actions return paginated responses, meaning you get a chunk of the total response and you have to ask for the next chunk if you want it. So if there are 150 items, you may need to pull them 25 at a time. How do we handle this in Step Functions, where each state only gets to make one call?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The key to handling pagination is to loop based on the presence of a continuation token item, usually named &lt;code&gt;NextToken&lt;/code&gt;. When a paginated response is returned, the JSON includes this token, which (1) indicates there additional items to be retrieved and (2) needs to be provided in the subsequent API calls to indicate you want the next page.&lt;/strong&gt; Unfortunately the token name isn’t consistent, see Ian McKay’s complete list of &lt;a href="https://github.com/iann0036/aws-pagination-rules" rel="noopener noreferrer"&gt;rules for AWS SDK pagination&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Here's a concrete example. Let's say we want to perform an action on every instance of the Amazon WorkSpaces service, each representing a virtual desktop, like updating it to use the latest image. &lt;code&gt;workspaces:DescribeWorkspaces&lt;/code&gt; returns 25 at a time, but we have several hundred.&lt;/p&gt;

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

&lt;p&gt;We call &lt;code&gt;DescribeWorkspaces&lt;/code&gt; to get the first set of items and map each one to our desired action, in this case &lt;code&gt;RebuildWorkspaces&lt;/code&gt;. Then a &lt;code&gt;Choice&lt;/code&gt; state checks if &lt;code&gt;NextToken&lt;/code&gt; is present in the task result. If so, call &lt;code&gt;DescribeWorkspaces&lt;/code&gt; again, this time using the &lt;code&gt;NextToken&lt;/code&gt; parameter. Critically, this overwrites the task result from the first call, so the next time we reach the &lt;code&gt;Choice&lt;/code&gt; state, it can check if there are even more items to retrieve (&lt;code&gt;NextToken&lt;/code&gt; is still present). If we've reached the end (&lt;code&gt;NextToken&lt;/code&gt; isn't present), we can move on with the workflow.&lt;/p&gt;

&lt;p&gt;Here's the full definition of this Step Functions state machine in AWS CDK with Python.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import aws_cdk.aws_stepfunctions as sfn
import aws_cdk.aws_stepfunctions_tasks as sfn_tasks

describe_workspaces = sfn_tasks.CallAwsService(
            self,
            id="DescribeWorkspaces",
            comment="Get workspaces",
            service="workspaces",
            action="describeWorkspaces",
            result_path="$.DescribeWorkspacesResult",
            iam_resources=["*"],
        )

        describe_more_workspaces = sfn_tasks.CallAwsService(
            self,
            id="DescribeMoreWorkspaces",
            comment="Get workspaces with NextToken",
            service="workspaces",
            action="describeWorkspaces",
            parameters={
                "NextToken": sfn.JsonPath.string_at(
                    "$.DescribeWorkspacesResult.NextToken"
                )
            },
            result_path="$.DescribeWorkspacesResult",
            iam_resources=["*"],
        )

        rebuild_workspaces = sfn_tasks.CallAwsService(
            self,
            id="RebuildWorkspaces",
            comment="Rebuild workspaces",
            service="workspaces",
            action="rebuildWorkspaces",
            parameters={
                "RebuildWorkspaceRequests": [
                    {"WorkspaceId": sfn.JsonPath.string_at("$.WorkspaceId")}
                ]
            },
            result_path="$.RebuildWorkspacesResult",
            iam_resources=["*"],
        )

        rebuild_each_workspace = sfn.Map(
            self,
            id="RebuildEachWorkspace",
            comment="Rebuild each workspace",
            items_path="$.DescribeWorkspacesResult.Workspaces",
            output_path=sfn.JsonPath.DISCARD,
        )
        rebuild_each_workspace.iterator(sfn.Pass(self, "Map State"))

        definition = describe_workspaces.next(rebuild_each_workspace).next(
            sfn.Choice(self, "ChoiceMoreWorkspaces")
            .when(
                sfn.Condition.is_present("$.DescribeWorkspacesResult.NextToken"),
                describe_more_workspaces.next(rebuild_each_workspace),
            )
            .otherwise(sfn.Succeed(self, "Done"))
        )

        state_machine = sfn.StateMachine(
            self,
            id="WorkSpacesRebuilderStateMachine",
            state_machine_type=sfn.StateMachineType.STANDARD,
            definition=definition,
        )


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

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Thanks to Karsten Lang for fixing an error in a previous version&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>aws</category>
      <category>stepfunctions</category>
      <category>serverless</category>
      <category>awscdk</category>
    </item>
    <item>
      <title>12-Factor AWS CDK Apps</title>
      <dc:creator>Steven Smiley</dc:creator>
      <pubDate>Thu, 03 Mar 2022 22:23:16 +0000</pubDate>
      <link>https://dev.to/stevensmiley/12-factor-aws-cdk-apps-35pe</link>
      <guid>https://dev.to/stevensmiley/12-factor-aws-cdk-apps-35pe</guid>
      <description>&lt;p&gt;The &lt;a href="https://12factor.net"&gt;twelve-factor app methodology&lt;/a&gt; outlines best practices for building web apps to enable scalability and reliability on cloud platforms. &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/home.html"&gt;AWS CDK&lt;/a&gt; focuses on deploying cloud infrastructure, but enables you to put infrastructure, application code, and configuration all in one place. &lt;strong&gt;What can we learn if we apply the twelve-factor app methodology apply to AWS CDK?&lt;/strong&gt; Is it still relevant in an increasingly serverless world?&lt;/p&gt;

&lt;p&gt;I won't bury the lede: &lt;strong&gt;CDK makes it easy to adhere to the 12-factor model, and embracing serverless on AWS makes many of the factors so easy that you won't have to worry about them.&lt;/strong&gt; Let's examine each factor and uncover practices we can apply.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Twelve Factors
&lt;/h2&gt;

&lt;h3&gt;
  
  
  I. Codebase
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;One codebase tracked in revision control, many deploys&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;CDK enables you to have one codebase by grouping an app's infrastructure and application code into the same repository. Without this, an app's source code isn't really complete -- how is this app deployed?&lt;/p&gt;

&lt;p&gt;CDK enables multiple apps to share the same code via &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/constructs.html"&gt;constructs&lt;/a&gt;, which are then imported as libraries. Without CDK, infrastructure code is often copied and pasted multiple times across apps or even within the same app -- violating this factor and the &lt;a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself"&gt;Don't Repeat Yourself (DRY) principle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;CDK makes many deploys easy as well, by introducing the concept of &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/environments.html"&gt;environments&lt;/a&gt;, especially in conjunction with &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines-readme.html"&gt;CDK Pipelines&lt;/a&gt;. By defining pipeline stages, logical groupings of stacks, you can deploy to as many environments as you want using the same codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  II. Dependencies
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Explicitly declare and isolate dependencies&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each CDK-supported language has a mechanism for both declaring dependencies (for example Python's &lt;code&gt;requirements.txt&lt;/code&gt;) and isolating dependencies (for example Python's Virtualenv). This greatly simplifies environment setup and ensure reliability when deploying, especially through a pipeline. &lt;/p&gt;

&lt;p&gt;Be sure to avoid installing or updating packages outside of this mechanism. If you &lt;code&gt;pip install xxxxx&lt;/code&gt; into your virtualenv but forget to add it to your &lt;code&gt;requirements.txt&lt;/code&gt;, your app will &lt;code&gt;cdk synth&lt;/code&gt; locally but fail on deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  III. Config
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Store config in the environment&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Separating config from app code is much easier with CDK than with purely declarative formats like CloudFormation. It can be tempting to hard-code values in templates rather than declare dozens or hundreds of variables, but this would prevent them from being portable.&lt;/p&gt;

&lt;p&gt;The line between app and environment can be difficult to discern with CDK, however, as CDK is deploying the infrastructure which makes up the environment. For specific application components, you should use CDK to provision infrastructure with the appropriate environment variables. For example, using Lambda environment variables, SSM parameters, or Secrets Manager secrets. But how do we handle configuration of the CDK app itself?&lt;/p&gt;

&lt;p&gt;Each CDK app has an &lt;code&gt;App&lt;/code&gt; construct which defines the entry point and takes an environment parameter, binding the app to the environment. With CDK Pipelines, the &lt;code&gt;App&lt;/code&gt; contains a pipeline stack, which then defines the rest of the deployment environments. In each pipeline stage, you'll need to provide the configuration parameters for that stage. &lt;/p&gt;

&lt;p&gt;We can store this config as context variables in the &lt;code&gt;cdk.json&lt;/code&gt;, but this doesn't scale well and it doesn't support types. &lt;strong&gt;Since CDK gives us the power of a general purpose programming language, let's use it!&lt;/strong&gt; For example, in Python we can create a &lt;code&gt;config.py&lt;/code&gt; file and define variables, or even objects with types, which we then import into the pipeline.&lt;/p&gt;

&lt;p&gt;You may be tempted to avoid declaring the environment in the CDK application altogether, to strictly separate the two. This is possible, but not recommended for production -- during CDK's &lt;code&gt;synth&lt;/code&gt; phase, it will resolve environment-specific details that you want to ensure are deterministic. For example, you might look up an existing VPC or distribute resources across availability zones. This information will be stored in &lt;code&gt;cdk.context.json&lt;/code&gt; and won't need to be looked up again, preventing unexpected behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  IV. Backing services
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Treat backing services as attached resources&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;CDK apps do not use backing services in the way this factor is traditionally viewed, but we can still learn from the principle. At its core, this factor advocates for strong encapsulation and loose coupling between app components. This allows us to separate concerns across clear boundaries, iterate on independent components without affecting interfacing components, and potentially swap them out completely if needed.&lt;/p&gt;

&lt;p&gt;To achieve this with CDK apps, encapsulate logical components into constructs and compose them into stacks and stages for deployment. This may increase the verbosity of an app since it requires passing parameters across construct boundaries but the resulting constructs are portable, testable, and maintainable.&lt;/p&gt;

&lt;h3&gt;
  
  
  V. Build, release, run
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Strictly separate build and run stages&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is straightforward: &lt;em&gt;build&lt;/em&gt; occurs during &lt;code&gt;cdk synth&lt;/code&gt;, &lt;em&gt;release&lt;/em&gt; occurs during  &lt;code&gt;cdk deploy&lt;/code&gt;, and &lt;em&gt;run&lt;/em&gt; is the resulting resources managed by AWS.&lt;/p&gt;

&lt;p&gt;To go a step further, CDK Pipelines automatically defines separate stages to build the CDK app and then perform CloudFormation deployments, each with separate &lt;em&gt;prepare&lt;/em&gt; and &lt;em&gt;deploy&lt;/em&gt; steps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This factor is built into CDK -- awesome!&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  VI. Processes
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Execute the app as one or more stateless processes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This factor does not directly translate to CDK apps but there is a principle we can extract from it -- strong delineation between stateful and stateless resources.&lt;/p&gt;

&lt;p&gt;In CDK, this is best performed at the stack level, separating stateful and stateless stacks. When defining a database, for instance, place it in a separate stack and use cross-stack references to connect other resources to it. You can also enable &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-protect-stacks.html"&gt;termination protection&lt;/a&gt; on stateful stacks. &lt;/p&gt;

&lt;p&gt;At the resource level, set &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.RemovalPolicy.html"&gt;&lt;code&gt;RemovalPolicy&lt;/code&gt;&lt;/a&gt; to &lt;code&gt;RETAIN&lt;/code&gt; or &lt;code&gt;SNAPSHOT&lt;/code&gt; to avoid deleting data.&lt;/p&gt;

&lt;h3&gt;
  
  
  VII. Port binding
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Export services via port binding&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Again, this factor does not apply to the CDK-portion of an app, but there's still an underlying principle: create self-contained apps that can interface with other apps through web services.  &lt;/p&gt;

&lt;p&gt;We accomplish this with CDK by grouping all related resources into a single CDK app, which may provide backing services for other apps using deliberately-exposed interfaces. This also encourages us to prevent other apps from modifying our apps underlying resources.&lt;/p&gt;

&lt;p&gt;In AWS, the strictest boundary is at the account level. By deploying CDK apps into separate AWS accounts, we prevent direct access to underlying resources, allowing access only across deliberately exposed interfaces, such as web APIs or cross-account shared resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  VIII. Concurrency
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Scale out via the process model&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One of the primary benefits of using AWS in the first place is that most services are designed from the ground up with this principle built-in. The more we build apps by connecting those services together instead of running our own processes, the less we have to think about concurrency.&lt;/p&gt;

&lt;p&gt;We do have to consider AWS service limits, however. CDK doesn't check to see if we have a sufficiently-high limit during the build phase, and will fail during deployment if these limits are reached. For example, currently &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html"&gt;CloudFormation limits&lt;/a&gt; each stack to 500 resources. I don't know of any tool that can check templates against service limits, but this could be an opportunity for the future.&lt;/p&gt;

&lt;h3&gt;
  
  
  IX. Disposability
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Maximize robustness with fast startup and graceful shutdown&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the context of a CDK app, this factor encourages us to minimize deployment time and design stateless stacks that can be deleted without side effects.&lt;/p&gt;

&lt;p&gt;CloudFormation deployments can feel slow compared to competing tools like Terraform because they perform many built-in checks to improve deployment safety. But that doesn't mean they have to be slow. One easy way to improve deployment speed is using CDK Pipelines &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines.Wave.html"&gt;waves&lt;/a&gt;, which deploy stacks and stages in parallel. This achieves a healthy balance between speed and safety.&lt;/p&gt;

&lt;h3&gt;
  
  
  X. Dev/prod parity
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Keep development, staging, and production as similar as possible&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without Infrastructure as Code, this factor is essentially impossible. But even with other approaches to IaC, this can be difficult to achieve and often requires significant effort in developing parameterized templates and a deployment pipeline. &lt;strong&gt;&lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines-readme.html"&gt;CDK Pipelines&lt;/a&gt; makes this so easy!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Define each stage and stack to take configuration parameters for anything that needs to be different across environments, then use those same constructs to deploy to each environment with their respective parameters. This one pipeline can deploy across AWS accounts, so each is isolated. You can even  set a &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines.ManualApprovalStep.html"&gt;&lt;code&gt;ManualApprovalStep&lt;/code&gt;&lt;/a&gt; between environments if desired, to gate deployment to production.&lt;/p&gt;

&lt;h3&gt;
  
  
  XI. Logs
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Treat logs as event streams&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is already the model for CloudWatch Logs, so there isn't much for us to consider here. One CDK tip, however, is to always set &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_logs.LogRetention.html"&gt;log retention&lt;/a&gt; policies. The default log retention is infinite, and you don't want to pay for accumulating a bunch of logs you don't need.&lt;/p&gt;

&lt;h3&gt;
  
  
  XII. Admin processes
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Run admin/management tasks as one-off processes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If there are admin tasks that you want to be prepared to execute on-demand, consider creating them with Systems Manager Automation documents or Step Functions, which can now &lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/supported-services-awssdk.html"&gt;perform any AWS API action&lt;/a&gt; directly. You can invoke them on-demand with input parameters from the AWS Console. By bundling maintenance operations with the app, whoever needs them later will be able to find them easily and will be very happy, that might even be future you!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We've seen that CDK is not only compatible with the 12 factor model, but much of it is built into the CDK model. And the more we leverage 'serverless' AWS services, the less we have to worry about these altogether. &lt;/p&gt;

&lt;p&gt;Below I have summarized the key practices for building 12-factor CDK apps. Unsurprisingly, this list is compatible with the &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/best-practices.html"&gt;CDK developer guide's best practices&lt;/a&gt;, but through this exercise we've learned some specific techniques and the reasoning behind them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Practices for 12-Factor CDK Apps
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Model apps using constructs, composed into stacks and stages for deployment.

&lt;ul&gt;
&lt;li&gt;When constructs need to be used in multiple apps, move them to their own repository so they can be maintained independently of the application's lifecycle.&lt;/li&gt;
&lt;li&gt;Separate stateful and stateless stacks. When defining a database, for instance, place it in a separate stack and use cross-stack references to connect other resources to it. 

&lt;ul&gt;
&lt;li&gt;Enable &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-protect-stacks.html"&gt;termination protection&lt;/a&gt; on stateful stacks and resources so they aren't accidentally deleted. &lt;/li&gt;
&lt;li&gt;Set &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.RemovalPolicy.html"&gt;&lt;code&gt;RemovalPolicy&lt;/code&gt;&lt;/a&gt; to &lt;code&gt;RETAIN&lt;/code&gt; or &lt;code&gt;SNAPSHOT&lt;/code&gt; to avoid deleting data.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines-readme.html"&gt;CDK Pipelines&lt;/a&gt; for painless Continuous Delivery

&lt;ul&gt;
&lt;li&gt;Group stacks and stages into &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines.Wave.html"&gt;waves&lt;/a&gt; for parallel deployment&lt;/li&gt;
&lt;li&gt;Deploy CDK apps into separate AWS accounts to prevent direct access to underlying resources, allowing access only across deliberately exposed interfaces, such as web APIs or cross-account shared resources.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Ensure you are using the dependency isolation mechanism expected by your CDK app's language. In Python, &lt;code&gt;source .venv/bin/activate&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;Declare and install dependencies using only the mechanism supported by your CDK app's language. In Python, add it to &lt;code&gt;requirements.txt&lt;/code&gt; and run &lt;code&gt;pip install -r requirements.txt&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Define separate files for constants and config

&lt;ul&gt;
&lt;li&gt;Import config only to your main app and pipeline. If you find yourself importing &lt;code&gt;config&lt;/code&gt; directly into a stage, stack, or construct -- stop. If it varies across environments it needs to be a parameter, and if it doesn't then it needs to be defined as a constant.&lt;/li&gt;
&lt;li&gt;Constants can be used across the app, as long as their values would not change across environments. For example, it can be useful to define tag keys and values that will apply globally.&lt;/li&gt;
&lt;li&gt;Explicitly specify production environments and commit &lt;code&gt;cdk.context.json&lt;/code&gt; to avoid non-deterministic behavior.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Set &lt;a href="https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_logs.LogRetention.html"&gt;&lt;code&gt;LogRetention&lt;/code&gt;&lt;/a&gt; policies&lt;/li&gt;
&lt;li&gt;Create admin processes in your app to support operations&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>awscdk</category>
      <category>cloud</category>
    </item>
    <item>
      <title>AWS CDK: 6 Things You Need To Know</title>
      <dc:creator>Steven Smiley</dc:creator>
      <pubDate>Thu, 27 Jan 2022 21:00:38 +0000</pubDate>
      <link>https://dev.to/stevensmiley/aws-cdk-6-things-you-need-to-know-1gcj</link>
      <guid>https://dev.to/stevensmiley/aws-cdk-6-things-you-need-to-know-1gcj</guid>
      <description>&lt;p&gt;The AWS Cloud Development Kit (CDK) has quickly become my preferred tool for defining AWS infrastructure. I built a project recently that would have been significantly more difficult with any other tool because it needed to span multiple AWS accounts, multiple AWS regions, and deploy several stacks of resources across them.&lt;/p&gt;

&lt;p&gt;While the &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/home.html"&gt;CDK docs&lt;/a&gt; are pretty good, and &lt;a href="https://www.thecdkbook.com"&gt;the CDK Book&lt;/a&gt; is a great way to learn the basics, here are some important features I have learned along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Direct CloudFormation, a.k.a. Escape Hatches
&lt;/h2&gt;

&lt;p&gt;CDK includes several 'level 2' constructs which are excellent: they provide sane defaults, improved autocompletion, boilerplate, and glue logic built-in. However, most AWS services off-the-beaten path don't have them yet, so you need to be prepared to effectively use 'level 1' constructs. These translate directly to CloudFormation resources, and include the full capability that CloudFormation does. This is huge, as you can confidently start a CDK project knowing that, at minimum, you'll have level 1 constructs for everything supported by CloudFormation.&lt;/p&gt;

&lt;p&gt;Level 1 constructs will begin with &lt;code&gt;Cfn&lt;/code&gt;, and you should refer to the &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html"&gt;CloudFormation resource reference&lt;/a&gt; when providing parameters, as there is no meaningful autocomplete support.&lt;/p&gt;

&lt;p&gt;To pass references between these resources, use &lt;code&gt;.ref&lt;/code&gt; to pass the ARN that will be generated at deploy-time. &lt;/p&gt;

&lt;p&gt;Here's an example, creating a Systems Manager Maintenance Window to run a Command Document on a schedule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;with open('example/example_ssm_document.json', 'r') as document_content:
    document_content = json.load(document_content)

document = ssm.CfnDocument(self, "ExampleCommandDocument",
                            content=document_content,
                            document_format="JSON",
                            document_type="Command",
                            )

window = ssm.CfnMaintenanceWindow(self, "ExampleWindow",
                                    allow_unassociated_targets=True,
                                    cutoff=0,
                                    name="ExampleWindow",
                                    duration=1,
                                    schedule='rate(1 hour)'
                                    )

task = ssm.CfnMaintenanceWindowTask(self, "ExampleTask",
                                    window_id=window.ref,
                                    max_concurrency='1',
                                    max_errors='1',
                                    priority=1,
                                    targets=[{
                                        'key': 'InstanceIds',
                                        'values': ['i-xxxxxxxxxxxxxx']
                                    }],
                                    task_arn=document.ref,
                                    task_type='RUN_COMMAND',
                                    )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Custom Resources
&lt;/h2&gt;

&lt;p&gt;In the rare case you need to define a resource that isn't supported by CloudFormation, you can still automate it using Custom Resources. Most of the time, these will just be single AWS API calls, and CDK has an awesome construct just for that: &lt;code&gt;AwsCustomResource&lt;/code&gt;. You provide the service, API action, and parameters, and CDK will create and invoke a Lambda function accordingly. But here are some tricks that aren't immediately obvious:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;AwsCustomResource&lt;/code&gt; uses the &lt;em&gt;JavaScript&lt;/em&gt; SDK, so you have to provide the services, actions, and parameters using that specification for spelling/casing/etc. Refer to the &lt;a href="https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/"&gt;JavaScript SDK&lt;/a&gt; for those.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;physical_resource_id&lt;/code&gt; parameter needs a verification token from the API response, which is easy to retrieve but wasn't intuitive: &lt;code&gt;PhysicalResourceId.from_response("VerificationToken")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;You can pass parameters directly, but you can't dynamically retrieve them, for example a secret from Secrets Manager. This can be a complete killer! I tried to create an AD Connector, and of course don't want to embed a password into the code, but attempting to retrieve it as below &lt;strong&gt;does not work&lt;/strong&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AwsCustomResource(self, id='ADConnector',
            policy=AwsCustomResourcePolicy.from_sdk_calls(
                resources=AwsCustomResourcePolicy.ANY_RESOURCE),
            on_create=AwsSdkCall(
                service="DirectoryService",
                action="connectDirectory",
                parameters={
                    "Name": ad_fqdn,
                    "Password": cdk.SecretValue.secrets_manager(secret_id=ad_service_account_secret_arn, json_field="password").to_string(),
                    "ConnectSettings": {
                        "VpcId": vpc_id,
                        "SubnetIds": subnet_ids,
                        "CustomerDnsIps": customer_dns_ips,
                        "CustomerUserName": cdk.SecretValue.secrets_manager(secret_id=ad_service_account_secret_arn, json_field="username").to_string(),
                    },
                    "Size": "Small",
                },
                physical_resource_id=PhysicalResourceId.from_response(
                    "VerificationToken")
            ))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Something on my #awswishlist would be custom resources backed by the new Step Functions SDK support, instead of a Lambda function. This could include a series of API calls that pass values between them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Import Existing Resources
&lt;/h2&gt;

&lt;p&gt;In many cases, you'll need to get information about resources defined outside your application, and you can import them quite easily. A common one is where networks have been created by some other mechanism, but you need to deploy into them. Importing a VPC is straightforward using &lt;code&gt;ec2.Vpc.from_lookup&lt;/code&gt;, but importing subnets by id wasn't immediately obvious. The answer is to use &lt;code&gt;ec2.SubnetSelection&lt;/code&gt; as easy as below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SubnetSelection(subnet_filters=[SubnetFilter.by_ids(subnet_ids)])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create and Use a Secrets from Secrets Manager
&lt;/h2&gt;

&lt;p&gt;When handling secrets, you have to be extra careful to avoid printing them into the template where they could be inadvertently accessed. To generate a secret securely, use &lt;code&gt;SecretStringGenerator&lt;/code&gt; from Secrets Manager, and to pass that into a resource, refer to the secret using &lt;code&gt;SecretValue.secrets_manager&lt;/code&gt;. For example, the below sample creates a Directory Service Microsoft AD with a generated admin password:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ad_admin_password = secretsmanager.Secret(
    self, "ADAdminPassword",
    secret_name="ad-admin-password",
    generate_secret_string=secretsmanager.SecretStringGenerator(),
    removal_policy=cdk.RemovalPolicy.RETAIN)

managed_ad = directoryservice.CfnMicrosoftAD(self, "ManagedAD",
    name=name,
    password=cdk.SecretValue.secrets_manager(
        ad_admin_password.secret_arn).to_string(),
    vpc_settings=directoryservice.CfnMicrosoftAD.VpcSettingsProperty(
        subnet_ids=subnet_ids,
        vpc_id=vpc_id
    ),
    create_alias=False,
    edition=edition,
    enable_sso=False,
    )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Pass Values Between Stacks
&lt;/h2&gt;

&lt;p&gt;One of the biggest reasons to use CDK is the ability to handle multiple CloudFormation stacks in one application and pass values between them.&lt;/p&gt;

&lt;p&gt;To do this, define a &lt;code&gt;CfnOutput&lt;/code&gt; with an export name that you will reference in another Stack. CDK is smart enough to identify this dependency and order stack deployment accordingly, awesome!&lt;/p&gt;

&lt;p&gt;For example, let's say we want to be able to access the alias or DNS addresses generated by the above Managed AD:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cdk.CfnOutput(self, "ManagedADId",
                value=managed_ad.attr_alias,
                export_name="ManagedADId")
cdk.CfnOutput(self, "ManagedADDnsIpAddresses",
                value=cdk.Fn.join(',', managed_ad.attr_dns_ip_addresses),
                export_name="ManagedADDnsIpAddresses")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that we use &lt;code&gt;cdk.Fn.join&lt;/code&gt; to combine multiple DNS addresses into a single value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Multiple Similar Resources with Unique IDs
&lt;/h2&gt;

&lt;p&gt;This is a dangerous one, but powerful if used carefully. You always want infrastructure definition to be &lt;em&gt;declarative&lt;/em&gt;, and using loops risks breaking that if the inputs are dynamic. &lt;/p&gt;

&lt;p&gt;That said, let's say you want a pipeline to deploy a set of stacks into multiple regions. You can define a stage with those stacks, and put multiple stages into a single wave to be deployed in parallel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for region in constants.REGIONS:
        pipeline_wave.add_stage(
            ExampleStage(self,
                id="ExampleStage-{}".format(
                    region.region),
                env=cdk.Environment(
                    account=constants.EXAMPLE_ACCOUNT_ID,
                    region=region.region),
        )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've defined a python &lt;a href="https://docs.python.org/3/library/dataclasses.html"&gt;dataclass&lt;/a&gt; with values needed in each region, and we can loop through it during the CDK Pipeline definition. Notice that the &lt;code&gt;id&lt;/code&gt; must be unique, so it includes the region name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I am optimistic about the future of AWS CDK, and I'll be using it for new projects until someone convinces me otherwise. At minimum, it's a better way to write CloudFormation. But it also empowers you well beyond that, and is just a pleasure to use.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>awscdk</category>
      <category>cdk</category>
      <category>serverless</category>
    </item>
  </channel>
</rss>
