From 448f6dc4947819fa07cf3e055c7ab7987d04588e Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Fri, 22 May 2026 10:18:58 +0200 Subject: [PATCH] fix: bootstrap fails if bucket, ECR repo already exist If a bootstrap stack is deleted, for safety it leaves behind the S3 Bucket and ECR repository used for storing assets. This will interfere with re-bootstrapping. Fortunately CloudFormation has an option to automatically adopt hard-named resources into a new stack, so we turn that on by default for bootstrapping. This will get rid of those errors. The chances this will interfere with normal operation is minimal, but on the off chance it does this behavior can be switched off with a `--no-import-existing-resources` flag. --- ...ootstrap-after-stack-deletion.integtest.ts | 21 +++++++++++++++++++ .../lib/api/bootstrap/bootstrap-props.ts | 8 +++++++ .../lib/api/bootstrap/deploy-bootstrap.ts | 6 +++++- packages/aws-cdk/lib/cli/cli-config.ts | 1 + .../aws-cdk/lib/cli/cli-type-registry.json | 5 +++++ packages/aws-cdk/lib/cli/cli.ts | 1 + .../aws-cdk/lib/cli/convert-to-user-input.ts | 2 ++ .../lib/cli/parse-command-line-arguments.ts | 5 +++++ packages/aws-cdk/lib/cli/user-input.ts | 7 +++++++ 9 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/bootstrap/cdk-bootstrap-can-re-bootstrap-after-stack-deletion.integtest.ts diff --git a/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/bootstrap/cdk-bootstrap-can-re-bootstrap-after-stack-deletion.integtest.ts b/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/bootstrap/cdk-bootstrap-can-re-bootstrap-after-stack-deletion.integtest.ts new file mode 100644 index 000000000..23320d345 --- /dev/null +++ b/packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/bootstrap/cdk-bootstrap-can-re-bootstrap-after-stack-deletion.integtest.ts @@ -0,0 +1,21 @@ +import { integTest, withoutBootstrap } from '../../../lib'; + +integTest('bootstrap can re-bootstrap after stack deletion', withoutBootstrap(async (fixture) => { + const bootstrapStackName = fixture.bootstrapStackName; + + // Bootstrap the environment + await fixture.cdkBootstrapModern({ + toolkitStackName: bootstrapStackName, + cfnExecutionPolicy: 'arn:aws:iam::aws:policy/AdministratorAccess', + }); + + // Delete the bootstrap stack (resources like the S3 bucket will be retained) + await fixture.aws.deleteStacks(bootstrapStackName); + + // Re-bootstrap should succeed because importExistingResources is enabled, + // allowing the change set to import the retained resources back into the stack. + await fixture.cdkBootstrapModern({ + toolkitStackName: bootstrapStackName, + cfnExecutionPolicy: 'arn:aws:iam::aws:policy/AdministratorAccess', + }); +})); diff --git a/packages/@aws-cdk/toolkit-lib/lib/api/bootstrap/bootstrap-props.ts b/packages/@aws-cdk/toolkit-lib/lib/api/bootstrap/bootstrap-props.ts index 8f0225126..ad525baa3 100644 --- a/packages/@aws-cdk/toolkit-lib/lib/api/bootstrap/bootstrap-props.ts +++ b/packages/@aws-cdk/toolkit-lib/lib/api/bootstrap/bootstrap-props.ts @@ -58,6 +58,14 @@ export interface BootstrapEnvironmentOptions { * @default true */ usePreviousParameters?: boolean; + + /** + * Whether to import existing resources into the stack instead of failing + * when they already exist. + * + * @default true + */ + readonly importExistingResources?: boolean; } /** diff --git a/packages/@aws-cdk/toolkit-lib/lib/api/bootstrap/deploy-bootstrap.ts b/packages/@aws-cdk/toolkit-lib/lib/api/bootstrap/deploy-bootstrap.ts index 4ab2dab56..89e88fc55 100644 --- a/packages/@aws-cdk/toolkit-lib/lib/api/bootstrap/deploy-bootstrap.ts +++ b/packages/@aws-cdk/toolkit-lib/lib/api/bootstrap/deploy-bootstrap.ts @@ -142,7 +142,11 @@ export class BootstrapStack { forceDeployment: options.forceDeployment, roleArn: options.roleArn, tags: options.tags, - deploymentMethod: { method: 'change-set', execute: options.execute }, + deploymentMethod: { + method: 'change-set', + execute: options.execute, + importExistingResources: options.importExistingResources ?? true, + }, parameters, usePreviousParameters: options.usePreviousParameters ?? true, // Obviously we can't need a bootstrap stack to deploy a bootstrap stack diff --git a/packages/aws-cdk/lib/cli/cli-config.ts b/packages/aws-cdk/lib/cli/cli-config.ts index 9c39a5083..ffb8f0119 100644 --- a/packages/aws-cdk/lib/cli/cli-config.ts +++ b/packages/aws-cdk/lib/cli/cli-config.ts @@ -102,6 +102,7 @@ export async function makeConfig(): Promise { 'toolkit-stack-name': { type: 'string', desc: 'The name of the CDK toolkit stack to create', requiresArg: true }, 'template': { type: 'string', requiresArg: true, desc: 'Use the template from the given file instead of the built-in one (use --show-template to obtain an example)' }, 'previous-parameters': { type: 'boolean', default: true, desc: 'Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)' }, + 'import-existing-resources': { type: 'boolean', desc: 'Whether to import existing resources into the bootstrap stack instead of failing if they already exist', default: true }, }, }, 'gc': { diff --git a/packages/aws-cdk/lib/cli/cli-type-registry.json b/packages/aws-cdk/lib/cli/cli-type-registry.json index fbab3f012..97aeea0b5 100644 --- a/packages/aws-cdk/lib/cli/cli-type-registry.json +++ b/packages/aws-cdk/lib/cli/cli-type-registry.json @@ -306,6 +306,11 @@ "type": "boolean", "default": true, "desc": "Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)" + }, + "import-existing-resources": { + "type": "boolean", + "desc": "Whether to import existing resources into the bootstrap stack instead of failing if they already exist", + "default": true } } }, diff --git a/packages/aws-cdk/lib/cli/cli.ts b/packages/aws-cdk/lib/cli/cli.ts index 173b2e193..f406498e0 100644 --- a/packages/aws-cdk/lib/cli/cli.ts +++ b/packages/aws-cdk/lib/cli/cli.ts @@ -385,6 +385,7 @@ export async function exec(args: string[], synthesizer?: Synthesizer): Promise): any { default: true, type: 'boolean', desc: 'Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)', + }) + .option('import-existing-resources', { + default: true, + type: 'boolean', + desc: 'Whether to import existing resources into the bootstrap stack instead of failing if they already exist', }), ) .command( diff --git a/packages/aws-cdk/lib/cli/user-input.ts b/packages/aws-cdk/lib/cli/user-input.ts index afb41f997..08d103b42 100644 --- a/packages/aws-cdk/lib/cli/user-input.ts +++ b/packages/aws-cdk/lib/cli/user-input.ts @@ -592,6 +592,13 @@ export interface BootstrapOptions { */ readonly previousParameters?: boolean; + /** + * Whether to import existing resources into the bootstrap stack instead of failing if they already exist + * + * @default - true + */ + readonly importExistingResources?: boolean; + /** * Positional argument for bootstrap */