Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cfn-include): allow renaming the template elements logical IDs #10169

Merged
merged 2 commits into from
Sep 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions packages/@aws-cdk/cloudformation-include/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,3 +289,32 @@ role.addToPolicy(new iam.PolicyStatement({
resources: [cfnBucket.attrArn],
}));
```

## Vending CloudFormation templates as Constructs

In many cases, there are existing CloudFormation templates that are not entire applications,
but more like specialized fragments, implementing a particular pattern or best practice.
If you have templates like that,
you can use the `CfnInclude` class to vend them as a CDK Constructs:

```ts
import * as path from 'path';

export class MyConstruct extends Construct {
constructor(scope: Construct, id: string) {
super(scope, id);

// include a template inside the Construct
new cfn_inc.CfnInclude(this, 'MyConstruct', {
templateFile: path.join(__dirname, 'my-template.json'),
preserveLogicalIds: false, // <--- !!!
});
}
}
```

Notice the `preserveLogicalIds` parameter -
it makes sure the logical IDs of all the included template elements are re-named using CDK's algorithm,
guaranteeing they are unique within your application.
Without that parameter passed,
instantiating `MyConstruct` twice in the same Stack would result in duplicated logical IDs.
40 changes: 27 additions & 13 deletions packages/@aws-cdk/cloudformation-include/lib/cfn-include.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@ export interface CfnIncludeProps {
*/
readonly templateFile: string;

/**
* Whether the resources should have the same logical IDs in the resulting CDK template
* as they did in the original CloudFormation template file.
* If you're vending a Construct using an existing CloudFormation template,
* make sure to pass this as `false`.
*
* **Note**: regardless of whether this option is true or false,
* the {@link CfnInclude.getResource} and related methods always uses the original logical ID of the resource/element,
* as specified in the template file.
*
* @default true
*/
readonly preserveLogicalIds?: boolean;

/**
* Specifies the template files that define nested stacks that should be included.
*
Expand Down Expand Up @@ -86,8 +100,7 @@ export class CfnInclude extends core.CfnElement {
// read the template into a JS object
this.template = futils.readYamlSync(props.templateFile);

// ToDo implement preserveLogicalIds=false
this.preserveLogicalIds = true;
this.preserveLogicalIds = props.preserveLogicalIds ?? true;

// check if all user specified parameter values exist in the template
for (const logicalId of Object.keys(this.parametersToReplace)) {
Expand Down Expand Up @@ -326,7 +339,7 @@ export class CfnInclude extends core.CfnElement {
mapping: cfnParser.parseValue(this.template.Mappings[mappingName]),
});
this.mappings[mappingName] = cfnMapping;
cfnMapping.overrideLogicalId(mappingName);
this.overrideLogicalIdIfNeeded(cfnMapping, mappingName);
}

private createParameter(logicalId: string): void {
Expand Down Expand Up @@ -357,7 +370,7 @@ export class CfnInclude extends core.CfnElement {
noEcho: expression.NoEcho,
});

cfnParameter.overrideLogicalId(logicalId);
this.overrideLogicalIdIfNeeded(cfnParameter, logicalId);
this.parameters[logicalId] = cfnParameter;
}

Expand All @@ -384,7 +397,7 @@ export class CfnInclude extends core.CfnElement {
assertions: ruleProperties.Assertions,
});
this.rules[ruleName] = rule;
rule.overrideLogicalId(ruleName);
this.overrideLogicalIdIfNeeded(rule, ruleName);
}

private createOutput(logicalId: string, scope: core.Construct): void {
Expand Down Expand Up @@ -422,7 +435,7 @@ export class CfnInclude extends core.CfnElement {
})(),
});

cfnOutput.overrideLogicalId(logicalId);
this.overrideLogicalIdIfNeeded(cfnOutput, logicalId);
this.outputs[logicalId] = cfnOutput;
}

Expand Down Expand Up @@ -455,8 +468,7 @@ export class CfnInclude extends core.CfnElement {
expression: cfnParser.parseValue(this.template.Conditions[conditionName]),
});

// ToDo handle renaming of the logical IDs of the conditions
cfnCondition.overrideLogicalId(conditionName);
this.overrideLogicalIdIfNeeded(cfnCondition, conditionName);
this.conditions[conditionName] = cfnCondition;
return cfnCondition;
}
Expand Down Expand Up @@ -533,11 +545,7 @@ export class CfnInclude extends core.CfnElement {
}
}

if (this.preserveLogicalIds) {
// override the logical ID to match the original template
l1Instance.overrideLogicalId(logicalId);
}

this.overrideLogicalIdIfNeeded(l1Instance, logicalId);
this.resources[logicalId] = l1Instance;
return l1Instance;
}
Expand Down Expand Up @@ -585,4 +593,10 @@ export class CfnInclude extends core.CfnElement {
}
return ret;
}

private overrideLogicalIdIfNeeded(element: core.CfnElement, id: string): void {
if (this.preserveLogicalIds) {
element.overrideLogicalId(id);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ describe('CDK Include', () => {
);
});

xtest('correctly changes the logical IDs, including references, if imported with preserveLogicalIds=false', () => {
test('correctly changes the logical IDs, including references, if imported with preserveLogicalIds=false', () => {
const cfnTemplate = includeTestTemplate(stack, 'bucket-with-encryption-key.json', {
preserveLogicalIds: false,
});
Expand Down Expand Up @@ -177,6 +177,11 @@ describe('CDK Include', () => {
],
},
},
"Metadata": {
"Object1": "Location1",
"KeyRef": { "Ref": "MyScopeKey7673692F" },
"KeyArn": { "Fn::GetAtt": ["MyScopeKey7673692F", "Arn"] },
},
"DeletionPolicy": "Retain",
"UpdateReplacePolicy": "Retain",
},
Expand Down Expand Up @@ -918,7 +923,7 @@ function includeTestTemplate(scope: core.Construct, testTemplate: string, props:
return new inc.CfnInclude(scope, 'MyScope', {
templateFile: _testTemplateFilePath(testTemplate),
parameters: props.parameters,
// preserveLogicalIds: props.preserveLogicalIds,
preserveLogicalIds: props.preserveLogicalIds,
});
}

Expand Down