Skip to content

Commit 70322dc

Browse files
committed
fix(cfn-include): allow parameters to be replaced across nested stacks
Fixes #9838
1 parent 5bed08a commit 70322dc

File tree

4 files changed

+127
-11
lines changed

4 files changed

+127
-11
lines changed

packages/@aws-cdk/cloudformation-include/lib/cfn-include.ts

+17-9
Original file line numberDiff line numberDiff line change
@@ -556,23 +556,31 @@ export class CfnInclude extends core.CfnElement {
556556

557557
const nestedStackProps = cfnParser.parseValue(nestedStackAttributes.Properties);
558558
const nestedStack = new core.NestedStack(this, nestedStackId, {
559-
parameters: nestedStackProps.Parameters,
559+
parameters: this.parametersForNestedStack(nestedStackProps.Parameters, nestedStackId),
560560
notificationArns: nestedStackProps.NotificationArns,
561561
timeout: nestedStackProps.Timeout,
562562
});
563+
const template = new CfnInclude(nestedStack, nestedStackId, this.nestedStacksToInclude[nestedStackId]);
564+
this.nestedStacks[nestedStackId] = { stack: nestedStack, includedTemplate: template };
563565

564566
// we know this is never undefined for nested stacks
565567
const nestedStackResource: core.CfnResource = nestedStack.nestedStackResource!;
566568
cfnParser.handleAttributes(nestedStackResource, nestedStackAttributes, nestedStackId);
569+
return nestedStackResource;
570+
}
567571

568-
const propStack = this.nestedStacksToInclude[nestedStackId];
569-
const template = new CfnInclude(nestedStack, nestedStackId, {
570-
templateFile: propStack.templateFile,
571-
nestedStacks: propStack.nestedStacks,
572-
});
573-
const includedStack: IncludedNestedStack = { stack: nestedStack, includedTemplate: template };
574-
this.nestedStacks[nestedStackId] = includedStack;
572+
private parametersForNestedStack(parameters: any, nestedStackId: string): { [key: string]: any } | undefined {
573+
if (parameters == null) {
574+
return undefined;
575+
}
575576

576-
return nestedStackResource;
577+
const parametersToReplace = this.nestedStacksToInclude[nestedStackId].parameters ?? {};
578+
const ret: { [key: string]: string } = {};
579+
for (const paramName of Object.keys(parameters)) {
580+
if (!(paramName in parametersToReplace)) {
581+
ret[paramName] = parameters[paramName];
582+
}
583+
}
584+
return ret;
577585
}
578586
}

packages/@aws-cdk/cloudformation-include/test/nested-stacks.test.ts

+62-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as path from 'path';
2-
import { ResourcePart } from '@aws-cdk/assert';
2+
import { ABSENT, ResourcePart } from '@aws-cdk/assert';
33
import '@aws-cdk/assert/jest';
44
import * as s3 from '@aws-cdk/aws-s3';
55
import * as core from '@aws-cdk/core';
@@ -9,7 +9,7 @@ import * as futils from '../lib/file-utils';
99
/* eslint-disable quote-props */
1010
/* eslint-disable quotes */
1111

12-
describe('CDK Include', () => {
12+
describe('CDK Include for nested stacks', () => {
1313
let stack: core.Stack;
1414

1515
beforeEach(() => {
@@ -612,6 +612,66 @@ describe('CDK Include', () => {
612612
);
613613
});
614614
});
615+
616+
describe('for a parameter passed to the included child stack', () => {
617+
let parentStack: core.Stack;
618+
let childStack: core.Stack;
619+
620+
beforeAll(() => {
621+
parentStack = new core.Stack();
622+
const parentTemplate = new inc.CfnInclude(parentStack, 'ParentStack', {
623+
templateFile: testTemplateFilePath('parent-two-parameters.json'),
624+
nestedStacks: {
625+
'ChildStack': {
626+
templateFile: testTemplateFilePath('child-two-parameters.json'),
627+
parameters: {
628+
'FirstParameter': 'test-value',
629+
},
630+
},
631+
},
632+
});
633+
childStack = parentTemplate.getNestedStack('ChildStack').stack;
634+
});
635+
636+
test('correctly removes the parameter from the child stack', () => {
637+
expect(childStack).toMatchTemplate({
638+
"Parameters": {
639+
"SecondParameter": {
640+
"Type": "String",
641+
},
642+
},
643+
"Resources": {
644+
"BucketImport": {
645+
"Type": "AWS::S3::Bucket",
646+
"Properties": {
647+
"BucketName": "test-value",
648+
"AccessControl": {
649+
"Ref": "SecondParameter",
650+
},
651+
},
652+
},
653+
"GrandChildStack": {
654+
"Type": "AWS::CloudFormation::Stack",
655+
"Properties": {
656+
"TemplateURL": "https://cfn-templates-set.s3.amazonaws.com/grandchild-import-stack.json",
657+
"Parameters": {
658+
"FirstParameter": "test-value",
659+
},
660+
},
661+
},
662+
},
663+
});
664+
});
665+
666+
test('correctly removes the parameter from the parent stack', () => {
667+
expect(parentStack).toHaveResourceLike('AWS::CloudFormation::Stack', {
668+
"Parameters": {
669+
"FirstParameter": ABSENT,
670+
"SecondParameter": "second-value",
671+
},
672+
});
673+
});
674+
});
615675
});
616676

617677
function loadTestFileToJsObject(testTemplate: string): any {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"Parameters": {
3+
"FirstParameter": {
4+
"Type": "String"
5+
},
6+
"SecondParameter": {
7+
"Type": "String"
8+
}
9+
},
10+
"Resources": {
11+
"BucketImport": {
12+
"Type": "AWS::S3::Bucket",
13+
"Properties": {
14+
"BucketName": {
15+
"Ref": "FirstParameter"
16+
},
17+
"AccessControl": {
18+
"Ref": "SecondParameter"
19+
}
20+
}
21+
},
22+
"GrandChildStack": {
23+
"Type": "AWS::CloudFormation::Stack",
24+
"Properties": {
25+
"TemplateURL": "https://cfn-templates-set.s3.amazonaws.com/grandchild-import-stack.json",
26+
"Parameters": {
27+
"FirstParameter": {
28+
"Ref": "FirstParameter"
29+
}
30+
}
31+
}
32+
}
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"Resources": {
3+
"ChildStack": {
4+
"Type": "AWS::CloudFormation::Stack",
5+
"Properties": {
6+
"TemplateURL": "https://cfn-templates-set.s3.amazonaws.com/grandchild-import-stack.json",
7+
"Parameters": {
8+
"FirstParameter": "first-value",
9+
"SecondParameter": "second-value"
10+
}
11+
}
12+
}
13+
}
14+
}

0 commit comments

Comments
 (0)