From 54083de9e8faef8b62a2004941647d9cd4984a0b Mon Sep 17 00:00:00 2001 From: Michael Sambol Date: Tue, 19 Sep 2023 07:18:35 -0500 Subject: [PATCH] feat(core): add setter for termination protection on stack (#26992) Closes #14463. Closes #21304. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../integ.stack.js.snapshot/Stack.assets.json | 19 ++++ .../Stack.template.json | 36 ++++++ .../core/test/integ.stack.js.snapshot/cdk.out | 1 + .../test/integ.stack.js.snapshot/integ.json | 12 ++ .../integ.stack.js.snapshot/manifest.json | 107 ++++++++++++++++++ ...efaultTestDeployAssert0386B0AD.assets.json | 19 ++++ ...aultTestDeployAssert0386B0AD.template.json | 36 ++++++ .../test/integ.stack.js.snapshot/tree.json | 101 +++++++++++++++++ .../test/core/test/integ.stack.ts | 12 ++ packages/aws-cdk-lib/core/README.md | 7 ++ packages/aws-cdk-lib/core/lib/stack.ts | 12 +- packages/aws-cdk-lib/core/test/stack.test.ts | 26 +++++ 12 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/Stack.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/Stack.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/stackDefaultTestDeployAssert0386B0AD.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/stackDefaultTestDeployAssert0386B0AD.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/Stack.assets.json b/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/Stack.assets.json new file mode 100644 index 0000000000000..2dabbe5b2dd68 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/Stack.assets.json @@ -0,0 +1,19 @@ +{ + "version": "34.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "Stack.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/Stack.template.json b/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/Stack.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/Stack.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/cdk.out new file mode 100644 index 0000000000000..2313ab5436501 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"34.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/integ.json new file mode 100644 index 0000000000000..d5fb120c891d1 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "34.0.0", + "testCases": { + "stack/DefaultTest": { + "stacks": [ + "Stack" + ], + "assertionStack": "stack/DefaultTest/DeployAssert", + "assertionStackName": "stackDefaultTestDeployAssert0386B0AD" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/manifest.json new file mode 100644 index 0000000000000..2630c9fb741f7 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/manifest.json @@ -0,0 +1,107 @@ +{ + "version": "34.0.0", + "artifacts": { + "Stack.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "Stack.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "Stack": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "Stack.template.json", + "terminationProtection": true, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "Stack.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "Stack.assets" + ], + "metadata": { + "/Stack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/Stack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "Stack" + }, + "stackDefaultTestDeployAssert0386B0AD.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "stackDefaultTestDeployAssert0386B0AD.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "stackDefaultTestDeployAssert0386B0AD": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "stackDefaultTestDeployAssert0386B0AD.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "stackDefaultTestDeployAssert0386B0AD.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "stackDefaultTestDeployAssert0386B0AD.assets" + ], + "metadata": { + "/stack/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/stack/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "stack/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/stackDefaultTestDeployAssert0386B0AD.assets.json b/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/stackDefaultTestDeployAssert0386B0AD.assets.json new file mode 100644 index 0000000000000..501878ac128f2 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/stackDefaultTestDeployAssert0386B0AD.assets.json @@ -0,0 +1,19 @@ +{ + "version": "34.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "stackDefaultTestDeployAssert0386B0AD.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/stackDefaultTestDeployAssert0386B0AD.template.json b/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/stackDefaultTestDeployAssert0386B0AD.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/stackDefaultTestDeployAssert0386B0AD.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/tree.json new file mode 100644 index 0000000000000..7499983ab0eca --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.js.snapshot/tree.json @@ -0,0 +1,101 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "Stack": { + "id": "Stack", + "path": "Stack", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "Stack/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "Stack/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "stack": { + "id": "stack", + "path": "stack", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "stack/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "stack/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.70" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "stack/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "stack/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "stack/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.2.70" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.ts b/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.ts new file mode 100644 index 0000000000000..f314579e7fd43 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/core/test/integ.stack.ts @@ -0,0 +1,12 @@ +import * as cdk from 'aws-cdk-lib'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; + +/** + * This test creates a stack and changes termination protection with the setter. + */ + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'Stack', { terminationProtection: false }); +stack.terminationProtection = true; + +new IntegTest(app, 'stack', { testCases: [stack] }); diff --git a/packages/aws-cdk-lib/core/README.md b/packages/aws-cdk-lib/core/README.md index 53afbfaeac38b..4c7e9ecd309d0 100644 --- a/packages/aws-cdk-lib/core/README.md +++ b/packages/aws-cdk-lib/core/README.md @@ -1174,6 +1174,13 @@ const stack = new Stack(app, 'StackName', { }); ``` +You can also set termination protection with the setter after you've instantiated the stack. + +```ts +const stack = new Stack(app, 'StackName', {}); +stack.terminationProtection = true; +``` + By default, termination protection is disabled. ### Description diff --git a/packages/aws-cdk-lib/core/lib/stack.ts b/packages/aws-cdk-lib/core/lib/stack.ts index 54424e8dcafd5..bea3f6814f452 100644 --- a/packages/aws-cdk-lib/core/lib/stack.ts +++ b/packages/aws-cdk-lib/core/lib/stack.ts @@ -314,7 +314,13 @@ export class Stack extends Construct implements ITaggable { /** * Whether termination protection is enabled for this stack. */ - public readonly terminationProtection?: boolean; + public get terminationProtection(): boolean { + return this._terminationProtection; + } + + public set terminationProtection(value: boolean) { + this._terminationProtection = value; + } /** * If this is a nested stack, this represents its `AWS::CloudFormation::Stack` @@ -389,6 +395,8 @@ export class Stack extends Construct implements ITaggable { */ private readonly _suppressTemplateIndentation: boolean; + private _terminationProtection: boolean; + /** * Creates a new stack. * @@ -426,7 +434,7 @@ export class Stack extends Construct implements ITaggable { this.account = account; this.region = region; this.environment = environment; - this.terminationProtection = props.terminationProtection; + this._terminationProtection = props.terminationProtection ?? false; if (props.description !== undefined) { // Max length 1024 bytes diff --git a/packages/aws-cdk-lib/core/test/stack.test.ts b/packages/aws-cdk-lib/core/test/stack.test.ts index 9d503ad65d941..98954be70ee58 100644 --- a/packages/aws-cdk-lib/core/test/stack.test.ts +++ b/packages/aws-cdk-lib/core/test/stack.test.ts @@ -2009,6 +2009,32 @@ describe('stack', () => { expect(artifact.terminationProtection).toEqual(true); }); + test('Set termination protection to true with setter', () => { + // if the root is an app, invoke "synth" to avoid double synthesis + const app = new App(); + const stack = new Stack(app, 'Stack', {}); + + stack.terminationProtection = true; + + const assembly = app.synth(); + const artifact = assembly.getStackArtifact(stack.artifactId); + + expect(artifact.terminationProtection).toEqual(true); + }); + + test('Set termination protection to false with setter', () => { + // if the root is an app, invoke "synth" to avoid double synthesis + const app = new App(); + const stack = new Stack(app, 'Stack', { terminationProtection: true }); + + stack.terminationProtection = false; + + const assembly = app.synth(); + const artifact = assembly.getStackArtifact(stack.artifactId); + + expect(artifact.terminationProtection).toEqual(false); + }); + test('context can be set on a stack using a LegacySynthesizer', () => { // WHEN const stack = new Stack(undefined, undefined, {