AWS CDK: Use Lambda with Application Load Balancer

March 11th, 2021 362 Words

When coming from a serverless perspective, setting up an Application Load Balancer with VPC sounds like much to do for just invoking an AWS Lambda function. Thanks to the AWS CDK, it’s not that complex.

Together with Route 53 and the AWS Certificate Manager, you can easily configure a custom domain as well.

import * as lambda from "@aws-cdk/aws-lambda-nodejs";
import * as ec2 from "@aws-cdk/aws-ec2";
import * as targets from "@aws-cdk/aws-elasticloadbalancingv2-targets";
import * as elbv2 from "@aws-cdk/aws-elasticloadbalancingv2";
import * as cdk from "@aws-cdk/core";
import * as route53 from "@aws-cdk/aws-route53";
import * as acm from "@aws-cdk/aws-certificatemanager";
import * as route53targets from "@aws-cdk/aws-route53-targets";

const hostname = "foo.example.com";

export class APIStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    /**
    
      YOUR RESOURCES
    
    **/
  }
}

After importing all the needed packages, you need to connect the dots:

// Create the Route 53 Hosted Zone
const zone = new route53.HostedZone(this, "HostedZone", {
  zoneName: hostname,
});

// Create a new SSL certificate in ACM
const cert = new acm.Certificate(this, "Certificate", {
  domainName: hostname,
  validation: acm.CertificateValidation.fromDns(zone),
});

// Reference your AWS Lambda function (in TypeScript)
const func = new lambda.NodejsFunction(this, "Handler", {
  entry: "src/handler/index.ts",
  handler: "run",
});

// Create a VPC in two AZ
const vpc = new ec2.Vpc(this, "VPC", {
  maxAzs: 2,
});

// Create an internet-facing Application Load Balancer using the VPC
const lb = new elbv2.ApplicationLoadBalancer(this, "LB", {
  vpc,
  internetFacing: true,
});

// Add a listener on port 443 for and use the certificate for HTTPS
const listenerHTTPS = lb.addListener("HTTPSListener", {
  port: 443,
  certificates: [cert],
});

// Add a target with the AWS Lambda function to the listener
listenerHTTPS.addTargets("HTTPSListenerTargets", {
  targets: [new targets.LambdaTarget(func)],
  healthCheck: {
    enabled: true,
  },
});

// Enable a redirect from port 80 to 443
lb.addRedirect();

// Add a Route 53 alias with the Load Balancer as the target
new route53.ARecord(this, "AliasRecord", {
  zone: zone,
  target: route53.RecordTarget.fromAlias(
    new route53targets.LoadBalancerTarget(lb)
  ),
});

The AWS Lambda function must return an object for the HTTP response.

// src/handler/index.ts

export const run = async (event: {}) => {
  console.log("Incoming Event", JSON.stringify(event));

  return {
    body: "<h1>Hello from Lambda!</h1>",
    statusCode: 200,
    statusDescription: "200 OK",
    isBase64Encoded: false,
    headers: {
      "Content-Type": "text/html",
    },
  };
};