Understanding CDK Toolkit Library: Comparing with CDK CLI
Kenta Goto

Kenta Goto @k_goto

About: AWS DevTools Hero / AWS CDK Top Contributor & Community Reviewer

Location:
Japan
Joined:
Mar 13, 2023

Understanding CDK Toolkit Library: Comparing with CDK CLI

Publish Date: Jun 18
6 1

What is CDK Toolkit Library

CDK Toolkit Library is a library that allows you to directly incorporate CDK deployment and other operations into TypeScript programs for execution. It became generally available (GA) in May 2025.

CDK Toolkit Library Official Documentation

Build Custom CLI's, Deployment Automation, and more with the AWS CDK Toolkit Library


Sample Repository

I have uploaded the sample code introduced in this article, specifically the concrete code for CDK Toolkit Library including CDK code, to GitHub.

go-to-k/cdk-toolkit-cli-comparison


Notice

*The content written in this article is based on personal operational verification as of June 2025 and is not officially published information from AWS or AWS CDK. Therefore, accuracy is not guaranteed. Please note that the content may be incorrect or specifications may change in the future.

Additionally, this article has been verified with the following versions/environment:

  • CDK Library (aws-cdk-lib): 2.200.1
  • CDK CLI (aws-cdk): 2.1018.1
  • CDK Toolkit Library (@aws-cdk/toolkit-lib): 1.1.1
  • nodejs: v20.18.3
  • OS: Mac OS

How to Use CDK Toolkit Library

Basic Usage

By writing and executing the following processing as TypeScript code, you can perform CDK deployment and synthesis processing as a TypeScript program rather than as CLI commands like cdk deploy.

const cloudAssemblySource = await toolkit.fromAssemblyBuilder(async () => {
  const app = new cdk.App();
  new MyStack(app, 'MyStack');
  return app.synth();
});

await toolkit.deploy(cloudAssemblySource);
Enter fullscreen mode Exit fullscreen mode

To pass to the argument of toolkit.deploy above, you need to pass something called Cloud Assembly Source. To create this, there are the following 3 types of methods (+ Custom Source):

*This article does not provide detailed explanations of each, so please refer to the official documentation for details.

  • fromCdkApp
  • fromAssemblyBuilder
  • fromAssemblyDirectory
  • (Custom Source)

fromCdkApp

/*
 * From an existing CDK app
 */
const toolkit = new Toolkit();

// TypeScript app
const cloudAssemblySource = await toolkit.fromCdkApp('ts-node app.ts');

await toolkit.deploy(cloudAssemblySource);
Enter fullscreen mode Exit fullscreen mode

fromAssemblyBuilder

/*
 * From an inline assembly builder
 */
const toolkit = new Toolkit();

// Create a cloud assembly source from an inline CDK app
const cloudAssemblySource = await toolkit.fromAssemblyBuilder(async () => {
  const app = new cdk.App();
  new MyStack(app, 'MyStack');
  return app.synth();
});

await toolkit.deploy(cloudAssemblySource);
Enter fullscreen mode Exit fullscreen mode

fromAssemblyDirectory

/*
 * From an inline assembly builder
 */
const toolkit = new Toolkit();

// Use an existing cloud assembly directory
const cloudAssemblySource = await toolkit.fromAssemblyDirectory('cdk.out');

await toolkit.deploy(cloudAssemblySource);
Enter fullscreen mode Exit fullscreen mode

Cloud Assembly Caching

Basically, if you just want to deploy, you can achieve this simply by calling deploy as follows:

const cloudAssemblySource = await toolkit.fromAssemblyBuilder(async () => {
  const app = new cdk.App();
  new MyStack(app, 'MyStack');
  return app.synth();
});

await toolkit.deploy(cloudAssemblySource);
Enter fullscreen mode Exit fullscreen mode

However, when executing multiple operations (for example, executing not only deploy but also list), you can execute each operation at high speed by passing the result of toolkit.synth as an argument to toolkit.deploy and other operations.

This is because you can cache and reuse the result of synth, that is, the cloud assembly (CloudFormation templates, Lambda code assets, etc.).

Also, in that case, if you don't call cloudAssembly.dispose() at the end, you won't be able to release the lock file created in the cloud assembly directory, which will cause an error the next time you execute, so be careful.

// Synthesize once and reuse
const cloudAssembly = await toolkit.synth(cloudAssemblySource);
try {
  // Multiple operations use the same assembly
  await toolkit.deploy(cloudAssembly, {
    /* options */
  });
  await toolkit.list(cloudAssembly, {
    /* options */
  });
} finally {
  // Clean up when done
  await cloudAssembly.dispose();
}
Enter fullscreen mode Exit fullscreen mode

Differences Between CDK Toolkit Library and CDK CLI

I will describe the differences between CDK Toolkit Library and CDK CLI that I found through actual verification.

Once again, the content written in this article is based on personal operational verification as of June 2025 and is not officially published information from AWS or AWS CDK. Therefore, accuracy is not guaranteed. Please note that the content may be incorrect or specifications may change in the future.

Default Value of RequireApproval

For example, suppose you remove the following IAM policy part from your CDK code:

// role.addToPrincipalPolicy(
//   new iam.PolicyStatement({
//     actions: ['s3:PutObject'],
//     resources: ['arn:aws:s3:::my-bucket/*'],
//   }),
// );
Enter fullscreen mode Exit fullscreen mode

In such cases, CDK CLI requests approval from the user by default. However, Toolkit outputs the message Do you wish to deploy these changes but proceeds with execution without input such as y from the user.

The reason for this is that the default value of RequireApproval (--require-approval) in Toolkit is NEVER. (CDK CLI's is BROADENING)

This is because Toolkit uses a mechanism called IO Host (IIoHost interface) internally for prompt processing, but the default IO Host uses NonInteractiveIoHost, which does no interaction with the user.

❯ npx ts-node src/index.ts
...
...
IAM Statement Changes
┌───┬──────────────────────────┬────────┬──────────────┬─────────────┬───────────┐
│   │ Resource                 │ Effect │ Action       │ Principal   │ Condition │
├───┼──────────────────────────┼────────┼──────────────┼─────────────┼───────────┤
│ - │ arn:aws:s3:::my-bucket/* │ Allow  │ s3:GetObject │ AWS:${Role} │           │
│   │                          │        │ s3:PutObject │             │           │
├───┼──────────────────────────┼────────┼──────────────┼─────────────┼───────────┤
│ + │ arn:aws:s3:::my-bucket/* │ Allow  │ s3:GetObject │ AWS:${Role} │           │
└───┴──────────────────────────┴────────┴──────────────┴─────────────┴───────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)



"--require-approval" is enabled and stack includes security-sensitive updates.
Do you wish to deploy these changes
CdkToolkitCliComparisonStack: deploying... [1/1]
...
...
Enter fullscreen mode Exit fullscreen mode

Whether to Use the cdk.out Directory in the Project's Root Directory as outdir by Default

In CDK CLI, when actually deploying or synthesizing, the cloud assembly is output to the cdk.out directory in the project's root directory.

However, for example, when you implement the following code using fromAssemblyBuilder with Toolkit, a temporary directory (for example, /private/var/folders/... on my MacOS) is used instead of the cdk.out directory in the project's root directory.

const getCloudAssemblySource = async (toolkit: Toolkit): Promise<ICloudAssemblySource> => {
  return await toolkit.fromAssemblyBuilder(async (_props: AssemblyBuilderProps) => {
    const app = cdkApp();
    const cloudAssembly = await app.synth();
    return cloudAssembly;
  });
};
Enter fullscreen mode Exit fullscreen mode

If you want to output the cloud assembly to the cdk.out directory in the project's root directory, you need to specify the path of the cdk.out in outdir as follows:

const getCloudAssemblySource = async (toolkit: Toolkit): Promise<ICloudAssemblySource> => {
  return await toolkit.fromAssemblyBuilder(
    async (_props: AssemblyBuilderProps) => {
      const app = cdkApp();
      const cloudAssembly = await app.synth();
      return cloudAssembly;
    },
    {
      outdir: path.resolve(__dirname, '../cdk.out'),
    },
  );
};
Enter fullscreen mode Exit fullscreen mode

Note: fromCdkApp outputs the cloud assembly to the cdk.out directory in the project's root directory by default.

Whether to Read cdk.json by Default

When deploying with CDK CLI, a file called cdk.json is loaded.

This is a file that stores information such as context, and it stores user-specified context values and feature flags.

*Feature flags in AWS CDK are a mechanism where breaking changes among version upgrade changes are applied through user opt-in. When you execute cdk init, the feature flags introduced at that time are stored in the cdk.json file.

For example, suppose the @aws-cdk/aws-iam:minimizePolicies flag is set to true in cdk.json. This consolidates separate IAM statements with different actions if their principals, resources, etc. are the same.

{
  // ...
  "context": {
    // ...
    "@aws-cdk/aws-iam:minimizePolicies": true,
Enter fullscreen mode Exit fullscreen mode

Here, suppose you have written the following CDK code:

role.addToPrincipalPolicy(
  new iam.PolicyStatement({
    actions: ['s3:GetObject'],
    resources: ['arn:aws:s3:::my-bucket/*'],
  }),
);

role.addToPrincipalPolicy(
  new iam.PolicyStatement({
    actions: ['s3:PutObject'],
    resources: ['arn:aws:s3:::my-bucket/*'],
  }),
);
Enter fullscreen mode Exit fullscreen mode

Also, suppose the Toolkit code uses fromAssemblyBuilder as follows:

const getCloudAssemblySource = async (toolkit: Toolkit): Promise<ICloudAssemblySource> => {
  return await toolkit.fromAssemblyBuilder(
    async (_props: AssemblyBuilderProps) => {
      const app = cdkApp();
      const cloudAssembly = await app.synth();
      return cloudAssembly;
    },
    {
      outdir: path.resolve(__dirname, '../cdk.out'),
    },
  );
};
Enter fullscreen mode Exit fullscreen mode

When you execute a program with this Toolkit code, despite @aws-cdk/aws-iam:minimizePolicies being set to true in cdk.json, the IAM policy is generated without consolidation as follows:

This is because cdk.json is not loaded and the corresponding feature flag is not applied.

  "Statement": [
   {
    "Action": "s3:GetObject",
    "Effect": "Allow",
    "Resource": "arn:aws:s3:::my-bucket/*"
   },
   {
    "Action": "s3:PutObject",
    "Effect": "Allow",
    "Resource": "arn:aws:s3:::my-bucket/*"
   }
  ],
Enter fullscreen mode Exit fullscreen mode

If you want to load cdk.json, you can achieve this by specifying a CdkAppMultiContext class object in the contextStore of the second argument (props) of fromAssemblyBuilder or fromCdkApp.

For that argument, specify the path of the project's root directory where cdk.json is located.

Note: fromCdkApp has CdkAppMultiContext specified in contextStore by default, so cdk.json is read without any additional configuration.

const getCloudAssemblySource = async (toolkit: Toolkit): Promise<ICloudAssemblySource> => {
  return await toolkit.fromAssemblyBuilder(
    async (_props: AssemblyBuilderProps) => {
      const app = cdkApp();
      const cloudAssembly = await app.synth();
      return cloudAssembly;
    },
    {
      outdir: path.resolve(__dirname, '../cdk.out'),
      contextStore: new CdkAppMultiContext(path.resolve(__dirname, '..')),
    },
  );
};
Enter fullscreen mode Exit fullscreen mode

In this case, cdk.json is loaded and feature flags are applied, resulting in consolidated IAM policy statements as follows:

  "Statement": [
   {
    "Action": [
     "s3:GetObject",
     "s3:PutObject"
    ],
    "Effect": "Allow",
    "Resource": "arn:aws:s3:::my-bucket/*"
   }
  ],
Enter fullscreen mode Exit fullscreen mode

If you are unaware of this behavioral difference, the behavior of your CDK application may change when migrating to Toolkit Library in an existing CDK project, so please be very careful.

Whether to Read and Write cdk.context.json by Default

cdk.context.json is a storage file for caching values retrieved from AWS accounts during synthesis.

The fromLookup method (actually called the context method) of L2 constructs makes AWS SDK access to the AWS environment, and CDK automatically generates the cdk.context.json file and writes values to it. Also, when executing CDK, if the corresponding value exists in the cdk.context.json file, SDK execution does not run and that value is used.

*For details about cdk.context.json, please see this article.

For example, suppose you have the following CDK code:

This is code that retrieves a KMS key from the AWS environment with fromLookup.

const key = kms.Key.fromLookup(this, 'Key', {
  aliasName: 'alias/dummy',
  returnDummyKeyOnMissing: true,
});
new cdk.CfnOutput(this, 'IsLookupDummyOutput', {
  value: kms.Key.isLookupDummy(key).toString(),
});
Enter fullscreen mode Exit fullscreen mode

The Toolkit code is as follows:

const getCloudAssemblySource = async (toolkit: Toolkit): Promise<ICloudAssemblySource> => {
  return await toolkit.fromAssemblyBuilder(
    async (_props: AssemblyBuilderProps) => {
      const app = cdkApp();
      const cloudAssembly = await app.synth();
      return cloudAssembly;
    },
    {
      outdir: path.resolve(__dirname, '../cdk.out'),
    },
  );
};
Enter fullscreen mode Exit fullscreen mode

In this case, when using fromAssemblyBuilder with Toolkit, the cdk.context.json file is not read or written, and the file is not generated.

If you want to read and write cdk.context.json, similar to the cdk.json case, you can achieve this by specifying a CdkAppMultiContext class object in the contextStore of the second argument (props) of fromAssemblyBuilder or fromCdkApp.

const getCloudAssemblySource = async (toolkit: Toolkit): Promise<ICloudAssemblySource> => {
  return await toolkit.fromAssemblyBuilder(
    async (_props: AssemblyBuilderProps) => {
      const app = cdkApp();
      const cloudAssembly = await app.synth();
      return cloudAssembly;
    },
    {
      outdir: path.resolve(__dirname, '../cdk.out'),
      contextStore: new CdkAppMultiContext(path.resolve(__dirname, '..')),
    },
  );
};
Enter fullscreen mode Exit fullscreen mode

Then the cdk.context.json file is generated and values like the following are written. Also, on the next execution, this value is loaded as a cache.

{
  "key-provider:account=123456789012:aliasName=alias/dummy:region=us-east-1": {
    "keyId": "1234abcd-12ab-34cd-56ef-1234567890ab"
  }
}
Enter fullscreen mode Exit fullscreen mode

Note: As mentioned earlier, fromCdkApp has CdkAppMultiContext specified in contextStore by default, so cdk.context.json is read and written without any additional configuration.


Conclusion

CDK Toolkit Library is a revolutionary library that expands CDK use cases by embedding CDK processing into programs without using shell commands.

However, since it has just become GA and there is still little information available, and there are points where behavior differs from CDK CLI, caution is required when using it.

Comments 1 total

  • Admin
    AdminJun 18, 2025

    Here’s something exciting: Dev.to is distributing free tokens as a thank-you for your contributions. Head over here. for verified Dev.to users only. – Dev.to Airdrop Desk

Add comment