Skip to main content

Deploy AWS CDK and CDK Terrain with Hooks

Use a synthesis hook to turn application code into infrastructure configuration before Atmos runs CloudFormation or Terraform. AWS CDK produces a CloudFormation template; CDK Terrain produces Terraform JSON. Keep the application as source, pass deployment inputs through Atmos stacks, and review the resulting plan before applying it.

This tutorial covers two tools for defining infrastructure in application code:

  • AWS CDK (cdk) generates CloudFormation templates with cdk synth. Its project configuration lives in cdk.json.
  • CDK Terrain (cdktn), the community fork of CDK for Terraform (CDKTF), generates .tf.json files with cdktn synth. These use Terraform's JSON configuration syntax and are deployed directly by Terraform or OpenTofu. Its project configuration file is still named cdktf.json in the version used here.

Both examples use TypeScript applications and Atmos hooks to run synthesis before deployment.

Choose a deployment track below. With CloudFormation deploys AWS CDK templates directly through Atmos. With Terraform covers two paths: AWS CDK templates deployed through a Terraform CloudFormation resource, and CDK Terrain configuration deployed directly by Terraform or OpenTofu. Terrain does not use CloudFormation. Choose one owner for each deployed stack. These examples use different CloudFormation stack names if you try both tracks independently.

Native CloudFormation Track​

Atmos can deploy CloudFormation templates whether you write them by hand, generate them with AWS CDK, or produce them with another tool. A lifecycle hook can generate the template before Atmos reads it, previews the changes, and submits the deployment to CloudFormation.

Runnable example

The complete native track is available as the cdk-cloudformation example. It runs against a local AWS emulator, so no AWS account is needed to try it.

atmos aws cloudformation lifecycle with AWS CDK
 
00:00.0 / 00:00.0

For this demo, we'll use a TypeScript AWS CDK app that creates an SSM parameter. You'll need Atmos, Node.js 22 or a compatible later runtime, npm, and AWS credentials with permission to manage CloudFormation stacks and SSM parameters. Deployment state stays in CloudFormation.

Native Layout and Configuration​

.
├── atmos.yaml
├── stacks/deploy/dev.yaml
└── components/cloudformation/greeting/
├── package.json
├── cdk.json
└── main.ts
atmos.yaml
base_path: .
components:
aws/cloudformation:
base_path: components/cloudformation
stacks:
base_path: stacks
included_paths:
- deploy/**/*

Create the AWS CDK Application​

Create these files in components/cloudformation/greeting/. The versions below are an example tested combination. Generate package-lock.json once with npm --prefix components/cloudformation/greeting install --package-lock-only, then commit it; the hook's install step runs npm ci against it.

components/cloudformation/greeting/package.json
{
"private": true,
"scripts": {
"synth": "cdk synth Demo --exclusively --quiet --output cdk.out"
},
"devDependencies": {
"aws-cdk": "2.1146.0",
"aws-cdk-lib": "2.274.0",
"constructs": "10.7.2",
"tsx": "4.20.6"
}
}
components/cloudformation/greeting/cdk.json
{
"app": "npx --no-install tsx main.ts"
}
components/cloudformation/greeting/main.ts
import {
App, BootstraplessSynthesizer, CfnOutput, CfnParameter, CfnResource, Stack,
} from 'aws-cdk-lib';

const app = new App();
const stack = new Stack(app, 'Demo', {
synthesizer: new BootstraplessSynthesizer(),
});
const message = new CfnParameter(stack, 'Message', { type: 'String' });
const marker = new CfnResource(stack, 'Marker', {
type: 'AWS::SSM::Parameter',
properties: { Type: 'String', Value: message.valueAsString },
});
new CfnOutput(stack, 'ParameterName', { value: marker.ref });
app.synth();

The application has no file/Docker assets or AWS lookups. BootstraplessSynthesizer avoids introducing a bootstrap dependency for this small example. Message is a CloudFormation parameter, so changing its value does not require changing the TypeScript application.

The synth command writes a JSON file; avoid capturing stdout, which can also contain CLI messages. cdk synth selects the CDK construct ID Demo, while stack_name in the stack configuration selects the deployed CloudFormation name. For more complex apps, inspect cdk.out/manifest.json to find the actual template artifact name.

Attach the Synthesis Hook and Deploy​

Add this stack manifest. The name greeting is arbitrary: choose a component name that fits your project and use it in the Atmos commands below. By default it also selects the component directory; set metadata.component to use a differently named directory. It is separate from the CDK construct ID Demo and the deployed CloudFormation stack_name.

stacks/deploy/dev.yaml
name: dev
components:
aws/cloudformation:
greeting:
path: cdk.out/Demo.template.json
stack_name: atmos-cdk-native-dev
parameters:
Message: Hello from AWS CDK and Atmos
region: us-east-1
hooks:
synth:
events:
- before.aws/cloudformation.diff
- before.aws/cloudformation.apply
- before.aws/cloudformation.changeset-create
kind: steps
on_failure: fail
with:
- name: install
type: shell
command: npm ci --no-audit --no-fund
inputs:
sources: [package-lock.json]
artifacts:
paths: [node_modules/.bin/cdk]
- name: synth
type: shell
command: npm run synth

This configuration points path at cdk.out/Demo.template.json and runs synthesis before diff, apply, and changeset create. Atmos authenticates and provisions the component source first, runs the hook in the component directory, then reads the generated template. A failed hook stops the operation before reading or submitting an old template. This works when cdk.out does not yet exist.

This lifecycle requires an Atmos version containing the CloudFormation hook-ordering fix introduced with this tutorial. On older versions, synthesize explicitly before invoking Atmos; a before hook alone cannot refresh the template already loaded by that executor.

Validate the stack and deploy from the repository root; the hook installs dependencies on first use:

atmos validate stacks
atmos aws cloudformation diff greeting -s dev
atmos aws cloudformation apply greeting -s dev
atmos aws cloudformation output greeting -s dev
Shorten CloudFormation commands with an alias

Use atmos config set to add a command alias:

atmos config set aliases.cft "aws cloudformation"
atmos cft diff greeting -s dev
atmos cft apply greeting -s dev

You can then use atmos cft for the CloudFormation commands throughout this track.

For a separate review and execution step, create a named change set, inspect the changes, and execute the returned change-set name:

atmos aws cloudformation changeset create greeting -s dev
atmos aws cloudformation changeset execute greeting -s dev --changeset-name <returned-name>

The changeset execute command uses the template stored in the reviewed change set. It does not synthesize again. The render and validate commands do not fire synthesis hooks; run npm --prefix components/cloudformation/greeting run synth (after an initial npm ci) before those commands.

The diff previews CloudFormation resource changes. Change parameters.Message or edit the CDK app and run diff again. To remove this example, run:

atmos aws cloudformation delete greeting -s dev

Reproducible Native Deployments​

The hook runs in the component directory. on_failure: fail stops deployment if synthesis fails. Try an edit to the app, then a syntax error: the next diff should use the changed template, and the invalid app must fail rather than reuse the prior artifact.

Commit package-lock.json, use npm ci in CI, and pin Atmos and Node.js. For separate review and execution, use the stored change set described above; executing it does not synthesize again. Keep node_modules/ and cdk.out/ out of version control. Deleting generated files does not delete deployed resources.

Assets, Multiple Stacks, and Existing Deployments​

AWS CDK Assets and Bootstrap Resources​

Synthesis alone does not publish Lambda bundles, Docker images, or nested-stack assets. CDK asset deployment also needs its S3/ECR destinations, bootstrap resources, and appropriate permissions. This example's bootstrapless synthesizer intentionally supports an app without those assets.

For an asset-bearing app, retain its appropriate synthesizer and bootstrap setup. Add an ordered asset publication step using the generated asset manifest before submitting the CloudFormation template. Verify the published object keys/image tags match the template. Neither deployment path reads CDK's deployment-role or asset-publication instructions from manifest.json. Configure the CloudFormation execution role and IAM capabilities when the template needs them. For native CloudFormation, configure artifact packaging for templates above the inline limit. For the Terraform wrapper, publish the template and use template_url; see aws_cloudformation_stack.

Multiple Stacks and Environments​

Give each independently deployed stack its own Atmos component and state identity. Use separate working directories for concurrent runs so two synthesis hooks cannot overwrite the same artifact. Avoid selecting default workspace in a shared directory for multiple environments. Declare producer/consumer relationships through component dependencies. Atmos does not automatically import CDK or Terrain assembly dependency graphs.

Adopting Existing Deployments​

For Terrain/CDKTF, preserve construct paths, generated resource addresses, provider versions, backend configuration, and the selected workspace. Keep the application's backend authoritative with auto_generate_backend_file: false; do not add an Atmos-generated second backend. Moving local execution directories requires explicitly preserving the old state path. Review a no-change plan before moving execution to Atmos.

For an existing AWS CDK deployment, the native path targets its existing CloudFormation stack name and region without introducing Terraform state. Preserve parameters, roles, capabilities, and tags, then review a no-change diff before cutover.

Using the Terraform wrapper requires importing the existing CloudFormation stack into aws_cloudformation_stack.demo. Preserve the deployed stack name, parameters, roles, capabilities, and tags. Do not also keep cdk deploy as an independent writer to the same stack.

Verification boundary

Synthesis, hook execution, and the local Terrain lifecycle can be verified without AWS access. The AWS deployment, bootstrap/assets, and remote-backend scenarios require validation in your own account. This tutorial does not claim an AWS deployment was performed.

Try it out​

Copy the example into a new directory and make it your own:

atmos init examples/cdk-cloudformation
cd cdk-cloudformation

Install Node.js 22 or later and Docker or Podman, then deploy the AWS CDK app through the native CloudFormation component against a local AWS emulator. The hook installs dependencies and synthesizes the template.

atmos emulator up aws -s local
atmos aws cloudformation deploy greeting -s local
atmos aws cloudformation output greeting -s local
atmos aws cloudformation delete greeting -s local --auto-approve
atmos emulator down aws -s local