in packages/aws-cdk-lib/cloudformation-include/lib/cfn-include.ts [627:756]
private getOrCreateResource(logicalId: string, cycleChain: string[] = []): core.CfnResource {
cycleChain = cycleChain.concat([logicalId]);
if (cycleChain.length !== new Set(cycleChain).size) {
if (!this.allowCyclicalReferences) {
throw new core.ValidationError(`Found a cycle between resources in the template: ${cycleChain.join(' depends on ')}`, this);
}
// only allow one placeholder per logical id
if (this.logicalIdToPlaceholderMap.get(logicalId)) {
return this.resources[this.logicalIdToPlaceholderMap.get(logicalId)!];
}
let placeholderResourceAttributes: any = this.template.Resources[logicalId];
let placeholderId: string = this.getPlaceholderID();
this.logicalIdToPlaceholderMap.set(logicalId, placeholderId);
let placeholderInstance = new core.CfnResource(this, placeholderId, {
type: placeholderResourceAttributes.Type,
properties: {},
});
placeholderInstance.overrideLogicalId(placeholderId);
this.resources[placeholderId] = placeholderInstance;
return placeholderInstance;
}
const ret = this.resources[logicalId];
if (ret) {
return ret;
}
const self = this;
const finder: cfn_parse.ICfnFinder = {
findCondition(conditionName: string): core.CfnCondition | undefined {
return self.conditions[conditionName];
},
findMapping(mappingName): core.CfnMapping | undefined {
return self.mappings[mappingName];
},
findResource(lId: string): core.CfnResource | undefined {
if (!(lId in (self.template.Resources || {}))) {
return undefined;
}
return self.getOrCreateResource(lId, cycleChain);
},
findRefTarget(elementName: string): core.CfnElement | undefined {
if (elementName in self.parameters) {
return self.parameters[elementName];
}
return this.findResource(elementName);
},
};
const cfnParser = new cfn_parse.CfnParser({
finder,
parameters: this.parametersToReplace,
});
const resourceAttributes: any = this.template.Resources[logicalId];
let l1Instance: core.CfnResource;
if (this.nestedStacksToInclude[logicalId] && this.dehydratedResources.includes(logicalId)) {
throw new core.ValidationError(`nested stack '${logicalId}' was marked as dehydrated - nested stacks cannot be dehydrated`, this);
} else if (this.nestedStacksToInclude[logicalId]) {
l1Instance = this.createNestedStack(logicalId, cfnParser);
} else if (this.dehydratedResources.includes(logicalId)) {
l1Instance = new core.CfnResource(this, logicalId, {
type: resourceAttributes.Type,
properties: resourceAttributes.Properties,
});
const cfnOptions = l1Instance.cfnOptions;
cfnOptions.creationPolicy = resourceAttributes.CreationPolicy;
cfnOptions.updatePolicy = resourceAttributes.UpdatePolicy;
cfnOptions.deletionPolicy = resourceAttributes.DeletionPolicy;
cfnOptions.updateReplacePolicy = resourceAttributes.UpdateReplacePolicy;
cfnOptions.version = resourceAttributes.Version;
cfnOptions.description = resourceAttributes.Description;
cfnOptions.metadata = resourceAttributes.Metadata;
this.resources[logicalId] = l1Instance;
return l1Instance;
} else {
const l1ClassFqn = cfn_type_to_l1_mapping.lookup(resourceAttributes.Type);
// The AWS::CloudFormation::CustomResource type corresponds to the CfnCustomResource class.
// Unfortunately, it's quite useless; it only has a single property, ServiceToken.
// For that reason, even the CustomResource class from @core doesn't use it!
// So, special-case the handling of this one resource type
if (l1ClassFqn && resourceAttributes.Type !== 'AWS::CloudFormation::CustomResource') {
const options: cfn_parse.FromCloudFormationOptions = {
parser: cfnParser,
};
const [moduleName, ...className] = l1ClassFqn.split('.');
const module = require(moduleName); // eslint-disable-line @typescript-eslint/no-require-imports
const jsClassFromModule = module[className.join('.')];
l1Instance = jsClassFromModule._fromCloudFormation(this, logicalId, resourceAttributes, options);
} else {
l1Instance = new core.CfnResource(this, logicalId, {
type: resourceAttributes.Type,
properties: cfnParser.parseValue(resourceAttributes.Properties),
});
cfnParser.handleAttributes(l1Instance, resourceAttributes, logicalId);
}
}
/*
1. remove placeholder version of object created for cycle breaking
2. override logical id before deletion so references to the placeholder instead reference the original
*/
if (this.logicalIdToPlaceholderMap.get(logicalId)) {
let placeholderId: string = this.logicalIdToPlaceholderMap.get(logicalId)!;
this.resources[placeholderId].overrideLogicalId(logicalId);
this.node.tryRemoveChild(placeholderId);
delete this.resources[placeholderId];
}
this.overrideLogicalIdIfNeeded(l1Instance, logicalId);
this.resources[logicalId] = l1Instance;
// handle any unknown attributes using overrides
const knownAttributes = [
'Condition', 'DependsOn', 'Description', 'Metadata', 'Properties', 'Type', 'Version',
'CreationPolicy', 'DeletionPolicy', 'UpdatePolicy', 'UpdateReplacePolicy',
];
for (const [attrName, attrValue] of Object.entries(resourceAttributes)) {
if (!knownAttributes.includes(attrName)) {
l1Instance.addOverride(attrName, cfnParser.parseValue(attrValue));
}
}
return l1Instance;
}