async function ensureUsEast1LambdaStack()

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),
  });
}