From 9ea8d5c2d638bdf1f5bc63be197ecefc775d6539 Mon Sep 17 00:00:00 2001 From: comcalvi <66279577+comcalvi@users.noreply.github.com> Date: Wed, 2 Sep 2020 14:38:01 -0400 Subject: [PATCH] fix(cfn-include): allow parameters to be replaced across nested stacks (#9842) Fixes #9838 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../cloudformation-include/lib/cfn-include.ts | 26 +++++--- .../test/nested-stacks.test.ts | 64 ++++++++++++++++++- .../nested/child-two-parameters.json | 34 ++++++++++ .../nested/parent-two-parameters.json | 14 ++++ 4 files changed, 127 insertions(+), 11 deletions(-) create mode 100644 packages/@aws-cdk/cloudformation-include/test/test-templates/nested/child-two-parameters.json create mode 100644 packages/@aws-cdk/cloudformation-include/test/test-templates/nested/parent-two-parameters.json diff --git a/packages/@aws-cdk/cloudformation-include/lib/cfn-include.ts b/packages/@aws-cdk/cloudformation-include/lib/cfn-include.ts index 0b875440fea22..2c135abba3377 100644 --- a/packages/@aws-cdk/cloudformation-include/lib/cfn-include.ts +++ b/packages/@aws-cdk/cloudformation-include/lib/cfn-include.ts @@ -558,23 +558,31 @@ export class CfnInclude extends core.CfnElement { const nestedStackProps = cfnParser.parseValue(nestedStackAttributes.Properties); const nestedStack = new core.NestedStack(this, nestedStackId, { - parameters: nestedStackProps.Parameters, + parameters: this.parametersForNestedStack(nestedStackProps.Parameters, nestedStackId), notificationArns: nestedStackProps.NotificationArns, timeout: nestedStackProps.Timeout, }); + const template = new CfnInclude(nestedStack, nestedStackId, this.nestedStacksToInclude[nestedStackId]); + this.nestedStacks[nestedStackId] = { stack: nestedStack, includedTemplate: template }; // we know this is never undefined for nested stacks const nestedStackResource: core.CfnResource = nestedStack.nestedStackResource!; cfnParser.handleAttributes(nestedStackResource, nestedStackAttributes, nestedStackId); + return nestedStackResource; + } - const propStack = this.nestedStacksToInclude[nestedStackId]; - const template = new CfnInclude(nestedStack, nestedStackId, { - templateFile: propStack.templateFile, - nestedStacks: propStack.nestedStacks, - }); - const includedStack: IncludedNestedStack = { stack: nestedStack, includedTemplate: template }; - this.nestedStacks[nestedStackId] = includedStack; + private parametersForNestedStack(parameters: any, nestedStackId: string): { [key: string]: any } | undefined { + if (parameters == null) { + return undefined; + } - return nestedStackResource; + const parametersToReplace = this.nestedStacksToInclude[nestedStackId].parameters ?? {}; + const ret: { [key: string]: string } = {}; + for (const paramName of Object.keys(parameters)) { + if (!(paramName in parametersToReplace)) { + ret[paramName] = parameters[paramName]; + } + } + 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 04448fa36f612..69ce97dcca07d 100644 --- a/packages/@aws-cdk/cloudformation-include/test/nested-stacks.test.ts +++ b/packages/@aws-cdk/cloudformation-include/test/nested-stacks.test.ts @@ -1,5 +1,5 @@ import * as path from 'path'; -import { ResourcePart } from '@aws-cdk/assert'; +import { ABSENT, ResourcePart } from '@aws-cdk/assert'; import '@aws-cdk/assert/jest'; import * as s3 from '@aws-cdk/aws-s3'; import * as core from '@aws-cdk/core'; @@ -9,7 +9,7 @@ import * as futils from '../lib/file-utils'; /* eslint-disable quote-props */ /* eslint-disable quotes */ -describe('CDK Include', () => { +describe('CDK Include for nested stacks', () => { let stack: core.Stack; beforeEach(() => { @@ -612,6 +612,66 @@ describe('CDK Include', () => { ); }); }); + + describe('for a parameter passed to the included child stack', () => { + let parentStack: core.Stack; + let childStack: core.Stack; + + beforeAll(() => { + parentStack = new core.Stack(); + const parentTemplate = new inc.CfnInclude(parentStack, 'ParentStack', { + templateFile: testTemplateFilePath('parent-two-parameters.json'), + nestedStacks: { + 'ChildStack': { + templateFile: testTemplateFilePath('child-two-parameters.json'), + parameters: { + 'FirstParameter': 'test-value', + }, + }, + }, + }); + childStack = parentTemplate.getNestedStack('ChildStack').stack; + }); + + test('correctly removes the parameter from the child stack', () => { + expect(childStack).toMatchTemplate({ + "Parameters": { + "SecondParameter": { + "Type": "String", + }, + }, + "Resources": { + "BucketImport": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketName": "test-value", + "AccessControl": { + "Ref": "SecondParameter", + }, + }, + }, + "GrandChildStack": { + "Type": "AWS::CloudFormation::Stack", + "Properties": { + "TemplateURL": "https://cfn-templates-set.s3.amazonaws.com/grandchild-import-stack.json", + "Parameters": { + "FirstParameter": "test-value", + }, + }, + }, + }, + }); + }); + + test('correctly removes the parameter from the parent stack', () => { + expect(parentStack).toHaveResourceLike('AWS::CloudFormation::Stack', { + "Parameters": { + "FirstParameter": ABSENT, + "SecondParameter": "second-value", + }, + }); + }); + }); }); function loadTestFileToJsObject(testTemplate: string): any { diff --git a/packages/@aws-cdk/cloudformation-include/test/test-templates/nested/child-two-parameters.json b/packages/@aws-cdk/cloudformation-include/test/test-templates/nested/child-two-parameters.json new file mode 100644 index 0000000000000..462f9a78d9389 --- /dev/null +++ b/packages/@aws-cdk/cloudformation-include/test/test-templates/nested/child-two-parameters.json @@ -0,0 +1,34 @@ +{ + "Parameters": { + "FirstParameter": { + "Type": "String" + }, + "SecondParameter": { + "Type": "String" + } + }, + "Resources": { + "BucketImport": { + "Type": "AWS::S3::Bucket", + "Properties": { + "BucketName": { + "Ref": "FirstParameter" + }, + "AccessControl": { + "Ref": "SecondParameter" + } + } + }, + "GrandChildStack": { + "Type": "AWS::CloudFormation::Stack", + "Properties": { + "TemplateURL": "https://cfn-templates-set.s3.amazonaws.com/grandchild-import-stack.json", + "Parameters": { + "FirstParameter": { + "Ref": "FirstParameter" + } + } + } + } + } +} diff --git a/packages/@aws-cdk/cloudformation-include/test/test-templates/nested/parent-two-parameters.json b/packages/@aws-cdk/cloudformation-include/test/test-templates/nested/parent-two-parameters.json new file mode 100644 index 0000000000000..cf1b9dc8fcbf8 --- /dev/null +++ b/packages/@aws-cdk/cloudformation-include/test/test-templates/nested/parent-two-parameters.json @@ -0,0 +1,14 @@ +{ + "Resources": { + "ChildStack": { + "Type": "AWS::CloudFormation::Stack", + "Properties": { + "TemplateURL": "https://cfn-templates-set.s3.amazonaws.com/grandchild-import-stack.json", + "Parameters": { + "FirstParameter": "first-value", + "SecondParameter": "second-value" + } + } + } + } +}