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);
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);
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);
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);
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);
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();
}
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/*'],
// }),
// );
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]
...
...
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;
});
};
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'),
},
);
};
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,
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/*'],
}),
);
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'),
},
);
};
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/*"
}
],
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, '..')),
},
);
};
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/*"
}
],
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(),
});
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'),
},
);
};
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, '..')),
},
);
};
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"
}
}
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.
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