Skip to content

Commit 094d843

Browse files
committed
Add includeNestedStack() method.
1 parent 495aeb9 commit 094d843

File tree

3 files changed

+77
-2
lines changed

3 files changed

+77
-2
lines changed

packages/@aws-cdk/cloudformation-include/README.md

+9
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,15 @@ role.addToPolicy(new iam.PolicyStatement({
311311
}));
312312
```
313313

314+
You can also include the nested stack after the `CfnInclude` object was created,
315+
instead of doing it on construction:
316+
317+
```ts
318+
parentTemplate.includeNestedStack('ChildTemplate', {
319+
templateFile: 'path/to/my-nested-template.json',
320+
});
321+
```
322+
314323
## Vending CloudFormation templates as Constructs
315324

316325
In many cases, there are existing CloudFormation templates that are not entire applications,

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

+54-2
Original file line numberDiff line numberDiff line change
@@ -289,10 +289,62 @@ export class CfnInclude extends core.CfnElement {
289289
return ret;
290290
}
291291

292+
/**
293+
* Includes a template for a child stack inside of this parent template.
294+
* A child with this logical ID must exist in the template,
295+
* and be of type AWS::CloudFormation::Stack.
296+
* This is equivalent to specifying the value in the {@link CfnIncludeProps.nestedStacks}
297+
* property on object construction.
298+
*
299+
* @param logicalId the ID of the stack to retrieve, as it appears in the template
300+
* @param nestedStackProps the properties of the included child Stack
301+
* @returns the same {@link IncludedNestedStack} object that {@link getNestedStack} returns for this logical ID
302+
*/
303+
public includeNestedStack(logicalId: string, nestedStackProps: CfnIncludeProps): IncludedNestedStack {
304+
if (logicalId in this.nestedStacks) {
305+
throw new Error(`Nested Stack '${logicalId}' was already included in its parent template`);
306+
}
307+
const cfnStack = this.resources[logicalId];
308+
if (!cfnStack) {
309+
throw new Error(`Nested Stack with logical ID '${logicalId}' was not found in the template`);
310+
}
311+
if (cfnStack instanceof core.CfnStack) {
312+
// delete the old CfnStack child - one will be created by the NestedStack object
313+
this.node.tryRemoveChild(logicalId);
314+
315+
const self = this;
316+
const finder: cfn_parse.ICfnFinder = {
317+
findResource(lId): core.CfnResource | undefined {
318+
return self.resources[lId];
319+
},
320+
findRefTarget(elementName: string): core.CfnElement | undefined {
321+
return self.resources[elementName] ?? self.parameters[elementName];
322+
},
323+
findCondition(conditionName: string): core.CfnCondition | undefined {
324+
return self.conditions[conditionName];
325+
},
326+
findMapping(mappingName): core.CfnMapping | undefined {
327+
return self.mappings[mappingName];
328+
},
329+
};
330+
const cfnParser = new cfn_parse.CfnParser({
331+
finder,
332+
parameters: this.parametersToReplace,
333+
});
334+
// createNestedStack() expects this to be filled
335+
this.nestedStacksToInclude[logicalId] = nestedStackProps;
336+
this.createNestedStack(logicalId, cfnParser);
337+
return this.nestedStacks[logicalId];
338+
} else {
339+
throw new Error(`Resource with logical ID '${logicalId}' is not a CloudFormation Stack`);
340+
}
341+
}
342+
292343
/**
293344
* Returns the NestedStack with name logicalId.
294-
* For a nested stack to be returned by this method, it must be specified in the {@link CfnIncludeProps.nestedStacks}
295-
* property.
345+
* For a nested stack to be returned by this method,
346+
* it must be specified either in the {@link CfnIncludeProps.nestedStacks} property,
347+
* or through the {@link includeNestedStack} method.
296348
*
297349
* @param logicalId the ID of the stack to retrieve, as it appears in the template
298350
*/

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

+14
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,20 @@ describe('CDK Include for nested stacks', () => {
424424
});
425425
});
426426

427+
test('can lazily include a single child nested stack', () => {
428+
const parentTemplate = new inc.CfnInclude(stack, 'ParentStack', {
429+
templateFile: testTemplateFilePath('parent-one-child.json'),
430+
});
431+
const includedChild = parentTemplate.includeNestedStack('ChildStack', {
432+
templateFile: testTemplateFilePath('child-no-bucket.json'),
433+
});
434+
435+
expect(includedChild.stack).toMatchTemplate(
436+
loadTestFileToJsObject('child-no-bucket.json'),
437+
);
438+
expect(includedChild.includedTemplate.getResource('GrandChildStack')).toBeDefined();
439+
});
440+
427441
describe('for a parent stack with children and grandchildren', () => {
428442
let assetStack: core.Stack;
429443
let parentTemplate: inc.CfnInclude;

0 commit comments

Comments
 (0)