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 */