Skip to content

(cli): the CLI still validates the bootstrap stack's version even when the asset producer says "don't" #21324

@huyphan

Description

@huyphan

Describe the bug

We have a setup that we don't rely on a bootstrap stack at all (i.e. we don't use CDKToolkit or any custom toolkit stack). We specifically omit the property requiresBootstrapStackVersion in the asset manifest. But when running cdk deploy <stack-name>, we saw that CDK still tries to validate the bootstrap stack and subsequently fails the deployment.

This behaviour is caused by the AssetManifestArtifact class where requiresBootstrapStackVersion is a required property of type number. Even if the custom synthesizer returns undefined (or null) for this property, AssetManifestArtifact will override it to 1. This causes the bootstrap stack version validation to happen in other operations.

This is only an issue with asset manifest artifact, but not with cloudformation artifact.

Expected Behavior

cdk deploy <stackname> should work without the need to validate the bootstrap stack.

Current Behavior

cdk deploy <stackname> command fails with the following error:

 ❌  MyStack-alpha failed: Error: MyStack-alpha: This deployment requires a bootstrap stack with a known name; pass '--toolkit-stack-name' or switch to using the 'DefaultStackSynthesizer' (see https://docs.aws.amazon.com/cdk/latest/guide/bootstrapping.html)
    at CloudFormationDeployments.validateBootstrapStackVersion (/some/workspace/node_modules/aws-cdk/lib/index.js:887147:17)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at CloudFormationDeployments.publishStackAssets (/some/workspace/node_modules/aws-cdk/lib/api/cloudformation-deployments.ts:466:3)
    at CloudFormationDeployments.deployStack (/some/workspace/node_modules/aws-cdk/lib/api/cloudformation-deployments.ts:349:5)
    at CdkToolkit.deploy (/some/workspace/node_modules/aws-cdk/lib/cdk-toolkit.ts:218:11)
    at initCommandLine (/some/workspace/node_modules/aws-cdk/lib/cli.ts:351:10)

MyStack-alpha: This deployment requires a bootstrap stack with a known name; pass '--toolkit-stack-name' or switch to using the 'DefaultStackSynthesizer' (see https://docs.aws.amazon.com/cdk/latest/guide/bootstrapping.html)

Reproduction Steps

We have a custom synthesizer that's rather large to copy-paste here, but here's an example of the asset manifest that our synthesizer produces:

# file: cdk.out/manifest.json

...
    "<stack-name>.assets": {
      "type": "cdk:asset-manifest",
      "properties": {
        "file": "<stack-name>.assets.json"
      }
    },
...

# file: cdk.out/<stack-name>.asset.json

{
  "version": "17.0.0",
  "files": {
    "20857a268eb30cdd4dce71c5558c8713b5697fe09b8593290d03b2836a8ae7af/artifact": {
      "source": {
        "executable": [
          "some", "command"
        ]
      },
      "destinations": {
        "development-foobar-us-east-1": {
          "bucketName": "some-bucket-name",
          "objectKey": "some-unique-hash/artifact",
          "region": "us-east-1"
        }
      }
    }
  },
  "dockerImages": {}
}

Notice that the manifests completely omit the requiresBootstrapStackVersion and bootstrapStackVersionSsmParameter properties.

Possible Solution

There are many ways to address this, two options that I can think of:

Option 1: Updating the constructor of AssetManifestArtifact to allow requireBootstrapStackVersion to be undefined:

This change makes the behaviour of AssetManifestArtifact consistent with CloudFormationStackArtifact:

❯ git diff packages
diff --git a/packages/@aws-cdk/cx-api/lib/artifacts/asset-manifest-artifact.ts b/packages/@aws-cdk/cx-api/lib/artifacts/asset-manifest-artifact.ts
index 10ddea69b..67f12efc0 100644
--- a/packages/@aws-cdk/cx-api/lib/artifacts/asset-manifest-artifact.ts
+++ b/packages/@aws-cdk/cx-api/lib/artifacts/asset-manifest-artifact.ts
@@ -38,7 +38,7 @@ export class AssetManifestArtifact extends CloudArtifact {
   /**
    * Version of bootstrap stack required to deploy this stack
    */
-  public readonly requiresBootstrapStackVersion: number;
+  public readonly requiresBootstrapStackVersion?: number;

   /**
    * Name of SSM parameter with bootstrap stack version
@@ -55,7 +55,7 @@ export class AssetManifestArtifact extends CloudArtifact {
       throw new Error('Invalid AssetManifestArtifact. Missing "file" property');
     }
     this.file = path.resolve(this.assembly.directory, properties.file);
-    this.requiresBootstrapStackVersion = properties.requiresBootstrapStackVersion ?? 1;
+    this.requiresBootstrapStackVersion = properties.requiresBootstrapStackVersion;
     this.bootstrapStackVersionSsmParameter = properties.bootstrapStackVersionSsmParameter;
   }
 }

This change is technically backward incompatible. It will break those who write their own synthesizers, omit the requiresBootstrapStackVersion property but still require a bootstrap stack behind the scene. If that use case exists, it's probably a wrong usage in the first place.

Option 2: Extending requireBootstrapStackVersion field to accept boolean values and relax the check on that field when deploying:

❯ git diff packages
diff --git a/packages/@aws-cdk/cx-api/lib/artifacts/asset-manifest-artifact.ts b/packages/@aws-cdk/cx-api/lib/artifacts/asset-manifest-artifact.ts
index 10ddea69b..ee35fcba9 100644
--- a/packages/@aws-cdk/cx-api/lib/artifacts/asset-manifest-artifact.ts
+++ b/packages/@aws-cdk/cx-api/lib/artifacts/asset-manifest-artifact.ts
@@ -38,7 +38,7 @@ export class AssetManifestArtifact extends CloudArtifact {
   /**
    * Version of bootstrap stack required to deploy this stack
    */
-  public readonly requiresBootstrapStackVersion: number;
+  public readonly requiresBootstrapStackVersion: number | boolean;

   /**
    * Name of SSM parameter with bootstrap stack version
diff --git a/packages/aws-cdk/lib/api/cloudformation-deployments.ts b/packages/aws-cdk/lib/api/cloudformation-deployments.ts
index ce81ab8d7..d02f38684 100644
--- a/packages/aws-cdk/lib/api/cloudformation-deployments.ts
+++ b/packages/aws-cdk/lib/api/cloudformation-deployments.ts
@@ -475,11 +475,11 @@ export class CloudFormationDeployments {
    */
   private async validateBootstrapStackVersion(
     stackName: string,
-    requiresBootstrapStackVersion: number | undefined,
+    requiresBootstrapStackVersion: number | boolean | undefined,
     bootstrapStackVersionSsmParameter: string | undefined,
     toolkitInfo: ToolkitInfo) {

-    if (requiresBootstrapStackVersion === undefined) { return; }
+    if (requiresBootstrapStackVersion === undefined || requiresBootstrapStackVersion === false) { return; }

     try {
       await toolkitInfo.validateVersion(requiresBootstrapStackVersion, bootstrapStackVersionSsmParameter);

Option 3: Using 0 as a special value of requireBootstrapStackVersion :

❯ git diff packages
diff --git a/packages/aws-cdk/lib/api/cloudformation-deployments.ts b/packages/aws-cdk/lib/api/cloudformation-deployments.ts
index ce81ab8d7..d02f38684 100644
--- a/packages/aws-cdk/lib/api/cloudformation-deployments.ts
+++ b/packages/aws-cdk/lib/api/cloudformation-deployments.ts
@@ -475,11 +475,11 @@ export class CloudFormationDeployments {
    */
   private async validateBootstrapStackVersion(
     stackName: string,
     requiresBootstrapStackVersion: number | undefined,
     bootstrapStackVersionSsmParameter: string | undefined,
     toolkitInfo: ToolkitInfo) {

-    if (requiresBootstrapStackVersion === undefined) { return; }
+    if (requiresBootstrapStackVersion === undefined || requiresBootstrapStackVersion === 0) { return; }

     try {
       await toolkitInfo.validateVersion(requiresBootstrapStackVersion, bootstrapStackVersionSsmParameter);

This is also a backward incompatible change. Version 0 implicitly represents the legacy bootstrap stack. When requireBootstrapStackVersion is set to 0, the current behaviour is to still check for the existence of the CDKToolkit stack, but do not care what version it is. With this code change, we will remove the checks entirely.

Additional Information/Context

No response

CDK CLI Version

5.33.0 (build 859272d)

Framework Version

No response

Node.js Version

v16.14.2

OS

Darwin Kernel Version 21.6.0

Language

Typescript

Language Version

No response

Other information

No response

Metadata

Metadata

Assignees

Labels

bugThis issue is a bug.needs-triageThis issue or PR still needs to be triaged.package/toolsRelated to AWS CDK Tools or CLI

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions