From 32098740575d6ff6bb6e2d36b4d35941fec7ba29 Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Fri, 11 Jun 2021 13:19:13 -0700 Subject: [PATCH] fix(cfn-include): NestedStack's Parameters are not converted to strings When we include a NestedStack when creating a CfnInclude instance, the conversion of the Parameters property of the AWS::CloudFormation::Stack resource is performed manually (because of the eventuality that one of those Parameters was also requested to be removed when including the nested stack). In that manual conversion, we did not correctly convert the values to strings, which is what the underlying CfnStack class expects. And so, if the parent stack passed a non-string primitive value to a nested stack Parameter, like a number or boolean, including the nested stack would fail with a validation exception. Fixes #15092 --- .../cloudformation-include/lib/cfn-include.ts | 4 +- .../test/nested-stacks.test.ts | 39 +++++++++++++++++++ .../nested/child-with-number-parameter.yaml | 9 +++++ .../nested/parent-number-in-child-params.yaml | 8 ++++ 4 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 packages/@aws-cdk/cloudformation-include/test/test-templates/nested/child-with-number-parameter.yaml create mode 100644 packages/@aws-cdk/cloudformation-include/test/test-templates/nested/parent-number-in-child-params.yaml diff --git a/packages/@aws-cdk/cloudformation-include/lib/cfn-include.ts b/packages/@aws-cdk/cloudformation-include/lib/cfn-include.ts index 5a54d36aefcc8..ac8568c0b3a55 100644 --- a/packages/@aws-cdk/cloudformation-include/lib/cfn-include.ts +++ b/packages/@aws-cdk/cloudformation-include/lib/cfn-include.ts @@ -694,7 +694,7 @@ export class CfnInclude extends core.CfnElement { return nestedStackResource; } - private parametersForNestedStack(parameters: any, nestedStackId: string): { [key: string]: any } | undefined { + private parametersForNestedStack(parameters: any, nestedStackId: string): { [key: string]: string } | undefined { if (parameters == null) { return undefined; } @@ -703,7 +703,7 @@ export class CfnInclude extends core.CfnElement { const ret: { [key: string]: string } = {}; for (const paramName of Object.keys(parameters)) { if (!(paramName in parametersToReplace)) { - ret[paramName] = parameters[paramName]; + ret[paramName] = cfn_parse.FromCloudFormation.getString(parameters[paramName]).value; } } return ret; diff --git a/packages/@aws-cdk/cloudformation-include/test/nested-stacks.test.ts b/packages/@aws-cdk/cloudformation-include/test/nested-stacks.test.ts index 1a0d9763280ca..3715ddcab8989 100644 --- a/packages/@aws-cdk/cloudformation-include/test/nested-stacks.test.ts +++ b/packages/@aws-cdk/cloudformation-include/test/nested-stacks.test.ts @@ -334,6 +334,28 @@ describe('CDK Include for nested stacks', () => { }).toThrow(/Nested Stack 'AnotherChildStack' was not included in the parent template/); }); + test('correctly handles references in nested stacks Parameters', () => { + new inc.CfnInclude(stack, 'ParentStack', { + templateFile: testTemplateFilePath('cross-stack-refs.json'), + loadNestedStacks: { + 'ChildStack': { + templateFile: testTemplateFilePath('child-import-stack.json'), + }, + }, + }); + + expect(stack).toHaveResourceLike('AWS::CloudFormation::Stack', { + "Parameters": { + "Param1": { + "Ref": "Param", + }, + "Param2": { + "Fn::GetAtt": ["Bucket", "Arn"], + }, + }, + }); + }); + test('correctly handles renaming of references across nested stacks', () => { const parentTemplate = new inc.CfnInclude(stack, 'ParentStack', { templateFile: testTemplateFilePath('cross-stack-refs.json'), @@ -424,6 +446,23 @@ describe('CDK Include for nested stacks', () => { }); }); + test('can ingest a NestedStack with a Number CFN Parameter passed as a number', () => { + new inc.CfnInclude(stack, 'MyScope', { + templateFile: testTemplateFilePath('parent-number-in-child-params.yaml'), + loadNestedStacks: { + 'NestedStack': { + templateFile: testTemplateFilePath('child-with-number-parameter.yaml'), + }, + }, + }); + + expect(stack).toHaveResourceLike('AWS::CloudFormation::Stack', { + "Parameters": { + "Number": "60", + }, + }); + }); + test('can lazily include a single child nested stack', () => { const parentTemplate = new inc.CfnInclude(stack, 'ParentStack', { templateFile: testTemplateFilePath('parent-one-child.json'), diff --git a/packages/@aws-cdk/cloudformation-include/test/test-templates/nested/child-with-number-parameter.yaml b/packages/@aws-cdk/cloudformation-include/test/test-templates/nested/child-with-number-parameter.yaml new file mode 100644 index 0000000000000..0101bc0c91baa --- /dev/null +++ b/packages/@aws-cdk/cloudformation-include/test/test-templates/nested/child-with-number-parameter.yaml @@ -0,0 +1,9 @@ +AWSTemplateFormatVersion: '2010-09-09' +Parameters: + Number: + Type: Number +Resources: + S3Bucket: + Type: AWS::S3::Bucket + Properties: + BucketName: 'testbucket1234unique' diff --git a/packages/@aws-cdk/cloudformation-include/test/test-templates/nested/parent-number-in-child-params.yaml b/packages/@aws-cdk/cloudformation-include/test/test-templates/nested/parent-number-in-child-params.yaml new file mode 100644 index 0000000000000..10ad404e4dc44 --- /dev/null +++ b/packages/@aws-cdk/cloudformation-include/test/test-templates/nested/parent-number-in-child-params.yaml @@ -0,0 +1,8 @@ +AWSTemplateFormatVersion: '2010-09-09' +Resources: + NestedStack: + Type: AWS::CloudFormation::Stack + Properties: + TemplateURL: 'https://s3.amazonaws.com/masonme-cdk-test/templates/nested-bucket.yaml' + Parameters: + Number: 60