From af9e6bae94c0c303364c2c4f2033eb3823fb59c9 Mon Sep 17 00:00:00 2001 From: Calvin Combs <66279577+comcalvi@users.noreply.github.com> Date: Tue, 10 Sep 2024 15:15:47 -0700 Subject: [PATCH] fix(cloudformation-include): can't use CFN intrinsics in Tags (#30515) ### Issue # (if applicable) Closes #27594. ### Reason for this change Templates that use intrinsics in resource Tags cannot be used with CFN Include. ### Description of changes Modifed the CFN Parser to not choke on Intrinsics found in resource Tags. ### Description of how you validated changes Unit tests. ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- ...efaultTestDeployAssert069A8F1A.assets.json | 19 ++ ...aultTestDeployAssert069A8F1A.template.json | 36 ++++ .../Stack.assets.json | 19 ++ .../Stack.template.json | 77 +++++++ .../cdk.out | 1 + .../integ.json | 12 ++ .../manifest.json | 131 +++++++++++ .../tree.json | 204 ++++++++++++++++++ .../integ.resource-tags-wtih-intrinsics.ts | 15 ++ .../test-templates/tags-with-intrinsics.json | 42 ++++ .../test-templates/tags-with-intrinsics.json | 46 ++++ .../tags-with-invalid-intrinsics.json | 46 ++++ .../test/valid-templates.test.ts | 14 ++ .../core/lib/helpers-internal/cfn-parse.ts | 3 +- packages/aws-cdk-lib/core/lib/runtime.ts | 1 + 15 files changed, 665 insertions(+), 1 deletion(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/ResourceTagIntrinsicStackDefaultTestDeployAssert069A8F1A.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/ResourceTagIntrinsicStackDefaultTestDeployAssert069A8F1A.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/Stack.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/Stack.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.ts create mode 100644 packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/test-templates/tags-with-intrinsics.json create mode 100644 packages/aws-cdk-lib/cloudformation-include/test/test-templates/tags-with-intrinsics.json create mode 100644 packages/aws-cdk-lib/cloudformation-include/test/test-templates/tags-with-invalid-intrinsics.json diff --git a/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/ResourceTagIntrinsicStackDefaultTestDeployAssert069A8F1A.assets.json b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/ResourceTagIntrinsicStackDefaultTestDeployAssert069A8F1A.assets.json new file mode 100644 index 0000000000000..ee152a8b62fae --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/ResourceTagIntrinsicStackDefaultTestDeployAssert069A8F1A.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "ResourceTagIntrinsicStackDefaultTestDeployAssert069A8F1A.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.resource-tags-wtih-intrinsics.js.snapshot/ResourceTagIntrinsicStackDefaultTestDeployAssert069A8F1A.template.json b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/ResourceTagIntrinsicStackDefaultTestDeployAssert069A8F1A.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/ResourceTagIntrinsicStackDefaultTestDeployAssert069A8F1A.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.resource-tags-wtih-intrinsics.js.snapshot/Stack.assets.json b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/Stack.assets.json new file mode 100644 index 0000000000000..c4ebf26bf97cd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/Stack.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "65183510cc1fe133ea25e9f8b474aa5adca1ac83c5e18072e74a62c2bb570cc2": { + "source": { + "path": "Stack.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "65183510cc1fe133ea25e9f8b474aa5adca1ac83c5e18072e74a62c2bb570cc2.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.resource-tags-wtih-intrinsics.js.snapshot/Stack.template.json b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/Stack.template.json new file mode 100644 index 0000000000000..fee4da18ca069 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/Stack.template.json @@ -0,0 +1,77 @@ +{ + "Parameters": { + "Param": { + "Type": "CommaDelimitedList", + "Default": "key,value" + }, + "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]" + } + }, + "Conditions": { + "ShouldIncludeTag": { + "Fn::Equals": [ + 2, + 2 + ] + } + }, + "Resources": { + "Bucket": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketName": "cdk-integ-cfn-include-bucket2", + "Tags": [ + { + "Fn::If": [ + "ShouldIncludeTag", + { + "Key": { + "Fn::Select": [ + 0, + { + "Ref": "Param" + } + ] + }, + "Value": "TagValue" + }, + { + "Ref": "AWS::NoValue" + } + ] + } + ] + } + } + }, + "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.resource-tags-wtih-intrinsics.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.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.resource-tags-wtih-intrinsics.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.resource-tags-wtih-intrinsics.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/integ.json new file mode 100644 index 0000000000000..d6e49c7d11c4b --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "ResourceTagIntrinsicStack/DefaultTest": { + "stacks": [ + "Stack" + ], + "assertionStack": "ResourceTagIntrinsicStack/DefaultTest/DeployAssert", + "assertionStackName": "ResourceTagIntrinsicStackDefaultTestDeployAssert069A8F1A" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/manifest.json new file mode 100644 index 0000000000000..e640cbfd06fcc --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/manifest.json @@ -0,0 +1,131 @@ +{ + "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}/65183510cc1fe133ea25e9f8b474aa5adca1ac83c5e18072e74a62c2bb570cc2.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/Param": [ + { + "type": "aws:cdk:logicalId", + "data": "Param" + } + ], + "/Stack/Stack/$Conditions/ShouldIncludeTag": [ + { + "type": "aws:cdk:logicalId", + "data": "ShouldIncludeTag" + } + ], + "/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" + }, + "ResourceTagIntrinsicStackDefaultTestDeployAssert069A8F1A.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "ResourceTagIntrinsicStackDefaultTestDeployAssert069A8F1A.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "ResourceTagIntrinsicStackDefaultTestDeployAssert069A8F1A": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "ResourceTagIntrinsicStackDefaultTestDeployAssert069A8F1A.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": [ + "ResourceTagIntrinsicStackDefaultTestDeployAssert069A8F1A.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": [ + "ResourceTagIntrinsicStackDefaultTestDeployAssert069A8F1A.assets" + ], + "metadata": { + "/ResourceTagIntrinsicStack/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/ResourceTagIntrinsicStack/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "ResourceTagIntrinsicStack/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.resource-tags-wtih-intrinsics.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/tree.json new file mode 100644 index 0000000000000..7b8ecf8423488 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.js.snapshot/tree.json @@ -0,0 +1,204 @@ +{ + "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" + } + }, + "Param": { + "id": "Param", + "path": "Stack/Stack/Param", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "$Conditions": { + "id": "$Conditions", + "path": "Stack/Stack/$Conditions", + "children": { + "ShouldIncludeTag": { + "id": "ShouldIncludeTag", + "path": "Stack/Stack/$Conditions/ShouldIncludeTag", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.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": { + "bucketName": "cdk-integ-cfn-include-bucket2", + "tags": [ + { + "Fn::If": [ + "ShouldIncludeTag", + { + "Key": { + "Fn::Select": [ + 0, + { + "Ref": "Param" + } + ] + }, + "Value": "TagValue" + }, + { + "Ref": "AWS::NoValue" + } + ] + } + ] + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "$Hooks": { + "id": "$Hooks", + "path": "Stack/Stack/$Hooks", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "$Outputs": { + "id": "$Outputs", + "path": "Stack/Stack/$Outputs", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "Stack/BootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "Stack/CheckBootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "ResourceTagIntrinsicStack": { + "id": "ResourceTagIntrinsicStack", + "path": "ResourceTagIntrinsicStack", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "ResourceTagIntrinsicStack/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "ResourceTagIntrinsicStack/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "ResourceTagIntrinsicStack/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "ResourceTagIntrinsicStack/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "ResourceTagIntrinsicStack/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.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": "constructs.Construct", + "version": "10.3.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.ts b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.ts new file mode 100644 index 0000000000000..62e697ad5d0d4 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/integ.resource-tags-wtih-intrinsics.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/tags-with-intrinsics.json', +}); + +new integ.IntegTest(app, 'ResourceTagIntrinsicStack', { + testCases: [stack], +}); diff --git a/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/test-templates/tags-with-intrinsics.json b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/test-templates/tags-with-intrinsics.json new file mode 100644 index 0000000000000..886560576e751 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/cloudformation-include/test/test-templates/tags-with-intrinsics.json @@ -0,0 +1,42 @@ +{ + "Parameters": { + "Param": { + "Type": "CommaDelimitedList", + "Default": "key,value" + } + }, + "Conditions": { + "ShouldIncludeTag": { + "Fn::Equals": [2, 2] + } + }, + "Resources": { + "Bucket": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketName": "cdk-integ-cfn-include-bucket2", + "Tags": [ + { + "Fn::If": [ + "ShouldIncludeTag", + { + "Key": { + "Fn::Select": [ + 0, + { + "Ref": "Param" + } + ] + }, + "Value": "TagValue" + }, + { + "Ref": "AWS::NoValue" + } + ] + } + ] + } + } + } +} \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloudformation-include/test/test-templates/tags-with-intrinsics.json b/packages/aws-cdk-lib/cloudformation-include/test/test-templates/tags-with-intrinsics.json new file mode 100644 index 0000000000000..3ba70c35443de --- /dev/null +++ b/packages/aws-cdk-lib/cloudformation-include/test/test-templates/tags-with-intrinsics.json @@ -0,0 +1,46 @@ +{ + "Parameters": { + "Param": { + "Type": "CommaDelimitedList", + "Default": "key,value" + } + }, + "Conditions": { + "ShouldIncludeTag": { + "Fn::Equals": [2, 2] + } + }, + "Resources": { + "Bucket": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketName": "cdk-integ-cfn-include-bucket2", + "Tags": [ + { + "Key": "Key1", + "Value": "Value1" + }, + { + "Fn::If": [ + "ShouldIncludeTag", + { + "Key": { + "Fn::Select": [ + 0, + { + "Ref": "Param" + } + ] + }, + "Value": "TagValue" + }, + { + "Ref": "AWS::NoValue" + } + ] + } + ] + } + } + } +} \ No newline at end of file diff --git a/packages/aws-cdk-lib/cloudformation-include/test/test-templates/tags-with-invalid-intrinsics.json b/packages/aws-cdk-lib/cloudformation-include/test/test-templates/tags-with-invalid-intrinsics.json new file mode 100644 index 0000000000000..81c8368f88521 --- /dev/null +++ b/packages/aws-cdk-lib/cloudformation-include/test/test-templates/tags-with-invalid-intrinsics.json @@ -0,0 +1,46 @@ +{ + "Parameters": { + "Param": { + "Type": "CommaDelimitedList", + "Default": "key,value" + } + }, + "Conditions": { + "ShouldIncludeTag": { + "Fn::Equals": [2, 2] + } + }, + "Resources": { + "Bucket": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketName": "cdk-integ-cfn-include-bucket2", + "Tags": [ + { + "Key": "Key1", + "Value": "Value1" + }, + { + "Fn::If": [ + "NonExistentCondition", + { + "Key": { + "Fn::Select": [ + 0, + { + "Ref": "Param" + } + ] + }, + "Value": "TagValue" + }, + { + "Ref": "AWS::NoValue" + } + ] + } + ] + } + } + } +} \ 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 70a26179415d0..8876e5c3fe50a 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 @@ -1121,6 +1121,20 @@ describe('CDK Include', () => { loadTestFileToJsObject('fn-select-with-novalue.json'), ); }); + + test('Fn::If can be used in Tags', () => { + includeTestTemplate(stack, 'tags-with-intrinsics.json'); + + Template.fromStack(stack).templateMatches( + loadTestFileToJsObject('tags-with-intrinsics.json'), + ); + }); + + test('throws an exception if Tags contains invalid intrinsics', () => { + expect(() => { + includeTestTemplate(stack, 'tags-with-invalid-intrinsics.json'); + }).toThrow(/expression does not exist in the template/); + }); }); interface IncludeTestTemplateProps { 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 2dce8dad956e9..fbbb26c0c566e 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 @@ -216,7 +216,8 @@ export class FromCloudFormation { }; } - public static getCfnTag(tag: any): FromCloudFormationResult { + public static getCfnTag(tag: any): FromCloudFormationResult { + if (isResolvableObject(tag)) { return new FromCloudFormationResult(tag); } return tag == null ? new FromCloudFormationResult({ } as any) // break the type system - this should be detected at runtime by a tag validator : new FromCloudFormationResult({ diff --git a/packages/aws-cdk-lib/core/lib/runtime.ts b/packages/aws-cdk-lib/core/lib/runtime.ts index 2da488d6805c2..11c067d319cd3 100644 --- a/packages/aws-cdk-lib/core/lib/runtime.ts +++ b/packages/aws-cdk-lib/core/lib/runtime.ts @@ -49,6 +49,7 @@ function pad(x: number) { * Turn a tag object into the proper CloudFormation representation */ export function cfnTagToCloudFormation(x: any): any { + if (!canInspect(x)) { return x; } return { Key: x.key, Value: x.value,