From a6df4b3439a2192023817a556d2f2de46bf537c5 Mon Sep 17 00:00:00 2001 From: Connor Robertson Date: Wed, 24 Jan 2024 15:22:57 -0800 Subject: [PATCH] fix(iam): allow intrinsic functions in deletion policy (#28834) > # Issue > > When using `CfnInclude` it was not possible to have an intrinsic function in the `DeletionPolicy`. It only allowed the DeletionPolicy to be explicitly defined. > # Solution > > Check if policy looks like an intrinsic. Check if it is an explicitly defined DeletionPolicy. Then Default to a case that checks if this is an intrinsic if it is return the policy after using parseValue. Else throw the same error it was previously. > # Important Design Decisions > > Unsure if any have been made please let me know if there are any I didn't think were Important Design Decisions. > > Remember to follow the [CONTRIBUTING GUIDE] and [DESIGN GUIDELINES] for any > code you submit. > > [CONTRIBUTING GUIDE]: https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md > [DESIGN GUIDELINES]: https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md Closes https://github.com/aws/aws-cdk/issues/28292 . ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- ...efaultTestDeployAssert6189EF04.assets.json | 19 ++ ...aultTestDeployAssert6189EF04.template.json | 36 ++++ .../Stack.assets.json | 19 ++ .../Stack.template.json | 58 ++++++ .../cdk.out | 1 + .../integ.json | 12 ++ .../manifest.json | 125 +++++++++++++ .../tree.json | 173 ++++++++++++++++++ .../test/integ.intrinsic-deletion-policy.ts | 15 ++ .../test-templates/fn-if-deletion-policy.json | 25 +++ .../intrinsic-deletion-policy-ref.json | 46 +++++ .../intrinsic-deletion-policy.json | 53 ++++++ .../test/valid-templates.test.ts | 16 ++ .../core/lib/helpers-internal/cfn-parse.ts | 13 +- 14 files changed, 608 insertions(+), 3 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/DeletionPolicyTestDefaultTestDeployAssert6189EF04.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/DeletionPolicyTestDefaultTestDeployAssert6189EF04.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/Stack.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/Stack.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.ts create mode 100644 packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/test-templates/fn-if-deletion-policy.json create mode 100644 packages/aws-cdk-lib/cloudformation-include/test/test-templates/intrinsic-deletion-policy-ref.json create mode 100644 packages/aws-cdk-lib/cloudformation-include/test/test-templates/intrinsic-deletion-policy.json diff --git a/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/DeletionPolicyTestDefaultTestDeployAssert6189EF04.assets.json b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/DeletionPolicyTestDefaultTestDeployAssert6189EF04.assets.json new file mode 100644 index 0000000000000..bf0cd5ed3a1c2 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/DeletionPolicyTestDefaultTestDeployAssert6189EF04.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "DeletionPolicyTestDefaultTestDeployAssert6189EF04.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/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/DeletionPolicyTestDefaultTestDeployAssert6189EF04.template.json b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/DeletionPolicyTestDefaultTestDeployAssert6189EF04.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/DeletionPolicyTestDefaultTestDeployAssert6189EF04.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/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/Stack.assets.json b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/Stack.assets.json new file mode 100644 index 0000000000000..4f2896026d3a3 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/Stack.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "2ec981d249c5fd2e1befd964a3ffcb115d1eb16b36017d923fe1ced27352e0e5": { + "source": { + "path": "Stack.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "2ec981d249c5fd2e1befd964a3ffcb115d1eb16b36017d923fe1ced27352e0e5.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/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/Stack.template.json b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/Stack.template.json new file mode 100644 index 0000000000000..690634ca5efe7 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/Stack.template.json @@ -0,0 +1,58 @@ +{ + "Conditions": { + "AlwaysFalseCond": { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "completely-made-up-region" + ] + } + }, + "Resources": { + "Bucket": { + "Type": "AWS::S3::Bucket", + "DeletionPolicy": { + "Fn::If": [ + "AlwaysFalseCond", + "Retain", + "Delete" + ] + } + } + }, + "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/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/integ.json new file mode 100644 index 0000000000000..a2fda9175aada --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "DeletionPolicyTest/DefaultTest": { + "stacks": [ + "Stack" + ], + "assertionStack": "DeletionPolicyTest/DefaultTest/DeployAssert", + "assertionStackName": "DeletionPolicyTestDefaultTestDeployAssert6189EF04" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/manifest.json new file mode 100644 index 0000000000000..9aeb2b31b724f --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/manifest.json @@ -0,0 +1,125 @@ +{ + "version": "36.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": 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}/2ec981d249c5fd2e1befd964a3ffcb115d1eb16b36017d923fe1ced27352e0e5.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/Stack": [ + { + "type": "aws:cdk:logicalId", + "data": "Stack" + } + ], + "/Stack/Stack/$Conditions/AlwaysFalseCond": [ + { + "type": "aws:cdk:logicalId", + "data": "AlwaysFalseCond" + } + ], + "/Stack/Stack/Bucket": [ + { + "type": "aws:cdk:logicalId", + "data": "Bucket" + } + ], + "/Stack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/Stack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "Stack" + }, + "DeletionPolicyTestDefaultTestDeployAssert6189EF04.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "DeletionPolicyTestDefaultTestDeployAssert6189EF04.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "DeletionPolicyTestDefaultTestDeployAssert6189EF04": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "DeletionPolicyTestDefaultTestDeployAssert6189EF04.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": [ + "DeletionPolicyTestDefaultTestDeployAssert6189EF04.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": [ + "DeletionPolicyTestDefaultTestDeployAssert6189EF04.assets" + ], + "metadata": { + "/DeletionPolicyTest/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/DeletionPolicyTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "DeletionPolicyTest/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/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/tree.json new file mode 100644 index 0000000000000..3c5b96273ac99 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.js.snapshot/tree.json @@ -0,0 +1,173 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "Stack": { + "id": "Stack", + "path": "Stack", + "children": { + "Stack": { + "id": "Stack", + "path": "Stack/Stack", + "children": { + "$Mappings": { + "id": "$Mappings", + "path": "Stack/Stack/$Mappings", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "$Conditions": { + "id": "$Conditions", + "path": "Stack/Stack/$Conditions", + "children": { + "AlwaysFalseCond": { + "id": "AlwaysFalseCond", + "path": "Stack/Stack/$Conditions/AlwaysFalseCond", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnCondition", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "$Rules": { + "id": "$Rules", + "path": "Stack/Stack/$Rules", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Bucket": { + "id": "Bucket", + "path": "Stack/Stack/Bucket", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::S3::Bucket", + "aws:cdk:cloudformation:props": {} + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_s3.CfnBucket", + "version": "0.0.0" + } + }, + "$Hooks": { + "id": "$Hooks", + "path": "Stack/Stack/$Hooks", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "$Ouputs": { + "id": "$Ouputs", + "path": "Stack/Stack/$Ouputs", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.cloudformation_include.CfnInclude", + "version": "0.0.0" + } + }, + "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" + } + }, + "DeletionPolicyTest": { + "id": "DeletionPolicyTest", + "path": "DeletionPolicyTest", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "DeletionPolicyTest/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "DeletionPolicyTest/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "DeletionPolicyTest/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "DeletionPolicyTest/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "DeletionPolicyTest/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.3.0" + } + } + }, + "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/cloudformation-include/test/integ.intrinsic-deletion-policy.ts b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.ts new file mode 100644 index 0000000000000..65e88a5e24757 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.intrinsic-deletion-policy.ts @@ -0,0 +1,15 @@ +import * as core from 'aws-cdk-lib'; +import * as inc from 'aws-cdk-lib/cloudformation-include'; +import * as integ from '@aws-cdk/integ-tests-alpha'; + +const app = new core.App(); + +const stack = new core.Stack(app, 'Stack'); + +new inc.CfnInclude(stack, 'Stack', { + templateFile: 'test-templates/fn-if-deletion-policy.json', +}); + +new integ.IntegTest(app, 'DeletionPolicyTest', { + testCases: [stack], +}); diff --git a/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/test-templates/fn-if-deletion-policy.json b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/test-templates/fn-if-deletion-policy.json new file mode 100644 index 0000000000000..f8d6909224108 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/test-templates/fn-if-deletion-policy.json @@ -0,0 +1,25 @@ +{ + "Conditions": { + "AlwaysFalseCond": { + "Fn::Equals": [ + { + "Ref": "AWS::Region" + }, + "completely-made-up-region" + ] + } + }, + "Resources": { + "Bucket": { + "Type": "AWS::S3::Bucket", + "DeletionPolicy": { + "Fn::If": [ + "AlwaysFalseCond", + "Retain", + "Delete" + ] + } + } + } + } + \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloudformation-include/test/test-templates/intrinsic-deletion-policy-ref.json b/packages/aws-cdk-lib/cloudformation-include/test/test-templates/intrinsic-deletion-policy-ref.json new file mode 100644 index 0000000000000..0b02ae68180bc --- /dev/null +++ b/packages/aws-cdk-lib/cloudformation-include/test/test-templates/intrinsic-deletion-policy-ref.json @@ -0,0 +1,46 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Transform": "AWS::LanguageExtensions", + "Parameters": { + "DeletionPolicyParam": { + "Type": "String", + "AllowedValues": [ + "Delete", + "Retain", + "Snapshot" + ], + "Default": "Delete" + }, + "UpdateReplacePolicyParam": { + "Type": "String", + "AllowedValues": [ + "Delete", + "Retain", + "Snapshot" + ], + "Default": "Delete" + } + }, + "Resources": { + "Table": { + "Type": "AWS::DynamoDB::Table", + "Properties": { + "KeySchema": [ + { + "AttributeName": "primaryKey", + "KeyType": "HASH" + }], + "AttributeDefinitions": [{ + "AttributeName": "primaryKey", + "AttributeType": "S" + }] + }, + "DeletionPolicy": { + "Ref": "DeletionPolicyParam" + }, + "UpdateReplacePolicy": { + "Ref": "UpdateReplacePolicyParam" + } + } + } +} \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloudformation-include/test/test-templates/intrinsic-deletion-policy.json b/packages/aws-cdk-lib/cloudformation-include/test/test-templates/intrinsic-deletion-policy.json new file mode 100644 index 0000000000000..722ed175d0556 --- /dev/null +++ b/packages/aws-cdk-lib/cloudformation-include/test/test-templates/intrinsic-deletion-policy.json @@ -0,0 +1,53 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Transform": "AWS::LanguageExtensions", + "Parameters": { + "Stage": { + "Type": "String", + "AllowedValues": [ + "Prod", + "Staging", + "Dev" + ] + } + }, + "Conditions": { + "IsProd": { + "Fn::Equals": [ + { + "Ref": "Stage" + }, + "Prod" + ] + } + }, + "Resources": { + "Table": { + "Type": "AWS::DynamoDB::Table", + "Properties": { + "KeySchema": [{ + "AttributeName": "primaryKey", + "KeyType": "HASH" + }], + "AttributeDefinitions": [{ + "AttributeName": "primaryKey", + "AttributeType": "S" + }] + }, + "DeletionPolicy": { + "Fn::If": [ + "IsProd", + "Retain", + "Delete" + ] + }, + "UpdateReplacePolicy": { + "Fn::If": [ + "IsProd", + "Retain", + "Delete" + ] + } + } + } +} \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloudformation-include/test/valid-templates.test.ts b/packages/aws-cdk-lib/cloudformation-include/test/valid-templates.test.ts index b6ff1bea6f681..e27c9bb3ebc17 100644 --- a/packages/aws-cdk-lib/cloudformation-include/test/valid-templates.test.ts +++ b/packages/aws-cdk-lib/cloudformation-include/test/valid-templates.test.ts @@ -262,6 +262,22 @@ describe('CDK Include', () => { ); }); + test('can ingest a template with fn:: intrinsic function used in deletion policy', () => { + includeTestTemplate(stack, 'intrinsic-deletion-policy.json'); + + Template.fromStack(stack).templateMatches( + loadTestFileToJsObject('intrinsic-deletion-policy.json'), + ); + }); + + test('can ingest a template with ref intrinsic functions used in deletion policy', () => { + includeTestTemplate(stack, 'intrinsic-deletion-policy-ref.json'); + + Template.fromStack(stack).templateMatches( + loadTestFileToJsObject('intrinsic-deletion-policy-ref.json'), + ); + }); + test('can ingest a JSON template with string-form Fn::GetAtt, and output it unchanged', () => { includeTestTemplate(stack, 'get-att-string-form.json'); diff --git a/packages/aws-cdk-lib/core/lib/helpers-internal/cfn-parse.ts b/packages/aws-cdk-lib/core/lib/helpers-internal/cfn-parse.ts index 4b7fd0cbb30a4..2dce8dad956e9 100644 --- a/packages/aws-cdk-lib/core/lib/helpers-internal/cfn-parse.ts +++ b/packages/aws-cdk-lib/core/lib/helpers-internal/cfn-parse.ts @@ -466,14 +466,21 @@ export class CfnParser { } private parseDeletionPolicy(policy: any): CfnDeletionPolicy | undefined { + if (policy === undefined || policy === null) { + return undefined; + } + const isIntrinsic = this.looksLikeCfnIntrinsic(policy); switch (policy) { - case null: return undefined; - case undefined: return undefined; case 'Delete': return CfnDeletionPolicy.DELETE; case 'Retain': return CfnDeletionPolicy.RETAIN; case 'Snapshot': return CfnDeletionPolicy.SNAPSHOT; case 'RetainExceptOnCreate': return CfnDeletionPolicy.RETAIN_EXCEPT_ON_CREATE; - default: throw new Error(`Unrecognized DeletionPolicy '${policy}'`); + default: if (isIntrinsic) { + policy = this.parseValue(policy); + return policy; + } else { + throw new Error(`Unrecognized DeletionPolicy '${policy}'`); + } } }