Tag Archives: AWS CDK

Route Amazon Bedrock Guardrails interventions to Amazon Security Lake

Post Syndicated from Dhananjay Karanjkar original https://aws.amazon.com/blogs/security/route-amazon-bedrock-guardrails-interventions-to-amazon-security-lake/

Security teams investigating AI-related incidents need guardrail intervention data alongside their existing security telemetry. Routing Amazon Bedrock Guardrails violations to Amazon Security Lake makes this possible. With this integration, you can query guardrail events alongside identity, network, and application security data in a single layer. When a guardrail blocks a prompt injection attempt or redacts sensitive data, that intervention carries investigative value comparable to a failed sign-in or a network intrusion alert. Amazon Bedrock publishes this telemetry to Amazon CloudWatch metrics and model invocation logs for operational monitoring. By using Security Lake, organizations can extend this telemetry into their security data lake for unified correlation.

In this post, I show you how to build an automated pipeline that transforms Amazon Bedrock Guardrails intervention events into Open Cybersecurity Schema Framework (OCSF) records and delivers them to Security Lake as a custom source. You can query the data using Amazon Athena or any Security Lake subscriber.

Use case

Consider a financial services organization deploying Amazon Bedrock across multiple business units. Each unit uses guardrails to enforce content policies (blocking harmful content), topic policies (preventing off-topic queries about competitors), sensitive information policies (redacting personally identifiable information (PII) such as account numbers), and prompt injection detection.

The security team needs to:

  • Identify which user accounts trigger the most guardrail interventions and whether those accounts also have unusual AWS Identity and Access Management (IAM) activity
  • Determine if prompt injection attempts correlate with specific source IP addresses that also appear in Amazon Virtual Private Cloud (Amazon VPC) Flow Logs
  • Track the organization-wide trend of guardrail violations across all business units and compare it against the baseline from 30 days ago

With guardrail events routed to Security Lake, a single Athena query covers all three.

Solution overview

The pipeline architecture routes Amazon Bedrock security events to Security Lake as OCSF-compliant records. The same infrastructure—subscription filter, AWS Lambda transformation, Parquet writer, Amazon Simple Storage Service (Amazon S3) partitioning—supports multiple event types by changing the filter pattern and OCSF mapping:

Guardrail interventions (this post) DETECTION_FINDING 2004
Model invocation API calls API_ACTIVITY 6003
Agent guardrail traces DETECTION_FINDING 2004
Token consumption anomalies DETECTION_FINDING 2004

This post demonstrates the guardrail interventions implementation as a working example. The solution captures Amazon Bedrock model invocation logs that contain guardrail trace data and filters for intervention events. It transforms matching events into OCSF-compliant Detection Finding records (class_uid 2004) and delivers them to Security Lake as Parquet files. Guardrail interventions are detection events: the guardrail detected and blocked prohibited content, so OCSF class 2004 (Detection Finding) under the Findings category is the appropriate classification.

Architecture

The following diagram shows the end-to-end pipeline from guardrail intervention to Security Lake ingestion.

Figure 1: Guardrail intervention routing

Figure 1: Guardrail intervention routing

The data flow consists of the following steps:

  1. An application calls Amazon Bedrock (InvokeModel or Converse API) with a guardrail attached.
  2. Amazon Bedrock evaluates the guardrail and logs the invocation (including guardrail trace data) to a CloudWatch Logs log group using model invocation logging. The subscription filter matches log entries where the guardrail action is INTERVENED (blocked or masked content).
  3. The subscription filter delivers matching records to a Lambda function (OCSF Transform).
  4. The Lambda function transforms each intervention event into an OCSF Detection Finding record (class_uid 2004), batches records, and converts them to Zstandard (zstd)-compressed Apache Parquet format. It writes the Parquet file to the Amazon S3 Security Lake bucket using the required partition path (ext/BedrockGuardrails/region=/accountId=/eventDay=/). If the Lambda function fails to process a record, the message routes to an Amazon Simple Queue Service (Amazon SQS) dead-letter queue for later analysis and redrive.
  5. Security Lake manages the ingested Parquet data in the S3 bucket.
  6. AWS Glue crawler detects new partitions and catalogs the Parquet files for query access.
  7. SOC analysts query guardrail violation data alongside other security sources using Athena.

OCSF mapping

The following table shows how Amazon Bedrock Guardrails intervention fields map to OCSF Detection Finding (class_uid 2004) attributes.

OCSF field Source Example value
class_uid Static 2004 (Detection Finding)
category_uid Static 2 (Findings)
severity_id Derived from policy type 3 (Medium) for content/topic; 4 (High) for prompt injection
activity_id Static 1 (Create)
time Invocation log timestamp 1721001600000
cloud.provider Static AWS
cloud.region Invocation log region us-east-1
cloud.account.uid Invocation log accountId 123456789012
actor.user.uid Invocation log identity.arn arn:aws:sts::123456789012:assumed-role/AppRole/session
finding_info.title Derived from policy type ContentPolicy Intervention
finding_info.desc Guardrail trace action/topic Blocked: HATE content detected on INPUT
resource.uid Model ARN arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-sonnet-4-6-20250514-v1:0
resource.type Static AwsBedrock:Model
metadata.product.name Static Amazon Bedrock Guardrails
metadata.product.vendor_name Static AWS
metadata.version Static 1.3.0
unmapped.guardrail_id Guardrail trace guardrailId my-content-guardrail
unmapped.guardrail_arn Guardrail trace guardrailArn arn:aws:bedrock:us-east-1:123456789012:guardrail/abc123
unmapped.guardrail_version Guardrail trace guardrailVersion 3
unmapped.guardrail_content_source Guardrail trace INPUT or OUTPUT
unmapped.guardrail_policy_type Guardrail trace ContentPolicy, TopicPolicy, SensitiveInformationPolicy, WordPolicy, ContextualGroundingPolicy, PromptAttack

Prerequisites

The following prerequisites are needed to deploy the reference implementation. Before you begin, clone the repository:

git clone https://github.com/aws-samples/sample-bedrock-guardrails-security-lake.git
cd sample-bedrock-guardrails-security-lake

Verify you have the following:

  • An AWS account with AWS Cloud Development Kit (AWS CDK) bootstrapped in the target AWS Region
  • Security Lake enabled in the target Region
  • Python 3.12 or later
  • Node.js 20 or later (for AWS CDK CLI)
  • An existing Amazon Bedrock guardrail (or create one during deployment)
  • Model invocation logging enabled on Amazon Bedrock (with guardrail trace data enabled)

Implementation

The reference implementation deploys three CloudFormation stacks: SecurityLakeSourceStack, TransformPipelineStack and MonitoringStack. The following commands deploy the stacks in dependency order:

cdk deploy SecurityLakeSourceStack \
  -c security_lake_bucket=<your-security-lake-bucket> \
  -c source_location=ext/BedrockGuardrails \
  -c security_lake_enabled=true

cdk deploy TransformPipelineStack \
  -c security_lake_bucket=<your-security-lake-bucket> \
  -c source_location=ext/BedrockGuardrails

cdk deploy MonitoringStack \
  -c security_lake_bucket=<your-security-lake-bucket> \
  -c source_location=ext/BedrockGuardrails

Enable model invocation logging

Model invocation logging captures the guardrail trace data you need. Turn on full request and response logging to a CloudWatch Logs log group. Configure textDataDeliveryEnabled to capture text request and response bodies, which include the guardrail trace output when a guardrail is attached to the invocation.

Register Security Lake custom source

Register BedrockGuardrails as a custom source with Security Lake using the DETECTION_FINDING event class. Security Lake creates the Amazon S3 prefix and IAM role for your source. The stack configures the AWS Glue crawler role for partition discovery.

Create the subscription filter

Create a CloudWatch Logs subscription filter on your model invocation log group with the filter pattern { $.output.guardrailAction = “INTERVENED” }. This captures only the events where a guardrail blocked or modified content, not the successful pass-through events. This reduces Lambda invocations and cost.

Transform to OCSF and write Parquet

The Lambda function performs three operations: parse the CloudWatch Logs event, transform each intervention to an OCSF Detection Finding record (class_uid 2004), and write batched records as Parquet files. The files are written to the Security Lake S3 bucket using the required partition path (ext/BedrockGuardrails/region=<region>/accountId=<accountId>/eventDay=<YYYYMMDD>/).

The transformation maps guardrail trace fields to OCSF attributes as described in the OCSF mapping table. Severity is set to High for prompt injection interventions and Medium for content, topic, or sensitive information interventions. For a concrete before-and-after example, see the sample invocation log and corresponding OCSF output in the companion repository.

Scaling considerations: At low intervention volumes (tens of events per hour), direct Lambda writes produce acceptably sized Parquet files. For higher volumes, consider buffering through Amazon Data Firehose with its native Parquet conversion and 5-minute buffering interval to produce fewer, larger files that optimize Athena query performance.

Multi-account deployment: The partition scheme (accountId=<account>) already supports multi-account environments. Deploy the subscription filter and transform pipeline in each workload account where model invocation logging is enabled. Each pipeline writes cross-account to the delegated-administrator Security Lake bucket. Distribute the pipeline using CloudFormation StackSets across the organization.

Query violations in Athena

After deployment, guardrail violations typically appear in your Security Lake tables within 5–10 minutes, depending on the AWS Glue crawler schedule. You can then run cross-service correlation queries. The following example identifies users who trigger both prompt injection interventions and unusual IAM activity:

WITH guardrail_violators AS (
    SELECT actor.user.uid AS user_arn, COUNT(*) AS violation_count
    FROM "amazon_security_lake_glue_db_us_east_1"."amazon_security_lake_table_us_east_1_bedrockguardrails"
    WHERE eventDay >= '20260701'
      AND unmapped.guardrail_policy_type = 'PromptAttack'
    GROUP BY actor.user.uid
),
iam_failures AS (
    SELECT actor.user.uid AS user_arn, COUNT(*) AS failure_count
    FROM "amazon_security_lake_glue_db_us_east_1"."amazon_security_lake_table_us_east_1_cloud_trail_mgmt_2_0"
    WHERE eventDay >= '20260701'
      AND status_id = 2
    GROUP BY actor.user.uid
)
SELECT g.user_arn, g.violation_count, i.failure_count
FROM guardrail_violators g
JOIN iam_failures i ON g.user_arn = i.user_arn
ORDER BY g.violation_count DESC;

You can also track violation trends by policy type over time to establish baselines and detect spikes. The following query shows the 30-day trend:

SELECT eventDay,
       unmapped.guardrail_policy_type AS policy_type,
       COUNT(*) AS violation_count
FROM "amazon_security_lake_glue_db_us_east_1"."amazon_security_lake_table_us_east_1_bedrockguardrails"
WHERE eventDay >= '20260623'
GROUP BY eventDay, unmapped.guardrail_policy_type
ORDER BY eventDay, violation_count DESC;

The OCSF mapping has been validated against schema version 1.3.0, and the Security Lake AWS Glue crawler correctly detects the partitioned Parquet files for querying.

Alternative for teams not yet using Security Lake: If your organization hasn’t adopted Security Lake, you can query guardrail intervention events directly in CloudWatch Logs Insights using the same subscription filter log group. CloudWatch Logs Insights supports cross-log-group queries, so you can correlate guardrail events with other CloudWatch log sources without the OCSF transformation step. Security Lake adds value when you need to join with non-CloudWatch sources in a single query layer. Examples include Amazon VPC Flow Logs, Amazon Route 53 DNS logs, and third-party findings.

Clean up

To avoid ongoing charges, destroy the stacks in reverse dependency order:

cdk destroy MonitoringStack --force \
  -c security_lake_bucket=<your-security-lake-bucket> \
  -c source_location=ext/BedrockGuardrails

cdk destroy TransformPipelineStack --force \
  -c security_lake_bucket=<your-security-lake-bucket> \
  -c source_location=ext/BedrockGuardrails

cdk destroy SecurityLakeSourceStack --force \
  -c security_lake_bucket=<your-security-lake-bucket> \
  -c source_location=ext/BedrockGuardrails \
  -c security_lake_enabled=true

Conclusion

In this post, you learned how to route Amazon Bedrock Guardrails intervention events to Amazon Security Lake as OCSF-compliant Detection Finding records. This integration extends guardrail telemetry from Amazon CloudWatch into your security data lake. Security analysts can then run cross-service correlation of AI intervention events with IAM, network, and application telemetry.

The pipeline filters for intervention events only, keeping costs low while capturing the security-relevant signals. The records use OCSF event class 2004 (Detection Finding), which integrates with supported Security Lake subscribers such as Amazon OpenSearch Service and third-party SIEM tools.

Clone the reference implementation and adapt the OCSF mapping and subscription filter to your organization’s guardrail configuration.

References

If you have feedback about this post, submit comments in the Comments section below.


Dhananjay Karanjkar

Dhananjay Karanjkar

Dhananjay is a Senior Lead Consultant at AWS Professional Services, specializing in agentic AI systems, multi-agent orchestration, and generative AI security. He holds two US patents and serves as a Responsible AI Champion, with a background spanning financial services, enterprise consulting, and enterprise-scale AI delivery. When not architecting AI solutions, he trains for triathlons, paints oil portraits, and is an avid reader.

How Company 3 Streamlines Studio Image Management with EC2 Image Builder and AWS CDK

Post Syndicated from Natalie White original https://aws.amazon.com/blogs/devops/how-company-3-streamlines-studio-image-management-with-ec2-image-builder-and-aws-cdk/

Guest post in collaboration with Company 3 Director of New Technology, Phil Wortas, and Senior New Technology Engineer, Matthew Galloway

Introduction

Company 3 provides specialized services for the entertainment industry, including post-production services, visual effects, and color grading for feature films, commercials, and television content. Their teams collaborate globally to Increase workflow efficiency and expand their roster of diverse movie-making talent.

Company 3’s New Technology team uses Amazon EC2 Image Builder to vend Amazon Machine Images (AMIs) and container images for compute environments where artists create and render content. Image Builder is a fully managed AWS service that helps you automate the creation, management, and deployment of customized, secure, and up-to-date server images. Company 3 also uses the AWS Cloud Development Kit (CDK) to scale the creation of consistent Image Builder components and recipes.

At scale, the respective concepts of versioning between Image Builder resources and CDK infrastructure as code made it challenging to reuse prior components and recipes, and update existing references with new version numbers over time. This challenge led to creative workarounds, collaborative problem-solving with AWS, and ultimately, product improvements that benefit the entire AWS community.

This blog follows their journey from manual version management, through creative workarounds, to native EC2 Image Builder features that solved the problem for good. Along the way, we’ll show how auto-versioning and CDK L2 constructs can simplify your own image pipelines.

Process flow diagram of Artists, Support Engineers, and New Technology Platform Engineers provisioning new studio environments. (1) Artists request new environments. (2) Automation determines whether a matching environment configuration (EC2 AMI) exists. If it does, (3) the new environment is provisioned for the Artist to securely access. If it does not, (4) a Support Engineer creates or update an (5) CDK definition of an EC2 Image Builder Pipeline to provision the correct environment. The CDK (6) generates a CloudFormation template and assets that are used to (7) create the Pipeline. This Pipeline (8) generates a new EC2 AMI, from which the rendering instance can be (9) provisioned and (10) provided for the Artist to securely access.

Figure 1: Personas and process flow

The Challenge: When Infrastructure-as-Code Gets Complicated

While Image Builder has historically supported semantic versioning for Components and Recipes, there was no mechanism to automatically detect version changes or update existing references to the latest version of a component using the CDK. This is because Image Builder only supported Layer 1 (L1) CDK constructs. Layer 1 constructs map directly to CloudFormation resources and their corresponding service APIs, but do not provide features that create a layer of abstraction above those foundational create / update / delete operations.

Version changes to Components and Recipes are a frequent occurrence because these resources are immutable; every change to them requires a new version. Version numbers are a part of these resource’s Amazon Resource Name (ARN), so changes must be propagated throughout the associated CDK code to correctly reference the latest version of each resource.

Figure 2 shows an architecture diagram of an EC2 Image Builder Pipeline, which consists of Infrastructure configuration, Components, Recipes, and distribution settings. Components and Recipes each have their own separate version numbers and are immutable. This Pipeline generates EC2 AMIs, which are tied to the recipe version used to generate them, and are used to provision EC2 rendering instances.

Figure 2: EC2 Image Builder Anatomy and Version Propagation

Figure 1, Step 5 represents a Platform Engineer having to update an existing Component. Figure 2 shows the required changes broken out by each of the comprising Image Builder resources:

  1. Update the Component configuration
  2. Increment the Component version
  3. Update the Recipe with the new Component version ARN
  4. Increment the Recipe version
  5. Update the Recipe version ARN in the Pipeline.

Manual version propagation across dozens of components via this multi-step process was error prone, wasn’t scalable, and created a risk of deployment failures and version churn due to version mismatches.

The team needed to prevent unnecessary update requests to Image Builder when components didn’t change but the recipes they were associated with did, orchestrate version propagations when the versions did need to change, and track component versions as they deployed updates across their infrastructure.

Short-term Workaround: Using Hashes to Identify Changes

Faced with these limitations, the customer’s engineering team got creative. Their first approach involved appending MD5 hashes to component names. This allowed them to track changes and force CDK updates and version increments when content changed, while preventing unnecessary update calls when the content of the component didn’t change but the rest of the resources in the CDK Stack did.

However, this approach had drawbacks. Component names became unwieldy and difficult to maintain. More importantly, the hash-based naming convention didn’t align with semantic versioning best practices that the rest of their infrastructure followed. The team knew they needed a better solution long-term.

Long-term Automation: Collaboration with AWS

Working with their AWS Solutions Architect and EC2 Image Builder Developer Support, Company 3 developed a more elegant solution using CDK Custom Resources. This approach eliminated hash-based naming and automated the propagation of version updates, but it came with technical debt.

The version increments themselves were still manual, and the solution required custom resources to create and maintain the suite of resources being deployed. The mesh of custom resources required specialized knowledge to maintain, which made it difficult to onboard new team members, and distracted engineers from focus on core business value of delivering the right studio environments to artists.

Managed Abstraction: AWS Launches Product Improvements

EC2 Image Builder auto-versioning

In November 2025, EC2 Image Builder introduced native auto-versioning capabilities that transformed how teams manage Component versions.

Components with the same name and semantic version now auto-increment build versions (eliminating steps 2-4 from Figure 2 when developers use ‘x’ as a wildcard placeholder (e.g., 1.2.x). Additionally, Pipelines can resolve to the highest available version of Components and Recipes, which ensures they are using the latest compatible versions without manual updates, eliminating step 5.

These enhancements eliminated the version propagation burden entirely, allowing Company 3 developers to focus only on the substantive changes to Components requested by Artists and Support Engineers.

CDK Layer 2 Constructs

The second major improvement came with comprehensive Layer 2 (L2) constructs for EC2 Image Builder (RFC 0789). L2 Constructs provide a layer of abstraction that default to best practice configuration, automatic least-privilege IAM Role and Policy provisioning, and convenience methods that make it easier to create and link to other AWS resources. These constructs transformed the developer experience and alleviated the need for custom resources. The EC2 Image Builder L2 Construct is currently in alpha stabilization phase, and sourcing customer feedback and adoption before migrating to the core CDK library per the CDK contribution process.

Before the L2 construct release, orchestrating an Image Builder Pipeline took over 50 lines of code, and required manual least-privilege IAM role creation, instance profile setup, and Pipeline configuration across 6 separate CloudFormation resources.

// Using L1 constructs
const instanceProfileRole = new iam.Role(stack, 'EC2InstanceProfileForImageBuilderRole', {
  assumedBy: iam.ServicePrincipal.fromStaticServicePrincipleName('ec2.amazonaws.com'),
  managedPolicies: [
    iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore'),
    iam.ManagedPolicy.fromAwsManagedPolicyName('EC2InstanceProfileForImageBuilder'),
  ],
});

const instanceProfile = new iam.InstanceProfile(stack, 'EC2InstanceProfileForImageBuilder', {
  role: instanceProfileRole,
});

const bucket = new s3.Bucket(stack, 'ImageBuilderLoggingBucket', {
 bucketName: `ec2imagebuilder-logs-${stack.region}-${stack.account}`,
  enforceSSL: true,
});

const l1InfrastructureConfiguration = new imagebuilder.CfnInfrastructureConfiguration(stack, 'L1InfrastructureConfiguration', {
  name: 'l1-infrastructure-configuration',
  instanceProfileName: instanceProfile.instanceProfileName,
  instanceMetadataOptions: { httpTokens: 'required' },
  logging: {
    s3Bucket: bucket.bucketName,
    s3KeyPrefix: 'imagebuilder-logging',
  },
});
const l1ImageRecipe = new imagebuilder.CfnImageRecipe(stack, 'L1ImageRecipe', {
  name: 'l1-image-recipe',
  version: '1.0.0',
  parentImage: `arn:${stack.partition}:imagebuilder:${stack.region}:aws:image/amazon-linux-2023-x86/x.x.x`,
  components: [
    {
      componentArn: `arn:${stack.partition}:imagebuilder:${stack.region}:aws:component/update-linux/x.x.x`,
    },
  ],
});

const l1ImagePipeline = new imagebuilder.CfnImagePipeline(stack, 'L1ImagePipeline', {
  name: 'l1-image-pipeline',
  imageRecipeArn: l1ImageRecipe.attrArn,
  infrastructureConfigurationArn: l1InfrastructureConfiguration.attrArn,
});

Using the ImagePipeline L2 construct allows the developer to provision a Pipeline in fewer than 10 lines of code while leveraging best practice configuration the construct sets by default.

// Equivalent, using L2 constructs
const l2ImagePipeline = new imagebuilder.ImagePipeline(stack, 'L2ImagePipeline', {
  recipe: new imagebuilder.ImageRecipe(stack, 'L2ImageRecipe', {
    baseImage: imagebuilder.AwsManagedImage.amazonLinux2023(stack, 'AL2023'),
    components: [
      {
        component: imagebuilder.AwsManagedComponent.updateOS(stack, 'UpdateOS', {
          platform: imagebuilder.Platform.Linux,
        }),
      },
    ],
  }),
});

The Impact: From Workarounds to Best Practices

For Company 3, these improvements meant they could retire their custom constructs entirely. The L2 constructs provided everything their custom solution did, plus additional capabilities.

The EC2 Image Builder service manages the complexity of version updates by default, and they gained enhanced security through AWS-managed secure defaults like IMDSv2 requirements and least-privileged IAM roles.

Perhaps most importantly, new team members can understand the infrastructure code in minutes rather than hours, dramatically accelerating onboarding.

Conclusion

The impact extends far beyond one customer. Every AWS user working with EC2 Image Builder and CDK now benefits from simplified workflows, automatic version management, and security best practices by default. What started as one team’s challenge became a catalyst for improvements that make everyone’s work easier and more secure. The evolution of EC2 Image Builder’s CDK support demonstrates AWS’s commitment to listening to customers and continuously improving the developer experience.

For teams currently managing EC2 Image Builder Pipelines manually or with L1 CDK constructs or with custom solutions, the path forward offers significant benefits. Explore how you can use EC2 Image Builder, its new auto-versioning capabilities, and its CDK L2 Constructs to automate your complex AMI Pipeline provisioning architecture via these resources:

EC2 Image Builder Documentation

EC2 Image Builder Auto-versioning Documentation

CDK L2 Constructs for EC2 Image Builder (currently in alpha stabilization)

EC2 Image Builder CDK Sample GitHub Repository

Authors

Rochelle Lakey

Rochelle Lakey is a Senior Solutions Architect specializing in Media and Entertainment at AWS helping customers architect and optimize their cloud infrastructure. She brings 28 years of managed services experience bridging traditional data centers and modern cloud computing. Rochelle is passionate about guiding organizations through their digital transformation journeys.

Phil Wortas

Phil Wortas is Director of New Technology at Company 3, where his team serves as the cloud infrastructure and platform engineering backbone for a global post-production and VFX organization. Together they focus on reducing manual toil through automation and IaC, so the creative teams they support can stay focused on the work that matters.

Matthew Galloway

Matthew Galloway is a Senior New Technology Engineer at Company 3, working within the cloud infrastructure team. He specializes in AWS deployment automation and developing tools that streamline and enhance artist workflows across the organization. Matthew’s work is driven by a commitment to reducing friction for creative teams, ensuring they have the reliable, efficient infrastructure that they need.

Tarun Belani

Tarun Belani is a Senior Software Development Engineer on the EC2 Image Builder team at Amazon Web Services, where he works on the service’s APIs and backend systems. He designed and built the AWS CDK L2 constructs for EC2 Image Builder.

Natalie White

Natalie White is a Principal Solutions Architect at Amazon Web Services. While her primary customers are in the Healthcare and Life Sciences industry, she leverages her prior Software Development experience as a specialist in AWS CDK and Infrastructure as Code automation, AI-DLC, and GenAI for Developer Productivity across all industries.

Accelerating AWS Network Firewall troubleshooting with AWS DevOps Agent

Post Syndicated from Salman Ahmed original https://aws.amazon.com/blogs/security/accelerating-aws-network-firewall-troubleshooting-with-aws-devops-agent/

When an administrator introduces a rule change in AWS Network Firewall and network connectivity is disrupted, pinpointing the cause requires inspecting multiple points in the traffic path. The firewall gives you stateless and stateful rule engines, domain rules, and routing to the firewall endpoint inside your Amazon Virtual Private Cloud (Amazon VPC). A network drop looks the same from the workload no matter where it started. Isolating the cause means correlating the alert and flow logs with the firewall configuration, route tables, and recent API calls in AWS CloudTrail that might have changed them. That manual correlation is exactly where AWS DevOps Agent helps, accelerating root cause analysis so you can restore connectivity in minutes instead of hours.

AWS DevOps Agent does that correlation for you. As your always-available operations teammate, it resolves and proactively prevents operational issues across AWS, multicloud, and on-premises environments. When an Amazon CloudWatch alarm triggers, it reaches the agent through a webhook. The agent then reads the firewall configuration and logs through AWS APIs, ties the drop to recent API activity, and returns a root cause with a mitigation plan you review before you apply it.

This post connects CloudWatch monitoring to DevOps Agent. It walks through three Network Firewall failures from end to end. The first is a domain deny list blocking a legitimate endpoint. The second is a stateless rule priority misconfiguration. The third is an asymmetric cross Availability Zone (AZ) routing drop. Each maps to a different layer, so each leads down a different investigation path. An AWS Cloud Development Kit (AWS CDK) app deploys the whole environment in your own account so you can reproduce each failure and follow along.

The sample workload

As part of this blog post, we provide a CDK stack that deploys both the AWS DevOps Agent Space and a sample workload used to walk through three separate troubleshooting scenarios. A single t3.micro instance in a protected subnet checks its connectivity to a test endpoint on a continuous loop and publishes results to CloudWatch. Traffic takes the internet egress path through Network Firewall, the NAT gateway, and the internet gateway, so the firewall can intercept or drop it. After completing the walkthrough, you can apply the same troubleshooting techniques with DevOps Agent against your own Network Firewall deployments.

The test endpoint runs in a separate VPC deployed by the same CDK app. It serves HTTPS on port 443 and TCP on port 9142, giving each scenario a different protocol layer to exercise: Scenario 1 targets a TLS connection on 443 (matched by Server Name Indication), Scenario 2 targets a TCP connection on 9142, and Scenario 3 exercises the whole egress path.

A live status page shows one card per scenario plus the network topology. The whole stack deploys from a single CDK app across two Availability Zones, each with a firewall endpoint and NAT gateway, which is what makes Scenario 3 possible.

As shown in the following figure, the egress data path runs from the workload through Network Firewall and the NAT and internet gateways to the test endpoint. The alarm pipeline runs from CloudWatch through Amazon Simple Notification Service (Amazon SNS) and the webhook AWS Lambda function to DevOps Agent.

Figure 1: The sample workload

Figure 1: The sample workload

To use this with your own workload, you need a CloudWatch alarm that detects the connectivity problem and the webhook pipeline (SNS topic and Lambda function) that delivers it to DevOps Agent. The agent reads your firewall configuration, logs, and CloudTrail through AWS APIs, so no additional instrumentation is needed on the firewall side.

Prerequisites

To follow along with this post, you need:

Deploy the sample workload

Clone the project and deploy it into us-east-1 with one command (set awsRegion to use another AWS Region).

git clone https://github.com/aws-samples/sample-accelerating-aws-network-firewall-troubleshooting-with-aws-devops-agent.git
cd sample-accelerating-aws-network-firewall-troubleshooting-with-aws-devops-agent
bash scripts/deploy.sh

The script checks prerequisites, installs dependencies, compiles and tests, and bootstraps the CDK if needed. It then deploys all the stacks from a clean baseline and prints the outputs, including the status-page URL and sign-in details.

  1. Open the status-page link (an https://<random-id>.cloudfront.net address).
  2. Sign in using the username and password provided from the CDK output and confirm all three cards show the green Healthy status.
  3. Keep the page open while you run the scenarios.

Connect AWS DevOps Agent

To connect AWS DevOps Agent to the alarm pipeline

  1. In the AWS DevOps Agent console, open the nf-devops-agent-space Agent Space created by the CDK deployment.
  2. Configure the DevOps Agent webhook and download the CSV file with the webhook URL and signing secret.
  3. On the status page, choose Configure webhook, paste the URL and signing secret, and save. The page writes them to the nf-devops-agent-webhook-credentials AWS Secrets Manager secret, so there is no AWS CLI or console step. Until you set it, the bridge Lambda function sees a placeholder and skips delivery.
  4. Verify the path before you run a scenario. In the Lambda console, open nf-devops-agent-webhook and use the Test tab with this event.
    {
      "Records": [
        {
          "Sns": {
            "Message": "{\"AlarmName\":\"TEST-webhook-verification\",\"AlarmDescription\":\"[TEST] Webhook integration test - not a real alarm.\",\"NewStateValue\":\"ALARM\",\"NewStateReason\":\"[TEST] Manual webhook connectivity test. Safe to ignore.\",\"Region\":\"us-east-1\"}"
          }
        }
      ]
    }

  5. A 200 response confirms the path, and a test investigation appears in the DevOps Agent Operator Web App view.

How the alarm pipeline works

Every scenario reaches DevOps Agent the same way. A CloudWatch alarm moves to ALARM and notifies the SNS topic. Amazon SNS invokes a Lambda function. The function reads the webhook URL and signing secret from Secrets Manager, signs an alarm payload, and POSTs it to the DevOps Agent webhook (as shown in Figure 1). Amazon SNS also provides delivery retries, fan-out to other subscribers, and cross-account publishing.

  • Prebuilt Network Firewall metric (Scenario 1) Alarm-1 watches the DroppedPackets metric, summed across the stateful streams, and triggers when drops rise above a baseline threshold. This requires no workload or custom metric and works on an already-deployed firewall. However, it only tells you that the firewall is dropping packets, not which rule is responsible.
  • Application health metric (Scenarios 2 and 3) Alarm-2 and Alarm-3 watch a custom metric from a connectivity check. Use this for an alarm tied to user-facing impact or to tell one traffic path from another, which requires running a component that emits the metric.
Alarm Source Triggers when
Alarm-1 Native AWS/NetworkFirewall DroppedPackets The firewall’s dropped-packet count rises above the baseline
Alarm-2 Custom application health metric The port 9142 (TCP) connectivity check to the test endpoint is being dropped
Alarm-3 Custom application health metric The cross Availability Zone connectivity check is being dropped

Run the scenarios

Work through each of the scenarios one at a time, following the same cycle. Interrupt network connectivity, watch the alarm trigger, let DevOps Agent investigate, apply the recommended fix, and confirm recovery before moving on.

The status-page cards follow the live CloudWatch alarm state. A card shows a green dot and the word Healthy when its alarm is clear, and a red dot and the word DROPPED when its alarm triggers. In the DROPPED state the card also adds a Condition: line describing what’s being dropped, which isn’t shown when the card is healthy. Network Firewall applies changes to new flows, so a change shows within a minute or two. Recovery comes from the mitigation DevOps Agent recommends, which you review and apply.

Scenario 1. Domain deny list blocking a legitimate endpoint

At baseline, the rg-domain Suricata domain rule group denies only an unused placeholder, so the test endpoint stays reachable. The rule group inspects the TLS Server Name Indication (SNI) on each outbound connection and drops any that matches a denied domain. The exact rule syntax and console steps follow.

To add the domain deny rule

  1. Go to the Amazon VPC console.
  2. In the navigation pane, under Network Firewall, choose Network Firewall rule groups.
  3. Choose the rg-domain rule group to open its details page.
  4. In the Rules section, choose Edit.
  5. The rules box already contains two baseline placeholder rules (they match blocked.placeholder.invalid, so nothing real is denied). Leave those in place. Find the <app-endpoint-dns> value for Scenario 1 in the deployment script output (a Nework Load Balancer (NLB) DNS name such as NfTest-AppNl-a1b2C3dEf4G5-1234abcd5678efgh.elb.us-east-1.amazonaws.com). On a new line below the existing rules, add a drop rule that matches that DNS name on the TLS SNI, then choose Save.
    drop tls $HOME_NET any -> $EXTERNAL_NET any (ssl_state:client_hello; tls.sni; content:"<app-endpoint-dns>"; startswith; nocase; endswith; msg:"S1 domain denylist"; flow:to_server, established; sid:2000002; rev:1;)

  6. After saving, the rules box holds all three lines. The two placeholders remain, plus the new drop rule for the endpoint DNS name (note the distinct sid 2000002).
Figure 2: Scenario 1 – Firewall rule change blocking the connection

Figure 2: Scenario 1 – Firewall rule change blocking the connection

What happens. The workload’s HTTPS check to the test endpoint times out, the “AWS/NetworkFirewall DroppedPackets metric climbs above baseline, and Alarm-1 moves to ALARM. The Scenario 1 card reads DROPPED (with the condition Firewall dropping the monitored domain on its allow/deny rules), while the Scenario 2 and Scenario 3 cards stay Healthy (Figure 3). On the topology, the alarm pipeline from CloudWatch through Amazon SNS and Lambda to DevOps Agent and the workload-to-firewall inspect lines both turn amber, which the legend defines as collateral / alarm active, because the packets are now dropped at the firewall. To demonstrate the resulting failure, the HTTPS · SNI line from the internet gateway to the test endpoint is shown in red, which the legend defines as dropped (root cause).

Figure 3: Scenario 1 active – Traffic blocked at the firewall

Figure 3: Scenario 1 active – Traffic blocked at the firewall

Let DevOps Agent investigate. The agent runs several lines of investigation in parallel and correlates them:

  1. Reads the DroppedPackets metric and correlates the spike with a simultaneous drop in passed packets, confirming the firewall is actively blocking traffic.
  2. Reads the ALERT log and finds the workload’s TLS connections to the test endpoint blocked by the S1 domain denylist rule.
  3. Compares the current state against a baseline window, where the same endpoint was reachable with no alerts, which shows the block is new.
  4. Searches CloudTrail and surfaces the UpdateRuleGroup call that added the deny rule, identifying the user, role, and timestamp approximately one minute before the drops began.
  5. Reports the root cause as that manual rule-group change. Recommends removing the deny entry or adding an allow exception and enabling FirewallPolicyChangeProtection to prevent unauthorized changes.
  6. Presents this as a plan you review and apply, not an automatic change.

In the DevOps Agent Operator Web App view, the agent first restates the Alarm-1 trigger and confirms the firewall is dropping packets above the threshold (Figure 4).

Figure 4: Scenario 1 – The symptom

Figure 4: Scenario 1 – The symptom

Next, the agent identifies the root cause: a manual update to the rg-domain rule group that added a domain deny rule (SID 2000002) shortly before the alarm fired, blocking TLS connections to the ELB endpoint (Figure 5).

Figure 5: Scenario 1 – The root cause

Figure 5: Scenario 1 – The root cause

Finally, the agent presents a mitigation plan, recommending you remove the problematic deny rule (SID 2000002) to restore connectivity (Figure 6).

Figure 6: Scenario 1 – The mitigation plan

Figure 6: Scenario 1 – The mitigation plan

Note: In a real-world environment, this type of rule typically exists for a reason. Before removing it, verify whether it was intentional but scoped too broadly. If so, refine the rule to block only unauthorized endpoints rather than removing it entirely.

Confirm recovery. Apply the change the agent recommends. After the deny entry is gone, DroppedPackets falls back to baseline, Alarm-1 clears, and the card returns to green. Move on to Scenario 2.

Scenario 2. Stateless rule priority misconfiguration

At baseline, the rg-stateless-priority stateless rule group keeps the allow rule at priority 100 and the drop rule at 200 for the test class, TCP destination port 9142. The workload opens a TCP connection to the test endpoint on this port. Lower priority numbers evaluate first, so the allow rule wins. This scenario uses port 9142 instead of 443 to demonstrate a stateless rule, which matches on the packet’s 5-tuple (protocol, ports, addresses) rather than application content.

Introduce the change. Invert the two rule priorities so the drop rule evaluates before the allow rule. This is the kind of change a rushed rule edit can introduce.

To invert the stateless rule priorities

  1. Go to the Amazon VPC console.
  2. In the navigation pane, under Network Firewall, choose Network Firewall rule groups.
  3. Choose the rg-stateless-priority rule group to open its details page.
  4. In the Rules section, choose Edit.
  5. Raise the (Action: Pass) rule’s priority number so it sits after the (Action: Drop) rule, then choose Save. For example, change the (Action: Pass) rule from 100 to 300 (any number higher than the drop rule’s 200 works). You only need to move one rule, and using 300 avoids a clash with the drop rule that already sits at 200. Network Firewall evaluates the lowest priority number first, so the (Action: Drop) rule at 200 now wins for this traffic class, ahead of the (Action: Pass) rule at 300.
Figure 7: Scenario 2 – Rule priority change blocking the traffic class

Figure 7: Scenario 2 – Rule priority change blocking the traffic class

What happens. The drop rule now wins, the TCP connection to the test endpoint on port 9142 times out, the StatelessRuleFailures metric climbs above baseline, and Alarm-2 moves to ALARM. The Scenario 2 card reads DROPPED (with the condition Stateless rules dropping the monitored traffic class), while the Scenario 1 and Scenario 3 cards stay Healthy (Figure 8). On the topology, the alarm pipeline from CloudWatch through Amazon SNS and Lambda to DevOps Agent and the workload-to-firewall inspect lines both turn amber, which the legend defines as collateral / alarm active, because the packets are now dropped at the firewall. To demonstrate the resulting failure, the TLS :9142 line from the internet gateway to the test endpoint is shown in red, which the legend defines as dropped (root cause).

Figure 8: Scenario 2 active

Figure 8: Scenario 2 active

Let DevOps Agent investigate. A stateless drop happens before traffic reaches the stateful inspection engine, so it produces no ALERT log entries. The agent turns to configuration and flow logs instead:

  1. Reads the stateless rule group state and finds the drop rule at the lower priority number, ahead of the pass rule, so the drop evaluates first.
  2. Reads the flow logs and sees passed packets drop to zero within a minute of the change.
  3. Searches CloudTrail and surfaces the UpdateRuleGroup call that inverted the priorities, identifying the user, role, and timestamp about a minute before the alarm.
  4. Reports the root cause as that priority inversion. Recommends removing the redundant drop rule and managing the rule group through infrastructure-as-code (IaC) to prevent manual misconfigurations.
  5. Presents this as a plan you review and apply, not an automatic change.

In the DevOps Agent Operator Web App view, the agent first restates the Alarm-2 trigger and confirms that a workload connectivity health check is failing because the firewall’s stateless rules are dropping egress (Figure 9).

Figure 9: Scenario 2 – The symptom

Figure 9: Scenario 2 – The symptom

Next, the agent identifies the root cause, using the rule-group state and CloudTrail to pinpoint the conflicting DROP/PASS rules, where the new DROP rule’s lower priority number makes it match first (Figure 10).

Figure 10: Scenario 2 – The root cause

Figure 10: Scenario 2 – The root cause

Finally, the agent presents a mitigation plan, recommending you remove the conflicting DROP rule at priority 200 to restore traffic flow (Figure 11).

Figure 11: Scenario 2 – The mitigation plan

Figure 11: Scenario 2 – The mitigation plan

Confirm recovery. Apply the change the agent recommends. After the allow rule is ahead of the drop rule again, Alarm-2 clears and the card returns to green. Move on to Scenario 3.

Scenario 3. Asymmetric cross Availability Zone routing drop

At baseline, the protected subnet in each Availability Zone routes its egress through the firewall endpoint in that same Availability Zone , and the matching return route uses that same endpoint. One endpoint sees both directions of the flow, so the stateful engine completes the handshake. The workload runs in the protected subnet in us-east-1a (CIDR 10.0.4.0/24), so at baseline its egress and its return both use the us-east-1a firewall endpoint.

Introduce the change. Make the flow asymmetric by sending egress out one Availability Zone endpoint while the return comes back through the other. This takes two route edits, and both are required. With only the first edit the flow can still complete, so the alarm will not trigger until both are saved. It makes no firewall-policy change, mirroring a real multi-Availability-Zone routing mistake.

To create asymmetric cross Availability Zone routing

  1. Go to the Amazon VPC console and choose Route tables in the navigation pane.
  2. Flip the egress. Select the NfNetworkStack/SampleVpc/protectedSubnet1 route table (the us-east-1a protected subnet, where the workload runs). On the Routes tab, choose Edit routes. Its 0.0.0.0/0 route currently targets the us-east-1a firewall endpoint. For the target, choose Gateway Load Balancer Endpoint and select the us-east-1b firewall endpoint, then choose Save changes.
  3. Move the return. Select the NfNetworkStack/SampleVpc/publicSubnet2 route table (the us-east-1b public subnet, where egress now exits). Choose Edit routes, then Add route. For the destination enter the workload CIDR 10.0.4.0/24. For the target, choose Gateway Load Balancer Endpoint and select the us-east-1a firewall endpoint. Choose Save changes.

After both edits, a flow’s egress leaves through the us-east-1b endpoint while its return is directed to the us-east-1a endpoint. Neither endpoint sees the whole flow.

Figure 12: Scenario 3 routing change breaking the flow’s symmetry

Figure 12: Scenario 3 routing change breaking the flow’s symmetry

What happens. A new connection leaves through one endpoint. Its return arrives at the other endpoint, which never saw the connection open, so the handshake fails. Unlike Scenarios 1 and 2, this affects the whole subnet, so all egress stops and Alarm-2 and Alarm-3 both move to ALARM. The AWS/NetworkFirewall DroppedPackets alarm (Alarm-1) stays quiet because no endpoint is making a drop decision. The flow is lost to asymmetric routing rather than counted as a firewall drop. This is why monitoring application connectivity matters. A routing fault is invisible to the firewall’s own drop counter. On the status page, the Scenario 2 card reads DROPPED (with the condition “Stateless rules dropping the monitored traffic class”) and the Scenario 3 card reads DROPPED (with the condition Return traffic dropped by asymmetric cross-Availability-Zone routing), while the Scenario 1 card stays Healthy (Figure 13). On the topology, the alarm pipeline from CloudWatch through Amazon SNS and Lambda to DevOps Agent and the workload-to-firewall inspect lines both turn amber, which the legend defines as collateral / alarm active, while the egress path from the firewall through the NAT gateway and the TLS :9142 and HTTPS · routing lines to the test endpoint turn red, which the legend defines as dropped (root cause).

Figure 13: Scenario 3 – The status page during a path-wide outage

Figure 13: Scenario 3 – The status page during a path-wide outage

Let DevOps Agent investigate. Both Alarm-2 and Alarm-3 fire in the same datapoint. DevOps Agent recognizes them as linked and merges them into a single investigation:

  1. Reads the flow logs and sees bidirectional TLS connections stop abruptly, with only one-way traffic remaining and no flows reaching the established state.
  2. Reads the firewall metrics and sees received and passed packets shift from one Availability Zone to the other at the moment of the change.
  3. Calls DescribeRouteTables and finds the egress route pointing at one Availability Zone firewall endpoint while the return route points at the other.
  4. Searches CloudTrail and surfaces the ReplaceRoute and CreateRoute calls by the same user, about a minute before both alarms fired.
  5. Reports the root cause as that asymmetric routing change. Recommends restoring symmetric same-Availability-Zone routing so egress and return traverse the same endpoint.
  6. Presents this as a plan you review and apply, not an automatic change.

A mitigation plan is a recommendation you review, not an automatic change, and the right fix depends on the intended design. Restoring symmetric routing can mean sending the workload subnet’s egress back through its own-Availability-Zone firewall endpoint (this sample’s architecture) or, in a design that doesn’t inspect this path, back through a NAT gateway. The agent infers a plausible target from what it can observe, so review the specific route it proposes against your intended topology before you apply it. (Connecting your pipeline or infrastructure-as-code, covered in the next section, lets the agent recommend the target that matches your design.)

In the DevOps Agent Operator Web App view, the agent restates the Alarm-3 (AsymmetricFlowFailures) trigger and confirms the workload’s egress to a monitored endpoint is being blocked by the Network Firewall (Figure 14).

Figure 14: Scenario 3 – The symptom

Figure 14: Scenario 3 – The symptom

Next, the agent identifies the root cause: manual route table changes that created cross-AZ asymmetric routing through the network firewall, breaking its symmetric routing requirement (Figure 15)

Figure 15: Scenario 3 – The root cause

Figure 15: Scenario 3 – The root cause

Finally, the agent presents a mitigation plan, recommending you restore symmetric routing by pointing protectedSubnet1‘s default route back to the same Availability Zone firewall endpoint, so one endpoint sees both directions of the flow again (Figure 16).

Figure 16: Scenario 3 – The mitigation plan

Figure 16: Scenario 3 – The mitigation plan

Confirm recovery. Apply the change the agent recommends, after checking the route target matches your intended design. After the workload subnet’s egress and return use the same Availability Zone firewall endpoint again, the control probe recovers, the alarms clear, and every card returns to green.

Further considerations

In production a single change can trigger several alarms at the same time, as Scenario 3 shows. DevOps Agent links related investigations and works them as one, so you review a single root cause. You can validate the linked findings or unlink an alarm to investigate it independently. If you would rather collapse alarms before they reach the agent, you can add correlation logic in the bridge Lambda function, buffering and grouping by firewall. You can also add email, Amazon Simple Queue Service (Amazon SQS), or HTTP subscribers to the SNS topic, or add the webhook Lambda function to a topic you already run. DevOps Agent produces a mitigation plan but does not change your environment on its own.

You can also give the agent more to work with. DevOps Agent connects to source repositories and CI/CD pipelines, integrating with GitHub (including GitHub Enterprise Server and GitLab Self-Managed through a private connection). It can associate AWS resources with deployments of AWS CloudFormation, AWS CDK, Amazon Elastic Container Registry (Amazon ECR) images, and Terraform. With deployed configuration and recent deployment events in view, the agent correlates the disruption against the change that introduced it and recommends a fix matching your intended design. For this sample, that means recommending the workload subnet’s own Availability Zone firewall endpoint rather than a generic symmetric path.

DevOps Agent also supports proactive incident prevention. It analyzes patterns across past investigations and delivers recommendations to prevent similar issues from recurring, including governance recommendations that strengthen deployment processes and pipeline controls. For Network Firewall rule changes, this means the agent can recommend guardrails for your CI/CD pipeline based on the classes of misconfigurations it has already resolved. You can access these recommendations through the Improvements page in the DevOps Agent Operator Web App.

Clean up

Clean up the environment with one command.

bash scripts/destroy.sh

It reverts any active scenario, runs cdk destroy for all stacks, and sweeps for stragglers by the Project = nf-devops-agent tag. The main cost drivers are the two Network Firewall endpoints, the NAT gateways (one in the main VPC for each Availability Zone, one in the test-endpoint VPC), and the test endpoint’s load balancers. Each of these bills at an hourly rate for as long as it’s provisioned, whether or not traffic is flowing, so a stack left running continues to accrue charges around the clock even while idle. Running the scenarios and tearing the stack down the same day limits the cost to a few active hours rather than days of idle hourly charges.

Conclusion

In this post, we showed you how AWS DevOps Agent accelerates troubleshooting for three common network firewall connectivity issues. The first was a domain deny list. The second was a stateless priority inversion. The third was an asymmetric cross-AZ routing drop. For each one, DevOps Agent investigated the drop and returned a root cause with a mitigation plan you approve before applying. The first scenario triggered on a prebuilt Network Firewall metric, and the other two on application health metrics. That shows both ways to alarm on a firewall problem through one pipeline.

The pattern isn’t specific to Network Firewall. The same flow fits any service that emits CloudWatch metrics and logs, such as AWS WAF, security groups, and network ACLs. Clone the sample repository to explore the solution, then apply what you learn to your own firewall, application, and alarms. For more details, see the AWS Network Firewall Developer Guide and the AWS Network Firewall pricing page. Start with the Getting Started with AWS DevOps Agent guide to connect your first webhook.

Salman Ahmed

Salman is a Senior Technical Account Manager at AWS, specializing in helping customers design, implement, and optimize their AWS environments. He combines deep networking expertise with a passion for exploring emerging technologies to help organizations get the most out of their cloud investments. Outside of work, he enjoys photography, traveling, and watching his favorite sports teams.

How CloudFormation Express mode accelerates your development cycle

Post Syndicated from Idriss Laouali Abdou original https://aws.amazon.com/blogs/devops/how-cloudformation-express-mode-accelerates-your-development-cycle/

AWS CloudFormation helps you model and provision cloud infrastructure as code using JSON or YAML templates, or through tools like the AWS Cloud Development Kit (CDK). Whether you create stacks directly, use change sets for preview, or deploy through CI/CD pipelines and AI agents, the speed of your deployment cycle directly impacts how fast you can iterate.

In March 2024, we published How we sped up AWS CloudFormation deployments with optimistic stabilization, where we explained how CloudFormation provisions resources and what happens during stabilization. That post introduced the CONFIGURATION_COMPLETE event and the optimistic stabilization strategy that reduced deployment times by up to 40%. Today, CloudFormation express mode takes this further.

Express mode is recommended for development workflows where you iterate frequently. It makes your deployments complete faster so you can get immediate feedback. For production deployments where you need resources ready to serve traffic before proceeding, the default behavior remains the right choice. Combined with pre-deployment validation, which catches template errors before provisioning begins, Express mode completes the iterate-faster picture: validate in seconds, deploy in seconds. To understand what express mode gives you, you first need to understand what CloudFormation has always done during deployment.

What CloudFormation does during deployment

When you add an AWS::SQS::Queue to your template and deploy, the queue is created. But can it receive messages yet? Often, no. There’s a window between “resource created” and “resource can serve traffic.” This is true across AWS services. An EC2 instance is “launched” before it can respond to HTTP requests. A CloudFront distribution is “created” before it propagates to edge locations worldwide. An ECS service is “active” before its containers pass health checks and reach desired capacity. And a Lambda function deletion isn’t complete until its network interfaces are cleaned up.

Figure 1 - Single resource lifecycle phases

Figure 1: Single resource lifecycle phases

This process is called stabilization. Stabilization means that when a stack operation reports CREATE_COMPLETE, the resources can serve traffic. This is useful for production pipelines where “stack complete” should mean “ready to shift traffic.” Now, with express mode, you have a second option.

Figure 2 - Default deployment mode behavior timeline

Figure 2: Default deployment mode behavior timeline

What Express mode changes

Express mode gives you control over when you want CloudFormation to report completion. With express mode, CloudFormation completes the stack operation as soon as resource configuration is applied. Resources continue becoming ready to serve traffic in the background. Regardless of mode, CloudFormation still:

  • Respects resource dependencies within the stack – if a resource references another resource’s ID or attribute, the referenced resource’s configuration is confirmed first. Resources with no dependencies on each other proceed in parallel.
  • Creates, updates, or deletes each resource the same way
  • Retries dependent resources that encounter transient failures during the operation

Here’s what that looks like for a deployment:Figure 3 - Express mode timeline

Figure 3: Express mode timeline

The resource takes the same time to become ready to serve traffic in both cases. Express mode makes your iteration cycle faster by not blocking you on the stabilization wait.

When this matters

Development iteration

You’re building a VPC with subnets, a security group, and an ALB. You need the ALB’s DNS name and the security group ID to configure the next layer of your application. You don’t need to send traffic to the ALB right now. You just need to know it exists and get its attributes. With Express mode, you get the ARN, the DNS name, and the security group bindings in seconds. You proceed to your next iteration.

AI agent workflows

An AI agent iterating on infrastructure needs a tight feedback loop: deploy, observe the result, adjust, deploy again. The agent doesn’t need the CloudFront distribution to propagate globally before deciding whether the template is correct. It needs confirmation that the configuration was accepted.

Express mode turns a 5-10 minute CloudFront deployment into a sub-minute confirmation. The agent can validate, refine, and redeploy multiple times in the window a single default deployment would have taken.

Dependent stack deployments

When you deploy multiple stacks in sequence, each stack operation completes faster with Express mode. Whether dependencies exist within a stack or across stacks via import/export, Express mode handles retries and waits to make sure dependent resources can still be provisioned. It moves faster without breaking your dependency chain

Getting started

Enable Express mode per operation with a single parameter:

aws cloudformation create-stack \   
     --stack-name my-app \   
    --template-body file://template.yaml \   
    --deployment-config '{"mode": "EXPRESS"}'

Express mode disables rollback by default for faster iteration. If a resource fails to configure, the stack stays in place and you can fix and retry immediately. To re-enable rollback:

--deployment-config '{"mode": "EXPRESS", "disableRollback": false}'

With CDK

cdk deploy --express  

#enable rollback  
cdk deploy --express --rollback

With SAM

Use the --express flag with sam deploy or sam sync:

sam deploy --express 

sam sync --express

To persist the setting, add --save-params and Express mode is saved to your samconfig.toml:

sam deploy --express --save-params

The --disable-rollback flag works alongside --express to control rollback behavior within the deployment configuration.

No template changes. No new resource types. The same template deploys the same resources. You’re choosing when to receive the “done” signal.

Additional considerations

Change sets: Express mode is supported with change sets. Specify --deployment-config at create-change-set time, and the configuration is stored with the change set and applied when executed.

Nested stacks: When you enable Express mode on a parent stack, it propagates to all nested stacks in the hierarchy. All resources across the hierarchy complete when configuration is applied.

For a complete list of supported features and limitations, see the CloudFormation Express mode documentation.

Conclusion

Express mode gives you immediate confirmation that your infrastructure configuration is applied, so you can move to your next iteration without waiting for stabilization. It separates “is my configuration correct” from “are my resources serving traffic” and lets you decide which question you need answered right now.

For development iteration, AI agent workflows, and dependent stack deployments where you need resource identifiers rather than traffic readiness, Express mode delivers that answer much more quickly. For production deployments where “stack complete” should mean “ready to serve traffic,” the default behavior remains the right choice.

Related resources

Idriss Laouali Abdou

Idriss is a Sr. Product Manager Technical on the AWS Infrastructure-as-Code team based in Seattle. He focuses on improving developer productivity through AWS CloudFormation and StackSets Infrastructure provisioning experiences. Outside of work, you can find him creating educational content for thousands of students, cooking, or dancing.

Ship infrastructure faster with CloudFormation and CDK pre-deployment validation on every stack operation

Post Syndicated from Idriss Laouali Abdou original https://aws.amazon.com/blogs/devops/ship-infrastructure-faster-with-cloudformation-and-cdk-pre-deployment-validation-on-every-stack-operation/

AWS CloudFormation helps you model and provision cloud infrastructure as code using JSON or YAML templates, or through tools like the AWS Cloud Development Kit (CDK). Whether you create stacks directly, use change sets for preview, or deploy through CI/CD pipelines and AI agents, fast feedback on template errors is critical to development velocity.

Previously, CloudFormation introduced pre-deployment validation during change set creation, catching property syntax errors, resource name conflicts, and S3 bucket emptiness constraints before execution.

Today, we are announcing that pre-deployment validation now runs automatically on every CreateStack and UpdateStack operation, so every deployment path benefits from pre-deployment checks with no configuration required. We are also introducing three new validation checks (Service Quotas limit exceeded, AWS Config Recorder conflicts, and ECR repository delete readiness), a new DisableValidation parameter for operation-level control, and the cdk validate command that leverages CloudFormation pre-deployment validation as part of the CDK developer experience.

In this blog post, we will walk you through how these capabilities work in practice. You will learn how to:

  • Catch property syntax errors and resource name conflicts on CreateStack and UpdateStack before any resources are provisioned
  • Review new WARN-mode validations (service quotas, Config Recorder, ECR delete readiness) during change set creation
  • Use cdk validate to get a validation report with construct-level source tracing
  • Control validation behavior with the DisableValidation parameter when you need to skip checks

Key Capabilities

  • Pre-deployment validation on all stack operations: Property syntax validation and resource name conflict detection (Resource Already Exists) now run in hard-fail mode on CreateStack and UpdateStack, in addition to CreateChangeSet. Errors are caught before any resources are provisioned.
  • Three new validation types: Service Quota validation, AWS Config Recorder conflict detection, and ECR Repository delete readiness checks are now available as warnings during change set creation.
  • CDK validate command: The cdk validate command leverages CloudFormation pre-deployment validation and provides a report with construct-level source tracing that maps errors back to your CDK code.
  • DisableValidation parameter: Operation-level control to skip pre-deployment validation when you need to prioritize deployment speed or bypass a known issue.

How It Works

Understanding Validation Modes

CloudFormation pre-deployment validation operates in two modes that determine how validation failures are handled:

  • FAIL mode stops the stack operation when validation detects errors, ensuring problematic templates cannot proceed to deployment. This applies to property syntax errors and resource name conflicts on CreateStack, UpdateStack, and CreateChangeSet operations.
  • WARN mode allows the operation to proceed despite validation findings, providing warnings that you can review and address before execution. This applies to service quota limits, AWS Config Recorder conflicts, and ECR repository delete readiness checks on CreateChangeSet.

What happens when validation fails:

  • CreateStack: Operation stops before any resources are provisioned.
  • UpdateStack: Operation stops, stack remains in its current state with no resources modified.
  • CreateChangeSet: Change set is not executable. Change set status shows FAILED.

The following scenarios demonstrate how pre-deployment validation works across different stack operations.

Scenario 1: Property Validation on CreateStack

CloudFormation evaluates each resource property definition before provisioning begins. The following template contains several common resource property errors:

Template (dashboard-stack.yaml)

AWSTemplateFormatVersion: "2010-09-09"
Description: Dashboard stack with property validation errors

Resources:
  Dashboard04:
    Type: "AWS::CloudWatch::Dashboard"
    Properties:
      DashboardName: "MyDashboard"

  LogStream08:
    Type: "AWS::Logs::LogStream"
    Properties:
      LogGroupName: "/aws/my-app"
      LogStreamName:                        # Expected string, found JSONArray
        - "stream-1"
        - "stream-2"

  MetricFilter03:
    Type: "AWS::Logs::MetricFilter"
    Properties:
      LogGroupName: "/aws/my-app"
      SomeUnsupportedProperty: "value"      # Unsupported property
      MetricTransformations:
        - MetricName: "ErrorCount"
          MetricNamespace: "MyApp"
          MetricValue: "1"

Step 1: Create Stack

aws cloudformation create-stack \
    --stack-name "dashboard-stack" \
    --template-body file://dashboard-stack.yaml

The command returns the stack ARN and operation begins. Pre-deployment validation runs automatically before any resources are provisioned.

Step 2: Check Validation Results

Use the describe-events API to review validation results:

aws cloudformation describe-events \
    --stack-name "dashboard-stack"

Example output:

The stack creation stopped before any resources were provisioned. Each validation error includes the logical resource ID, resource type, and a precise status reason describing the property issue.

{
    "OperationEvents": [
        {
            "EventId": "ed0f6cc4-3f85-4ad9-abc3-1f9aad2ab931",
            "StackId": "arn:aws:cloudformation:us-west-1:1234:stack/dashboard-stack/6877f3c0-73e6-11f1-a1e1-02ff57e5af93",
            "OperationId": "68790530-73e6-11f1-a1e1-02ff57e5af93",
            "OperationType": "CREATE_STACK",
            "EventType": "VALIDATION_ERROR",
            "LogicalResourceId": "MetricFilter03",
            "PhysicalResourceId": "",
            "ResourceType": "AWS::Logs::MetricFilter",
            "Timestamp": "2026-06-29T18:14:49.255000+00:00",
            "ValidationFailureMode": "FAIL",
            "ValidationName": "PROPERTY_VALIDATION",
            "ValidationStatus": "FAILED",
            "ValidationStatusReason": "Unsupported property [SomeUnsupportedProperty]",
            "ValidationPath": "/Resources/MetricFilter03/Properties/SomeUnsupportedProperty"
        },
        {
            "EventId": "4f9f12ce-498c-4d79-af31-730238b85139",
            "StackId": "arn:aws:cloudformation:us-west-1:1234:stack/dashboard-stack/6877f3c0-73e6-11f1-a1e1-02ff57e5af93",
            "OperationId": "68790530-73e6-11f1-a1e1-02ff57e5af93",
            "OperationType": "CREATE_STACK",
            "EventType": "VALIDATION_ERROR",
            "LogicalResourceId": "LogStream08",
            "PhysicalResourceId": "",
            "ResourceType": "AWS::Logs::LogStream",
            "Timestamp": "2026-06-29T18:14:49.255000+00:00",
            "ValidationFailureMode": "FAIL",
            "ValidationName": "PROPERTY_VALIDATION",
            "ValidationStatus": "FAILED",
            "ValidationStatusReason": "Property [LogStreamName] expected type: String, found: JSONArray",
            "ValidationPath": "/Resources/LogStream08/Properties/LogStreamName"
        },
    ]
}

Console Experience

In the CloudFormation console, navigate to your stack’s Events tab and click the operation ID (or the link in the banner or status reason column) to open the Operation view page. The page will open directly on the Deployment validations tab to see the validation results table:

  • LogStream08 (AWS::Logs::LogStream) – FAIL: Property [LogStreamName] expected string, found: JSONArray
  • MetricFilter03 (AWS::Logs::MetricFilter) – FAIL: Unsupported property [SomeUnsupportedProperty]
Figure 1 - Deployment validations tab showing property validation failures on CreateStack

Figure 1: Deployment validations tab showing property validation failures on CreateStack

Figure 2 Deployment validations tab showing property validation failures on CreateStack

Figure 2: Deployment validations tab showing property validation failures on CreateStack

Scenario 2: Resource Name Conflict on UpdateStack

Resource name conflict detection (RAE) identifies when your template specifies a resource name that already exists in your account. This validation now runs on CreateStack and UpdateStack operations in addition to CreateChangeSet.

Template (update-bucket.yaml)

AWSTemplateFormatVersion: "2010-09-09"
Description: Update stack adding a bucket with a conflicting name

Resources:
  ExistingFunction:
    Type: "AWS::Lambda::Function"
    Properties:
      FunctionName: "my-existing-function"
      Runtime: "python3.12"
      Handler: "index.handler"
      Role: !Sub "arn:aws:iam::${AWS::AccountId}:role/lambda-role"
      Code:
        ZipFile: |
          def handler(event, context):
              return {"statusCode": 200}

  ConflictingBucket:
    Type: "AWS::S3::Bucket"
    Properties:
      BucketName: "production-data-bucket"   # Already exists in the account

Update Stack

aws cloudformation update-stack \     
     --stack-name "my-app-stack" \     
     --template-body file://update-bucket.yaml

Validation output (via describe-events):

{
    "OperationEvents": [
        ...
        {
            "EventId": "bde0f986-3b47-48d8-91bc-f384195f842a",
            "StackId": "arn:aws:cloudformation:us-west-1:1234:stack/my-app-stack-blog-test/164ff580-73e5-11f1-ab70-026546ec19e3",
            "OperationId": "65e641d0-73e5-11f1-abdb-06073274cc09",
            "OperationType": "UPDATE_STACK",
            "EventType": "VALIDATION_ERROR",
            "LogicalResourceId": "ConflictingBucket",
            "PhysicalResourceId": "",
            "ResourceType": "AWS::S3::Bucket",
            "Timestamp": "2026-06-29T18:07:36.139000+00:00",
            "ValidationFailureMode": "FAIL",
            "ValidationName": "NAME_CONFLICT_VALIDATION",
            "ValidationStatus": "FAILED",
            "ValidationStatusReason": "Resource of type 'AWS::S3::Bucket' with identifier 'production-data-bucket-blog-test-208004920468' already exists.",
            "ValidationPath": "/Resources/ConflictingBucket"
        },
        ...
    ]
}

The update stops before any resources are modified. You can either rename the resource in your template or remove the existing resource that causes the conflict.

Figure 3 - Resource Name Conflict on UpdateStack Event tab

The deployment validation view below provide moe detail about the error, include status reason and path to the resource.

Figure 3 - Resource Name Conflict on UpdateStack Deployment validation

Figure 3: Resource Name Conflict on UpdateStack

Scenario 3: Service Quota Warning on CreateChangeSet

Service Quota validation is one of three new warning-mode validations available during change set creation. It checks whether creating or updating resources would exceed your AWS service quotas.

Create Change Set

aws cloudformation create-change-set \
    --stack-name "vpc-stack" \
    --change-set-name "add-subnets" \
    --template-body file://vpc-with-many-subnets.yaml

Validation output:

{
    "EventId": "3ba6f27b-4d3c-4e73-bac2-8d8cbf71a6d3",
    "StackId": "arn:aws:cloudformation:us-west-1:1234:stack/vpc-quota-test/492a84d0-73ee-11f1-a714-02e5a60ac85d",
    "OperationId": "b55e1e8f-28d1-4cbe-a6c3-59c92707e180",
    "OperationType": "CREATE_CHANGESET",
    "EventType": "VALIDATION_ERROR",
    "LogicalResourceId": "VPC1",
    "PhysicalResourceId": "",
    "ResourceType": "AWS::EC2::VPC",
    "Timestamp": "2026-06-29T19:11:12.727000+00:00",
    "ValidationFailureMode": "WARN",
    "ValidationName": "SERVICE_QUOTA_VALIDATION",
    "ValidationStatus": "FAILED",
    "ValidationStatusReason": "Service quota will be exceeded: AWS::EC2::VPC current usage 1/5, creating 6 would exceed limit",
    "ValidationPath": "/Resources/VPC1"
}

Because this validation operates in WARN mode, the change set is created successfully. You can review the warning, request a quota increase through the Service Quotas console, and then proceed with execution. The two other new warning validations (AWS Config Recorder conflict detection and ECR Repository delete readiness) follow the same pattern.

Figure 4 - Service Quotas Warning on CreateChangeSet

Figure 4: Service Quota Warning on CreateChangeSet

Scenario 4: CDK Validate Experience

The cdk validate command provides a unified validation experience that combines multiple validation sources into a single report with construct-level source tracing. Under the hood, cdk validate synthesizes your CDK app, creates a change set to invoke server-side pre-deployment validation, collects the results via DescribeEvents, and produces a report that maps errors back to your CDK source code with construct-level tracing.

Each error traces back to the specific construct and source file location in your CDK code, not just the CloudFormation logical resource ID. This construct-level tracing is what makes cdk validate uniquely valuable: you see the exact line in your code that needs to change.

Scenario 5: Controlling Validation with DisableValidation

Pre-deployment validation is enabled by default on all stack operations. If you need to skip validation for a specific operation, use the DisableValidation parameter.

When to disable validation:

  • When you have already validated your template through other means (cdk validate, cfn-lint, CI/CD checks)
  • When you need to minimize operation latency for time-sensitive deployments

CLI usage:

# Skip validation on create-stack
aws cloudformation create-stack \
    --stack-name "my-stack" \
    --template-body file://template.yaml \
    --disable-validation

# Skip validation on update-stack
aws cloudformation update-stack \
    --stack-name "my-stack" \
    --template-body file://template.yaml \
    --disable-validation

Important: Disabling validation means common errors will not be caught until resource provisioning is attempted. Use this option only when you understand the trade-off between deployment speed and early error detection.

AI Agents and Automated Workflows

Pre-deployment validation gives AI agents and automation tools the fast feedback loop they need to self-correct. When an agent provisions infrastructure and the template has an error, validation returns a structured error in seconds rather than waiting minutes for a full provision-and-rollback cycle to complete. The agent can parse the error, fix the template, and retry immediately.

With cdk validate, agents get construct-level source tracing that maps errors directly to the line of CDK code that needs to change, enabling fully automated fix-and-retry loops without human intervention.

To get started with the agent experience, install the CloudFormation agent skill from the AWS Agent Toolkit. This skill gives AI agents the ability to create stacks, validate templates, and iterate on errors using pre-deployment validation feedback.

Getting Started

Pre-deployment validation runs automatically on all CreateStack, UpdateStack, and CreateChangeSet operations with no configuration required. To start benefiting:

  • Create or update a stack as you normally would. Validation runs automatically.
  • Review validation results using the DescribeEvents API, the CloudFormation Console Events tab (click the operation ID, then the Deployment validations tab), or the cdk validate command.
  • Fix identified issues in your template and retry the operation.
  • Optionally disable validation using --disable-validation for specific operations

Required IAM permissions for validation checks

Validation on CreateStack and UpdateStack (property syntax validation and resource name conflict detection) requires no additional IAM permissions beyond what is needed for the stack operation itself. For the new validation checks available during change set creation, your IAM role needs the following additional permissions:

Service Quota Check:

  • cloudwatch:GetMetricData
  • lambda:GetAccountSettings
  • servicequotas:GetServiceQuota
  • ec2:DescribeSecurityGroups
  • iam:GetAccountSummary

Config Recorder Check:

  • config:ListConfigurationRecorders

S3 Bucket Empty Check:

  • s3:ListBucketV2

ECR Repository Delete Readiness Check:

  • ecr:ListImages

If these permissions are not granted, the corresponding validation checks will be skipped without blocking the operation.

For CDK users:

# Run unified validation before deploying 
cdk validate

Best Practices

  • Use cdk validate as your primary pre-deployment check. It leverages CloudFormation pre-deployment validation in a single command, giving you comprehensive coverage before any deployment is attempted.
  • Place CreateChangeSet as the first pipeline stage. For pipelines that use change sets, this ensures pre-deployment validation fires at the pipeline entry point. CDK Pipelines integrates this by default.
  • Let validation run by default. The few seconds of validation time pay for themselves by preventing full provision-and-rollback cycles that take minutes or longer.
  • Use DisableValidation intentionally. Reserve it for cases where you have already validated through other means or need to bypass a known false positive. Do not disable validation globally.
  • Integrate validation into PR/CI workflows. Run cdk validate or cfn-lint as part of your pull request checks to catch errors before code is merged, preventing invalid templates from reaching deployment pipelines.
  • Monitor validation warnings. WARN-mode validations (service quota, Config Recorder, ECR delete readiness) indicate potential issues that may cause failures at execution time. Address them proactively.

Conclusion

Pre-deployment validation on all stack operations represents a significant step forward in CloudFormation’s shift-left validation strategy. By catching common deployment errors in seconds before any resources are provisioned, this capability eliminates unnecessary rollback cycles and accelerates development workflows across the board.

Combined with the cdk validate command, which provides a unified validation experience with construct-level tracing, and the DisableValidation parameter for operation-level control, teams now have a complete toolkit for managing the trade-off between validation coverage and deployment speed. AI agents and automated pipelines benefit from structured, machine-readable feedback that enables immediate self-correction, turning what were once multi-minute debugging cycles into second-level iteration loops.

Pre-deployment validation is available in all AWS Regions where CloudFormation is supported. No configuration or opt-in is required. To learn more, visit the Validate stack deployments User Guide.

Blog Authors Bio:

Idriss Laouali Abdou

Idriss is a Sr. Product Manager Technical on the AWS Infrastructure-as-Code team based in Seattle. He focuses on improving developer productivity through AWS CloudFormation and StackSets Infrastructure provisioning experiences. Outside of work, you can find him creating educational content for thousands of students, cooking, or dancing.

Olivia Biswas

Olivia is a Software Development Manager on the AWS Infrastructure-as-Code team based in Seattle, where she leads developer productivity initiatives through CloudFormation. During her tenure at Amazon, she has built several customer-obsessed software solutions within Alexa and Buy With Prime. Outside of work, she is a globe trotter who enjoys baking, dancing, reading, and watching documentaries.

Subha Velayutham

Subha is a Senior Software Engineer on the AWS Infrastructure-as-Code team, where she builds features to improve developer productivity. Outside of work, she enjoys reading, traveling, and experimenting with new creative hobbies.

Announcing AWS CDK Mixins: Composable Abstractions for AWS Resources

Post Syndicated from Michael Kaiser original https://aws.amazon.com/blogs/devops/announcing-aws-cdk-mixins-composable-abstractions-for-aws-resources/

We are excited to announce CDK Mixins, a feature of the AWS Cloud Development Kit (CDK) that fundamentally changes how you compose and reuse infrastructure abstractions. In this post, you will learn how to use CDK Mixins to apply sophisticated features to any construct – whether L1, L2, or custom – without being locked into specific implementations.

Background

The AWS Cloud Development Kit (CDK) is an open-source software development framework for defining cloud infrastructure in code and provisioning it through AWS CloudFormation. It contains pre-written, modular, and reusable cloud components known as constructs. Constructs are the basic building blocks representing one or more AWS CloudFormation resources and their configuration.

Traditionally, we organize CDK constructs into three levels. L1 constructs map directly to CloudFormation resources. L2 constructs offer higher-level abstractions with convenience methods, security defaults, and helper functions. L3 constructs (also known as patterns) combine multiple resources to solve specific use cases. However, this architecture creates a fundamental trade-off: you must choose between immediate access to new AWS features (L1) and sophisticated abstractions (L2/L3). Teams often need to customize L2 constructs, rebuilding entire construct libraries to meet their specific requirements.

CDK Mixins solve this problem by decoupling abstractions from construct implementations. Instead of bundling all features into monolithic L2 constructs, Mixins allow you to compose exactly the capabilities you need, apply them to any construct type, and maintain full access to underlying CloudFormation properties.

What are CDK Mixins?

CDK Mixins let you compose reusable abstractions and apply them to constructs after creation. You mix and match modular capabilities to build exactly the infrastructure you need. Unlike traditional L2 constructs that bundle all features together, Mixins give you fine-grained control over which abstractions apply.

Key benefits include:

  • Universal Compatibility: Apply the same abstractions to L1 constructs, L2 constructs, or custom constructs
  • Composable Design: Mix and match features without inheriting unwanted behaviors
  • Cross-Service Abstractions: Create custom mixins that work across different AWS services
  • Day-One Coverage: Access new AWS features immediately while keeping existing L2 or L3 constructs
  • Type Safety: Maintain compile-time guarantees and IDE support

Mixins and Aspects

CDK Aspects are a way to apply an operation to all constructs in a given scope, commonly used for validation, compliance, and tagging. Mixins and Aspects are complementary. Mixins apply features immediately to specific constructs, while Aspects enforce rules broadly across a scope during synthesis. A common pattern is to use Mixins to configure resources and Aspects to validate that the configuration is correct.

Using CDK Mixins

CDK Mixins ship with aws-cdk-lib, and you access service-specific mixins through the same imports you already use. They work across L1, L2, and L3 constructs:

import * as cdk from 'aws-cdk-lib/core';
import * as s3 from 'aws-cdk-lib/aws-s3';
import { CfnBucketPropsMixin } from '@aws-cdk/cfn-property-mixins/aws-s3';

// CDK Mixins can be used with L1s
new s3.CfnBucket(stack, "MixinsL1DemoBucket")
  // Use the fluent .with() syntax (available in JavaScript/TypeScript)
  // .with() silently skips unsupported constructs
  .with(new s3.mixins.BucketVersioning());

// ... or with L2s
new s3.Bucket(stack, "MixinsL2DemoBucket")
  // Cfn Property Mixins provide type-safe fallbacks for L2s
  // and configuration after initial creation
  .with(new CfnBucketPropsMixin({
    objectLockEnabled: true,
    objectLockConfiguration: {
      objectLockEnabled: "Enabled",
      rule: {
        defaultRetention: {
          mode: "COMPLIANCE",
          days: 30,
        },
      },
    },
  }));

You can also use Mixins.of() to apply Mixins in other languages or with more control over which constructs receive the mixin:

// Use Mixins.of() to apply Mixins in other languages
// This also gives you more options to apply only to certain constructs
cdk.Mixins.of(stack, cdk.ConstructSelector.byId('MixinsL1DemoBucket'))
  .apply(new s3.mixins.BucketAutoDeleteObjects());

Apply mixins at scale to entire construct trees or specific resource types:

// Apply your Mixins to the whole app
cdk.Mixins.of(app).apply(new MyDataRecovery());
// ... or only to some constructs
cdk.Mixins.of(app, cdk.ConstructSelector.resourcesOfType(s3.CfnBucket.CFN_RESOURCE_TYPE_NAME)).apply(new MyDataRecovery());

Creating Custom Mixins

Creating your own Mixins is straightforward; they are simple classes extending cdk.Mixin and implementing the IMixin interface. The supports() method determines which constructs the mixin can apply to, and applyTo() modifies the construct in place. Here’s a custom mixin that enables data recovery features across both Amazon Simple Storage Service (Amazon S3) buckets and Amazon DynamoDB tables:

// It's easy to develop your own Mixins
class MyDataRecovery extends cdk.Mixin implements IMixin {
  public supports(construct: any): construct is s3.CfnBucket | dynamodb.CfnTable {
    // Mixins can be cross-service and support different resources at once
    return s3.CfnBucket.isCfnBucket(construct) || dynamodb.CfnTable.isCfnTable(construct);
  }

  // applyTo modifies the construct in place (returns void)
  public applyTo(construct: IConstruct): void {
    if (s3.CfnBucket.isCfnBucket(construct)) {
      construct.versioningConfiguration = {
        status: 'Enabled',
      };
    }

    if (dynamodb.CfnTable.isCfnTable(construct)) {
      construct.pointInTimeRecoverySpecification = {
        pointInTimeRecoveryEnabled: true,
      };
    }
  }
}

Once defined, you can apply your custom mixin to resources:

// ... and to use them:
new s3.Bucket(stack, 'AcmeBucket');
new dynamodb.TableV2(stack, 'AcmeTable', {
  partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
});

// Apply your Mixins to the whole app
cdk.Mixins.of(app).apply(new MyDataRecovery());
// ... or only to some constructs
cdk.Mixins.of(app, cdk.ConstructSelector.resourcesOfType(s3.CfnBucket.CFN_RESOURCE_TYPE_NAME)).apply(new MyDataRecovery());

This pattern enables organizations to create reusable abstractions that work across any construct type, ensuring consistent security and compliance policies throughout their infrastructure.

Mixin Behavior Control

Control how to apply Mixins with three distinct modes: graceful application, requireAll, and requireAny. The report getter lets you inspect which constructs were successfully modified and add custom assertions:

// Graceful: apply() silently skips unsupported constructs
const logGroup = new logs.CfnLogGroup(stack, 'LogGroup');
cdk.Mixins.of(logGroup).apply(new s3.mixins.BucketAutoDeleteObjects());

// requireAll: Throws if ANY selected construct is not supported by the mixin
cdk.Mixins.of(logGroup).apply(new s3.mixins.BucketAutoDeleteObjects()).requireAll();

// requireAny: Throws if NO selected construct is supported by the mixin
cdk.Mixins.of(stack).apply(new s3.mixins.BucketVersioning()).requireAny();

Use the report getter to inspect application results and the selectedConstructs getter to see which constructs matched the selector:

const applicator = cdk.Mixins.of(app, cdk.ConstructSelector.resourcesOfType(s3.CfnBucket.CFN_RESOURCE_TYPE_NAME));
const result = applicator.apply(new s3.mixins.BucketVersioning());

// See which constructs were matched by the selector
console.table(applicator.selectedConstructs.map(c => c.node.path));

// Inspect which constructs were successfully modified, grouped by construct
console.table(result.report.map(r => ({ construct: r.construct.node.path, mixin: util.inspect(r.mixin) })));

This flexibility allows you to choose the right behavior for your use case, whether you want to apply Mixins opportunistically, enforce that at least one construct matches, or require that every selected construct is supported.

ECS ClusterSettings mixin

The ClusterSettings mixin enables you to apply Amazon ECS cluster settings like enhanced Container Insights to both L1 and L2 clusters. It handles array merging intelligently, updating existing settings by name or appending new ones:

import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';

// Works with L1 constructs
new ecs.CfnCluster(stack, 'L1Cluster', { clusterName: 'my-cluster' })
  .with(new ecs.mixins.ClusterSettings([
    { name: 'containerInsights', value: 'enhanced' },
  ]));

// Works with L2 constructs too
new ecs.Cluster(stack, 'L2Cluster', { vpc, clusterName: 'my-cluster' })
  .with(new ecs.mixins.ClusterSettings([
    { name: 'containerInsights', value: 'enhanced' },
  ]));

S3 mixins: PublicAccessBlock and BucketPolicyStatements

New S3 mixins provide fine-grained controls. The PublicAccessBlockMixin configures public access settings, and the BucketPolicyStatements lets you add bucket policy statements declaratively:

import * as s3 from 'aws-cdk-lib/aws-s3';

// Block all public access on S3 buckets
new s3.CfnBucket(stack, 'SecureBucket')
  .with(new s3.mixins.BucketBlockPublicAccess());

// Apply public access block across all S3 buckets in the app
cdk.Mixins.of(app, cdk.ConstructSelector.resourcesOfType(s3.CfnBucket.CFN_RESOURCE_TYPE_NAME))
  .apply(new s3.mixins.BucketBlockPublicAccess())
  .requireAll();

Vended logs and log delivery

Setting up vended log delivery in CloudFormation typically requires coordinating multiple resources – AWS::Logs::DeliverySource, AWS::Logs::DeliveryDestination, and AWS::Logs::Delivery to connect them – along with the correct IAM permissions for each destination type. You must repeat this boilerplate for every resource you want to deliver logs from, and it varies by service.

CDK Mixins collapse this complexity into a single .with() call. Because Mixins decouple the log delivery abstraction from any specific construct, the same pattern works across all 47 supported AWS resources – whether you’re using L1 or L2 constructs. While still in preview, this is one of the most compelling examples of why Mixins matter: you get a sophisticated, cross-service abstraction that would traditionally require dedicated L2 construct support for each of those 47 resources.

// NOTE: Vended log delivery mixins are still in @aws-cdk/mixins-preview
import * as wafv2Mixins from '@aws-cdk/mixins-preview/aws-wafv2/mixins';

// Set up vended log delivery to an S3 bucket
const bucket = new s3.Bucket(stack, 'LogBucket');
new wafv2.CfnWebACL(stack, 'WebAcl', { /* ... */ })
  .with(new wafv2Mixins.CfnWebACLAccessLogs().toS3(bucket));

// Same pattern, different destination - works identically
const logGroup = new logs.LogGroup(stack, 'LogGroup');
new wafv2.CfnWebACL(stack, 'WebAcl2', { /* ... */ })
  .with(new wafv2Mixins.CfnWebACLAccessLogs().toLogGroup(logGroup));

Without Mixins, adding vended log delivery to an L1 construct would mean either waiting for L2 support or manually wiring up the three CloudFormation resources and permissions yourself. Mixins let you bring this L2-quality abstraction to any construct immediately.

For cross-account centralized logging, the toDestination() method sends logs to a pre-created delivery destination, so you can aggregate logs in a shared account without granting direct access to the destination resource:

const destination = logs.CfnDeliveryDestination.fromDeliveryDestinationName(
  stack, 'Dest', 'my-cross-account-destination'
);
new wafv2.CfnWebACL(stack, 'WebAcl3', { /* ... */ })
  .with(new wafv2Mixins.CfnWebACLAccessLogs().toDestination(destination));

Getting Started

CDK Mixins core functionality – including cdk.Mixins, cdk.ConstructSelector, and the .with() syntax – is included in aws-cdk-lib. You access service mixins through standard service imports (e.g. s3.mixins, ecs.mixins). CloudFormation property mixins for type-safe L1 property overrides come from the separate @aws-cdk/cfn-property-mixins package.

  1. Install the packages:
    npm install aws-cdk-lib @aws-cdk/cfn-property-mixins
  2. Import core classes from aws-cdk-lib/core:
    import * as cdk from 'aws-cdk-lib/core';
    // e.g. cdk.Mixins, cdk.ConstructSelector, cdk.Mixin
  3. Service-specific mixins are namespaced under mixins in each aws-cdk-lib service module:
    import * as s3 from 'aws-cdk-lib/aws-s3';
    import * as ecs from 'aws-cdk-lib/aws-ecs';
    // e.g. s3.mixins.BucketVersioning, ecs.mixins.ClusterSettings
  4. For Cfn Property Mixins, import from the separate package:
    import { CfnBucketPropsMixin } from '@aws-cdk/cfn-property-mixins/aws-s3';
  5. For log delivery mixins (preview), install the preview package:
    npm install @aws-cdk/mixins-preview
    import * as wafv2Mixins from '@aws-cdk/mixins-preview/aws-wafv2/mixins';
  6. Explore the CDK Mixins package README for detailed examples and API references.

Conclusion

CDK Mixins represent a fundamental shift in how we think about infrastructure abstractions. By decoupling capabilities from construct implementations, Mixins give you the freedom to compose exactly the infrastructure you need whether you are using L1 constructs for access to new CloudFormation resources, L2 constructs for convenience, or custom constructs for enterprise requirements.

Since the initial developer preview, the ecosystem has grown rapidly: log delivery mixins for 47 resources, EventBridge event pattern helpers for 26 services, ECS cluster settings, S3 security mixins, and resource policy traits that bring L2-style permissions to L1 constructs. We refined the API with requireAll/requireAny for precise behavior control and application reporting.

We are excited to see what the community builds with CDK Mixins. Share your feedback, create custom Mixins, and help shape the future of infrastructure as code with AWS CDK.

For more information, check out:

 

Michael Kaiser portrait

Momo Kornher

Momo is a Senior Software Development Engineer on the AWS CDK team. A CDK user since its public preview, he joined AWS to tackle infrastructure-as-code problems at large scale from the inside. He is passionate about open source and building abstractions that help teams manage complex cloud environments with less effort.

Michael Kaiser portrait

Michael Kaiser

Michael is a Solution Architect as AWS. He works with State and Local Public Sector customers to help modernize their business processes. He is the CDK Champion for the AWS TFC, owner of the CDK workshop, and maintainer for the CDK Examples Repo on GitHub

Streamlining Cloud Compliance at GoDaddy Using CDK Aspects

Post Syndicated from Juan Pablo Melgarejo Zamora original https://aws.amazon.com/blogs/devops/streamlining-cloud-compliance-at-godaddy-using-cdk-aspects/

This is a guest post written by Jasdeep Singh Bhalla from GoDaddy.

AWS Cloud Development Kit (CDK) Aspects are a powerful mechanism that allows you to apply organization-wide policies, like security rules, tagging standards, and compliance requirements across your entire infrastructure as code. By implementing the Visitor pattern, Aspects can inspect and modify every construct in your CDK application before it’s synthesized into AWS CloudFormation templates, enabling you to enforce organizational standards automatically at build time.

At GoDaddy, we’ve used CDK Aspects to transform how we enforce compliance across our massive AWS footprint. Our Cloud Governance team is responsible for ensuring every AWS resource deployed across thousands of accounts adheres to strict security, compliance, and operational standards.

We have a simple goal:

Make it easy for developers to do the right thing without slowing them down.

Traditionally, we relied on documentation, Slack threads, and peer reviews to flag misconfigurations. But as our cloud footprint grew, this approach quickly hit its limits. It simply didn’t scale.

We needed something proactive.

From reactive to proactive: CloudFormation Hooks

Our first major leap forward came through CloudFormation Hooks. These allow us to validate every resource in a CloudFormation template against our compliance rules at deployment time. If a resource passes, it gets deployed. If not, the deployment is blocked, and we provide developers with clear, actionable error messages to help them fix the template.

This worked well, but it wasn’t perfect. Developers would often only discover issues after writing their entire templates using manual values to make the template compliant, and attempting deployment. This was a time-consuming process, and it was not a good developer experience.

We needed an automated way to make CloudFormation templates compliant with our compliance rules without manual effort.

This is where CDK Aspects come in.

CDK Aspects: compliance while you code

CDK Aspects are our answer to proactive, early-stage compliance enforcement — catching and resolving issues at the code level, before deployment.

In AWS CDK, an Aspect is a lightweight Visitor that can inspect and act on every construct in your infrastructure code before it’s synthesized into a CloudFormation template. This means you can apply organization-wide rules as developers write CDK code, not after.

Think of it as linting for your infrastructure.

Want to ensure all Amazon Simple Storage Service (Amazon S3) buckets have encryption enabled? Require specific tags on resources? Block public access on security groups? With CDK Aspects, all of that becomes not only possible, but automatic.

Under the hood: how CDK Aspects work

CDK Aspects are a powerful mechanism for inspecting and modifying your infrastructure as code. At their core, they use the Visitor Pattern, which allows you to traverse a tree of objects (constructs) and perform operations on each node without modifying the constructs themselves directly.

Interface IAspect

An Aspect is a class that implements the IAspect interface:

interface IAspect {
    visit(node: IConstruct): void;
}

The single visit() method is called for every construct in the scope where the Aspect is applied. Inside visit(), you can inspect, modify, or enforce rules on the construct.

Adding Aspects

To attach an Aspect to a construct (or a tree of constructs), use the following method:

Aspects.of(myConstruct).add(new SomeAspect());

This adds the Aspect to the construct’s internal list.

When cdk deploy is run, a CDK app goes through several phases:

A horizontal flow diagram illustrating the CDK App Lifecycle consisting of six sequential stages connected by arrows from left to right. The stages are: 1) CDK app source code, 2) Construct, 3) Prepare, 4) Validate, 5) Synthesize, and 6) Deploy. Each stage is represented by a light blue rectangular box with centered text. The entire lifecycle flow is contained within a gray bordered frame with the title 'CDK App Lifecycle' at the top. This diagram shows the progression of AWS Cloud Development Kit (CDK) applications from initial source code through deployment.

Figure 1: CDK app lifecycle from source code to deployment

  • Construction – Constructs are instantiated.
  • Preparation – Final modifications and Aspects are applied.
  • Validation – Checks for invalid configurations.
  • Synthesis – CloudFormation templates are generated.
  • Deployment – Resources are provisioned in AWS.

Aspects are executed during the Preparation phase, which happens automatically. This ensures all rules, validations, or mutations are applied before synthesis, so your generated CloudFormation templates are compliant and valid before deployment.

During the Preparation phase of the CDK lifecycle, CDK traverses the construct tree and calls visit() on each node in top-down order (parent → children). Inside visit(), you can inspect, modify, or enforce rules on the construct.

visit(node: IConstruct) {
    if (node instanceof s3.Bucket) {
        node.encryption = s3.BucketEncryption.KMS; // Mutates the resource
    }
}

Example: CDK Aspects to automatically add S3 Encryption to all S3 buckets in a stack by mutating the resource.

class EnforceBucketEncryption implements IAspect {
    visit(node: IConstruct) {
        if (node instanceof s3.Bucket) {
            node.encryption = s3.BucketEncryption.KMS; // Mutates the resource
        }
    }
}

This aspect can be registered on the stack by calling the following method:

Aspects.of(this).add(new EnforceBucketEncryption());

The template generated by CDK will look something like this:

Resources:
    MyBucket:
        Type: AWS::S3::Bucket
        Properties:
            BucketEncryption:
                ServerSideEncryptionConfiguration:
                    - ServerSideEncryptionByDefault:
                          SSEAlgorithm: aws:kms

From the example above, you can see that the BucketEncryption property is added to the MyBucket resource by the Aspect.

During the prepare phase, CDK traverses the construct tree from top to bottom – starting at the App, then each Stack, and down to resources like S3 buckets. At each node, Aspects are applied by invoking aspect.visit(node), allowing inspection and modification of resources. By the time CDK reaches the synth step, the CloudFormation template already includes these Aspect-driven changes, ensuring compliance and best practices are consistently enforced before deployment.

Types of CDK Aspects

AWS CDK distinguishes between two types of Aspects based on how they interact with your infrastructure: those that modify resources (mutating) and those that only inspect them (read-only).

Mutating Aspects

Mutating Aspects modify resources automatically (like adding encryption or logging). These change the properties of resources as they traverse your constructs. They are ideal for enforcing compliance and best practices like:

Mutating Aspects are powerful, but overusing them can introduce unintended changes, because they modify resources without explicit developer action.Always make sure you are aware of the changes, and avoid applying them to production resources without caution. Use logging or annotations to help you understand and debug the changes.

For example, the following aspect sets the default timeout on all Lambda functions to 300 seconds:

class SetDefaultTimeouts implements IAspect {
    visit(node: IConstruct) {
        if (node instanceof lambda.Function) {
            node.timeout = 300; // mutates the resource
        }
    }
}

Read-only Aspects

Read-only Aspects only inspect resources and report findings without modifying them. They’re ideal for compliance checks, tagging audits, and policy validation.

class RequireTags implements IAspect {
    visit(node: IConstruct) {
        const tags = Tags.of(node);
        if (!tags.hasTag("project_budget_number")) {
            Annotations.of(node).addWarning(
                "Missing required tag: project_budget_number",
            );
        }
    }
}

At GoDaddy, we use mutating Aspects to enforce compliance automatically, reducing manual work and ensuring stacks are compliant with our standards by default. We add read-only Aspects for stricter audits where mutation isn’t appropriate.

Common use cases for CDK Aspects

When building cloud infrastructure at scale, enforcing consistency across stacks can be an ongoing challenge. AWS CDK Aspects let you automatically enforce security, compliance, and operational standards across your entire infrastructure.

Below are some of the most impactful use cases where Aspects can save you time, reduce risk, and improve governance.

  • Security & compliance: Security and compliance go hand in hand, and Aspects are a powerful way to enforce both before resources ever reach AWS. With Aspects, you can:
  • IAM policies: Aspects make it trivial and consistent across all stacks to apply the same permissions boundary to every IAM role.
  • Tagging enforcement: Tags are the backbone of cost allocation, compliance, and automation. Yet, they’re easy to forget. With Aspects, you can enforce required tags across every resource.
  • Operational best practices: Use Aspects to enforce sensible defaults and operational hygiene:
  • Testing made easier: You can use Aspects to override the default RemovalPolicy for test stacks to DESTROY, ensuring everything is cleaned up automatically.
  • Working around 3rd-party constructs: Sometimes external constructs create resources you can’t fully configure, like an S3 bucket without encryption or logging options. Instead of waiting on maintainers or forking the library, you can apply an Aspect to modify those resources directly.
  • Network policies: Networking issues often lead to security incidents. With Aspects, you can:
  • Cost control: Budgets matter. Aspects can help by:
    • Flagging high-cost instance types before deployment
    • Limiting use of expensive storage classes
    • Warning when provisioned throughput exceeds thresholds

You can combine these use cases into reusable, testable policies that run consistently across all CDK stacks.

CDK Aspects in action at GoDaddy

At GoDaddy, we define CDK Aspects and distribute them through a wrapper Stack that development teams use when building infrastructure with CDK. Every template a team creates is automatically made compliant with GoDaddy’s cloud compliance rules, without requiring manual updates or fixes.

As an example, some of the Aspects are:

  • S3BucketAspect – Enforces encryption, logging, and public access block on S3 buckets.
  • IAMRoleAspect – Flags wildcard permissions and enforces naming conventions.
  • LambdaFunctionAspect – Validates timeouts, memory limits, and Amazon VPC configurations.

These Aspects enforce security, operational, and tagging standards at the code level. When you use the wrapper stack, the relevant Aspects are applied automatically, injecting the necessary properties into the CloudFormation template before deployment. This is intended to support compliance without slowing down development.

Each of these aspects implements the IAspect interface and is applied to stacks directly:

Aspects.of(myStack).add(new S3BucketAspect());
Aspects.of(myStack).add(new IAMRoleAspect());
Aspects.of(myStack).add(new LambdaFunctionAspect());

This approach means that when a developer defines a new resource, like an S3 bucket or IAM role, all relevant compliance rules are automatically applied:

const bucket = new s3.Bucket(myStack, "MyBucket", {
    // developer doesn't need to manually configure encryption or logging
});

During the CDK preparation phase, the aspect injects the required properties into the CloudFormation template, which is done before the template is synthesized and deployed. This ensures that the resources are deployed with the required properties at GoDaddy’s standards.

# s3-bucket.yaml - generated by `cdk synth`
Resources:
    MyBucket:
        Type: AWS::S3::Bucket
        Properties:
            BucketEncryption:
                ServerSideEncryptionConfiguration:
                    - ServerSideEncryptionByDefault:
                          SSEAlgorithm: AES256
            PublicAccessBlockConfiguration:
                BlockPublicAcls: true
                BlockPublicPolicy: true
                IgnorePublicAcls: true
                RestrictPublicBuckets: true
            LoggingConfiguration:
                DestinationBucketName: logging-bucket
                LogFilePrefix: logs/

Consider a team that deploys hundreds of S3 buckets per month, where each bucket requires encryption, logging, versioning, and public access block. With CDK Aspects, you don’t have to manually update the CloudFormation template to add the required properties.

This saves a lot of engineering effort and time, and ensures that the resources are deployed with the required properties that meet GoDaddy’s standards.

Conclusion

At GoDaddy, CDK Aspects have significantly transformed our approach to cloud infrastructure compliance.

Because Aspects run during the CDK preparation phase, before synthesis and deployment, they inject required properties like encryption, logging, and access controls into CloudFormation templates automatically. Developers write their CDK code as usual, and compliance happens behind the scenes. This eliminates the manual effort of configuring each resource to meet security and compliance standards.

This proactive approach has also improved developer productivity. Instead of discovering compliance failures after attempting deployment (as was the case with CloudFormation Hooks alone), developers now get compliant templates on the first synthesis. Fewer failed deployments mean faster iteration cycles and less time spent debugging configuration issues.

Policy enforcement is consistent because every team uses the same wrapper Stack with the same Aspects applied. Whether a team deploys an S3 bucket, an IAM role, or a Lambda function, the same tagging, encryption, network isolation, and cost control rules are applied uniformly.

Finally, this model scales. Adding a new compliance rule means updating a single Aspect in the shared library, and every stack that uses the wrapper inherits the change automatically. This lets our Cloud Governance team enforce standards across thousands of accounts with minimal operational overhead.

If you’re working with AWS CDK at scale, adopting CDK Aspects isn’t just a nice-to-have, it’s essential. Your future self and your security team will thank you.

To get started, pick one compliance rule, like S3 encryption, and implement it as a mutating Aspect. Once you see how it works, expand to cover tagging and IAM policies. From there, package your Aspects into a shared library that teams across your organization can adopt.

References

The content and opinions in this blog are those of the third-party author and AWS is not responsible for the content or accuracy of this blog.

Jasdeep Singh Bhalla

is a Senior Software Engineer at GoDaddy, focused on scaling Cloud Infrastructure governance and automation across thousands of AWS accounts. He builds frameworks, APIs, and tools that make it easy for developers to follow best practices in AWS, while ensuring every resource is secure, compliant, and production-ready by default.

Juan Pablo Melgarejo Zamora

is a Sr. Solutions Architect at Amazon Web Services, where he helps customers architect and optimize their cloud solutions. Throughout his career, he has built expertise across Data Engineering, High Performance Computing (HPC), and DevOps practices. He leverages this diverse technical background to provide comprehensive solutions for AWS customers. Outside of work, he enjoys cooking and traveling.

Standardizing construct properties with AWS CDK Property Injection

Post Syndicated from Marco Frattallone original https://aws.amazon.com/blogs/devops/standardizing-construct-properties-with-aws-cdk-property-injection/

Standardizing CDK construct properties across a large organization requires repetitive manual effort that scales poorly as teams and repositories grow. Development teams working with AWS Cloud Development Kit (AWS CDK) must apply the same configuration properties across similar resources to meet security, compliance, and operational standards but manual configuration leads to drift, maintenance burden, and compliance gaps. In this post, you learn how to use Property Injection, a feature introduced in AWS CDK v2.196.0, to automatically apply default properties to constructs without modifying existing code.

The Challenge of Infrastructure Standardization

Organizations implementing infrastructure as code face a fundamental tension between developer productivity and operational consistency. CDK provides abstractions for defining cloud resources, but ensuring compliance with organizational security policies, compliance requirements, and operational standards requires repetitive manual configuration.
Consider this scenario: an organization’s security policy requires that all SecurityGroups disable outbound traffic by default. Development teams must apply these settings to every SecurityGroup:


new SecurityGroup(stack, 'api-sg', {
  vpc: myVpc,
  allowAllOutbound: false,        // Required by security policy
  allowAllIpv6Outbound: false     // Required by security policy
});

new SecurityGroup(stack, 'db-sg', {
  vpc: myVpc,
  allowAllOutbound: false,        // Same configuration repeated
  allowAllIpv6Outbound: false     // Same configuration repeated
});

This manual approach creates four specific problems:

  • Configuration drift: Teams omit required properties or apply them inconsistently
  • Maintenance burden: Policy updates require coordinated changes across multiple repositories and teams
  • Developer friction: Repetitive configuration tasks slow development velocity and increase cognitive load
  • Compliance gaps: Manual processes introduce human error, creating security or compliance violations

Custom construct libraries address these challenges but require refactoring every construct instantiation in existing code and create learning curves for development teams already familiar with standard CDK patterns.

Introducing Property Injection

AWS CDK Property Injection addresses these challenges by automatically applying default properties to constructs without requiring changes to existing code.

Property Injection is a feature introduced in AWS CDK v2.196.0 that intercepts construct creation and automatically applies organizational defaults. With this approach, you can enforce standards consistently while preserving existing development workflows and code patterns.

After implementing Property Injection, the same SecurityGroup creation requires only the vpc parameter, security defaults are applied automatically:

// Your existing code remains unchanged
new SecurityGroup(stack, 'my-sg', {
  vpc: myVpc
  // Security defaults applied automatically by Property Injection
});

The key benefits of this approach include:

  • Zero-impact adoption: Existing CDK code continues to work without modification
  • Centralized policy management: Standards are defined once and applied automatically
  • Consistent enforcement: Policies are applied uniformly across all applications and teams
  • Reduced maintenance overhead: Policy updates require changes in only one location
This diagram shows the five-step Property Injection process in a clear two-column format. The left column outlines each process step, while the right column shows the corresponding implementation details with properly formatted TypeScript code. The flow demonstrates how CDK intercepts SecurityGroup creation, applies organizational security defaults through property injectors, merges them with developer-specified properties, and creates a fully configured SecurityGroup that meets both developer requirements and organizational standards.

Figure 1: CDK Property Injection Mechanism

Property Injection operates transparently within CDK, intercepting construct creation to apply predefined defaults before merging them with any properties explicitly provided by developers. This ensures that organizational standards are consistently applied while maintaining the flexibility for developers to override defaults when specific use cases require it.

Understanding the Implementation Approach

Property Injection works by implementing the IPropertyInjector interface, which allows you to define default properties for specific construct types. These injectors are registered with CDK stacks and automatically apply their defaults during construct instantiation.
The implementation follows three steps: define the defaults you want to apply, register the injector with your stack, and let CDK handle the automatic application of these defaults to matching constructs.

Implementation Guide

This section shows you how to implement Property Injection for SecurityGroup constructs.

Step 1: Create a Property Injector

Create a class that implements the IPropertyInjector interface:

import { IPropertyInjector, InjectionContext } from 'aws-cdk-lib';
import { SecurityGroup, SecurityGroupProps } from 'aws-cdk-lib/aws-ec2';

export class SecurityGroupDefaults implements IPropertyInjector {
  readonly constructUniqueId: string;

  constructor() {
    this.constructUniqueId = SecurityGroup.PROPERTY_INJECTION_ID;
  }

  inject(originalProps: SecurityGroupProps, context: InjectionContext): SecurityGroupProps {
    return {
      // Apply organizational defaults
      allowAllIpv6Outbound: false,
      allowAllOutbound: false,
      // Original properties override defaults when specified
      ...originalProps,
    };
  }
}

Step 2: Add the Injector to Your Stack

Apply the injector to your CDK stack:

import { Stack } from 'aws-cdk-lib';
import { SecurityGroupDefaults } from './security-defaults';

const stack = new Stack(app, 'MyStack', {
  propertyInjectors: [
    new SecurityGroupDefaults()
  ]
});

Step 3: Use Constructs Normally

Create constructs as usual. The injector applies defaults automatically:

// This SecurityGroup receives the injected defaults:
// - allowAllOutbound: false
// - allowAllIpv6Outbound: false
new SecurityGroup(stack, 'my-sg', {
  vpc: myVpc
});

// You can override defaults when necessary
new SecurityGroup(stack, 'special-sg', {
  vpc: myVpc,
  allowAllOutbound: true  // Overrides the injected default
});
This side-by-side comparison shows the difference between manual configuration and Property Injection. The left side (Before) shows three SecurityGroup definitions, each requiring manual specification of allowAllOutbound: false and allowAllIpv6Outbound: false, leading to repetitive code, inconsistency risk, and maintenance burden. The right side (After) shows the same SecurityGroups created with the VPC parameter alone after a one-time Property Injection setup, demonstrating the DRY principle, consistent defaults, and reduced maintenance.

Figure 2: CDK Code Before vs After Property Injection

Property Injection vs L2 Constructs

You can achieve the same enforcement of default properties by creating custom L2 constructs with built-in defaults. However, Property Injection is better suited for standardizing existing codebases without refactoring, while L2 Constructs are better suited for new projects where you want custom APIs and multi-resource abstractions.

This decision tree guides the selection between Property Injection and L2 Constructs for CDK standardization. Starting with existing CDK applications, it evaluates willingness to accept potential breaking changes from new defaults. If breaking changes are acceptable or no existing code exists, it assesses whether custom APIs, naming improvements, or multi-resource patterns are needed beyond simple defaults. The tree leads to three outcomes: Property Injection (blue) for transparent defaults with existing code compatibility, L2 Constructs (orange) for custom APIs and purpose-built abstractions, or a Hybrid approach (green) combining both techniques for maximum flexibility.

Figure 3: Decision Tree – Property Injection vs L2 Constructs

Implementation Comparison

Consider an application with multiple SecurityGroup instantiations that need standardized security defaults.

L2 Construct approach requires creating a custom construct and updating each instantiation:

// Step 1: Create custom L2 construct
export class SecureSecurityGroup extends SecurityGroup {
  constructor(scope: Construct, id: string, props: SecurityGroupProps) {
    super(scope, id, {
      allowAllOutbound: false,
      allowAllIpv6Outbound: false,
      ...props
    });
  }
}

// Step 2: Update each instantiation throughout your codebase
// Change from:
new SecurityGroup(stack, 'sg1', { vpc: myVpc })
new SecurityGroup(stack, 'sg2', { vpc: myVpc })
new SecurityGroup(stack, 'sg3', { vpc: myVpc })

// To:
new SecureSecurityGroup(stack, 'sg1', { vpc: myVpc })
new SecureSecurityGroup(stack, 'sg2', { vpc: myVpc })
new SecureSecurityGroup(stack, 'sg3', { vpc: myVpc })

Property Injection approach requires one-time stack configuration:

// Step 1: Add injector to stack configuration
stack.propertyInjectors = [new SecurityGroupDefaults()];

// Step 2: Existing SecurityGroup calls receive defaults automatically
new SecurityGroup(stack, 'sg1', { vpc: myVpc })  // Gets defaults
new SecurityGroup(stack, 'sg2', { vpc: myVpc })  // Gets defaults  
new SecurityGroup(stack, 'sg3', { vpc: myVpc })  // Gets defaults

Key Differences

Property Injection works with existing construct calls, requiring no changes to how developers instantiate SecurityGroups or other constructs. This approach overrides constructs from external libraries and can be implemented without modifying existing code. Developers continue using familiar CDK APIs without learning new interfaces.

L2 Constructs require updating all constructor calls throughout your codebase. This approach cannot modify third-party construct creation since you must change each instantiation to use your custom construct. Implementation requires refactoring existing code and developers must learn your custom construct APIs instead of standard CDK interfaces. L2 constructs serve multiple purposes beyond complex business logic – simple L2 constructs provide domain-specific naming conventions and cleaner APIs, while complex L2 constructs orchestrate three or more resources and implement business rules.

When to Choose Each Approach

Choose Property Injection when you need to standardize existing infrastructure. Property Injection excels in scenarios where you already have CDK applications deployed and need to apply consistent defaults retroactively. Property Injection works transparently with existing code, requiring no changes to how developers instantiate constructs. This makes it useful when you have existing CDK applications that you want to standardize without disrupting current development workflows.

Property Injection also solves the challenge of applying defaults to constructs from third-party libraries. Since you cannot modify external library code, Property Injection enforces organizational standards on any construct type, regardless of its source. Additionally, when you want to implement standards without changing existing code, Property Injection operates at the framework level, automatically applying defaults during construct instantiation without requiring developers to modify their existing implementations.

Choose L2 Constructs when you need custom APIs or multi-resource patterns. L2 Constructs provide the right abstraction when you want to create purpose-built interfaces that differ from standard CDK APIs. This includes simple wrappers with domain-specific naming, complex business logic, validation rules, or multi-resource orchestration patterns. L2 Constructs excel when you want to create opinionated APIs that simplify common patterns by hiding complexity behind intuitive interfaces.

L2 Constructs suit new application development where you can design the API from the start. This approach creates purpose-built abstractions that match your organization’s specific use cases and terminology. Unlike Property Injection, which applies defaults to existing construct APIs, with L2 Constructs you can design entirely new APIs that directly represent your business domain and operational patterns.

Implementation Patterns

Stack Integration Methods

The CDK provides two methods for adding Property Injectors to stacks:

This diagram demonstrates two methods for adding Property Injectors to CDK stacks. Method 1 (blue) shows adding injectors directly in the Stack constructor’s propertyInjectors array. Method 2 (orange) shows using PropertyInjectors.of(stack).add() after stack creation. Both methods produce identical results with green checkmarks indicating success. The diagram includes usage examples showing normal SecurityGroup instantiation (blue) that inherits defaults automatically, and override scenarios (orange) where developers explicitly override injected defaults. The bottom section shows the resulting CloudFormation output: default SecurityGroups have empty egress rules (green), while overridden ones include outbound traffic rules (orange).

Figure 4: CDK Stack Integration Methods

Method 1: Stack Constructor

const stack = new Stack(app, 'MyStack', {
  propertyInjectors: [new SecurityGroupDefaults()]
});

Method 2: PropertyInjectors.of()

const stack = new Stack(app, 'MyStack');
PropertyInjectors.of(stack).add(new SecurityGroupDefaults());

Both methods produce the same result. Choose the method that best fits your existing code structure. For more details, see the PropertyInjectors API documentation.

Organization-Wide Implementation

For organization-wide standardization, create a shared library of injectors:

// @myorg/cdk-injectors package
export const ORGANIZATION_INJECTORS: IPropertyInjector[] = [
  new SecurityGroupDefaults(),
  new LambdaFunctionDefaults(),
  new S3BucketDefaults(),
];

// Teams import and use the shared injectors
import { ORGANIZATION_INJECTORS } from '@myorg/cdk-injectors';

const stack = new Stack(app, 'TeamStack', {
  propertyInjectors: ORGANIZATION_INJECTORS
});

Scope Hierarchy

Property Injectors can be applied at different levels in the CDK construct tree:

This diagram illustrates the three-level hierarchy of Property Injector scopes in CDK with integrated resolution examples. The App level (blue) shows a BucketInjector ‘b1’ that applies globally, with an example showing how Stack2 buckets use this injector. The Stage level (green) demonstrates a FunctionInjector ‘f1’ that applies to all stacks within the stage, including an example of Stack1 functions using this injector. The Stack level (orange) shows two stacks: Stack1 with its own BucketInjector ‘b2’ that overrides the app-level injector, and Stack2 with no injectors that inherits from parent scopes. The Resolution Rules box (green) explains that CDK searches from most specific (stack) to most general (app), with the first match winning per construct type. Arrows show the hierarchical relationship between scopes.

    Figure 5: CDK Scope Hierarchy & Injector Resolution
  • App level: Applies to all stacks in the application
  • Stage level: Applies to all stacks within a specific stage
  • Stack level: Applies only to constructs within a specific stack

CDK searches for applicable injectors starting from the construct’s immediate parent scope and moving upward. The first matching injector for each construct type is used.

Best Practices

When implementing Property Injection, begin with high-impact constructs like SecurityGroups, VPCs, and Lambda functions that require repetitive configuration.

These constructs have the highest frequency of misconfiguration and the most direct compliance impact, making them the most valuable targets for early adoption.

Document your defaults by explaining what properties your injectors provide and why. Include examples and link to relevant policies that drive the requirements. With this documentation, developers can understand standards and make informed override decisions.

Write automated tests using CDK testing utilities to verify that injectors apply expected defaults. Test both standard scenarios and cases where developers override properties to prevent regressions when updating injector logic.

Version injectors carefully using semantic versioning principles because changes affect all applications. Coordinate updates across teams and provide migration guides for breaking changes or changes to default values.

Design override mechanisms so that developers can handle edge cases while benefiting from organizational standards. Property Injection operates as defaults, not restrictions, so design injectors to merge gracefully with developer-specified properties.

Limitations and Considerations

Property Injection operates as a default mechanism rather than a compliance enforcement system. Developers retain the ability to override injected properties, which means organizations cannot rely solely on Property Injection for strict compliance requirements. For teams that need mandatory compliance, combine Property Injection with CDK Aspects or AWS Config rules to validate and enforce standards.

The feature works exclusively with L2 constructs, as documented in the official AWS CDK guidance. The IPropertyInjector interface targets specific L2 construct types, and L1 (CloudFormation) constructs use different instantiation patterns that bypass the property injection mechanism entirely. Organizations with L1 construct usage need alternative standardization approaches.

Property Injection introduces debugging complexity because injected properties do not appear directly in application code. Developers troubleshooting construct behavior must understand which injectors apply to specific construct types and how those injectors modify properties. This hidden behavior requires documentation that lists each injector, the properties it sets, and the policy it enforces, along with clear naming conventions to maintain code clarity.

The feature requires CDK v2.196.0 or later, which affects adoption timelines for organizations using older CDK versions. Teams must plan upgrade paths and test compatibility before implementing Property Injection across their applications.

Conclusion

Property Injection provides a mechanism for applying consistent default properties to CDK constructs without requiring changes to existing code. This approach reduces repetitive configuration, improves consistency, and simplifies maintenance of CDK applications.
Property Injection is the right choice for organizations that need to standardize construct configurations across existing codebases while preserving developer workflows. When combined with proper testing and documentation, Property Injection becomes a reliable foundation for infrastructure governance across your organization.

About the authors:

Put Cheung

Put Cheung is a Senior Software Development Engineer at AWS Security. He is a part of a team that is making it easier for builders to configure AWS Resources securely. AWS CDK Property Injection is an important step toward this goal.

Rico Huijbers

Rico Huijbers is a Software Engineer at Amazon Web Services. He is extremely lazy and is therefore on a quest to eradicate the need for repetitive manual work from software engineering. Rico loves working on AWS CDK—it’s the tool he wishes he had 5 years earlier.

Marco Frattallone

Marco Frattallone is a Senior Technical Account Manager at AWS focused on supporting Partners. He works closely with Partners to help them build, deploy, and optimize their solutions on AWS, providing guidance and leveraging best practices. Marco focuses on helping Partners adopt emerging AWS services and translate technical capabilities into business outcomes. Outside work, he enjoys outdoor cycling, sailing, and exploring new cultures.

AWS CloudFormation 2025 Year In Review

Post Syndicated from Idriss Laouali Abdou original https://aws.amazon.com/blogs/devops/aws-cloudformation-2025-year-in-review/

AWS CloudFormation enables you to model and provision your cloud application infrastructure as code-base templates. Whether you prefer writing templates directly in JSON or YAML, or using programming languages like Python, Java, and TypeScript with the AWS Cloud Development Kit (CDK), CloudFormation and CDK provide the flexibility you need. For organizations adopting multi-account strategies, CloudFormation StackSets offers a powerful capability to deploy resources across multiple regions and accounts in parallel.

In 2025, we delivered a comprehensive set of major enhancements focused on three core areas: reducing dev-test cycle through early validation, improving deployment safety with improved configuration drift management, and integrating IaC context to AI-powered development tools.

These launches address common pain points in infrastructure development workflows, from catching deployment errors before resource provisioning to managing configuration drift systematically. The features span the entire development lifecycle, from template authoring in your IDE to multi-account deployments at scale.

This blog provides an overview of the key capabilities we launched in 2025 and how they improve your infrastructure development workflow.

Accelerating Development Cycles

Early Validation & Enhanced Troubleshooting: Pre-Deployment Error Detection

CloudFormation now validates your templates during change set (preview of infrastructure changes before deployment) creation, catching common deployment errors before resource provisioning begins. The validation checks for invalid property syntax, resource name conflicts with existing resources in your account, and S3 bucket emptiness constraints on delete operations.

Figure 1: Pre-deployment validations view

Figure 1: Pre-deployment validations view

When validation fails, the change set status shows ‘FAILED’ with detailed information about each issue, including the property path where problems occur. This early feedback helps you fix issues faster rather than waiting for deployment failures.

Figure 2: CloudFormation Validation of Invalid ENUM value for nested property
Figure 2: Validation of Invalid ENUM value for nested property

Improved Deployment troubleshooting

For runtime errors that occur during deployment, every stack operation now receives a unique operation ID. You can filter stack events by operation ID to quickly identify root causes, reducing troubleshooting time from minutes to seconds. The new describe-events API provides grouped access to events. You can query events for a specific operation, filter to FAILED status events, and extract the root cause without parsing through the entire stack event history.

Figure 3: New CloudFormation stack operation pageFigure 3: New CloudFormation stack operation page

Figure 4: Filter operation failure root causes
Figure 4: Filter operation failure root causes

Learn more:

CloudFormation IDE Experience: Language Server Protocol Integration

We launched the AWS CloudFormation Language Server, bringing end-to-end infrastructure development directly into your IDE. Available through the AWS Toolkit for Visual Studio Code, Kiro, and other compatible IDEs, this capability transforms how you author CloudFormation templates.

Figure 1: Filter operation failure root causes

Figure 1: Initializing a CloudFormation project with environment configuration

The Language Server provides context-aware auto-completion that understands CloudFormation semantics. When you define resources, it suggests only required properties automatically, while optional properties appear on hover. Built-in validation catches issues before deployment integrating early validation capabilities, flagging invalid resource properties, missing IAM permissions, and security policy violations using CloudFormation Guard.

Figure 2: Hover information displaying optional properties and their documentation

The drift-aware deployment view highlights differences between your template and deployed infrastructure, helping you spot configuration changes made outside CloudFormation. The Language Server also provides semantic navigation features, go-to-definition for logical IDs, find-all-references for resource dependencies, and hover documentation that pulls from the CloudFormation resource specification. These features work across intrinsic functions like !Ref, !GetAtt, and !Sub, understanding the CloudFormation template structure. By integrating validation and real-time feedback directly into your authoring experience, the Language Server keeps you in flow state, reducing context switching between your IDE, AWS Console, and documentation.

Figure 3: Type-aware completions for intrinsic functions like !GetAtt & !Ref

Learn more:

Stack Refactoring: Adapt your infrastructure to your organization evolution

Stack Refactoring enables you to reorganize your CloudFormation and CDK infrastructure without disrupting deployed resources. You can move resources between stacks, rename logical IDs, and decompose monolithic stacks into focused components while maintaining resource stability and operational state.

Whether you’re modernizing legacy stacks, aligning infrastructure with evolving architectural patterns, or improving long-term maintainability, Stack Refactoring adapts your CloudFormation and CDK organization to changing requirements. The console and CDK experience, launched this year, extends the earlier CLI capability, making refactoring accessible through your preferred interface.

Provide a description to help you identify your stack refactor.

Learn more:
Blog Post – Refactor CloudFormation from Console
Blog Post – Refactor CDK

Safer Deployments

Drift-Aware Change Sets

Configuration drift occurs when infrastructure managed by CloudFormation is modified through the AWS Console, SDK, or CLI. Drift-aware change sets address this challenge by providing a three-way comparison between your new template, last-deployed template, and actual infrastructure state.

Examine the drift-aware change set to see the dangerous memory reduction that would occur

Examine the drift-aware change set to see the dangerous memory reduction that would occur

Figure 4: Examine the drift-aware change set to see the dangerous memory reduction that would occur

This capability helps you prevent unexpected overwrites of drift. If your change set preview shows unintended changes, you can update your template values and recreate the change set before deployment. During execution, CloudFormation matches resource properties with template values and recreates resources deleted outside of CloudFormation.

Drift-aware change sets enable you to systematically revert drift and keep infrastructure in sync with templates, strengthening reproducibility for testing and disaster recovery while maintaining your security posture.

Learn more:

Enforcing Proactive Controls

CloudFormation Hooks: Control Catalog with Hooks

AWS CloudFormation Hooks now supports managed proactive controls, enabling customers to validate resource configurations against AWS best practices without writing custom Hooks logic. Customers can select controls from the AWS Control Tower Controls Catalog and apply them during CloudFormation operations. When using CloudFormation, customers can configure these controls to run in warn mode, allowing teams to test controls without blocking deployments and giving them the flexibility to evaluate control behavior before enforcing policies in production. This significantly reduces setup time, eliminates manual errors, and ensures comprehensive governance coverage across your infrastructure.

AWS also introduced a new Hooks Invocation Summary page in the CloudFormation console. This centralized view provides a complete historical record of Hooks activity, showing which controls were invoked, their execution details, and outcomes such as pass, warn, or fail. This simplifies compliance reporting issues faster.

With this launch, customers can now leverage AWS-managed controls as part of their provisioning workflows, eliminating the overhead of writing and maintaining custom logic. These controls are curated by AWS and aligned with industry best practices, helping teams enforce consistent policies across all environments. The new summary page delivers essential visibility into Hook invocation history, enabling faster issue resolution and streamlined compliance reporting.

Learn more:

Scaling Multi-Account Infrastructure

StackSets Deployment Ordering

Figure : Example of a multi-region AWS CloudFormation StackSet architecture with an administrative account and target accounts

CloudFormation StackSets now supports deployment ordering for auto-deployment mode, enabling you to define the sequence in which stack instances automatically deploy across accounts and regions. This capability coordinates complex multi-stack deployments where foundational infrastructure must be provisioned before dependent application components.

Figure : CloudFormation StackSets Console – Auto-deployment options view

When creating or updating a StackSet, you can specify up to 10 dependencies per stack instance using the DependsOn parameter in the AutoDeployment configuration. StackSets automatically orchestrates deployments based on your defined relationships. For example, you can ensure networking and security stack instances complete deployment before application stack instances begin, preventing deployment failures due to missing dependencies.

StackSets includes built-in cycle detection to prevent circular dependencies and provides error messages to help resolve configuration issues. This feature is available at no additional cost in all AWS Regions where CloudFormation StackSets is available.

Learn more:

AI-Powered Infrastructure Development

AWS IaC Server

We introduced the AWS Infrastructure-as-Code (IaC) MCP Server, bridging AI assistants with your AWS infrastructure development workflow. Built on the Model Context Protocol (MCP), this server enables AI assistants like Kiro CLI, Claude, or Cursor to help you search CloudFormation and CDK documentation, validate templates, troubleshoot deployments, and follow best practices, all while maintaining the security of local execution.

Figure 1: Kiro-CLI with AWS IaC MCP server

Figure 1: Kiro-CLI with AWS IaC MCP server

The IaC MCP Server provides nine specialized tools organized into two categories. Remote documentation search tools connect to AWS knowledge bases to retrieve up-to-date information about CloudFormation resources, CDK APIs, and implementation guidance. Local validation and troubleshooting tools run entirely on your machine, performing syntax validation with cfn-lint, security checks with CloudFormation Guard, and deployment failure analysis with integrated CloudTrail events.

Figure 4: Validate my CloudFormation template with AWS IaC MCP Server

Figure 4: Validate my CloudFormation template with AWS IaC MCP Server

Key Use Cases

  1. Intelligent Documentation Assistant

Instead of manually searching through documentation, ask your AI assistant natural language questions:

“How do I create an S3 bucket with encryption enabled in CDK?”

The server searches CDK best practice and samples, returning relevant code examples and explanations.

     2. Proactive Template Validation

Before deploying infrastructure changes:

User: “Validate my CloudFormation template and check for security issues”

AI Agent: [Uses validate_cloudformation_template and check_cloudformation_template_compliance]

“Found 2 issues: Missing encryption on EBS volumes,

and S3 bucket lacks public access block configuration”

 3. Rapid Deployment Troubleshooting

When a stack deployment fails:

User: “My stack ‘stack_03’ in us-east-1 failed to deploy. What happened?”

AI Agent: [Uses troubleshoot_stack_deployment with CloudTrail integration]

“The deployment failed due to insufficient IAM permissions.

CloudTrail shows AccessDenied for ec2:CreateVpc.

You need to add VPC permissions to your deployment role.”

     4. Learning and Exploration

New to AWS CDK? The server helps you discover constructs and patterns:

User: “Show me how to build a serverless API”

AI Agent: [Searches CDK constructs and samples]

“Here are three approaches using API Gateway + Lambda…”

Learn more: Detailed Blog Post

Learn more

Here are some resources to help you get started learning and using CloudFormation to manage your cloud infrastructure:

Conclusion

As we begin 2026, our focus remains on making infrastructure deployment faster, safer, and more manageable. The launches in 2025 reflect our commitment to solving real customer challenges and improving the CloudFormation developer experience. From intelligent IDE integrations to AI-powered assistance, these capabilities help you build infrastructure with greater confidence and efficiency.

We encourage you to try these features and share your feedback. For detailed information about any of these launches, visit our documentation or check out the AWS DevOps Blog.

Blog Author Bio:

Idriss Laouali Abdou

Idriss is a Sr. Product Manager Technical on the AWS Infrastructure-as-Code team based in Seattle. He focuses on improving developer productivity through AWS CloudFormation and StackSets Infrastructure provisioning experiences. Outside of work, you can find him creating educational content for thousands of students, cooking, or dancing.

Introducing the AWS Infrastructure as Code MCP Server: AI-Powered CDK and CloudFormation Assistance

Post Syndicated from Idriss Laouali Abdou original https://aws.amazon.com/blogs/devops/introducing-the-aws-infrastructure-as-code-mcp-server-ai-powered-cdk-and-cloudformation-assistance/

Streamline your AWS infrastructure development with AI-powered documentation search, validation, and troubleshooting

Introduction

Today, we’re excited to introduce the AWS Infrastructure-as-Code (IaC) MCP Server, a new tool that bridges the gap between AI assistants and your AWS infrastructure development workflow. Built on the Model Context Protocol (MCP), this server enables AI assistants like Kiro CLI, Claude or Cursor to help you search AWS CloudFormation and Cloud Development Kit (CDK) documentation, validate templates, troubleshoot deployments, and follow best practices – all while maintaining the security of local execution.

Whether you’re writing AWS CloudFormation templates or AWS Cloud Development Kit (CDK) code, the IaC MCP Server acts as an intelligent companion that understands your infrastructure needs and provides contextual assistance throughout your development lifecycle.

The Model Context Protocol (MCP) is an open standard that enables AI assistants to securely connect to external data sources and tools. Think of it as a universal adapter that lets AI models interact with your development tools while keeping sensitive operations local and under your control.

The IaC MCP Server provides nine specialized tools organized into two categories:

Remote Documentation Search Tools

These tools connect to the AWS Knowledge MCP backend to retrieve relevant, up-to-date information:

  1.  search_cdk_documentation
    Search the AWS CDK knowledge base for APIs, concepts, and implementation guidance.
  2. search_cdk_samples_and_constructs
    Discover pre-built AWS CDK constructs and patterns from the AWS Construct Library.
  3. search_cloudformation_documentation
    Query CloudFormation documentation for resource types, properties, and intrinsic functions.
  4. read_cdk_documentation_page
    Retrieve and read full documentation pages returned from searches or provided URLs.

Local Validation and Troubleshooting Tools

These tools run entirely on your machine

  1. cdk_best_practices
    Access a curated collection of AWS CDK best practices and design principles.
  2. validate_cloudformation_template
    Perform syntax and schema validation using cfn-lint to catch errors before deployment.
  3. check_cloudformation_template_compliance
    Run security and compliance checks against your templates using AWS Guard rules and cfn-guard.
  4. troubleshoot_cloudformation_deployment
    Analyze CloudFormation stack deployment failures with integrated CloudTrail event analysis. This tool will use your AWS credentials to analyze your stack status.
  5. get_cloudformation_pre_deploy_validation_instructions
    Returns instructions for CloudFormation’s pre-deployment validation feature, which validates templates during change set creation.

Key Use Cases

  1. Intelligent Documentation Assistant

Instead of manually searching through documentation, ask your AI assistant natural language questions:

“How do I create an S3 bucket with encryption enabled in CDK?”

The server searches CDK best practic and samples, returning relevant code examples and explanations.

     2. Proactive Template Validation

Before deploying infrastructure changes:

User: “Validate my CloudFormation template and check for security issues”

AI Agent: [Uses validate_cloudformation_template and check_cloudformation_template_compliance]

“Found 2 issues: Missing encryption on EBS volumes,

and S3 bucket lacks public access block configuration”

 3. Rapid Deployment Troubleshooting

When a stack deployment fails:

User: “My stack ‘stack_03’ in us-east-1 failed to deploy. What happened?”

AI Agent: [Uses troubleshoot_stack_deployment with CloudTrail integration]

“The deployment failed due to insufficient IAM permissions.

CloudTrail shows AccessDenied for ec2:CreateVpc.

You need to add VPC permissions to your deployment role.”

     4. Learning and Exploration

New to AWS CDK? The server helps you discover constructs and patterns:

User: “Show me how to build a serverless API”

AI Agent: [Searches CDK constructs and samples]

“Here are three approaches using API Gateway + Lambda…”

Architecture and Security

Security Design

Local Execution: The MCP server runs entirely on your local machine using uv (the fast Python package manager). No code or templates are sent to external services except for documentation searches.

AWS Credentials: The server uses your existing AWS credentials (from ~/.aws/credentials, environment variables, or IAM roles) to access CloudFormation and CloudTrail APIs. This follows the same security model as the AWS CLI.

stdio Communication: The server communicates with AI assistants over standard input/output (stdio), with no network ports opened.

Minimal Permissions: For full functionality, the server requires read-only access to CloudFormation stacks and CloudTrail events—no write permissions needed for validation and troubleshooting workflows.

Getting Started

Prerequisites

  • Python 3.10 or later
    uv package manager
    AWS credentials configured locally
    MCP-compatible AI client (e.g., Kiro CLI, Claude Desktop)

Configuration

Configure the MCP server in your MCP client configuration. For this blog we will focus on Kiro CLI. Edit .kiro/settings/mcp.json):

{
  "mcpServers": {
    "awslabs.aws-iac-mcp-server": {
      "command": "uvx",
      "args": ["awslabs.aws-iac-mcp-server@latest"],
      "env": {
        "AWS_PROFILE": "your-named-profile",
        "FASTMCP_LOG_LEVEL": "ERROR"
      },
      "disabled": false,
      "autoApprove": []
    }
  }
}

Security Considerations

Privacy Notice: This MCP server executes AWS API calls using your credentials and shares the response data with your third-party AI model provider (e.g., Amazon Q, Claude Desktop, Cursor, VS Code). Users are responsible for understanding your AI provider’s data handling practices and ensuring compliance with your organization’s security and privacy requirements when using this tool with AWS resources.

IAM Permissions

The MCP server requires the following AWS permissions:

For Template Validation and Compliance:

  • No AWS permissions required (local validation only)

For Deployment Troubleshooting:

  • cloudformation:DescribeStacks
  • cloudformation:DescribeStackEvents
  • cloudformation:DescribeStackResources
  • cloudtrail:LookupEvents (for CloudTrail deep links)

Example IAM policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "cloudformation:DescribeStacks",
        "cloudformation:DescribeStackEvents",
        "cloudformation:DescribeStackResources",
        "cloudtrail:LookupEvents"
      ],
      "Resource": "*"
    }
  ]
}

Example Use Case With Kiro CLI

IMPORTANT: Ensure you have satisfied all prerequisites before attempting these commands.

1. With the mcp.json file correctly set, try to run a sample prompt. In your terminal, run kiro-cli chat to start using Kiro-cli in the CLI.

Figure 1: Kiro-CLI with AWS IaC MCP server

Figure 1: Kiro-CLI with AWS IaC MCP server

Scenarios:

  • “What are the CDK best practices for Lambda functions?”

Figure 2 Search the CDK best practices for Lambda functions

Figure 2: Search the CDK best practices for Lambda functions

  • “Search for CDK samples that use DynamoDB with Lambda”

Figure 3: Search for CDK samples that use DynamoDB with Lambda

Figure 3: Search for CDK samples that use DynamoDB with Lambda

  • “Validate my CloudFormation template at ./template.yaml”

Figure 4: Validate my CloudFormation template with AWS IaC MCP Server

Figure 4: Validate my CloudFormation template with AWS IaC MCP Server

  • “Check if my template complies with security best practices”

Figure 5: Check if my template complies with security best practices with AWS IaC MCP Server

Figure 5: Check if my template complies with security best practices with AWS IaC MCP Server

Best Practices

  • Start with Documentation Search: Before writing code, search for existing constructs and patterns
  • Validate Early and Often: Run validation tools before attempting deployment
  • Check Compliance: Use check_template_compliance to catch security issues during development
  • Leverage CloudTrail: When troubleshooting, the CloudTrail integration provides detailed failure context
  • Follow CDK Best Practices: Use the cdk_best_practices tool to align with AWS recommendations

What’s Next?

The IAC MCP Server represents a new paradigm in the AI agentic workflow infrastructure development – one where AI assistants understand your tools, help you navigate complex documentation, and provide intelligent assistance throughout the development lifecycle.

Get Involved

The AWS IaC MCP Server is available now:

  • Documentation and GitHub Repository: aws-iac-mcp-server
  • Feedback: We welcome issues and pull requests! Or respond to our IaC survey here.

Ready to supercharge your infrastructure as code development? Install the IaC MCP Server today and experience AI-powered assistance for your AWS CDK and CloudFormation workflows.

Have questions or feedback? Reach out to the blog authors on the AWS Developer Forums.

About Authors

Idriss Laouali Abdou

Idriss is a Sr. Product Manager Technical on the AWS Infrastructure-as-Code team based in Seattle. He focuses on improving developer productivity through AWS CloudFormation and StackSets Infrastructure provisioning experiences. Outside of work, you can find him creating educational content for thousands of students, cooking, or dancing.

Brian Terry

Brian Terry, Senior WW Data & AI PSA, is an innovation leader with more than 20 years of experience in technology and engineering. Brian is pursuing a PhD in computer science at the University of North Dakota and has spearheaded generative AI projects, optimized infrastructure scalability, and driven partner integration strategies. He is passionate about leveraging technology to deliver scalable, resilient solutions that foster business growth and innovation.

Safely Handle Configuration Drift with CloudFormation Drift-Aware Change Sets

Post Syndicated from JJ Lei original https://aws.amazon.com/blogs/devops/safely-handle-configuration-drift-with-cloudformation-drift-aware-change-sets/

Introduction

Is configuration drift preventing you from accessing the speed, safety, and governance benefits of AWS CloudFormation for infrastructure management? Configuration drift occurs when cloud resources are modified outside of CloudFormation, leading to a mismatch in the actual state and template definition of resources. Drift tends to accumulate from infrastructure changes that engineers make via the AWS Management Console to resolve production incidents or troubleshoot malfunctioning applications. Drift can cause unexpected changes during subsequent IaC deployments or leave resources in a non-compliant state. Unresolved drift can lead to cost increases when resources are over-provisioned outside of template definitions, or compliance violations that may result in audit penalties. Additionally, drift makes it hard to reproduce applications for testing or disaster recovery.

CloudFormation now offers drift-aware change sets that allow you to safely handle configuration drift and keep your infrastructure in sync with your templates. In this post, we will explore the process of leveraging drift-aware change sets to resolve common scenarios in which drift impacts the availability or security of your application.

Solution Overview

Drift-aware change sets are a type of CloudFormation change sets that can bring drifted resources in line with template definitions and preview the required changes to actual infrastructure states before deployment. Drift-aware change sets surface a three-way comparison of your new template, actual resource states, and previous template before deployment, allowing you to prevent unexpected overwrites of drift. Additionally, drift-aware change sets offer you a systematic mechanism to restore drifted resources to approved template definitions, strengthening the reproducibility and compliance posture of applications. You can create drift-aware change sets either from the CloudFormation Management Console or from the AWS CLI or SDK by passing the --deployment-mode REVERT_DRIFT parameter to the CreateChangeSet API.

Prerequisites

AWS CLI latest version with CloudFormation permissions configured.

AWS Identity and Access Management (IAM) permissions required: Permissions to create and manage CloudFormation stacks, AWS Lambda functions, Security Groups, Amazon Simple Storage Service (Amazon S3) buckets, and IAM roles. PowerUserAccess or Administrator access recommended for testing.

• Test environment (non-production AWS account recommended)

• Basic CloudFormation knowledge (stacks, templates, change sets)

Important Note: These sample templates are provided for educational purposes only and should not be used in production environments without proper security review and testing. You are responsible for testing, securing, and optimizing these templates based on your specific quality control practices and standards. Deploying these templates may incur AWS charges for creating or using AWS resources. Work with your security and legal teams to meet your organizational security, regulatory, and compliance requirements before any production deployment.

Scenario 1: Prevent Dangerous Overwrites

This scenario demonstrates how drift-aware change sets prevent dangerous overwrites when Lambda function memory is increased outside of CloudFormation during an outage, and a subsequent template update could accidentally reduce memory, causing performance issues.

Story: Your team deploys a Lambda function with 128 MB memory via CloudFormation. During a production outage, an engineer increases the memory to 512 MB through the Lambda Console to resolve performance issues. Later, another developer updates the template to 256 MB for a code change, unaware of the console modification. Without drift-aware change sets, CloudFormation would unexpectedly reduce memory from 512 MB to 256 MB—potentially causing the outage to recur.

User journey: Create stack with 128MB => Increase memory to 512MB via console during outage => Create drift-aware change set with 256MB template => Review three-way comparison showing dangerous memory reduction => Cancel change set to prevent outage => Update template to match production state (512MB) => Create and execute drift-aware change set with updated template (512MB) to resolve drift

Scenario Flow

1. Create Stack

Deploy CloudFormation stack with Lambda function (128 MB memory).

Figure 1

CloudFormation stack “lambda-memory-drift-test” successfully deployed with CREATE_COMPLETE status

2. Emergency Memory Increase (Console)

Manually increase Lambda memory to 512 MB through AWS Console (simulating emergency performance fix during outage).

Figure 2

Initial Lambda function showing 128 MB memory as configured in template

Figure 3

Lambda memory increased to 512 MB through console during outage, creating drift from template

3. Create Drift-Aware Change Set

Create change set with 256 MB template using drift-aware mode to reveal the dangerous memory reduction.

Figure 4

CloudFormation console showing the new “Drift aware change set” option selected. This compares the new template with the live state of your stack and shows changes to drifted resources before deployment, unlike standard change sets that only compare templates.

aws cloudformation create-change-set \
--stack-name lambda-memory-drift-test \
--change-set-name detect-memory-overwrite \
--template-body file://lambda-memory-drift-scenario-256mb.yaml \
--deployment-mode REVERT_DRIFT \
--capabilities CAPABILITY_IAM \
--region us-east-1

4. Review Change Set – The Critical Three-Way Comparison

Examine the drift-aware change set to see the dangerous memory reduction that would occur.

Figure 5

Critical insight revealed: The change set shows Live resource state (512 MB) vs Proposed resource state (256 MB), revealing a dangerous memory reduction that would impact performance.

Figure 6: view drift

Drift analysis: Clicking “View drift” reveals the complete picture – Previous template (128 MB) vs Live resource state (512 MB). This shows the live state has 4x more memory than the original template, indicating emergency changes were made during the outage that must be preserved.

Key Insight: The drift-aware change set reveals that:

  • Previous template: 128 MB (original deployment)
  • Live resource state: 512 MB (emergency change during outage)
  • Proposed template: 256 MB (new deployment)

This would cause a dangerous reduction from 512 MB to 256 MB, potentially recreating the original performance issue. Without drift-aware change sets, this critical information would be hidden.

5. Recreate Drift-aware Change Set with Updated Template (512MB) to Resolve Drift

Update the template to match the live production state (512 MB) and create a new drift-aware change set to safely resolve the drift.

Figure 7

Resolution confirmed: The drift-aware change set shows both Live resource state and Proposed resource state at 512 MB, with change set action ” Sync with live”. This verifies that the updated template now matches production, preventing the dangerous memory reduction and safely resolving the drift without impacting performance.

CloudFormation Templates

Initial Template (128 MB):

Resources:
  DriftTestFunction:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: python3.9
      Handler: index.lambda_handler
      MemorySize: 128
      ReservedConcurrentExecutions: 5
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: |
          def lambda_handler(event, context):
              return {'statusCode': 200, 'body': 'Hello!'}
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

Updated Template (256 MB – lambda-memory-drift-scenario-256mb.yaml):

Resources:
  DriftTestFunction:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: python3.9
      Handler: index.lambda_handler
      MemorySize: 256
      ReservedConcurrentExecutions: 5
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: |
          def lambda_handler(event, context):
              return {'statusCode': 200, 'body': 'Hello!'}
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

CLI Commands

  1. Create stack:
aws cloudformation create-stack --stack-name lambda-memory-drift-test --template-body file://lambda-memory-drift-scenario.yaml --capabilities CAPABILITY_IAM --region us-east-1
  1. Get function name:
aws cloudformation describe-stack-resources --stack-name lambda-memory-drift-test --logical-resource-id DriftTestFunction --query 'StackResources[0].PhysicalResourceId' --output text --region us-east-1
  1. Create drift-aware change set:
aws cloudformation create-change-set --stack-name lambda-memory-drift-test --change-set-name detect-memory-overwrite --template-body file://lambda-memory-drift-scenario-256mb.yaml --deployment-mode REVERT_DRIFT --capabilities CAPABILITY_IAM --region us-east-1
  1. Describe change set:
aws cloudformation describe-change-set --change-set-name detect-memory-overwrite --stack-name lambda-memory-drift-test --region us-east-1

Scenario 2: Remediate Unauthorized Changes

This scenario demonstrates how drift-aware change sets systematically remediate unauthorized changes when a developer adds temporary debugging rules to a security group but forgets to remove them, creating a compliance violation.

Story: Your team deploys a security group with only HTTP access via CloudFormation for compliance. During debugging, a developer adds SSH access (port 22) through the AWS Console for their IP address to troubleshoot an application issue. They forget to remove this rule after debugging. Later, security compliance requires reverting to the original template state. A standard change set shows no changes since the template is unchanged, but a drift-aware change set can detect and systematically remove the unauthorized SSH rule.

User journey: Create stack with HTTP-only access => Add SSH rule via console for debugging => Forget to remove SSH rule => Create drift-aware change set with REVERT_DRIFT mode => Review change set showing SSH rule removal => Execute change set to restore compliance

Scenario Flow

1. Create Stack

Deploy CloudFormation stack with security group allowing only HTTP traffic.

Figure 8

CloudFormation stack “sg-revert-drift-test” successfully deployed with DriftTestSecurityGroup resource

2. Make Unauthorized Changes (Console)

Manually add SSH ingress rule through AWS Console (simulating developer debugging access that wasn’t removed).

Figure 9: http only

Initial security group showing only HTTP (port 80) access as configured in template – compliant state

Figure 10: ssh-added

Security group now shows 2 permission entries: SSH (port 22) for specific IP and HTTP (port 80) for all traffic. The SSH rule creates drift and a compliance violation that needs systematic removal.

3. Create Drift-Aware Change Set

Create change set using REVERT_DRIFT mode to systematically remove the unauthorized SSH rule.

Figure 11

Creating drift-aware change set for security group compliance restoration. Note the “Drift aware change set” option is selected to compare with live state and detect unauthorized changes.

aws cloudformation create-change-set \
--stack-name sg-revert-drift-test \
--change-set-name revert-ssh-drift \
--use-previous-template \
--deployment-mode REVERT_DRIFT \
--region us-east-1

4. Review Change Set – Systematic Compliance Restoration

Examine the drift-aware change set to see systematic removal of unauthorized SSH rule.

Figure 12

Compliance violation detected: The drift -aware change set shows that the SSH rule in the live resource state (rule 232 for IP 15.248.7.53/32 on port 22) is not present in the proposed resource state derived from the template. This unauthorized SSH rule violates security policy and will be systematically removed

Key Insight: The drift-aware change set enables systematic compliance restoration by:

  • Previous template: Only HTTP (port 80) access – compliant state
  • Live resource state: HTTP + SSH (port 22) for 15.248.7.53/32 – compliance violation
  • Action: Remove unauthorized SSH rule to restore compliance

This provides a systematic, auditable way to remove unauthorized changes rather than manual cleanup.

Figure 13

Stack events showing successful execution of the drift-aware change set – SSH rule removed

CloudFormation Templates

security-group-drift-scenario.yaml:

Resources:
  DriftTestSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "Security group for drift testing"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
          Description: "Allow HTTP traffic for demo purposes"
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0
          Description: "Allow all outbound traffic"

CLI Commands

  1. Create stack:
aws cloudformation create-stack --stack-name sg-revert-drift-test --template-body file://security-group-drift-scenario.yaml --region us-east-1
  1. Get security group ID:
aws ec2 describe-security-groups --filters "Name=tag:aws:cloudformation:stack-name,Values=sg-revert-drift-test" --query 'SecurityGroups[0].GroupId' --output text --region us-east-1
  1. Create drift-aware change set:
aws cloudformation create-change-set --stack-name sg-revert-drift-test --change-set-name revert-ssh-drift --template-body file://security-group-drift-scenario.yaml --deployment-mode REVERT_DRIFT --region us-east-1
  1. Describe change set:
aws cloudformation describe-change-set --change-set-name revert-ssh-drift --stack-name sg-revert-drift-test --region us-east-1

Scenario 3: Recreate Deleted Resources

This scenario demonstrates drift detection when a dependent resource (logs bucket) is accidentally deleted outside of CloudFormation during troubleshooting. The main application bucket depends on this logs bucket for access logging. You need to recreate the deleted resource while maintaining the existing infrastructure dependencies.

Story: Your team deploys a main S3 bucket with a dependent logs bucket for access logging via CloudFormation. During troubleshooting, an operator accidentally deletes the logs bucket through the AWS Console. The main bucket still exists but its logging configuration now references a non-existent bucket. You need to recreate the deleted logs bucket while maintaining the dependency relationship.

User journey: Create stack with main and logs buckets => Accidentally delete logs bucket => Create drift-aware change set with REVERT_DRIFT mode => Review change set showing LogBucket will be recreated => Execute change set to restore deleted resource

Scenario Flow

1. Create Stack

Deploy CloudFormation stack with main S3 bucket and dependent logs bucket.

Figure 14

CloudFormation stack “s3-deletion-drift-test” successfully deployed with both LogBucket and MainBucket resources in CREATE_COMPLETE status

2. Accidental Deletion (Console)

Manually delete the logs bucket through AWS Console (simulating accidental deletion during troubleshooting).

Figure 15

LogBucket accidentally deleted outside of CloudFormation during troubleshooting, creating drift – the MainBucket still exists but its logging configuration now references a non-existent bucket

3. Create Drift-Aware Change Set

Create change set using REVERT_DRIFT mode to recreate the deleted LogBucket.

Figure 16

Creating drift-aware change set with “Drift aware change set” option selected to detect and recreate the deleted resource by comparing template with live state

aws cloudformation create-change-set \
--stack-name s3-deletion-drift-test \
--change-set-name recreate-deleted-bucket \
--use-previous-template \
--deployment-mode REVERT_DRIFT \
--region us-east-1

4. Review Change Set – Resource Recreation

Examine change set to see LogBucket recreation while preserving MainBucket dependencies.

Figure 17

Change set preview showing LogBucket will be recreated to restore the deleted resource and MainBucket updated to maintain infrastructure dependencies

Key Insight: The drift-aware change set detects that:

  • Template expectation: Both LogBucket and MainBucket should exist
  • Live resource state: Only MainBucket exists, LogBucket is missing
  • Action: Recreate LogBucket with original configuration to restore logging functionality

This enables systematic recovery of accidentally deleted resources while maintaining infrastructure dependencies.

CloudFormation Templates

s3-drift-scenario.yaml:

Resources:
  LogBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
  
  MainBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      LoggingConfiguration:
        DestinationBucketName: !Ref LogBucket

CLI Commands

  1. Create stack:
aws cloudformation create-stack --stack-name s3-deletion-drift-test --template-body file://s3-drift-scenario.yaml --region us-east-1
  1. Get LogBucket name:
aws cloudformation describe-stack-resources --stack-name s3-deletion-drift-test --logical-resource-id LogBucket --query 'StackResources[0].PhysicalResourceId' --output text --region us-east-1
  1. Create drift-aware change set:
aws cloudformation create-change-set --stack-name s3-deletion-drift-test --change-set-name recreate-deleted-bucket --template-body file://s3-drift-scenario.yaml --deployment-mode REVERT_DRIFT --region us-east-1
  1. Describe change set:
aws cloudformation describe-change-set --change-set-name recreate-deleted-bucket --stack-name s3-deletion-drift-test --region us-east-1

Best Practices

When working with drift-aware change sets, consider these best practices:

Always review three-way comparisons before executing change sets to understand the full impact

Use REVERT_DRIFT deployment mode when you want to bring resources back to template compliance

Document emergency changes made outside of CloudFormation to inform future template updates

Implement change management processes to minimize unauthorized drift

Regular drift detection helps identify configuration changes before they become problematic

Test drift-aware change sets in non-production environments first

Cleanup

Important: Execute these cleanup commands promptly after completing the scenarios to avoid incurring unnecessary AWS charges. Resources such as Lambda functions, S3 buckets (even if empty), and security groups may incur costs if left running. Ensure all stacks are successfully deleted by verifying the DELETE_COMPLETE status.

Commands to delete all test resources:

# Scenario 1: Lambda Memory Drift
aws cloudformation delete-stack --stack-name lambda-memory-drift-test --region us-east-1

# Scenario 2: Security Group Drift
aws cloudformation delete-stack --stack-name sg-revert-drift-test --region us-east-1

# Scenario 3: S3 Bucket Deletion Drift
aws cloudformation delete-stack --stack-name s3-deletion-drift-test --region us-east-1

# Verify all stacks are deleted
aws cloudformation list-stacks --stack-status-filter DELETE_COMPLETE --region us-east-1

Note: CloudFormation will automatically clean up all resources created by the stacks, including Lambda functions, security groups, and S3 buckets.

Conclusion

Drift-aware change sets enable you to mitigate the operational and security risks of configuration drift, allowing you to confidently automate and govern your infrastructure updates with CloudFormation. Through the scenarios described in this post, you have seen how you can leverage drift-aware change sets to prevent outages in production environments, maintain the integrity of your test environments, and manage the compliance posture of all environments. Remember to thoroughly review the infrastructure changes previewed by drift-aware change sets before executing deployments.

Available Now

Drift-aware change sets are available in AWS Regions where CloudFormation is available. Please refer to the AWS Region table to learn more.

Accelerate infrastructure development with CloudFormation pre-deployment validation and simplified troubleshooting

Post Syndicated from Idriss Laouali Abdou original https://aws.amazon.com/blogs/devops/accelerate-infrastructure-development-with-cloudformation-pre-deployment-validation-and-simplified-troubleshooting/

AWS CloudFormation makes it easy to model and provision your cloud application infrastructure as code. CloudFormation templates can be written directly in JSON or YAML, or they can be generated by tools like the AWS Cloud Development Kit (CDK). Resources are created and managed by CloudFormation as units called Stacks. Additionally, change set enable you to preview the stack changes before deployment.

CloudFormation now offers powerful new features that transform how you develop and troubleshoot infrastructure as code, pre-deployment validation that catches errors in seconds, enhanced operation tracking, and simplified failure debugging. These capabilities shift-left infrastructure code validation, helping you prevent infrastructure deployment failures that impacts development velocity.

In this blog post, we’ll explore how these new features accelerate development cycles by catching common errors during change set creation and providing precise troubleshooting through operation tracking and failure filtering. Whether you’re a platform engineer managing complex multi-service deployments or a developer iterating on infrastructure templates, we’ll show you how to:

  • Validate resource properties and detect naming conflicts before deployment
  • Prevent deployment failures by checking S3 bucket emptiness before deletion operations
  • Track operations with unique IDs for focused troubleshooting
  • Quickly identify root causes using the new describe-events API

This comprehensive guide will walk through real-world scenarios demonstrating how these capabilities can reduce infrastructure deployment failures from hours of debugging to seconds of validation, helping you deliver cloud infrastructure faster and more reliably.

Key Capabilities

  • Pre-deployment Validation: Catch template errors instantly instead of discovering them after resource provisioning attempts. These include pre-deployment validation for resource property syntax errors, resource naming conflicts for existing resources in your account, and S3 bucket emptiness constraint violations on delete operations.
  • Operation Tracking: Say goodbye to long debugging sessions. Each stack action now comes with a unique Operation ID, transforming the “needle in haystack” troubleshooting experience into precise, targeted problem-solving.
  • Streamlined Events API for simplified Debugging: Use the new describe-events API and FailedEvents=true filter to instantly pinpoint issues. One command tells you exactly what went wrong, eliminating the need to scroll through endless logs.
  • Immediate Feedback: Transform your CI/CD pipeline from a potential bottleneck into a rapid iteration engine. Get immediate feedback on common deployment issues, allowing your team to fix and deploy faster than ever before.

How It works

Pre-deployment Validation

The following scenarios show how you can leverage CloudFormation pre-deployment validation to detect property syntax errors, resource naming conflicts, and constraint violations during change set creation.

Understanding Validation Modes
CloudFormation pre-deployment validation operates in two modes that determine how validation failures are handled.

  • FAIL mode prevents change set execution when validation detects errors, ensuring problematic templates cannot proceed to deployment. This applies to property syntax errors and resource naming conflicts.
  • WARN mode allows change set creation to succeed despite validation failures, providing warnings that developers can review and address before execution. This applies to constraint violations like S3 bucket emptiness that may be resolvable through manual intervention.

Understanding these modes helps you anticipate whether validation issues will block your deployment workflow or simply require attention before execution.

Let’s walk you through practical scenarios:

Scenario 1: Validate Resource Property Syntax

CloudFormation evaluates each resource property definition or value before provisioning begins. The following example illustrates several common resource property errors:

  1. The “AWS::Lambda::Function” Role property requires an ARN pattern.
  2. The “AWS::Lambda::Function” Timeout property expects an integer instead of a string.
  3. The “AWS::Lambda::Function” TracingConfig.Mode nested property ENUM value is invalid.
  4. The “AWS::Lambda::Alias” Name property is required but not defined.
  5. The “AWS::Lambda::Alias” the extra property Description in a nested path RoutingConfig.AdditionalVersionWeights.0 is not supported.

Prior to this launch, these resource configuration errors would be detected at the resource provisioning time only. However, with the pre-deployment validations feature, these errors can be identified ahead of the deployment phase, streamlining the development-test lifecycle efficiency and minimizing rollbacks during deployments.

Template

AWSTemplateFormatVersion: "2010-09-09"

Description: This template demonstrates how pre-deployment validation and enhanced troubleshooting work

Resources:
  MyLambdaFunction:
    Type: "AWS::Lambda::Function"
    Properties:
      FunctionName: "dev-test"
      Role: 'MyRole'          #1. Non-matching pattern
      Runtime: "python3.11"
      Handler: "index.lambda_handler"
      Code:
        ZipFile: |
          import json
          
          def lambda_handler(event, context):
              return {
                  'statusCode': 200,
                  'body': json.dumps('Hello from Lambda!')
              }
      Timeout: "30s"          #2. Type mismatch
      MemorySize: 128
      TracingConfig:
        Mode: "DISABLED"       #3. Invalid ENUM

  MyCandidateReleaseVersion:
    Type: "AWS::Lambda::Version"
    Properties:
      FunctionName: !Ref "MyLambdaFunction"
      Description: "v2"

  MyLambdaAlias:
    Type: AWS::Lambda::Alias
    Properties:
                              #4. Missing required property "Name"
      FunctionName: !Ref "MyLambdaFunction"
      FunctionVersion: "$LATEST"
      RoutingConfig:
        AdditionalVersionWeights:
          - FunctionVersion: !GetAtt "MyCandidateReleaseVersion.Version"
            FunctionWeight: 0.1
            Description: "10% traffic to the new version" #5. Unsupported property


Step 1: Create Change Set

Console
Create a new stack using the change set creation flow, provide the template and all required parameters.

CloudFormation Create change set

Figure 1: Create a change set view

CLI Command

aws cloudformation create-change-set \
    --stack-name "dev-lambda-stack" \
    --change-set-name "updateAlias" \
    --change-set-type "CREATE" \
    --template-body file://lambda-with-alias-template.yaml

Step 2: Check Change Set Status
To review the status of your change set

Console

Figure 2: Describe change set status

Figure 2: Describe change set status

CLI command

aws cloudformation describe-change-set \
  --change-set-name "arn:aws:cloudformation:us-west-2:123456789012:changeSet/updateAlias/94498df5-1afb-43b1-9869-9f82b2d877ac"
{
  "ChangeSetName": "updateAlias",
  "ChangeSetId": "arn:aws:cloudformation:us-west-2:123456789012:changeSet/updateAlias/94498df5-1afb-43b1-9869-9f82b2d877ac",
  "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/dev-lambda-stack/2d2c3240-bb59-11f0-b080-0613dc96740d",
  "StackName": "dev-lambda-stack",
  "CreationTime": "2025-11-06T21:40:13.333000+00:00",
 <strong> "ExecutionStatus": "UNAVAILABLE",
  "Status": "FAILED",
  "StatusReason": "The following hook(s)/validation failed: [AWS::EarlyValidation::PropertyValidation]. To troubleshoot Early Validation errors, use the DescribeEvents API for detailed failure information.",
  "NotificationARNs": [],</strong>
  "RollbackConfiguration": {},
  "Capabilities": [],
  "Changes": [
    {
      "Type": "Resource",
      "ResourceChange": {
        "Action": "Add",
        "LogicalResourceId": "MyCandidateReleaseVersion",
        "ResourceType": "AWS::Lambda::Version",
        "Scope": [],
        "Details": []
      }
    },
    {
      "Type": "Resource",
      "ResourceChange": {
        "Action": "Add",
        "LogicalResourceId": "MyLambdaAlias",
        "ResourceType": "AWS::Lambda::Alias",
        "Scope": [],
        "Details": []
      }
    },
    {
      "Type": "Resource",
      "ResourceChange": {
        "Action": "Add",
        "LogicalResourceId": "MyLambdaFunction",
        "ResourceType": "AWS::Lambda::Function",
        "Scope": [],
        "Details": []
      }
    }
  ],
  "IncludeNestedStacks": false
}

You can see the status of the change set is failed with a detailed status reason. You can now proceed to review the change set validation results.

Step 3: Review validation results

Console

With the console, you can review multiple validation errors in a single interface. When you click on a validation, CloudFormation pinpoints the location of the invalid property error in your template.

Figure 3: Pre-deployment validations view

Figure 3: Pre-deployment validations view

Use Case: Invalid ENUM value for nested property
Catching invalid configuration values before deployment. This demonstrates validation of nested properties like TracingConfig.Mode. The tool helpfully shows the supported values “Active” & “Pass through” as well as the provided invalid value “DISABLED”.

Figure 4: CloudFormation Validation of Invalid ENUM value for nested property
Figure 4: Validation of Invalid ENUM value for nested property

Use Case: Lambda Function Timeout property type mismatch
Preventing type-related deployment failures. Shows how validation catches string values (“30s”) where integers are required, saving developers from runtime errors.

Figure 5: Validation of Lambda Function Timeout property type mismatch
Figure 5: Validation of Lambda Function Timeout property type mismatch

Use Case: Lambda Function Role property pattern mismatch
Validating ARN format requirements. Demonstrates pattern validation ensuring Role properties match required ARN format.

Figure 6: Lambda Function Role property pattern mismatch

Figure 6: Lambda Function Role property pattern mismatch

Use Case: Undefined required Lambda Alias Name property
Catching missing required properties. Shows validation detecting absent mandatory fields, preventing incomplete resource definitions from reaching deployment.

Figure 7: Validation of undefined required Lambda Alias Name property
Figure 7: Validation of undefined required Lambda Alias Name property

Notice how the validation Path field (e.g., “/Resources/MyLambdaFunction/Properties/TracingConfig/Mode”) pinpoints the exact template location of each error. This eliminates manual searching through hundreds of lines of infrastructure code – a common time sink that can take minutes in complex templates.

Use case: Unsupported property
Shows how CloudFormation validation catches unsupported properties. In this example, the AWS::Lambda::Alias resource had an unsupported extra property Description in a nested path RoutingConfig.AdditionalVersionWeights.0.

Figure 8: CloudFormation validation of unsupported resource property

Figure 8: CloudFormation validation of unsupported resource property

CLI command
You can also use the new describe-events API to review the validation responses.

aws cloudformation describe-events \
  --change-set-id "arn:aws:cloudformation:us-west-2:123456789012:changeSet/updateAlias/94498df5-1afb-43b1-9869-9f82b2d877ac"
{
  "OperationEvents": [
    {
      "EventId": "d3221796-d6a4-40c3-a987-93b103e7fcc1",
      "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/dev-lambda-stack/2d2c3240-bb59-11f0-b080-0613dc96740d",
      "OperationId": "94498df5-1afb-43b1-9869-9f82b2d877ac",
      "OperationType": "CREATE_CHANGESET",
      "OperationStatus": "FAILED",
      "EventType": "STACK_EVENT",
      "Timestamp": "2025-11-06T21:40:18.428000+00:00",
      "StartTime": "2025-11-06T21:40:13.399000+00:00",
      "EndTime": "2025-11-06T21:40:18.428000+00:00"
    },
    {
      "EventId": "87b628b4-fbcb-42b0-bf07-779007bf0d85",
      "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/dev-lambda-stack/2d2c3240-bb59-11f0-b080-0613dc96740d",
      "OperationId": "94498df5-1afb-43b1-9869-9f82b2d877ac",
      "OperationType": "CREATE_CHANGESET",
      "EventType": "VALIDATION_ERROR",
      "LogicalResourceId": "MyLambdaFunction",
      "PhysicalResourceId": "",
      "ResourceType": "AWS::Lambda::Function",
      "Timestamp": "2025-11-06T21:40:18.163000+00:00",
      "ValidationFailureMode": "FAIL", "ValidationName": "PROPERTY_VALIDATION", "ValidationStatus": "FAILED", "ValidationStatusReason": "DISABLED is not a valid enum value. Supported values: [Active, PassThrough]", "ValidationPath": "/Resources/MyLambdaFunction/Properties/TracingConfig/Mode" },
    {
      "EventId": "2f89cf64-e810-4285-8936-b77f7b72228c",
      "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/dev-lambda-stack/2d2c3240-bb59-11f0-b080-0613dc96740d",
      "OperationId": "94498df5-1afb-43b1-9869-9f82b2d877ac",
      "OperationType": "CREATE_CHANGESET",
      "EventType": "VALIDATION_ERROR",
      "LogicalResourceId": "MyLambdaFunction",
      "PhysicalResourceId": "",
      "ResourceType": "AWS::Lambda::Function",
      "Timestamp": "2025-11-06T21:40:18.163000+00:00",
      "ValidationFailureMode": "FAIL", "ValidationName": "PROPERTY_VALIDATION", "ValidationStatus": "FAILED", "ValidationStatusReason": "Property [Timeout] expected type: Integer, found: String", "ValidationPath": "/Resources/MyLambdaFunction/Properties/Timeout"    },
    {
      "EventId": "b2448484-4e41-4c53-b19e-6355dafeac6b",
      "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/dev-lambda-stack/2d2c3240-bb59-11f0-b080-0613dc96740d",
      "OperationId": "94498df5-1afb-43b1-9869-9f82b2d877ac",
      "OperationType": "CREATE_CHANGESET",
      "EventType": "VALIDATION_ERROR",
      "LogicalResourceId": "MyLambdaAlias",
      "PhysicalResourceId": "",
      "ResourceType": "AWS::Lambda::Alias",
      "Timestamp": "2025-11-06T21:40:18.134000+00:00",
     "ValidationFailureMode": "FAIL", "ValidationName": "PROPERTY_VALIDATION", "ValidationStatus": "FAILED", "ValidationStatusReason": "Required property [Name] not found", "ValidationPath": "/Resources/MyLambdaAlias/Properties"   },
    {
      "EventId": "694e94f0-a2f1-49fd-8045-545a9cb41ca9",
      "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/dev-lambda-stack/2d2c3240-bb59-11f0-b080-0613dc96740d",
      "OperationId": "94498df5-1afb-43b1-9869-9f82b2d877ac",
      "OperationType": "CREATE_CHANGESET",
      "EventType": "VALIDATION_ERROR",
      "LogicalResourceId": "MyLambdaAlias",
      "PhysicalResourceId": "",
      "ResourceType": "AWS::Lambda::Alias",
      "Timestamp": "2025-11-06T21:40:18.132000+00:00",
      "ValidationFailureMode": "FAIL",
      "ValidationName": "PROPERTY_VALIDATION",
      "ValidationStatus": "FAILED",
      "ValidationStatusReason": "Unsupported property [Description]",
      "ValidationPath": "/Resources/MyLambdaAlias/Properties/RoutingConfig/AdditionalVersionWeights/0"
    },
    {
      "EventId": "935cbd72-a637-4ad5-908d-e2ce241022ad",
      "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/dev-lambda-stack/2d2c3240-bb59-11f0-b080-0613dc96740d",
      "OperationId": "94498df5-1afb-43b1-9869-9f82b2d877ac",
      "OperationType": "CREATE_CHANGESET",
      "EventType": "VALIDATION_ERROR",
      "LogicalResourceId": "MyLambdaFunction",
      "PhysicalResourceId": "",
      "ResourceType": "AWS::Lambda::Function",
      "Timestamp": "2025-11-06T21:40:18.126000+00:00",
     "ValidationFailureMode": "FAIL", "ValidationName": "PROPERTY_VALIDATION", "ValidationStatus": "FAILED", "ValidationStatusReason": "Property value [MyRole] does not match pattern: ^arn:(aws[a-zA-Z-]*)?:iam::\\d{12}:role/?[a-zA-Z_0-9+=,.@\\-_/]+$", "ValidationPath": "/Resources/MyLambdaFunction/Properties/Role"    },
    {
      "EventId": "c4d25b22-9e8f-42f9-bd2e-3391b9bdacbd",
      "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/dev-lambda-stack/2d2c3240-bb59-11f0-b080-0613dc96740d",
      "OperationId": "94498df5-1afb-43b1-9869-9f82b2d877ac",
      "OperationType": "CREATE_CHANGESET",
      "OperationStatus": "IN_PROGRESS",
      "EventType": "STACK_EVENT",
      "Timestamp": "2025-11-06T21:40:13.399000+00:00",
      "StartTime": "2025-11-06T21:40:13.399000+00:00"
    }
  ]
}

Scenario 2: Resource Name Conflict Validation
Resource name conflict validation makes sure that new resources added to a template are not already present in your AWS account or globally (e.g: Amazon S3, Amazon Route 53 DNS), preventing deployment errors caused due to resource name conflicts

After reviewing the property validation exceptions, let’s assume that you resolved all the issues and successfully deployed the stack. Next, the you have decided to include a S3 bucket resource in the template. You name the bucket “dev-thumbnails” but didn’t verify if the bucket with this name already exists. If a bucket with this name already exists, the CreateChangeSet operation will fail, reporting to the developer that the bucket already exists.

...

  MyDevThumbnailsBucket:
    Type: "AWS::S3::Bucket"
    Properties:
      BucketName: "dev-thumbnails"

Step 1: Create Change Set

aws cloudformation create-change-set \                  
    --stack-name "dev-lambda-stack" \
    --change-set-name "addBucket" \ 
    --template-body file://lambda-with-alias-template.yaml | jq .

Step 2: Review Deployment Validations
Use CloudFormation change set console to review validations response or use the new DescribeEvents API in the CLi.

Figure 8: Resource name conflict validation
Figure 9: Resource name conflict validation

CLI Command

aws cloudformation describe-events \
    --change-set-name "arn:aws:cloudformation:us-west-2:123456789012:changeSet/addBucket/eafcdb2b-e018-4e0f-9e87-86b251f4eac5"
{
  "OperationEvents": [
    {
      "EventId": "e6049394-30e4-466d-9fb4-b5f525144058",
      "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/dev-lambda-stack/2d2c3240-bb59-11f0-b080-0613dc96740d",
      "OperationId": "eafcdb2b-e018-4e0f-9e87-86b251f4eac5",
      "OperationType": "CREATE_CHANGESET",
      "OperationStatus": "FAILED",
      "EventType": "STACK_EVENT",
      "Timestamp": "2025-11-06T21:58:49.872000+00:00",
      "StartTime": "2025-11-06T21:58:44.252000+00:00",
      "EndTime": "2025-11-06T21:58:49.872000+00:00"
    },
    {
      "EventId": "bca310c3-61e6-4478-9b0a-3a89f816aec0",
      "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/dev-lambda-stack/2d2c3240-bb59-11f0-b080-0613dc96740d",
      "OperationId": "eafcdb2b-e018-4e0f-9e87-86b251f4eac5",
      "OperationType": "CREATE_CHANGESET",
      "EventType": "VALIDATION_ERROR",
      "LogicalResourceId": "MyDevThumbnailsBucket",
      "PhysicalResourceId": "",
      "ResourceType": "AWS::S3::Bucket",
      "Timestamp": "2025-11-06T21:58:49.606000+00:00",
      "ValidationFailureMode": "FAIL", "ValidationName": "NAME_CONFLICT_VALIDATION", "ValidationStatus": "FAILED", "ValidationStatusReason": "Resource of type 'AWS::S3::Bucket' with identifier 'dev-thumbnails' already exists.", "ValidationPath": "/Resources/MyDevThumbnailsBucket"   },
    {
      "EventId": "8158f79f-ee58-4c3b-b3eb-3beace064139",
      "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/dev-lambda-stack/2d2c3240-bb59-11f0-b080-0613dc96740d",
      "OperationId": "eafcdb2b-e018-4e0f-9e87-86b251f4eac5",
      "OperationType": "CREATE_CHANGESET",
      "OperationStatus": "IN_PROGRESS",
      "EventType": "STACK_EVENT",
      "Timestamp": "2025-11-06T21:58:44.252000+00:00",
      "StartTime": "2025-11-06T21:58:44.252000+00:00"
    }
  ]
}

Scenario 3: S3 bucket not empty
Since AWS S3 service does not allow customers to delete S3 Buckets when there are objects in them, the new pre-deployment validations will warn you if you try to delete a bucket that is not empty.

Resuming our journey, let’s assume that you fix the name conflict issue by renaming the bucket to “dev-test-tumbnails”, and then updates the stack. After testing the lambda function’s integration with S3, the dev-cycle generated a few thumbnail objects in the S3 bucket.

Later, you decide to fix the bucket name because you notice a typo: “dev-test-tumbnails” should be “dev-test-thumbnails” (missing “h”). When you update the template to use the corrected name, CloudFormation will need to create the new bucket then delete the old one during the clean-up phase.

Step 1: Create Change Set

aws cloudformation create-change-set \                  
    --stack-name "dev-lambda-stack" \
    --change-set-name "renameBucket" \ 
    --template-body file://lambda-with-alias-template.yaml | jq .

Step 2: Review Validation

Use CloudFormation change set console to review validations response or use the new DescribeEvents API in the CLI.

Figure 9: S3 bucket emptiness on delete operation validation

Figure 10: S3 bucket emptiness on delete operation validation

CLI Command

aws cloudformation describe-events \
    --change-set-name "arn:aws:cloudformation:us-west-2:123456789012:changeSet/addBucket/eafcdb2b-e018-4e0f-9e87-86b251f4eac5"
{
  "OperationEvents": [
    {
      "EventId": "24920e0f-1941-45a5-9177-786bc805b724",
      "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/dev-lambda-stack/2d2c3240-bb59-11f0-b080-0613dc96740d",
      "OperationId": "8fef2b60-b411-4d0e-920e-7ec7c7aa39f2",
      "OperationType": "CREATE_CHANGESET",
      "OperationStatus": "SUCCEEDED",
      "EventType": "STACK_EVENT",
      "Timestamp": "2025-11-06T22:52:26.355000+00:00",
      "StartTime": "2025-11-06T22:52:21.071000+00:00",
      "EndTime": "2025-11-06T22:52:26.355000+00:00"
    },
    {
      "EventId": "c117e02d-a652-4755-9586-6d4ccb0f6504",
      "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/dev-lambda-stack/2d2c3240-bb59-11f0-b080-0613dc96740d",
      "OperationId": "8fef2b60-b411-4d0e-920e-7ec7c7aa39f2",
      "OperationType": "CREATE_CHANGESET",
      "EventType": "VALIDATION_ERROR",
      "LogicalResourceId": "MyDevThumbnailsBucket",
      "PhysicalResourceId": "",
      "ResourceType": "AWS::S3::Bucket",
      "Timestamp": "2025-11-06T22:52:25.960000+00:00",
      "ValidationFailureMode": "WARN", "ValidationName": "BUCKET_EMPTINESS_VALIDATION", "ValidationStatus": "FAILED", "ValidationStatusReason": "The bucket 'dev-tumbnails' is not empty. You must either delete all objects and versions or use the deletion policy to retain it, otherwise the delete operation will fail.", "ValidationPath": "/Resources/MyDevThumbnailsBucket"
    },
    {
      "EventId": "6c66ff53-6751-4b4c-96b8-d1a33fc43b4f",
      "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/dev-lambda-stack/2d2c3240-bb59-11f0-b080-0613dc96740d",
      "OperationId": "8fef2b60-b411-4d0e-920e-7ec7c7aa39f2",
      "OperationType": "CREATE_CHANGESET",
      "OperationStatus": "IN_PROGRESS",
      "EventType": "STACK_EVENT",
      "Timestamp": "2025-11-06T22:52:21.071000+00:00",
      "StartTime": "2025-11-06T22:52:21.071000+00:00"
    }
  ]
}

Bucket emptiness validation uses WARN mode, which allows change set creation to succeed even when the validation check fails. This gives you time to review and empty the bucket before execution. However, if you execute the change set without emptying the bucket, the delete operation will fail.

Notice in the output above:

  • ValidationStatus: "FAILED" – The emptiness check detected objects in the bucket
  • ValidationFailureMode: "WARN" – This is a warning, not a blocking error
  • OperationStatus: "SUCCEEDED" – Change set creation completed successfully despite the warning

This design allows you to review the warning, take corrective action (such as emptying the bucket), and then proceed with execution.

Beyond catching errors early, these capabilities also transform how you troubleshoot failed deployments with enhanced operation tracking and filtering.

New DescribeEvents API with Operation IDs and root cause filtering

The new DescribeEvents API retrieves CloudFormation events based on flexible query criteria. It groups stack operations by operation ID, enabling you to focus specifically on individual stack operations involved during your stack deployment.

Operation: An operation is any action performed on a stack, including stack lifecycle actions (Create, Update, Delete, Rollback), change set creation, nested stack creation, and automatic rollbacks triggered by failures. Each operation has a unique identifier and represents a discrete change attempt on the stack.

Figure 10: Stack Events grouped by Operation Id

 Figure 11: Stack Events grouped by Operation Id

Scenario
When an update operation on an existing stack fails and results in a rollback, and you want to understand the reason behind the update stack failure. Using the operation ID obtained from the update stack response or from the describe stacks response, you can call describe events to get details on the failure.

Step 1: Update Stack

aws cloudformation update-stack \
 --stack-name test-1106 \
 --template-body file://test-1106-update.yaml
Output:
{
    "StackId": "arn:aws:cloudformation:us-west-2:012345678901:stack/test-1106/07580010-bb79-11f0-8f6c-0289bb5c804f",
    "OperationId": "1c211b5a-4538-4dc9-bfed-e07734371e57"
}

Step 2: Review stack status with describe stacks

The stack description available via describe-stacks API now includes LastOperations information showing recent operation IDs and their types. This enables you to quickly identify which operations occurred and their current status without parsing through event logs.

Figure 11: CloudFormation Stack Info page showing new operation IDs
Figure 11: CloudFormation Stack Info page showing new operation IDs

CLI Command

aws cloudformation describe-stacks \
 --stack-name test-1106
{
    "Stacks": [
        {
            "StackId": "arn:aws:cloudformation:us-west-2:012345678901:stack/test-1106/07580010-bb79-11f0-8f6c-0289bb5c804f",
            "StackName": "test-1106",
            "Description": "A simple CloudFormation template to create an S3 bucket.",
            "CreationTime": "2025-11-07T01:28:13.778000+00:00",
            "LastUpdatedTime": "2025-11-07T01:43:39.838000+00:00",
            "RollbackConfiguration": {},
            "StackStatus": "UPDATE_ROLLBACK_COMPLETE",
            "DisableRollback": false,
            "NotificationARNs": [],
            "Tags": [],
            "EnableTerminationProtection": false,
            "DriftInformation": {
                "StackDriftStatus": "NOT_CHECKED"
            },
            "LastOperations": [ { "OperationType": "ROLLBACK", "OperationId": "d0f12313-7bdb-414d-a879-828a99b36f29" }, { "OperationType": "UPDATE_STACK", "OperationId": "1c211b5a-4538-4dc9-bfed-e07734371e57" }
            ]
        }
    ]
}

Step 3: Review operation status with describe events API and operation id
Using the operation ID from the previous step, you can now query specific operation events to understand exactly what happened during that operation. This targeted approach eliminates the need to search through all stack events to find relevant information.

Figure 12: New CloudFormation stack operation pageFigure 12: New CloudFormation stack operation page

CLI Command

aws cloudformation describe-stacks \
 --stack-name test-1106
{
    "OperationEvents": [
        {
            "EventId": "76358afe-01ff-45e1-bf4d-8b89109aca57",
            "StackId": "arn:aws:cloudformation:us-west-2:012345678901:stack/test-1106/07580010-bb79-11f0-8f6c-0289bb5c804f",
 "OperationId": "1c211b5a-4538-4dc9-bfed-e07734371e57",             "OperationType": "UPDATE_STACK",
            "OperationStatus": "FAILED",
            "EventType": "STACK_EVENT",
            "Timestamp": "2025-11-07T01:43:44.322000+00:00",
            "StartTime": "2025-11-07T01:43:39.820000+00:00",
            "EndTime": "2025-11-07T01:43:44.322000+00:00"
        },
        {
            "EventId": "01fcd898-38f3-477d-891d-e950d964d594",
            "StackId": "arn:aws:cloudformation:us-west-2:012345678901:stack/test-1106/07580010-bb79-11f0-8f6c-0289bb5c804f",
 "OperationId": "1c211b5a-4538-4dc9-bfed-e07734371e57",             "EventType": "PROVISIONING_ERROR",
            "LogicalResourceId": "MyS3Bucket",
            "PhysicalResourceId": "test-1106-bucket",
            "ResourceType": "AWS::S3::Bucket",
            "Timestamp": "2025-11-07T01:43:43.561000+00:00",
            "ResourceStatus": "UPDATE_FAILED",
            "ResourceStatusReason": "The target bucket for logging does not exist (Service: Amazon S3; Status Code: 400; Error Code: InvalidTargetBucketForLogging; Request ID: ZQAPTT7646A9GQ0H; S3 Extended Request ID: 5Cl/xSAfQgs8UJ7rdq4EvsJT8pxnYLZlc3FzTgpQCxZlukoIiWYXkuds6xDzkmpurH+6epy2s9g7Ro7XN4ZFoQ==; Proxy: null)",
            "ResourceProperties": "{\"BucketName\":\"test-1106-bucket\",\"LoggingConfiguration\":{\"LogFilePrefix\":\"access-logs/\",\"DestinationBucketName\":\"logs-1106-bucket\"},\"LifecycleConfiguration\":{\"Rules\":[{\"Status\":\"Enabled\",\"ExpirationInDays\":\"90\",\"Id\":\"DeleteOldVersions\"}]},\"Tags\":[{\"Value\":\"Development\",\"Key\":\"Environment\"},{\"Value\":\"CloudFormationDemo\",\"Key\":\"Project\"}]}"
        },
        {
            "EventId": "2976d65e-44cc-4674-b771-a22d86a7d3f8",
            "StackId": "arn:aws:cloudformation:us-west-2:012345678901:stack/test-1106/07580010-bb79-11f0-8f6c-0289bb5c804f",
 "OperationId": "1c211b5a-4538-4dc9-bfed-e07734371e57",             "EventType": "PROGRESS",
            "LogicalResourceId": "MyS3Bucket",
            "PhysicalResourceId": "test-1106-bucket",
            "ResourceType": "AWS::S3::Bucket",
            "Timestamp": "2025-11-07T01:43:43.034000+00:00",
            "ResourceStatus": "UPDATE_IN_PROGRESS",
            "ResourceProperties": "{\"BucketName\":\"test-1106-bucket\",\"LoggingConfiguration\":{\"LogFilePrefix\":\"access-logs/\",\"DestinationBucketName\":\"logs-bucket\"},\"LifecycleConfiguration\":{\"Rules\":[{\"Status\":\"Enabled\",\"ExpirationInDays\":\"90\",\"Id\":\"DeleteOldVersions\"}]},\"Tags\":[{\"Value\":\"Development\",\"Key\":\"Environment\"},{\"Value\":\"CloudFormationDemo\",\"Key\":\"Project\"}]}"
        },
        {
            "EventId": "daf7e299-df02-4eab-b3e9-11a4659f789f",
            "StackId": "arn:aws:cloudformation:us-west-2:012345678901:stack/test-1106/07580010-bb79-11f0-8f6c-0289bb5c804f",
 "OperationId": "1c211b5a-4538-4dc9-bfed-e07734371e57",             "EventType": "PROGRESS",
            "LogicalResourceId": "test-1106",
            "PhysicalResourceId": "arn:aws:cloudformation:us-west-2:012345678901:stack/test-1106/07580010-bb79-11f0-8f6c-0289bb5c804f",
            "ResourceType": "AWS::CloudFormation::Stack",
            "Timestamp": "2025-11-07T01:43:39.838000+00:00",
            "ResourceStatus": "UPDATE_IN_PROGRESS",
            "ResourceStatusReason": "User Initiated"
        },
        {
            "EventId": "0b1ebf05-4496-4a8c-978e-7c081def3e4d",
            "StackId": "arn:aws:cloudformation:us-west-2:012345678901:stack/test-1106/07580010-bb79-11f0-8f6c-0289bb5c804f",
 "OperationId": "1c211b5a-4538-4dc9-bfed-e07734371e57",             "OperationType": "UPDATE_STACK",
            "OperationStatus": "IN_PROGRESS",
            "EventType": "STACK_EVENT",
            "Timestamp": "2025-11-07T01:43:39.820000+00:00",
            "StartTime": "2025-11-07T01:43:39.820000+00:00"
        }
    ]
}

Step 4: Identify failure root cause(s) with FailedEvents filter
The new failure root cause filter instantly surfaces only the events that caused the operation to fail. This eliminates the need to manually scan through progress events to identify the root cause of deployment failures.

Figure 13: Filter operation failure root causes
Figure 13: Filter operation failure root causes

CLI Command

aws cloudformation describe-events \
 --operation-id 1c211b5a-4538-4dc9-bfed-e07734371e57 \
 --filter FailedEvents=true
{
    "OperationEvents": [
        {
            "EventId": "01fcd898-38f3-477d-891d-e950d964d594",
            "StackId": "arn:aws:cloudformation:us-west-2:012345678901:stack/test-1106/07580010-bb79-11f0-8f6c-0289bb5c804f",
            "OperationId": "1c211b5a-4538-4dc9-bfed-e07734371e57",
            "EventType": "PROVISIONING_ERROR",
            "LogicalResourceId": "MyS3Bucket",
            "PhysicalResourceId": "test-1106-bucket",
            "ResourceType": "AWS::S3::Bucket",
            "Timestamp": "2025-11-07T01:43:43.561000+00:00",
            "ResourceStatus": "UPDATE_FAILED",
            "ResourceStatusReason": "The target bucket for logging does not exist (Service: Amazon S3; Status Code: 400; Error Code: InvalidTargetBucketForLogging; Request ID: ZQAPTT7646A9GQ0H; S3 Extended Request ID: 5Cl/xSAfQgs8UJ7rdq4EvsJT8pxnYLZlc3FzTgpQCxZlukoIiWYXkuds6xDzkmpurH+6epy2s9g7Ro7XN4ZFoQ==; Proxy: null)",
            "ResourceProperties": "{\"BucketName\":\"test-1106-bucket\",\"LoggingConfiguration\":{\"LogFilePrefix\":\"access-logs/\",\"DestinationBucketName\":\"logs-bucket\"},\"LifecycleConfiguration\":{\"Rules\":[{\"Status\":\"Enabled\",\"ExpirationInDays\":\"90\",\"Id\":\"DeleteOldVersions\"}]},\"Tags\":[{\"Value\":\"Development\",\"Key\":\"Environment\"},{\"Value\":\"CloudFormationDemo\",\"Key\":\"Project\"}]}"
        }
    ]
}

The FailedEvents=true filter transforms troubleshooting from parsing dozens of progress events to instantly seeing only what matters. This can make diagnosis of issues during an incident much easier..

Real-World Impact
These features improve your Infrastructure development experience with CloudFormation:

  • Template syntax errors: Previously discovered after minutes of provisioning, now caught in seconds
  • Resource conflicts: No more failed deployments due to existing resources
  • Debugging complexity: Transform troubleshooting sessions into faster targeted fixes
  • CI/CD reliability: Reduce pipeline failures and improve deployment confidence

Getting Started

These capabilities are available today in all AWS Regions where CloudFormation is supported. Pre-deployment validation is automatically enabled for all change set operations, no configuration required.

Try it now:

  1. Create any change set from the CloudFormation console or via SDK or CLI with aws cloudformation create-change-set
  2. Use `aws cloudformation describe-events –change-set-name <your-changeset-arn>` to see validation results
  3. Filter failure root causes instantly: via console or CLI with aws cloudformation describe-events –operation-id <id> –filter FailedEvents=true

Best Practices

  • Always use change sets: Even for simple updates, change sets now provide validation feedback
  • Leverage Operation IDs: Use the unique identifiers for focused troubleshooting
  • Filter events strategically: Use –filters FailedEvents=true to focus on problems
  • Automate validation: Integrate the describe-events API into your CI/CD pipelines
  • Use Console: CloudFormation console provides a visual experience with error source mapping to the specific line on your template.

Conclusion

Start using these features today in your development workflow. Whether you’re building new infrastructure or maintaining existing stacks, early validation and enhanced troubleshooting will accelerate your deployment cycles and make it easier to manage infrastructure.

Ready to experience faster CloudFormation development? Create your first change set and see validation in action.

Blog Authors Bio:

Idriss Laouali Abdou

Idriss is a Sr. Product Manager Technical on the AWS Infrastructure-as-Code team based in Seattle. He focuses on improving developer productivity through AWS CloudFormation and StackSets Infrastructure provisioning experiences. Outside of work, you can find him creating educational content for thousands of students, cooking, or dancing.

Olivia Biswas

Olivia is a Software Development Manager on the AWS Infrastructure-as-Code team based in Seattle, where she leads developer productivity initiatives through CloudFormation. During her tenure at Amazon, she has built several customer-obsessed software solutions within Alexa and Buy With Prime. Outside of work, she is a globe trotter who enjoys baking, dancing, reading, and watching documentaries.

Marcus Ramos

Marcus is a Software Engineer on the AWS Infrastructure-as-Code team. He’s passionate about building features that minimize customers’ effort, and improving efficiency. Outside of work, he enjoys traveling, spending time with his family, and playing PC games.

Subha Velayuthams

Subha is a Senior Software Engineer on the AWS Infrastructure-as-Code team, where she builds features to improve developer productivity. Outside of work, she enjoys reading, traveling, and experimenting with new creative hobbies.

How to Simplify Multi-Account Deployments Monitoring: Centralized Logs for AWS CloudFormation StackSets

Post Syndicated from Idriss Laouali Abdou original https://aws.amazon.com/blogs/devops/how-to-simplify-multi-account-deployments-monitoring-centralized-logs-for-aws-cloudformation-stacksets/

Introduction

As organizations adopt multi-account strategies for improved security features and governance, AWS CloudFormation StackSets enables organizations to deploy infrastructure across multiple accounts and regions. However, monitoring and tracking these distributed deployments across multiple accounts presents operational challenges. When a critical security baseline deployed across 50 accounts suddenly starts failing, teams face the daunting task of logging into each account individually to understand what went wrong and which accounts were affected.

This operational overhead scales exponentially with organization growth, requiring platform teams to spend countless hours switching between accounts and manually correlating deployment events. The lack of centralized visibility slows incident response and makes it difficult to identify patterns or implement proactive monitoring. In this blog post, we’ll explore a solution that centralizes AWS CloudFormation logs from multiple accounts into a single management account, making it easier to monitor and troubleshoot StackSets deployments.

Solution Architecture

Our solution creates a centralized logging system that collects AWS CloudFormation events from all target accounts and forwards them to a central management account. This approach provides a single pane of glass for monitoring and troubleshooting AWS CloudFormation deployments across your entire organization.

Figure 1. Architecture diagram showing event flow from member accounts to management account through EventBridge and CloudWatch Logs

Figure 1. Architecture diagram showing event flow from member accounts to management account through EventBridge and CloudWatch Logs.

The architecture consists of four main components:

  1. Management Account Setup: Creates a central event bus, log group, and necessary permissions in the organization’s management account.
  2. Target Account Configuration: Deployed via StackSets to configure event rules that forward AWS CloudFormation events to the management account.
  3. Resource Deployment: Uses StackSets to deploy common resources across target accounts, generating the events we want to monitor.
  4. Monitoring and Visualization: Provides dashboards and queries for operational insights.

How It Works

The solution follows this event flow:

  1. Event Generation: AWS CloudFormation operations in target accounts generate events (stack creation, updates, deletions, resource changes).
  2. Event Capture: Amazon EventBridge rules in each target account capture these AWS CloudFormation events based on defined patterns.
  3. Cross-Account Forwarding: Events are forwarded to a custom event bus in the management account using cross-account permissions.
  4. Centralized Logging: The central event bus routes all events to a Amazon CloudWatch Log Group with structured logging.
  5. Monitoring and Alerting: Administrators can view consolidated logs, create custom queries, and set up alerts from a single location.

Prerequisites

Before implementing this solution, ensure you have the following prerequisites in place:

  • AWS account: Ensure you have valid AWS account.
  • AWS Organizations: You must have an AWS Organization structure set up with a primary management account and several member accounts under the management account.
  • Trusted Access: Enable trusted access for AWS CloudFormation StackSets from the management account (this allows StackSets to assume roles in member accounts).
  • Appropriate Permissions: You must have access to the management account or be configured as a delegated administrator to create and manage StackSets. For detailed information about permissions and security considerations when using StackSets with AWS Organizations, please review the Prerequisites in the AWS CloudFormation StackSets documentation.

Implementation Deep Dive

The solution is implemented using two AWS CloudFormation templates that work together to create a comprehensive monitoring system:

1. Management Account Logging Setup (log-setup-management.yaml)

This template establishes the central logging infrastructure in the management account by creating a custom Amazon EventBridge event bus with cross-account access policies and an encrypted Amazon CloudWatch Log Group using a customer-managed AWS Key Management Service (AWS KMS) key. A key feature is the included stack set resource that automatically deploys the target account configuration to all member accounts, eliminating manual setup and ensuring consistent configuration across the entire organization.

2. Stack set Deployment Template (common-resources-stackset.yaml)

This template creates a service-managed stack set that deploys common resources to all accounts in specified organizational units. The StackSet is configured with auto-deployment enabled to automatically provision new accounts added to the organization and includes operation preferences for parallel regional deployment with fault tolerance settings.

Step-by-Step Deployment Guide

Step 1: Download the templates:

Step 2: Deploy the Management Account Infrastructure

Deploy the centralized logging infrastructure to your management account.

Using CLI:

aws cloudformation deploy \
  --template-file log-setup-management.yaml \
  --stack-name log-setup-management \
  --parameter-overrides \
    OUID=your-organizational-unit-id \
    OrgID=your-organization-id \
  --capabilities CAPABILITY_IAM \
  --region us-east-1

AWS CLI command execution for stack deployment

Using AWS Console:

  1. Open the AWS CloudFormation console at https://console.aws.amazon.com/cloudformation.
  2. On the Stacks page, choose Create stack at top right, and then choose With new resources (standard).
  3. On the Create stack page, Upload a template file, choose Choose File to choose a template file from your local computer.
  4. Choose Next to continue and to validate the template.
  5. On the Specify stack details page, type a stack name in the Stack name box.
  6. In the Parameters section, specify values for the parameters that were defined in the template.
  7. Choose Next to continue creating the stack.
  8. Acknowledge capabilities and transforms.
  9. Choose Next to continue.
  10. Choose Submit to launch your stack.

This single deployment:

  1. Creates the central logging infrastructure in the management account.
  2. Automatically deploys Amazon EventBridge rules to all accounts in the specified OU.
  3. Sets up the necessary IAM roles and policies for cross-account access.

Figure 2: Screenshot showing successful deployment of log-setup-management.yaml template in the management account

Figure 2.1: Screenshot showing successful deployment of log-setup-management.yaml template in the management account

Figure 2.2: Screenshot showing deployment timeline of log-setup-management.yaml template in the management account

Figure 2.2: Deployment timeline view of log-setup-management.yaml template in the management account

Step 3: Deploy Common Resources

Deploy the sample common resources to demonstrate the logging functionality.

Using CLI:

aws cloudformation deploy \
  --template-file common-resources-stackset.yaml \
  --stack-name common-resources-stackset \
  --parameter-overrides \
    OUID=your-organizational-unit-id \
  --capabilities CAPABILITY_IAM \
  --region us-east-1

AWS CLI command execution for stack deployment

Using AWS Console:

  1. Open the AWS CloudFormation console at https://console.aws.amazon.com/cloudformation.
  2. On the Stacks page, choose Create stack at top right, and then choose With new resources (standard).
  3. On the Create stack page, Upload a template file, choose Choose File to choose a template file from your local computer.
  4. Choose Next to continue and to validate the template.
  5. On the Specify stack details page, type a stack name in the Stack name box.
  6. In the Parameters section, specify values for the parameters that were defined in the template.
  7. Choose Next to continue creating the stack.
  8. Acknowledge capabilities and transforms.
  9. Choose Next to continue.
  10. Choose Submit to launch your stack.

This creates a stack set that deploys Amazon Simple Storage Service (Amazon S3) infrastructure to all target accounts, generating AWS CloudFormation events that will be captured by your centralized logging system.

Screenshot showing successful deployment of common-resources-stackset.yaml template for target accounts

Figure 3: Screenshot showing successful deployment of common-resources-stackset.yaml template for target accounts

Step 4: Validation and Testing

Confirm event flow and monitoring functionality by viewing the log streams in the ‘central-cloudformation-logs’ log group.

Monitoring and Visualization

The centralized logging solution provides advanced monitoring capabilities through Amazon CloudWatch Logs Insights and custom dashboards.

You can customize your queries to get:

  • Recent AWS CloudFormation events across all accounts.
  • Failed stack operations for quick troubleshooting.
  • Successful deployments for verification.
  • Event distribution by account and region.
  • Status breakdown of all AWS CloudFormation operations.

The following query helps you analyze CloudFormation events across your organization by showing:

  • Timestamp of events
  • Account ID where the event occurred
  • Region of deployment
  • Resource types being deployed
  • Deployment status
  • Logical resource identifiers

fields @timestamp, account, region
| parse @message /"resource-type":"(?<resource_type>[^"]+)"/ 
| parse @message /"status":"(?<status>[^"]+)"/ 
| parse @message /"logical-resource-id":"(?<logical_resource_id>[^"]+)"/ 
| sort @timestamp desc

Figure 4: CloudWatch Logs Insights query results showing CloudFormation events across accounts

Figure 4: CloudWatch Logs Insights query results showing CloudFormation events across accounts

You can customize your queries to filter for specific conditions such as failed deployment status, particular resource types, or specific accounts to quickly identify and troubleshoot issues across your organization’s AWS CloudFormation deployments.

Cost Implications

When implementing this centralized monitoring solution, you should consider the following cost components:

Clean up

To clean up the resources created in this solution, follow these steps:

  1. First, delete the common resources stack set (common-resources-stackset) from the AWS CloudFormation console in your management account. This will remove all the resources deployed across your member accounts.
  2. After the stack set operations are complete, delete the management account logging setup stack (log-setup-management) to remove the centralized logging infrastructure, including the event bus, log groups, and associated IAM roles.

Note: Make sure all stack set operations are complete before deleting the management account logging setup to ensure proper cleanup of all resources.

Conclusion

Managing infrastructure across multiple AWS accounts doesn’t have to be complex. By centralizing AWS CloudFormation logs, you can gain visibility into your multi-account deployments, troubleshoot issues more efficiently, and help achieve consistent resource deployment across your organization.

This solution demonstrates how AWS services like AWS CloudFormation StackSets, Amazon EventBridge, and Amazon CloudWatch Logs can be combined to create a powerful monitoring system for your infrastructure as code deployments.

Get started today by implementing this solution in your AWS Organization to gain immediate visibility into your multi-account deployments. Download the templates from our GitHub repository and follow the step-by-step guide to enhance your cloud operations.

Authors:

Fatima Bzioui

Fatima Bzioui is a Cloud Support Engineer with a focus on DevOps best practices and cloud-native solutions. Fatima’s expertise includes Infrastructure as Code and CI/CD implementations, which she uses to help organizations overcome complex technical challenges and achieve their cloud goals.

Idriss Laouali Abdou

Idriss Laouali Abdou is a Sr. Product Manager Technical for AWS Infrastructure-as-Code based in Seattle. He focuses on improving developer productivity through StackSets and CloudFormation Infrastructure provisioning experiences. Outside of work, you can find him creating educational content for thousands of students, cooking, or dancing.

Infrastructure as Code at Thomson Reuters with AWS CDK

Post Syndicated from Vu San Ha Huynh original https://aws.amazon.com/blogs/devops/infrastructure-as-code-at-thomson-reuters-with-aws-cdk/

This post is cowritten by Danilo Tommasina and Lalit Kumar B from Thomson Reuters.

Large organizations often struggle with infrastructure management challenges including compliance issues, development bottlenecks and errors from inconsistent AWS resource creation across teams. Without standardized naming, tagging and policy enforcement, teams face repeated boilerplate code and difficulty accessing centrally-managed resources.

In this post, we will show you how Thomson Reuters developed an extension of the AWS Cloud Development Kit (CDK) to automate compliance, standardization and policy enforcement in Infrastructure as Code (IaC) scripts. We will explore the strategic reasoning behind this initiative, outline foundational design principles, and provide technical details on TR’s journey from concept to implementation. The solution accelerates and standardizes cloud infrastructure deployment and management through seamless integration between TR’s custom library and AWS CDK.

Thomson Reuters (TR) is one of the world’s leading information organizations for businesses and professionals. TR provides companies with the intelligence, technology, and human expertise they need to find trusted answers, enabling them to make better decisions more quickly. TR’s customers span the financial, risk, legal, tax, accounting, and media industries.

Overview

In a large organization that offers a variety of customer products, it is essential to manage numerous cloud resources effectively. This involves overseeing multiple AWS accounts, implementing access control or addressing financial tracking challenges. These tasks require the application of centrally defined standards and conventions, with additional requirements tailored to specific sub-organizations.

Infrastructure as Code (IaC) is an effective method for managing cloud resources. However, utilizing vanilla AWS CloudFormation for extensive and intricate infrastructure can pose challenges. It requires careful attention to naming conventions, tagging standards, security, and best practices for infrastructure deployments. Additionally, repeating infrastructure patterns across various services and products often leads to excessive use of copy-paste and dealing with boilerplate code. When projects require configurable and dynamic components – including conditionals, loops, repeatable patterns, and distribution to a large user base – delivering CloudFormation scripts can become quite cumbersome and prone to errors.

AWS CDK addresses these challenges by enabling IaC development in high-level programming languages like TypeScript, JavaScript, Python, Java. AWS CDK Level 2 and 3 constructs simplify and reduce the amount of code to be written to manage complex infrastructure. It allows TR to create custom libraries that extend the vanilla AWS CDK with additional patterns and utilities. The extension libraries can also be distributed for multiple programming languages and package managers thanks to JSII. JSII enables TypeScript libraries to be automatically compiled and packaged for native consumption in each target language, allowing CDK libraries to be written once but used in many different programming environments.

Solution to optimize the process

In a medium to large company, different teams provide the fundamental infrastructure services (e.g. authentication and authorization, networking, security, financial tracking and optimization, base infrastructure provisioning, etc.) to enable use of the cloud for a large community of developers.

Figure 1 illustrates the conventional method involving teams producing documentation that outlines the usage of pre-deployed infrastructure. This includes naming and tagging standards, required security boundaries, default settings and other relevant guidelines. Subsequently, the implementation team reviews these documents and integrates the established rules into their tool chain consistently, often working in isolation. This results in inefficiencies, misinterpretation risks and maintenance challenges when specifications change.

Figure 1. The traditional approach with separate documentation and implementation teams.

Figure 1: The traditional approach

TR’s optimized approach replaces documentation with working code as shown in Figure 2.

Figure 2: The optimized approach with shared CDK extension library

Figure 2: The optimized approach

Infrastructure teams contribute their specifications into an extension library for AWS CDK, while the implementation teams can also contribute common patterns back into the central extension. The central extension library is released as polyglot packages allowing the implementation teams to pick the programming language that fits best to their knowledge.

With this approach, TR introduce a “shift-left” in the development and delivery lifecycle. Standards and best practices are introduced early, things are done right by default, and TR minimizes the risks of getting inappropriately configured resources to be deployed, which leads to a reduction in the number of governance and security incidents.
Implementation delivery teams can share well architected patterns for re-use by other teams to improve overall effectiveness.

Implementation

Design principles

Key factors for the adoption of a framework are:

  • Simplicity, ease-of-use, self-service, and fast onboarding
  • Low maintenance effort and cost
  • Controlled roll-out, ability to quickly roll-back

With the above in mind, TR delivered a minimally invasive framework that can be enabled with a tiny set of custom code on top of vanilla AWS CDK code.

Using the TR-AWS CDK core library is straightforward – users simply import the package and adapt their entry point. From there, they can leverage standard AWS CDK code and documentation for most development tasks. There’s no need to learn custom construct classes or follow extensive specialized tutorials – vanilla AWS CDK knowledge is sufficient for most requirements. Additionally, developers can quickly incorporate open-source construct libraries through standard package managers. These third-party libraries integrate seamlessly with the TR implementation, automatically conforming to company standards without requiring additional configuration.

By managing distribution of the library following standard software packaging and release procedures TR enable consumers to adopt new capabilities in a controlled way, with the ability to roll-back to previous versions if something goes wrong during an update.

All this together allows TR to tick off the key factors listed above.

The monorepo approach

TR created a monorepo (monolithic repository) which is a version control strategy where multiple projects or packages are stored in a single repository. This approach offers several advantages over maintaining separate repositories for each package: unified versioning, simplified dependency management, consistent tooling, atomic changes across packages and improved collaboration.

This setup mirrors the configuration used by AWS CDK itself.

TR organized their monorepo following this structure:

  • repo/package.json: Defines dev dependencies and global scripts used by all packages
  • repo/packages: contains the different modules
  • repo/packages/core/package.json: deps of core module and scripts for core module
  • repo/packages/core/lib/*: typescript code that composes the core module
  • repo/packages/core/lib/augmentation/*: module augmentations for AWS CDK core components
  • repo/packages/constructs-pattern-X: define multiple reusable and independent level 3 constructs
  • repo/packages/tr-cdk-lib/package.json: assembly module that defines scripts to assemble the final mono package that will be shared via a npm repository

Figure 3. The monorepo structure

Figure 3: Repo structure

This structure enables TR to maintain a collection of related, but distinct CDK constructs while making sure they work together seamlessly.

The modules are assembled and released into one single versioned package which simplifies the end-user’s consumption.

The core module: Foundation of TR AWS CDK library

The core module is the foundation of TR’s CDK extension library, it consists of several key components that work together to “TR-ify” AWS resources and offer simplified access to centrally managed infrastructure resources that are provided by TR’s AWS landing zone teams.

TR refers to “TR-ification”, as the process of dynamically adapting AWS CDK constructs to meet their standards and best practices. From a user perspective, the process happens in a minimally invasive way, for most of the time the user is coding with vanilla AWS CDK components, while having access to short-cuts to a variety of TR specific resources.

The core module serves several critical purposes:

  1. Standardization: makes sure the AWS resources follow TR naming conventions and tagging standards
  2. Simplification: abstracts away complex configurations required for TR compliance
  3. Integration: provides seamless access to TR-managed resources like VPCs, security groups, and Route53 hosted zones
  4. Policy Enforcement: automatically applies custom security and financial optimization policies

The “TR-ification” process happens on every construct following a consistent order, for each construct it will:

  1. If applicable, set a name following a consistent pattern
  2. Apply custom initialization logic (e.g. set IAM permission boundary)
  3. Apply security and financial optimization defaults (if not set)
  4. Perform custom validations
  5. Verify security and financial optimization policies
  6. Tag resources

TR uses a single root-level Aspect instead of multiple Aspects to avoid complex resource type checking and improve maintainability:

// This is the entrypoint that triggers the trification process on all CDK constructs
// we apply all TR specific transformations at this point
Aspects.of(this).add({
  visit: (node: IConstruct) => {
    node.getTRifier().trify();
  },
});

The careful readers at this point will scream:
Wait a moment! node.getTRifier().trify() won’t compile!

Which is absolutely correct… unless you know a topic in TypeScript called module augmentation, in TR’s case, they augment the IConstruct interface and Construct class as follows:

/** Defines the set of functionality needed when trifying resources */
export interface ITRifier {
    trify(): void;
    readonly name: string | undefined;
    readonly nameFromTree: string;
}

declare module 'constructs/lib/construct' {
    interface IConstruct {
        /** Obtain the ITRifier responsible to add TR specific features to this CDK IConstruct */
        getTRifier(): ITRifier;
        
        trContext(): AppContext | StageContext | StackContext;
    }
    
    interface Construct extends IConstruct {
        /** Build the ITRifier responsible to add TR specific features to this CDK IConstruct */
        buildTRifier(): ITRifier;
    }
}

Then provide default implementations for the generic Construct:

Construct.prototype.getTRifier = function () {
    // Lazy getter, build the TRifier only when needed and cache it
    return ObjectUtils.lazyGetFrom(this, 'trifier', () => this.buildTRifier());
};

Construct.prototype.buildTRifier = function () {
    return new ConstructTRifier(this); // Default dummy implementation
};

Construct.prototype.trContext = function (): StackContext {
    return Stack.of(this).trContext() as StackContext;
};

Since AWS CDK constructs implement the IConstruct interface, respectively extend the Construct class automatically, the “TR-ification” process becomes available for many types of constructs.
All you need to do now is inject your custom logic for all resources you need customization and make sure the module is loaded, e.g. in case of a Lambda function, it uses:

lambda.CfnFunction.prototype.buildTRifier = function () {
    return new CfnResourceTRifierLambda.CfnFunction(
        this,
        () => { // Accessor for retrieving the lambda function name
            return this.functionName;
        },
        (name: string) => { // Accessor for setting the lambda function name
                this.functionName = name;
        },
        () => {
            // Our own stuff to set defaults for financial optimizations
            const policyChecker = FinOps.Lambda.Defaults.apply(this);
            
            this.node.addValidation({
                validate: () => {
                    // Inject a custom validation logic to check compliance with financial policies
                    return policyChecker.addErrorIfNotCompliant(this);
                }
            });
        }
    );
};

TR targets L1 (Cfn) constructs like CfnFunction because the higher-level L2 and L3 constructs internally create L1 constructs during synthesis. This architectural decision makes sure TR-ification is applied universally, whether users write new lambda.Function() or new lambda.CfnFunction(), both will be TR-ified. This approach provides complete coverage with a single implementation point while remaining completely transparent to library users who can continue using their preferred abstraction level without awareness of this internal mechanism.

Naming standardization

TR uses standardized naming to support IAM policy filtering and consistent resource management. In order to support a broad range of use-cases, TR defined the resource name pattern as follows:
<segregationPrefix>[-appPrefix]-<resourceName>[-region]-<envSuffix>
where the elements mean:

  • segregationPrefix: A prefix used for grouping resources for a specific asset, it implies that a segregated administrative group is responsible for this resource, where applicable it is used for ARN based IAM resource filtering.
  • appPrefix: Optional, a prefix used to map a resource to a specific application or service, this is shared across stacks within a CDK app.
  • resourceName: The name of a resource indicating its purpose.
  • region: Optional, applied only to resources that are global but are part of a CDK stack that is bound to a specific region.
  • envSuffix: A suffix used to segregate different deployment environments, e.g. development, continuous integration, quality assurance, production.

Traditional approaches require developers to manually construct these names, propagating prefixes and suffixes throughout their code:

new lambda.Function(stack, 'foo', {
    runtime: lambda.Runtime.NODEJS_LATEST,
    handler: 'index.handler',
    code: new lambda.InlineCode('bar'),
    functionName: `\${segregationPrefix}-\${appPrefix}-compute-stats-\${envSuffix}`,
});

With TR AWS CDK extension, the code is simplified to:

new lambda.Function(stack, 'MyFunction', {
  runtime: lambda.Runtime.NODEJS_LATEST,
  handler: 'index.handler',
  code: new lambda.InlineCode('foo'),
  functionName: 'compute-stats',
});

The functionName describes what the function does without “noise”, TR AWS CDK will transparently generate and inject the name into the synthetized CloudFormation script, matching the specification. Note that functionName is optional and TR-CDK will either TR-ify a provided name or automatically generate a valid one if the user omits it, making sure CloudFormation receives a properly formatted name.

Access to “Landing Zone” resources

TR’s central AWS Landing Zone team is responsible of inflating a set of standard resources (e.g. VPC, subnets, security groups, Route 53 zones, golden AMIs, etc.) into AWS accounts that are made available to application development teams.

Through module augmentation (shown earlier), the TR-ifier defines the function trContext() which provides access to a context-aware utility. When calling this function on a resource that resides within a Stack, it will return an object that implements StackContext interface.

export interface StackContext extends StageContext {
  /** Get access to the TR IVpc */
  readonly vpc: IVpc;

  /** Provides access to standard security groups that are available in all TR accounts */
  readonly securityGroups: trparams.ISecurityGroupsResolver;

  /** Provides access to private and public hosted zones (with numeric digits) that are available in all TR accounts */
  readonly route53: trparams.IRoute53Resolver;

  /** Provides access to TR golden AMIs that are available in all TR accounts */
  readonly goldenAMI: TRGoldenAMI;
}

The readonly attributes are accessors for the AWS Landing Zones resources listed above. With calls like the following examples, you have a simple way to obtain access to the standard VPC, subnets selections, route 53 private hosted zone, …

// Get the IVpc:
const trVpc: IVpc = stack.trContext().vpc;

// Get the private subnets as array
const privateSubnets: ISubnet[] = trVpc.privateSubnets;

// Get the private subnets as SubnetSelection
const privateSubSel: SubnetSelection = trVpc.selectSubnets({
    subnetType: SubnetType.PRIVATE_WITH_EGRESS,
});

// Get the private Route53 hosted zone
const privateHZ = stack.trContext().route53.privateHostedZone;

You might now wonder how TR resolves the resources and obtain objects implementing IVpc, ISubnet, ISecurityGroup, …

Instead of using hard-coded resource attributes (e.g. Id, ARN, …) or complex lookups, TR uses CloudFormation’s ability to resolve Systems Manager parameters at execution time, as part of the AWS account initial inflation along with the resources, Systems Manager parameters are registered as well. The parameter names are the same across TR’s AWS accounts, the value contains e.g. the id of the matching AWS Landing Zone standard resource, e.g. /landing-zone/vpc/vpc-id, /landing-zone/vpc/subnets/private-1-id, /landing-zone/vpc/subnets/private-2-id, …

TR then defined custom IVpc, ISubnet, IHostedZone… implementations and for each function they implemented dynamic resolution of resource attributes via Systems Manager parameters. With this approach, TR obtains portable code that runs on AWS accounts initialized via TR inflation process. There are no hard-coded resource identifiers, and there is no need for lookups via AWS SDK during synthesis.

As a user of the TR AWS CDK library, TR developers interact with an object implementing the IVpc interface and do not have to care about how to obtain e.g. the VPC-id and subnet ids. The same principle applies to Route53 hosted zones, Golden AMI ids, etc.

Application initialization

As mentioned previously, one key design principle is to minimize the custom code that a user of TR AWS CDK is required to use compared to using vanilla AWS CDK. This approach leverages existing AWS CDK and reduces the learning curve for developers.

This is how TR developers initialize an App with vanilla CDK, compared to how they initialize it with TR AWS CDK.

// Initialize a vanilla AWS CDK application
const app = new cdk.App()

// Initialize a TR CDK application
const app = TRCdk.newApp({
  segregationId: '123456',
  resourceOwner: '[email protected]',
  namingProps: { prefix: 'myapp' },
  deploymentEnv: TRDeploymentEnv.DEV
});

From this point on, the developers can continue using vanilla AWS CDK code, the value returned by TRCdk.newApp(…) is an instance of an extension of CDK’s App class and is fully compatible with it. It, however, injects the TR-ification aspect, manages the tagging process, and initializes contextual information.

Here and there, e.g. when they need to pass the VPC into a construct, they will need to call TR AWS CDK code via the trContext() entry point that is exposed on CDK constructs through TypeScript’s module augmentation feature, but that’s it! 99% of the code is vanilla AWS CDK code.

The segregationId, namingProps, and deploymentEnv attributes are used for multiple purposes like formatting resource names and tagging resources.

Standardized Tagging

TR defines tagging standards, there are mandatory tags (e.g. for attribution to a specific product asset and for tracking resource ownership), and there are optional tags (e.g. for specifying resources that belong to different services within the same product asset).

The segregationId, the resourceOwner, and deploymentEnv attributes are used to set mandatory tags using CDK’s built-in functionality for tagging.
TR also defines a standardized set of optional tags that can be passed into the application context or set ad-hoc on individual constructs.

// Initialize a vanilla AWS CDK Application
const app = new cdk.App()

// Initialize a TR CDK application
const app = TRCdk.newApp({
  segregationId: '123456',
  resourceOwner: '[email protected]',
  namingProps: { prefix: 'myapp' },
  deploymentEnv: TRDeploymentEnv.DEV
  optionalTRTags: {
    financialId: '123456789',
    projectName: 'my-project',
    serviceName: 'ServiceX',
    environmentName: 'Dev environment for ServiceX'
  }

This approach maintains consistency in the use of tag names and setting the values, it happens automatically behind the scenes and will be applied to the taggable constructs. No copy-pasting of tag definitions like in AWS CloudFormation, no issues dealing with CloudFormation’s inconsistent syntax for tag declarations, no forgetting of tagging resources.

Conclusion

In this post, we discussed how the monorepo approach to AWS CDK development, centered around the core module, has significantly improved the infrastructure management at Thomson Reuters. By providing well-architected L3 constructs, standardizing and simplifying AWS resource creation, they’ve reduced errors, enhanced governance, and accelerated development.

The core module’s ability to enforce policies, standardize naming and tagging, and provide access to TR-managed resources makes it an invaluable tool for teams working with AWS infrastructure at Thomson Reuters.

To get started with AWS CDK and build your CDK solutions, check out the AWS CDK Developer Guide.

Danilo Tommasina is a Distinguished Engineer at Thomson Reuters. With over 25 years of experience working in technology roles ranging from Software Engineer, over Director of Engineering and now as Distinguished Engineer. As a passionate generalist, proficient in multiple programming languages, cloud technologies, DevOps practices and with engineering knowledge in the ML space, he contributed to the scaling of TR Labs’ engineering organization. He is also a big fan of automation including but not limited to MLOps, LLMOps processes and Infrastructure as Code principles.

Lalit Kumar B is an Associate Cloud & AI Solutions Architect at Thomson Reuters with over 15 years of experience in various technology roles, including Software Engineer, Database Engineer, DevOps Architect, and Solutions Architect, and now as an AI Architect in Platform Engineering. He helped scaling AWS CDK within TR through the ‘tr-cdk-lib’ solution which is an enterprise-grade centralized library of patterns. He enjoys tackling complex challenges and prioritizing effectiveness over efficiency.

Vu San Ha Huynh is a Solutions Architect at AWS with a PhD in Computer Science. He helps large Enterprise customers drive innovation across different domains with a focus on AI/ML and Generative AI solutions.

Paul Wright is a Senior Technical Account Manger, with over 20 years experience in the IT industry and over 7 years of dedicated cloud focus. Paul has helped some of the largest enterprise customers grow their business and improve their operational excellence. In his spare time Paul is a huge football and NFL fan.

Beyond Bootstrap: Bootstrapless CDK Deployments at GoDaddy

Post Syndicated from Juan Pablo Melgarejo Zamora original https://aws.amazon.com/blogs/devops/beyond-bootstrap-bootstrapless-cdk-deployments-at-godaddy/

This is a guest post written by Ramanathan Nachiappan from GoDaddy.

In the world of infrastructure as code, the AWS Cloud Development Kit (AWS CDK) has revolutionized how teams define and provision cloud resources. Central to its operation is the bootstrapping process, which ensures all required resources and permissions are in place to enable secure and scalable deployments.

At GoDaddy, our cloud journey has always prioritized governance, compliance, and a great developer experience. As our AWS footprint expanded across hundreds of teams and thousands of deployments, we faced a classic engineering dilemma: how do we uphold rigorous governance standards without compromising developer velocity?

AWS CDK’s default bootstrapping process—while essential—often clashed with our governance model, creating friction, workarounds, and wasted cycles. This post details how we evolved beyond that friction, eliminating the explicit bootstrap step entirely and replacing it with a seamless, zero-touch experience. The result: a “bootstrapless” CDK deployment flow that enforces governance invisibly and empowers developers to deploy with a single command.

The Governance Imperative: Security by Design

GoDaddy’s governance model isn’t just a checkbox for compliance; it’s the foundation of our cloud security posture. Our approach requires all AWS resource modifications to flow through AWS CloudFormation, with each deployment evaluated against our rule sets covering:

  • Security configurations: Encryption requirements, network controls, access management
  • Compliance standards: Data protection, regulatory requirements, audit capabilities
  • Operational practices: Resource tagging, backup strategies, monitoring configurations
  • Cost optimization: Resource sizing, lifecycle management, utilization thresholds

Our CloudFormation hooks evaluate every resource against these rules pre-deployment, helping to reduce the likelihood of non-compliant resources being created. This proactive approach is designed to support governance from day one, rather than retroactively detecting violations.

The CDK Bootstrap Challenge

AWS CDK V1 vs AWS CDK V2:

  • AWS CDK v1: Used the active AWS CLI credentials for all deployments.
  • AWS CDK v2: Introduced a new bootstrap template with five new AWS Identity and Access Management (IAM) roles, designed primarily for CDK Pipelines. These roles must be assumed or passed by the AWS CLI. It’s worth noting that AWS CDK v2 still fully supports the legacy synthesizer, allowing users to maintain their existing v1-style workflows.

When AWS CDK v2 arrived, its bootstrap process introduced crucial changes designed to standardize authentication across multiple deployment tools and scenarios (CLI, cross-account deployments, pipelines, etc.). The standard cdk bootstrap command creates several essential components:

# Creates the default bootstrap stack with resources
cdk bootstrap

This command provisions:

Here’s where things got interesting for GoDaddy. While the default AWS CDK setup includes security measures (like encrypted Amazon S3 buckets), our enterprise governance requirements had additional specifications that created some difficulty with the default bootstrap resources:

  • Amazon S3 buckets needed additional encryption, logging, and compliance settings beyond the defaults
  • IAM roles required alignment with our specific permission boundaries and organizational policies
  • Amazon ECR repositories needed mandatory GoDaddy tags and access configurations
  • Additional compliance requirements around resource naming, backup policies, and monitoring

These GoDaddy-specific governance requirements meant the default bootstrap resources do not pass our validation checks, creating deployment slowdown for developers and increasing support overhead for GoDaddy’s governance platform as teams worked around the governance failures.

Phase 1: Custom Bootstrap Templates

Our first step toward enhancing the developer experience was creating a customized bootstrap approach using two key components:

1. The GDStack and Conformers

We developed a specialized CDK construct called GDStack extending the native CDK Stack. This custom stack framework used CDK Aspects to automatically ensure governance compliance:

  • Automatic Resource Conformers: We built a system of “conformers” that apply company-wide governance standards to every resource automatically. For example, our S3Conformer ensures all buckets have required encryption, logging, and access settings.
  • CDK Aspects Under the Hood: These conformers use AWS CDK’s powerful Aspects system—a visitor pattern that traverses all constructs in a stack and applies transformations. This allowed us to inspect and modify any non-compliant resources during synthesis without requiring developers to learn complicated rules.
  • Seamless Governance: When developers added resources to a GDStack, these aspects would automatically transform the resources to align with our governance rules before deployment—all invisible to the developer.

This approach dramatically reduced turnaround time for developers, who previously had to manually correct violations in their application specific CloudFormation stacks after failed deployments. Instead, the system intelligently fixed issues before they became deployment failures.

2. CliCredentialsStackSynthesizer

Instead of using AWS CDK’s default deployment roles, we used the CliCredentialsStackSynthesizer to:

  • Use the developer’s CLI credentials directly for deployments
  • Eliminate the need for complex cross-account role assumptions
  • Respect our existing IAM permission boundaries
  • Simplify the authentication flow

Our solution required a custom bootstrap command:

# Custom bootstrap with governance-compliant template
npx cdk bootstrap --template node_modules/internal-constructs/bootstrap-template.yaml --tags governance=safeguard

This approach worked well, but still required teams to run a bootstrap step with precise GoDaddy-specific parameters. Although our platform documentation was extensive, some users still encountered issues as they continued to use the native cdk bootstrap command instead of the custom command. This behavior likely stemmed from the habit of running cdk bootstrap first, as trained by the native AWS CDK workflow. As a result, this approach still maintained some support troubleshooting workload for teams. We needed a more elegant solution for our needs!

Phase 2: The Revolutionary Bootstrapless Approach

As AWS CDK evolved, so did our thinking. The introduction of the AppStagingSynthesizer opened new possibilities, leading us to develop a completely bootstrapless solution.

The Factory Pattern Solution

We engineered an elegant chain of specialized components:

A flowchart diagram showing the relationship between different components in a software development stack. The chart flows from top to bottom, starting with "Developer Stack" which extends to "GDStack". GDStack uses "GDStackSynthesizerFactory" which returns "AppStagingSynthesizer with custom factory". This then calls "GDStagingStackFactory" which creates "GDStagingStack". Finally, GDStagingStack provisions "Governance-Compliant Resources". Each component is represented by a rectangle, with arrows and labels indicating the relationships between them.

Bootstrapless CDK Factory Pattern Design

Each component plays a crucial role:

1. GDStack: The Developer Interface

This is the only component developers interact with directly:

// Developer simply extends GDStack instead of Stack
export class MyApplicationStack extends GDStack {
  constructor(scope: Construct, id: string, props: GDStackProps) {
    super(scope, id, props);

    // Normal CDK resource definitions
    new s3.Bucket(this, 'MyBucket', { ... });
  }
}

2. GDStackSynthesizerFactory: The Orchestrator

This factory connects our custom components with CDK’s synthesis system:

export const GDStackSynthesizerFactory = () => {
    return AppStagingSynthesizer.customFactory({
        factory: new GDStagingStackFactory(),
        deploymentIdentities: DeploymentIdentities.cliCredentials(),
    });
};

3. GDStagingStackFactory: The Resource Producer Factory

This implements the IStagingResourcesFactory interface to dynamically create staging resources:

export class GDStagingStackFactory implements IStagingResourcesFactory {
    public obtainStagingResources(
        stack: cdk.Stack,
        context: ObtainStagingResourcesContext,
    ): IStagingResources {
        const app = cdk.App.of(stack)!;
        const appId = getAppIdFromContext(app.node);

        const stagingStack = new GDStagingStack(
            app!,
            `StagingStack-${appId}-${context.environmentString}`,
            {
                env: { region: stack.region, account: stack.account },
                appId: appId,
            },
        );

        return stagingStack;
    }
}

4. GDStagingStack: The Resource Producer for App-Level bootstrapping

This stack implements IStagingResources and creates rule-compliant assets on demand:

export class GDStagingStack extends cdk.Stack implements IStagingResources {
    constructor(scope: Construct, id: string, props: GDStagingStackProps) {
        super(scope, id, {
            ...props,
            // The magic ingredient - BootstraplessCliSynthesizer
            synthesizer: new BootstraplessCliSynthesizer(),
            description: `This stack includes resources needed to deploy the AWS CDK app ${props.appId} into this environment`,
        });

        // Apply governance conformers to everything
        this.applyGovernanceConformers();

        // Create compliant resources
        const bucket = new s3.Bucket(this, "CdkStagingBucket", {
            bucketName: `cdk-${this.appId}-staging-${this.account}-${this.region}`,
            // Conformers ensure encryption, logging, and other requirements
        });

        // Additional resource creation...
    }
}

The Secret Sauce: BootstraplessCliSynthesizer

The cornerstone of our solution is a custom synthesizer BootstraplessCliSynthesizer that combines the best aspects of AWS CDK’s built-in synthesizers BootstraplessSynthesizer and CliCredentialsStackSynthesizer.

It brings together key features from both AWS CDK synthesizers while adding our own innovations:

  • From CliCredentialsStackSynthesizer: Uses the CLI credentials directly for all operations
  • From BootstraplessSynthesizer: Eliminates the need for bootstrap resources
  • Our custom approach: Purpose-built specifically for the GDStagingStack with explicit asset rejection where GDStagingStack itself essentially creates the required asset resources on demand for the CDK Application.

This synthesizer:

  • Requires no bootstrapping in any region
  • Uses AWS CLI credentials directly for all operations
  • Maintains a minimal implementation focused solely on template generation
export class BootstraplessCliSynthesizer extends cdk.StackSynthesizer {
    constructor() {
        super();
    }

    // Prevent asset uploads to enforce governance compliance
    public addFileAsset(_asset: cdk.FileAssetSource): cdk.FileAssetLocation {
        throw new Error(
            "Cannot add assets to a Stack that uses the BootstraplessCliSynthesizer",
        );
    }

    public addDockerImageAsset(
        _asset: cdk.DockerImageAssetSource,
    ): cdk.DockerImageAssetLocation {
        throw new Error(
            "Cannot add assets to a Stack that uses the BootstraplessCliSynthesizer",
        );
    }

    // Minimal synthesis - just template generation and artifact emission
    public synthesize(session: cdk.ISynthesisSession): void {
        // Same as LegacySynthesizer
        this.synthesizeTemplate(session);
        this.emitArtifact(session);
    }
}

Our innovation was creating a synthesizer used only for the GDStagingStack that works in concert with our factory pattern. Rather than assuming pre-existing bootstrap resources, it enables the staging stack itself to create the required asset resources on demand, achieving enhanced bootstrapless deployments while maintaining governance compliance.

The Elegant Workflow: Dynamic Asset Management

Our solution transformed the developer experience through intelligent, on-demand resource provisioning:

Our Previous Custom Approach:

# Pre-provision compliant bootstrap resources
npx cdk bootstrap --template node_modules/internal-constructs/bootstrap-template.yaml --tags governance=safeguard

# Deploy applications
npx cdk deploy

Our New Enhanced Bootstrapless Approach:

# Deploy directly - compliant staging resources created automatically when needed
npx cdk deploy

The key advantage is our intelligent asset management:

  1. On-Demand Resource Creation: Staging resources (Amazon S3 buckets, Amazon ECR repositories) are created automatically when needed, rather than requiring pre-provisioning
  2. Governance Integration: All staging resources are created with full compliance built-in from the start
  3. Simplified Credential Flow: Uses existing CLI credentials without complex role assumption chains
  4. Multi-Account Scalability: Works seamlessly across any number of AWS accounts and regions

Behind the scenes, our architecture:

  • Creates governance-compliant staging resources dynamically as applications require them
  • Uses the developer’s existing CLI credentials for all operations
  • Applies security and compliance requirements transparently
  • Eliminates the need to manage bootstrap stacks across environments

Evolution of Approaches

Approach Bootstrap Required Security Model Asset Management GoDaddy Governance Developer Workflow
AWS CDK v2 Default Yes (one-time) 5 deployment roles Pre-provisioned bootstrap stack  Failed validation checks Standard setup + deploy
Custom Template + CliCredentialsStackSynthesizer Yes (one-time) CLI credentials Compliant bootstrap stack via custom template Passes all checks Setup + deploy
GDStagingStack + BootstraplessCliSynthesizer No CLI credentials Compliant staging resources created dynamically on-demand Passes all checks Deploy only

Business Impact: GoDaddy’s Transformation

The business value of our bootstrapless approach has been significant for GoDaddy’s infrastructure teams:

  • Streamlined developer focus: Our teams now focus entirely on writing infrastructure implementation logic, with AWS CDK bootstrapping fully abstracted and automated. Developers no longer need to work with bootstrap configurations, even though it was a one-time setup per environment previously.
  • Automated compliance: Deployments automatically meet GoDaddy’s governance requirements without developer intervention, addressing the validation failures we experienced with default bootstrap resources.
  • Simplified support model: Our platform support team handles fewer bootstrap-related configuration requests, allowing them to focus on broader platform improvements.
  • Broader CDK adoption: The streamlined workflow has encouraged more teams at GoDaddy to adopt AWS CDK from native CloudFormation YAML code for their infrastructure management.

This bootstrapless approach has worked well for GoDaddy’s specific governance requirements and development workflow preferences, demonstrating one way to integrate enterprise compliance seamlessly into AWS CDK deployments.

Conclusion: The Invisible Framework

The evolution from bootstrap – dependent to bootstrapless CDK deployments represents more than a technical improvement—it demonstrates a pathway to eliminate friction while strengthening organization specific governance. Our implementation at GoDaddy validates that enterprise compliance and developer productivity can be achieved simultaneously.

  1. Organizations seeking to implement similar solutions should begin by evaluating the AppStagingSynthesizer capabilities within their current AWS CDK deployment patterns. This assessment will reveal opportunities to reduce bootstrap dependencies while maintaining security and compliance standards. For comparison, teams can also examine the BootstraplessSynthesizer to understand alternative approaches to eliminating traditional bootstrap resources.
  2. The implementation approach we’ve outlined leverages established AWS CDK patterns, including the CliCredentialsStackSynthesizer for credential management and dynamic resource provisioning interfaces. These core AWS CDK interfaces — IStagingResourcesFactory and IStagingResources — form the foundation for creating governance-compliant, bootstrapless deployment workflows that scale across enterprise environments.

The future of infrastructure as code lies in systems that enforce governance invisibly while empowering developers to focus on business logic. As AWS CDK continues to evolve, the patterns we’ve demonstrated at GoDaddy provide a foundation for organizations to build their own invisible frameworks—where compliance becomes a catalyst for velocity rather than an obstacle to innovation.

The content and opinions in this blog are those of the third-party author and AWS is not responsible for the content or accuracy of this blog.

Ramanathan Nachiappan

is a Senior Software Engineer at GoDaddy, specializing in cloud infrastructure automation, governance frameworks, and AI-driven solutions. He designs and implements tools, platforms, and policies to enhance developer productivity while ensuring compliance with security standards. His current focus includes developing agentic workflows and AI-powered automation systems that streamline enterprise infrastructure operations.

AWS Cloud Development Kit (CDK) Launches Refactor

Post Syndicated from Natalie White original https://aws.amazon.com/blogs/devops/aws-cloud-development-kit-cdk-launches-refactor/

We are excited to announce a new AWS Cloud Development Kit (CDK) feature that makes it easier and safer to refactor your infrastructure as code. CDK Refactor aims to preserve your AWS resources as you rename constructs, move resources between stacks, and reorganize your CDK applications – operations that previously risked resource replacement.

When writing infrastructure as code with the CDK, developers occasionally need to rename Constructs or move them between Stacks or directories. Whether they need to better organize their code, adhere to coding best practices, or take advantage of object-oriented programming patterns like class inheritance, these changes can be risky in environments with deployed resources, because they change the CDK-generated logical ID of those resources. During a CDK deploy, AWS CloudFormation interprets these changes as new resources, which often requires deletion of the existing resource and creation of a new resource with the new logical ID. For stateful resources, this could cause potential downtime and even data loss. To mitigate this effect of ID changes, developers had to stage their changes to create new resources, create a data or network migration plan, and then delete the old resources to prevent these refactoring effects. Sometimes, developers decide the risk of these changes outweigh the benefit of the refactor and choose not perform the refactor at all.

Today, developers can use the new CDK refactor command to detect, review, confirm, and safely apply refactored changes to their resources without resource replacement. This feature leverages the recently-launched AWS CloudFormation refactor feature, but the CDK automatically computes the mappings that CloudFormation needs to redefine the refactored resources, providing a layer of abstraction that allows developers to focus on code rather than resource configuration. Let’s walk through an example to demonstrate the benefits of this refactor capability.

Prerequisites

Along with the usual CDK prerequisites, if you bootstrapped your CDK project before this launch, you need to re-bootstrap your environment to obtain the new permissions associated with the CDK refactor capabilities before attempting your refactor.

Monolith to micro-service example

For this example, let’s say that we have a legacy CDK App that deploys a monolithic Stack with Amazon DynamoDB tables for users, products, and orders, and an AWS Lambda function that implements CRUD operations on all entities.

Architecture diagram of a monolithic application with an API Gateway, single Lambda function, and three DynamoDB tables for users, products, and orders. The application is contained in a single CDK Stack within the CDK App.

Monolithic application

function monolithApp() {
  const monolith = new CdkAppStack(app, monolithStackName, {env});
  const usersTable = makeTable(monolith, 'users');
  const productsTable = makeTable(monolith, 'products');
  const ordersTable = makeTable(monolith, 'orders');

  // We have a single Lambda function in our application
  const func = new Function(monolith, `MonolithFunction`, {
    code: Code.fromInline(`Some code that accesses all three tables`),
    runtime: Runtime.NODEJS_22_X,
    handler: 'index.handler',
  });

  usersTable.grantReadWriteData(func);
  productsTable.grantReadWriteData(func);
  ordersTable.grantReadWriteData(func);

  // This function creates a REST API, resources, methods, and links
  // everything together to the functions. Right now, we are passing
  // the same function in three places.
  makeApi(monolith, {
    usersFunction: func,
    productsFunction: func,
    ordersFunction: func,
  });
}

monolithApp();

We’ve been asked to adhere to Well Architected Framework best practices and break up the monolith into separate Lambda functions so they can scale independently. Because they’re so similar, we’re also going to create an inheritable Lambda class that we can reuse to improve readability and maintainability of the code, and avoid having to re-define Lambda configuration settings that are consistent across all of the functions.

Finally, the monolith uses only L1 CDK Constructs. To further abstract our code and take advantage of helper functions, we’re going to start using L2 CDK Constructs for DynamoDB, Lambda, and API Gateway. This change will allow the IAM Roles and permissions to be defined automatically, further simplifying our code.

Architecture diagram of the same application refactored into four child stacks: one for the APIGateway and three for the user, product, and order domains. Each child Stack has its own respective Lambda function and DynamoDB table.

Proposed refactored application into separate stacks for each domain.

Without the refactor feature, CloudFormation would delete and re-create the Lambda and DynamoDB resources, which would cause all of the data in the latter to be lost. Alternatively, you could create net-new Lambdas and Amazon DynamoDB tables in one deployment, execute an out-of-band, point-in time and streaming data migration from the old tables to the new ones, update the API Gateway configuration to target the new Lambdas in a second deploy, and turn off the streaming migration process.

With the refactor feature, we can move the resource definitions to new files, update them to L2 Constructs, and leave the stateful resources in place!

Replace stateless resources
First, let’s refactor our CDK code to break the monolithic Lambda into 3 domain-specific Lambdas. CloudFormation’s refactor capability doesn’t support creating new resources or updating configuration of existing resources, so we will deploy these changes as usual, without using the new refactor feature. All resources will stay inside the monolithic stack for now.

Architecture diagram of the same application with the single Lambda function broken into separate functions corresponding to the three DynamoDB tables for users, products, and orders. All resources are still contained in the single API Stack and CDK App.

Refactor stateless single lambda function into 3 functions as a prerequisite to the refactor of stateful DynamoDB tables.

function singleStackMicroservicesApp() {
  // We still have a single stack
  const monolith = new CdkAppStack(app, monolithStackName, {env});

  // makeFunctionAndTable creates a different Lambda function and a DynamoDB table
  // for each domain that is passed as a parameter.
  // In a real CDK application, you would probably define each of them independently.
  makeApi(monolith, {
    usersFunction: makeFunctionAndTable(monolith, 'users'),
    productsFunction: makeFunctionAndTable(monolith, 'products'),
    ordersFunction: makeFunctionAndTable(monolith, 'orders'),
  });
}

singleStackMicroservicesApp();

Refactor stateful resources
Now we can refactor the stateful DynamoDB tables and their respective Lambdas to their own stacks, using cdk refactor to map their new IDs without replacing the resources.

Before refactoring, though, we need to create the new stacks that will receive the functions and tables:

  singleStackMicroservicesApp();

  const usersStack = new Stack(app, 'Users', {env});
  const productsStack = new Stack(app, 'Products', {env});
  const ordersStack = new Stack(app, 'Orders', {env});
Architecture diagram of the final refactored application with four child stacks: one for the APIGateway and three for the user, product, and order domains. Each child Stack has its own respective Lambda function and DynamoDB table.

Refactored Lambda functions and DynamoDB tables into their own separate stacks.

function fullMicroservicesApp() {
    const monolith = new Stack(app, monolithStackName, {env});

    const usersStack = new Stack(app, 'Users', {env});
    const productsStack = new Stack(app, 'Products', {env});
    const ordersStack = new Stack(app, 'Orders', {env});

    makeApi(monolith, {
        // Now each pair function + table is in its own stack
        usersFunction: makeFunctionAndTable(usersStack, 'users'),
        productsFunction: makeFunctionAndTable(productsStack, 'products'),
        ordersFunction: makeFunctionAndTable(ordersStack, 'orders'),
    });
}

fullMicroservicesApp();

Running cdk refactor –unstable=refactor starts the process. (The unstable flag is required as this feature is still subject to breaking changes.) The CDK will compare the current state of your application (the deployed monolithic app) with the new state (the output of your refactored CDK application).

Screenshot of the CDK CLI confirmation dialog while executing the refactor command. The output has three columns: Resource Type, Old Construct Path, and New Construct Path, with rows of resources that will be refactored from one ID to another. The confirmation prompt asks "Do you want to refactor these resources? (yes/no)"

CDK refactor confirmation dialog

As expected, it shows a table of resources that were moved from the Monolith stack to their respective refactored stacks. By default, the CLI asks for confirmation before proceeding. Bypass the confirmation by passing the –force flag, or confirm the changes and execute the refactor:
All resources, including the stateful tables, were safely moved to other stacks, and we now have our well-architected application.

Screenshot of the CDK CLI results after completing the refactor command. The output is similar to the confirmation with three columns: Resource Type, Old Construct Path, and New Construct Path, with rows of resources that were refactored from one ID to another.

CDK refactor results

Conclusion
With the CDK refactor feature, developers can take full advantage of the object-oriented definition of AWS resources, including the ability to change the structure and layers of abstraction without orchestrating complex migration mechanisms or scheduled downtime. Since the CDK is open source, you can learn more about how the CDK automatically determines what resources need to be refactored via the README. Understanding when resources need to be replaced and refactored will help you plan your infrastructure as code roadmap and when you should use this new refactoring capability.

If you’ve got a refactor that you’ve been waiting to execute, read more about the feature set in the CDK refactor documentation and start refactoring your own CDK App today!

Authors

Natalie White

Natalie White is a Principal Solutions Architect at Amazon Web Services. She helps Healthcare and Life Sciences customers deploy solutions to AWS, and uses her software development background to accelerate infrastructure as code automation using the AWS Cloud Development Kit (CDK). She also consults engineering leaders on DevOps cultural transformations.

Otavio Macedo

Otavio Macedo is a Software Development Engineer at Amazon Web Services. He has been with the AWS Cloud Development Kit (CDK) team since 2021, helping deliver the unique experience that CDK provides for customers.

Announcing the end of support for Node.js 18.x in AWS CDK

Post Syndicated from Charles Meruwoma original https://aws.amazon.com/blogs/devops/announcing-the-end-of-support-for-node-js-18-x-in-aws-cdk/

On November 30th, 2025, the AWS Cloud Development Kit (CDK) will no longer support Node.js 18.x, which reached end of life on April 30, 2025. This change applies to all AWS CDK components that depend on Node.js, including the AWS CDK CLI, the Construct Library, and broader CDK ecosystem projects such as JSII, Projen, and CDK8s.

We encourage you to upgrade to a Node.js Active Long Term Support (LTS) version, which is Node.js 22.x as of July 6, 2025. Given that Node.js 18.x is past end of life, we recommend migrating your CDK projects to newer Node.js LTS versions as soon as possible.

Why are we doing this?

Node.js 18.x reached its End of Life support on April 30, 2025, per the Node.js Release Schedule. This means the Node.js community no longer provides bug fixes or security updates for this version. By dropping support for end-of-life versions, we ensure that AWS CDK users benefit from the latest security patches, performance improvements, and modern Node.js capabilities. This approach aligns with AWS’s commitment to security best practices and our standard policy of supporting only actively maintained runtime versions.

What’s changing?

Starting December 1, 2025, AWS CDK will officially end support for Node.js 18.x. While your existing CDK deployments may continue to function, we will no longer address issues, provide bug fixes, or offer technical support for problems that occur specifically with Node.js 18.x. Any support cases or bug reports related to Node.js 18.x will require reproduction on a supported Node.js version (20.x or 22.x as of June 2025) before we can assist.

Key points

Moving forward, projects that remain on Node.js 18.x will gradually lose access to new AWS CDK capabilities as we develop features using modern Node.js APIs that are not available in older versions. This creates a growing compatibility gap that will make it increasingly challenging to leverage CDK innovations and improvements. The security implications are equally concerning, as any vulnerabilities discovered in the unsupported Node.js 18.x runtime will not receive patches or workarounds from our development team, potentially exposing your infrastructure to known security risks.

The challenges extend throughout the development lifecycle. Without regular compatibility testing against Node.js 18.x, we cannot ensure reliable CDK behavior, and you may encounter unexpected issues in production environments. When problems do arise, our support team will need to reproduce any reported issues on supported Node.js versions before providing assistance, which could delay resolution during critical incidents. Additionally, the broader CDK ecosystem, including third-party libraries and tools your projects depend on, will likely follow similar deprecation schedules, creating compounding compatibility challenges that become more difficult to resolve over time.

Timeline

We’re announcing this change in July 2025, to provide you with a five-month transition period before support officially ends on November 30th, 2025. During this transition window, our team will continue to address issues that arise with Node.js 18.x, giving you time to plan, test, and execute your upgrade strategy without immediate pressure. This period is designed to help you thoroughly validate your CDK projects against newer Node.js versions and ensure smooth deployments in your production environment.

Beginning December 1st, 2025, AWS CDK will officially discontinue support for Node.js 18.x across all components and ecosystem projects. From this point forward, all bug fixes, security patches, and new feature development will target only supported Node.js versions, currently 20.x and 22.x as of June 2025. We strongly recommend using this transition period to migrate to Node.js 22.x, the current Active Long Term Support version, which will provide the longest runway for future compatibility as the Node.js release cycle continues.

Version validation and update steps

Begin your migration by checking which Node.js version you’re currently running across all environments where you deploy CDK projects. Run `node -v` in your local development environment, CI/CD pipelines, and any automated deployment systems to get a complete picture of your current setup.

Once you’ve identified all instances of Node.js 18.x, update your runtime to a supported version using either a version manager like nvm or by downloading the official installer from nodejs.org. We recommend upgrading directly to Node.js 22.x since it’s the current Active Long Term Support version and will provide the longest compatibility runway. After updating your runtime, thoroughly test your CDK projects in non-production environments to ensure your deployment scripts and third-party dependencies work correctly with the new version. Pay particular attention to any custom constructs or complex deployment workflows that may be sensitive to changes in Node.js versions.

Finally, establish a process for staying current with future Node.js releases by bookmarking the AWS CDK Node.js Version Support Timeline, which provides up-to-date information on runtime compatibility and upcoming deprecations. This proactive approach will help you anticipate future changes and plan your upgrade strategies well in advance, avoiding the pressure of last-minute migrations when support windows close.

Conclusion

This deprecation is part of our ongoing commitment to provide a secure, high-quality experience for AWS CDK users. By migrating to a Node.js Active Long Term Support (LTS) version, you will benefit from enhanced performance, ongoing security updates, and continued AWS CDK advancements. If you have any questions or concerns about this deprecation, please reach out and open an issue in our GitHub repo.

Announcing the General Availability of the Amazon EventBridge Scheduler L2 Construct

Post Syndicated from Svenja Raether original https://aws.amazon.com/blogs/devops/announcing-the-general-availability-of-the-amazon-eventbridge-scheduler-l2-construct/

Today we’re announcing the general availability (GA) of the Amazon EventBridge Scheduler and Targets Level 2 (L2) constructs in the AWS Cloud Development Kit (AWS CDK) construct library. EventBridge Scheduler is a serverless scheduler that enables users to schedule tasks and events at scale. Prior to the launch of these L2 constructs, developers had to define all relevant properties (via L1 constructs) across schedules and provide the glue logic between resources when defining their AWS CDK applications. The graduated constructs make it easier for users to configure EventBridge schedules, groups, and targets for AWS service integrations. They follow the AWS CDK L2 higher-level API design simplifications and provide a backwards-compatible guarantee across minor versions. Developers can use those alongside other existing stable AWS CDK constructs ready for production use.

Background

The AWS Cloud Development Kit (CDK) is an open-source software development framework for defining cloud infrastructure in code and provisioning it through AWS CloudFormation. It contains pre-written modular and reusable cloud components known as constructs. Constructs are the basic building blocks representing one or more AWS CloudFormation resources and their configuration. They are available in different abstraction levels. L1 constructs are the lowest-level constructs which map directly to AWS CloudFormation resources without abstractions. L2 constructs are thoughtfully developed and provide a higher-level abstraction through an intuitive intent-based API. They leverage default property configurations, best practice security policies, and convenience methods that make it simpler and quicker to define and deploy resources.

Amazon EventBridge Scheduler is a serverless scheduler that allows users to create, run, and manage tasks from one central, managed service. With EventBridge Scheduler, users can create schedules using cron and rate expressions for recurring patterns, or configure one-time invocations. EventBridge supports templated and universal targets. Templated targets include common API operations across a group of core AWS services, such as publishing a message to an Amazon Simple Notification Service (Amazon SNS) topic or invoking an AWS Lambda function. Universal targets are customized triggers supporting more than 270 AWS services and over 6,000 API operations on a schedule. Users can use schedule groups to organize their schedules.

With the L2 constructs for Amazon EventBridge Scheduler and Targets, it becomes even simpler for users to configure and integrate those resources into their CDK applications. Let’s explore the benefits by looking at some examples.

Using the L2 EventBridge Scheduler construct

We introduce two use cases for the EventBridge Scheduler and Targets L2 constructs to demonstrate their usage within common scenarios. Each example is equipped with sample code, emphasizing the simplifications achieved by the L2 constructs.

Example 1 – One time reminder through Amazon SNS

In the first use case, users want to configure one-time notifications to receive reminders of their favorite conferences at a specific time, for example a user may want to set a reminder one month before the start of AWS re:Invent to be reminded of their participation.

The example below uses the EventBridge Scheduler construct with a templated Amazon SNS target. The target applies an on-time schedule configuration and is configured with an Amazon Simple Queue Service (Amazon SQS) dead-letter queue to capture and retry failed events. The schedule payload is encrypted using a customer-managed AWS Key Management Service (AWS KMS) key.

const snsTarget = new targets.SnsPublish(topic, {
   input: ScheduleTargetInput.fromObject({
     message: "Reminder: AWS re:Invent starts in one month.",
   }),
   deadLetterQueue: deadLetterQueue,
});
 
const schedule = new Schedule(this, "ReminderSchedule", {
  description:
     "This schedule publishes a one-time notification to an Amazon SNS topic.",
   schedule: ScheduleExpression.at(
     new Date(2025, 10, 1), // Nov 01, 2025
     cdk.TimeZone.AMERICA_LOS_ANGELES
   ),
   target: snsTarget,
   key: key,
});

From the code example, we can see that well-defined interfaces for ScheduleTargetInput, and ScheduleExpression make it easy to select matching configuration values.

The SnsPublish target and Schedule constructs seamlessly integrate with the existing L2 constructs for Amazon SNS, Amazon SQS, and Amazon KMS. They abstract away the gluing logic used to configure the target API operation, dead-letter queue, and encryption settings with correct references. Instead of manually crafting permissions, the construct generates an AWS Identity and Access Management (IAM) execution role with the minimum necessary permissions to interact with the templated target, as shown in the policy below.

{
 "Version": "2012-10-17",
 "Statement": [
 {
 "Action": "sns:Publish",
 "Resource": "arn:aws:sns:us-east-1:123456789012:<TOPIC_NAME>",
 "Effect": "Allow"
 },
 {
 "Action": "kms:Decrypt",
 "Resource": "arn:aws:kms:us-east-1:123456789012:key/<UUID>",
 "Effect": "Allow"
 },
 {
 "Action": "sqs:SendMessage",
 "Resource": "arn:aws:sqs:us-east-1:123456789012:<QUEUE_NAME>",
 "Effect": "Allow"
 }
 ]
 }

The construct sets default properties. For example, it applies default configurations for the retry policy if not explicitly stated. As shown in Figure 1, the above defined schedule has been defined with a 1-day maximum event retention time and 185 maximum retries.

Default configurations for the Retry Policy

Example 2 – Start / Stop EC2 instance during business hours

In the second scenario, a recurring cron schedule is used to automatically stop Amazon EC2 instances during the business hours of a specific time zone.

The example below uses the EventBridge Scheduler construct with a universal target to perform the Amazon EC2 stopInstance API operation. It creates a custom schedule group to organize the schedules by time zone and allows an Amazon Lambda function to read all schedules in it for administrative purposes.

const group = new ScheduleGroup(this, "ScheduleGroup", {
  scheduleGroupName: "Europe-London",
});
 
new Schedule(this, "Schedule", {
  schedule: ScheduleExpression.cron({
minute: "0",
hour: "23",
timeZone: cdk.TimeZone.EUROPE_LONDON,
  }),
  target: new targets.Universal({
service: "ec2",
action: "stopInstances",
input: ScheduleTargetInput.fromObject({
  InstanceIds: [ec2Instance.instanceId],
}),
  }),
  scheduleGroup: group,
});
 
group.grantReadSchedules(lambdaFunction);

Similar to the first example, the ScheduleExpression and ScheduleTargetInput help users to define the correct input types. The universal target is one of the options allowed by the scheduler-target constructs that allow users to perform SDK API operations on AWS services such as Amazon EC2.

The ScheduleGroup construct is used to create the group, which is used as a property on the Schedule construct. The group implements convenience methods that allow simplified permissions management. The example above grants read permissions for the schedule group to an Amazon Lambda function, which is applied to the resources without additional configuration.

Community Shout-Outs

The CDK team would like to give a huge shout-out to the awesome members of the community that contributed to this construct to help get it where it is today! Thank you to:

sakurai-ryo

Kenta Goto

Hiroki Yamazaki

Joshua Weber

Manuel

Jacco Kulman

Conclusion

In this post, we introduced the general availability of the AWS CDK L2 construct for Amazon EventBridge Scheduler and Targets. We showcased practical implementations of the new construct, leveraging two example use cases. For more details on the EventBridge Scheduler L2 construct and examples of its use, see the Scheduler CDK Documentation.

If you’re new to AWS CDK and want to get started, we highly recommend checking out the CDK documentation and the CDK workshop.

Announcing the end of support for Node.js 14.x and 16.x in AWS CDK

Post Syndicated from Adam Keller original https://aws.amazon.com/blogs/devops/announcing-the-end-of-support-for-node-js-14-x-and-16-x-in-aws-cdk/

On May 30th, 2025, the AWS Cloud Development Kit (CDK) will no longer support Node.js 14.x and 16.x, which reached end of life on 4/30/2023 (14.x) and 9/11/2023 (16.x). This change applies to all AWS CDK components that depend on Node.js, including the AWS CDK CLI, the Construct Library, and broader CDK ecosystem projects such as JSII, Projen, and CDK8s.

We encourage you to upgrade to a Node.js Active Long Term Support (LTS) version, which is Node.js 22.x as of March 11th, 2025. Given that Node.js 14.x and 16.x are past end of life, we recommend migrating your CDK projects to newer Node.js LTS versions as soon as possible.

Why are we doing this?

Node.js 14x and 16.x are past their End of Life and are no longer supported by the Node.js community. This means that there have not been any bug fixes or security updates to these versions. To make sure that we are providing up-to-date and secure libraries, we will drop support for these versions.

What’s changing?

After May 30th, 2025, the AWS CDK will no longer support Node.js 14.x and 16.x. While your existing deployments may continue to work, we will not address issues specific to these versions. Any bug reports or support cases that stem from using Node.js 14.x or 16.x will require reproducing the issue on a supported version of Node.js (18.x, 20.x, 22.x – as of February 26th, 2025) before further assistance can be provided.

Key points

  • New features for the AWS CDK may rely on APIs or functionalities only available in supported versions of Node.js.
  • Critical security patches and fixes related to Node.js 14.x or 16.x will not be backported.
  • Compatibility testing will no longer be performed for Node.js 16.x, making it difficult to guarantee the CDK’s behavior with that runtime.

Timeline

  1. March 11, 2025 through May 30, 2025
    1. We will continue to support that arise for Node.js 14.x and 16.x during this period.
    2. Use this time to plan and test upgrades for your CDK projects to a Node.js Active Long Term Support (LTS) version.
  2. May 30, 2025 and onwards
    1. The AWS CDK is officially dropping support for Node.js 14.x and 16.x.
    2. Any bug fixes or security patches will target only supported versions of Node.js (18.x, 20.x, 22.x – as of March 11th, 2025)

Version validation and update steps

  • Check your current Node.js version
    • Run node -v in your environment or CI/CD pipelines to see which version of Node.js you’re currently using.
  • Update your environment
    • Install or switch your runtime to Node.js using a supported version via a version manager (e.g., nvm) or by downloading an official installer from nodejs.org.
  • Validate your AWS CDK projects
    • Ensure your deployment scripts, and any third-party dependencies work correctly under a supported version. Test thoroughly in non-production environments.
  • Looking ahead
    • For more information on our deprecation strategy moving forward, please see this RFC, which provides more details.

Conclusion

This deprecation is part of our ongoing commitment to provide a secure, high-quality experience for AWS CDK users. By moving to a Node.js Active Long Term Support (LTS) version, you’ll benefit from improved performance, ongoing security patches, and continued AWS CDK innovations. If you have any questions or concerns about this deprecation, please reach out and open an issue in our GitHub repo.

Announcing CDK Garbage Collection

Post Syndicated from Kaizen Conroy original https://aws.amazon.com/blogs/devops/announcing-cdk-garbage-collection/

The AWS Cloud Development Kit (CDK) is an open source framework that enables developers to define cloud infrastructure using a familiar programming language. Additionally, CDK provides higher level abstractions (Constructs), which reduce the complexity required to define and integrate AWS services together when building on AWS. CDK also provides core functionality like CDK Assets, which gives users the ability to bundle application assets into their CDK applications. These assets can be local files (main.py), directories (python_app/), or Docker images (Dockerfile). CDK Assets are stored in an Amazon Simple Storage Service (Amazon S3) Bucket or Amazon Elastic Container Registry (Amazon ECR) Repository that is created during CDK bootstrapping.

For CDK developers that leverage assets at scale, they may notice over time that the bootstrapped bucket or repository accumulated old or unused data. If users wanted to clean this data on their own, CDK didn’t provide a clear way of determining which data is safe to delete. To solve this problem, we are excited to announce the preview launch of CDK Garbage Collection, a new feature of the CDK that automatically deletes old assets in your bootstrapped Amazon S3 Bucket and Amazon ECR Repository, saving users time and money. This feature is available starting in AWS CDK version 2.165.0.

We expect CDK Garbage Collection to help AWS CDK customers save on storage costs associated with using the product while not affecting how customers use CDK.

Quickstart

CDK Garbage Collection is exposed as a CDK CLI command named gc. To use CDK Garbage Collection in its default configuration, run the following command on a terminal in your CDK application.

cdk gc --unstable=gc

The --unstable flag is meant to acknowledge that CDK Garbage Collection is in preview mode. This indicates that the scope and API of the feature might still change, but otherwise the feature is generally production ready and fully supported.

Walkthrough

CDK Garbage Collection works at the environment level, so it will attempt to delete isolated assets in the AWS account / region that you call it in. For the purposes of this walkthrough, you will be re-bootstrapping the environment with a custom qualifier so that you do not delete isolated assets before you are ready.

cdk bootstrap --qualifier=abcdef --toolkit-stack-name=CDKToolkitDemo

You now have a new bootstrap template under the name CDKToolkitDemo and bootstrap resources associated with it. Next, set up a CDK application with both Amazon S3 and Amazon ECR assets:

mkdir garbage-collection-demo && cd garbage-collection-demo
cdk init -l typescript app

Your next step is to replace the existing code In lib/garbage-collection-demo-stack.ts with the following CDK Stack:

import * as path from 'path';
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';

export class GarbageCollectionDemoStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const fn1 = new lambda.Function(this, 'my-function-s3', {
    code: lambda.Code.fromAsset(path.join(__dirname, '..', 'lambda')),
    runtime: lambda.Runtime.NODEJS_LATEST,
    handler: 'index.handler',
    });

    const fn2 = new lambda.Function(this, 'my-function-ecr', {
    code: lambda.Code.fromAssetImage(path.join(__dirname, '..', 'docker')),
    runtime: lambda.Runtime.FROM_IMAGE,
    handler: lambda.Handler.FROM_IMAGE,
    });
  }
}

This creates two AWS Lambda functions, one which uses an Amazon S3 asset as its source code and one that uses an Amazon ECR image as its source code. You need to add the assets that are referenced to our CDK application. In lambda/index.js add a simple Lambda function:

exports.handler = async function(event) {
  const response = require('./response.json');
  return response;
};

And in docker/Dockerfile add a simple Docker image:

FROM public.ecr.aws/docker/library/alpine:latest Now you can run cdk deploy and get your initial CDK application set up in your AWS Account.
cdk deploy \
  --toolkit-stack-name=CDKToolkitDemo \
  --context='@aws-cdk/core:bootstrapQualifier=abcdef'

At this point you can check to make sure that assets have been correctly added into the bootstrapped Amazon S3 bucket and Amazon ECR repository:

cdk assets inside s3 bucket

Two objects exist in the bootstrapped Amazon S3 Bucket after the initial AWS CDK Deploy.

1 Image exists in the bootstrapped Amazon ECR Repository after the initial AWS CDK Deploy.

One image exists in the bootstrapped Amazon ECR Repository after the initial AWS CDK Deploy.

The output shows that you have the data you expect in both bootstrapped resources. The Amazon S3 Bucket also stores the json file of the AWS CloudFormation Template that was generated when you ran cdk deploy.

You can now simulate a typical CDK development cycle by updating both assets. Add a small change to the Amazon S3 asset that lives in lambda/index.js:

exports.handler = async function(event) {
  console.log('hello world');
  const response = require('./response.json');
  return response;
};

And do the same in docker/Dockerfile:

FROM public.ecr.aws/docker/library/alpine:latest
CMD echo 'Hello World'

You can now run cdk deploy again, and both assets should be re-uploaded under a new hash.

4 Objects exist in the bootstrapped Amazon S3 Bucket after the second AWS CDK Deploy.

Four objects exist in the bootstrapped Amazon S3 Bucket after the second AWS CDK Deploy.

2 Images exist in the bootstrapped Amazon ECR Repository after the second AWS CDK Deploy.

Two images exist in the bootstrapped Amazon ECR Repository after the second AWS CDK Deploy.

This output confirms that everything is as expected and the new assets have been added in. Because you are using new bootstrapped resources, you can still tell which resources are currently isolated and which are not. Right now, only the zipfile prefixed with 50f409b9 is referenced in AWS CloudFormation, and in Amazon ECR, only the image prefixed a5801b5b is referenced. That means that every other asset — 3 objects in Amazon S3 and 1 object in Amazon ECR — are isolated and can be deleted.

One item to note is the additional files in Amazon S3 that are not your local assets — these are AWS CloudFormation templates that are uploaded to Amazon S3 as an intermediary step before being sent to AWS CloudFormation. They are not needed after being copied over and are a perfect candidate for deletion via CDK Garbage Collection.

Here is where CDK Garbage Collection comes in. With the right parameters, you are able to clean up the isolated objects while not disturbing the assets that are actively in use.

cdk gc \
  --unstable=gc \
  --bootstrap-stack-name=CDKToolkitDemo \
  --rollback-buffer-days=0 \
  --created-buffer-days=0

Because you want to delete assets immediately, and not tag them for deletion later, set rollback-buffer-days to 0. You also want to delete assets that were just created, so be sure to set created-buffer-days to 0 as well. The default for created-buffer-days is 1.

 ⏳ Garbage Collecting environment aws://912331974472/us-east-1...
Found 3 objects to delete based off of the following criteria:
- objects have been isolated for > 0 days
- objects were created > 0 days ago

Delete this batch (yes/no/delete-all)? 

CDK Garbage Collection found three assets to be deleted from Amazon S3, which is to be expected. It prompts you to verify that you want to delete, which you do, so enter yes. You will then get this response:

[100.00%] 4 files scanned: 0 assets (0.00 MiB) tagged, 3 assets (0.02 MiB) deleted.

Followed by:

Found 1 image to delete based off of the following criteria:
- images have been isolated for > 0 days
- images were created > 0 days ago

Delete this batch (yes/no/delete-all)?

Once again, this is to be expected for Amazon ECR, so you enter yes again. You then get the response:

[100.00%] 2 files scanned: 0 assets (0.00 MiB) tagged, 1 assets (3.90 MiB) deleted.

At this point, CDK Garbage Collection is finished.

Details

CDK Garbage Collection exposes some parameters to help you customize the experience to your specific scenario. These options help you determine how aggressive you want your garbage collection to be.

  • rollback-buffer-days: this is the amount of days an asset has to be marked as isolated before it is eligible for deletion.
  • created-buffer-days: this is the amount of days an asset must live before it is eligible for deletion.

Rollback Buffer Days should be considered when you are not using cdk deploy and instead use a deployment method that operates on templates only, like a pipeline. If your pipeline can rollback without any involvement of the CDK CLI, this parameter will help ensure that assets are not prematurely deleted. When used, instead of deleting unused objects, cdk gc tags them with the current date. Subsequent runs of cdk gc will check this tag and delete the asset only after it has been tagged for longer than the specified buffer days.

Created Buffer Days should be considered if you want to be extra safe about assets that have been recently uploaded. When used, cdk gc filters out any assets that have not persisted that number of days. Note that this may not include assets that have been shared across multiple CDK Apps CDK reuses assets that are identical, and its possible that a recent deploy of a CDK App references an asset that was uploaded earlier.

For example, if you want to ensure that only assets that are over a month old and have been isolated for a week are deleted, you can specify:

cdk gc --unstable --rollback-buffer-days=7 --created-buffer-days=30.
Decision flow diagram of an asset as it gets audited for garbage collection.

Decision flow diagram of an asset as it gets audited for garbage collection.

Limitations of CDK Garbage Collection

During CDK Garbage Collection, we collect all stack templates to see what assets are in use. If garbage collection runs between the asset upload and stack deployment, there is a chance that it does not pick up the latest stack deployment, but it does pick up the latest asset. In this scenario, CDK Garbage Collection may delete those assets.

We recommend not deploying stacks while running CDK Garbage Collection. If that is unavoidable, setting --created-buffer-days will help as garbage collection will avoid deleting assets that are recently created. Finally, if you do experience a failed deployment, the mitigation is to redeploy, as the asset upload step will be able to re-upload the missing asset. In practice, this race condition is only for a specific edge case and unlikely to happen. However, we are working on a new method of storing CDK Assets to reduce the risk of this race condition. That work is being tracked in this issue.

Conclusion

CDK Garbage Collection helps users manage the lifecycle of unused CDK Assets in their AWS account. As users continue to scale with the CDK, tools like CDK Garbage Collection will play a crucial role in maintaining clean, efficient, and cost-effective cloud environments. We encourage CDK users to explore this feature, provide feedback, and incorporate it into their workflows to optimize their AWS resource management.