in packages/@aws-cdk/toolkit-lib/lib/api/cloudformation/evaluate-cloudformation-template.ts [210:317]
public async evaluateCfnExpression(cfnExpression: any): Promise<any> {
const self = this;
/**
* Evaluates CloudFormation intrinsic functions
*
* Note that supported intrinsic functions are documented in README.md -- please update
* list of supported functions when adding new evaluations
*
* See: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html
*/
class CfnIntrinsics {
public evaluateIntrinsic(intrinsic: Intrinsic): any {
const intrinsicFunc = (this as any)[intrinsic.name];
if (!intrinsicFunc) {
throw new CfnEvaluationException(`CloudFormation function ${intrinsic.name} is not supported`);
}
const argsAsArray = Array.isArray(intrinsic.args) ? intrinsic.args : [intrinsic.args];
return intrinsicFunc.apply(this, argsAsArray);
}
async 'Fn::Join'(separator: string, args: any[]): Promise<string> {
const evaluatedArgs = await self.evaluateCfnExpression(args);
return evaluatedArgs.join(separator);
}
async 'Fn::Split'(separator: string, args: any): Promise<string> {
const evaluatedArgs = await self.evaluateCfnExpression(args);
return evaluatedArgs.split(separator);
}
async 'Fn::Select'(index: number, args: any[]): Promise<string> {
const evaluatedArgs = await self.evaluateCfnExpression(args);
return evaluatedArgs[index];
}
async Ref(logicalId: string): Promise<string> {
const refTarget = await self.findRefTarget(logicalId);
if (refTarget) {
return refTarget;
} else {
throw new CfnEvaluationException(`Parameter or resource '${logicalId}' could not be found for evaluation`);
}
}
async 'Fn::GetAtt'(logicalId: string, attributeName: string): Promise<string> {
// ToDo handle the 'logicalId.attributeName' form of Fn::GetAtt
const attrValue = await self.findGetAttTarget(logicalId, attributeName);
if (attrValue) {
return attrValue;
} else {
throw new CfnEvaluationException(
`Attribute '${attributeName}' of resource '${logicalId}' could not be found for evaluation`,
);
}
}
async 'Fn::Sub'(template: string, explicitPlaceholders?: { [variable: string]: string }): Promise<string> {
const placeholders = explicitPlaceholders ? await self.evaluateCfnExpression(explicitPlaceholders) : {};
return asyncGlobalReplace(template, /\${([^}]*)}/g, (key) => {
if (key in placeholders) {
return placeholders[key];
} else {
const splitKey = key.split('.');
return splitKey.length === 1 ? this.Ref(key) : this['Fn::GetAtt'](splitKey[0], splitKey.slice(1).join('.'));
}
});
}
async 'Fn::ImportValue'(name: string): Promise<string> {
const exported = await self.lookupExport.lookupExport(name);
if (!exported) {
throw new CfnEvaluationException(`Export '${name}' could not be found for evaluation`);
}
if (!exported.Value) {
throw new CfnEvaluationException(`Export '${name}' exists without a value`);
}
return exported.Value;
}
}
if (cfnExpression == null) {
return cfnExpression;
}
if (Array.isArray(cfnExpression)) {
// Small arrays in practice
// eslint-disable-next-line @cdklabs/promiseall-no-unbounded-parallelism
return Promise.all(cfnExpression.map((expr) => this.evaluateCfnExpression(expr)));
}
if (typeof cfnExpression === 'object') {
const intrinsic = this.parseIntrinsic(cfnExpression);
if (intrinsic) {
return new CfnIntrinsics().evaluateIntrinsic(intrinsic);
} else {
const ret: { [key: string]: any } = {};
for (const [key, val] of Object.entries(cfnExpression)) {
ret[key] = await this.evaluateCfnExpression(val);
}
return ret;
}
}
return cfnExpression;
}