in src/cfn-custom-resources/us-east-1-lambda-stack/index.ts [105:216]
async function ensureUsEast1LambdaStack(props: {
stackId: string;
stackName: string;
checkAuthHandlerArn: string;
parseAuthHandlerArn: string;
refreshAuthHandlerArn: string;
httpHeadersHandlerArn: string;
signOutHandlerArn: string;
trailingSlashHandlerArn?: string;
lambdaRoleArn: string;
requestType: "Create" | "Update" | "Delete";
physicalResourceId: string | undefined;
}) {
// This function will create/update a stack in us-east-1, with the Lambda@Edge functions
// (or clean up after itself upon deleting)
// If we're deleting, delete the us-east-1 stack, if it still exists
if (props.requestType === "Delete") {
console.log("Getting status of us-east-1 stack ...");
const { Stacks: stacks } = await CFN_CLIENT_US_EAST_1.describeStacks({
StackName: props.stackName,
})
.promise()
.catch(() => ({ Stacks: undefined }));
if (stacks?.length) {
console.log("Deleting us-east-1 stack ...");
await CFN_CLIENT_US_EAST_1.deleteStack({
StackName: props.stackName,
}).promise();
console.log("us-east-1 stack deleted");
} else {
console.log("us-east-1 stack already deleted");
}
return;
}
// To be able to create the Lambda@Edge functions in us-east-1 we first need to create
// an S3 bucket there, to hold the code.
const deploymentBucket = await ensureDeploymentUsEast1Stack(props);
// To get the Lambda@Edge configuration, we'll simply download the CloudFormation template for
// this, the current, stack, and use the configuration that is in there.
console.log("Getting CFN template ...");
const { TemplateBody: originalTemplate } = await CFN_CLIENT.getTemplate({
StackName: props.stackId,
TemplateStage: "Processed",
}).promise();
if (!originalTemplate)
throw new Error(
`Failed to get template for stack ${props.stackName} (${props.stackId})`
);
const parsedOriginalTemplate = JSON.parse(
originalTemplate
) as CfnTemplateWithLambdas;
const templateForUsEast1 = JSON.parse(
US_EAST_1_STACK_BASE_TEMPLATE
) as CfnTemplateWithLambdas;
// For each concerned lambda, extract it's configuration from the downloaded CloudFormation template
// and add it to the new template, for us-east-1 deployment
await Promise.all(
LAMBDA_NAMES.map((lambdaName) => {
const lambdaProperty = Object.entries(props).find(
([key, lambdaArn]) =>
key.toLowerCase().startsWith(lambdaName.toLowerCase()) && !!lambdaArn
);
if (!lambdaProperty) {
console.log(
`Couldn't locate ARN for lambda ${lambdaName} in input properties: ${JSON.stringify(
props,
null,
2
)}`
);
return;
}
// Copy the Lambda code to us-east-1, and set that location in the new CloudFormation template
const lambdaResource = parsedOriginalTemplate.Resources[lambdaName]!;
return copyLambdaCodeToUsEast1({
lambdaArn: lambdaProperty[1]!,
toBucket: deploymentBucket,
key: lambdaResource.Properties.Code.S3Key,
}).then(() => {
const updatedLambdaResource: CfnLambdaResource = lambdaResource;
updatedLambdaResource.Properties.Code.S3Bucket = deploymentBucket;
delete updatedLambdaResource.Condition;
updatedLambdaResource.Properties.Role = props.lambdaRoleArn;
templateForUsEast1.Resources[lambdaName] = updatedLambdaResource;
templateForUsEast1.Outputs[lambdaName] = {
Value: {
"Fn::GetAtt": [lambdaName, "Arn"],
},
Export: {
Name: {
"Fn::Sub": "${AWS::StackName}-" + lambdaName,
},
},
};
});
})
);
console.log(
"Constructed CloudFormation template for Lambda's:",
JSON.stringify(templateForUsEast1, null, 2)
);
// Deploy the template with the Lambda@Edge functions to us-east-1
return ensureLambdaUsEast1Stack({
...props,
newTemplate: JSON.stringify(templateForUsEast1),
});
}