Adding flexibility to your deployments with Lambda Web Adapter
Tags: lambdawebadapter, lambda, fargate, flexiblearchitecture
Published: 2024-04-19 08:48:07

![Photo by [Lea L](https://unsplash.com/@leladesign?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&utm_medium=referral)](https://cdn-images-1.medium.com/max/8510/0*XWhUspOFaIMUneQD)

[Lambda Web Adapter](https://github.com/awslabs/aws-lambda-web-adapter) (LWA) is an open-source project that enables running Web apps on Lambda functions without the need to change or adapt the code.

In my opinion, LWA opens up interesting pathways for architecture evolution: While it’s an interesting tool that helps lift & shift Web apps and APIs to Lambda functions without a lot of effort, it can also enable another migration path : Start deploying your application in Lambda as a Lambdalith and then transition to a classical container deployment when needed (_e.g._ ECS Fargate). In some scenarios, it may happen that you don’t have enough data to decide whether it’s better to host on Lambda or on Fargate. LWA contributes by adding portability to your deployments.

In this article, we’ll explore how to use LWA with CDK to simplify the deployment of your Web apps in Lambda and how to easily transition to ECS Fargate.

## How Lambda Web adapter works ?

LWA is a Lambda extension. It creates an independent process within the Lambda execution environment that listens for incoming events and forwards them to your HTTP server.

![Lambda Web Adapter](https://cdn-images-1.medium.com/max/3964/1*Nxwb4iEtSkvmTvFGzpGjYQ.png)

LWA can integrate with invocations from Lambda function URLs (FURL), ALB, and API Gateway, converting invocation JSON payloads into HTTP requests that web frameworks like fastify or ASP.NET can handle. LWA also supports non-HTTP triggers (_e.g._ SQS, S3 notifications), but in these cases, it acts as a pass-through without converting the invocation payload.

LWA supports functions packaged as zip as well as Docker or OCI images.

## Solution overview

Let’s have a look on how we’ll create our flexible deployment using CDK. In this example we’ll be focusing on deploying a public Web application using [fastify](https://fastify.dev/) as a Web framework:

![](https://cdn-images-1.medium.com/max/4272/1*MZgr0wk-x9Mn0QpP6SFJ4Q.png)

My objective is to create a CDK construct supporting two deployment strategies: **Lambda** or **ECSFargate**. Depending on the selected strategy, only the required components will be deployed:

- When in Lambda mode, we’ll configure the function to use LWA extension. We’ll also configure a REST API Gateway with lambda proxy integration.

- When the deployment mode is ECSFargate, We will deploy our application in an ECS Fargate service that is exposed via an ALB.

In both of these deployment strategies, users access the Web app through CloudFront. We will associate a WAF Web ACL to restrict access to both the API Gateway and the Application Load Balancer. These origins will only respond to requests that include a custom verification header added by the CloudFront distribution. This approach prevents bypassing the CloudFront distribution to access the origin directly.

☝️**Some notes:**

When deploying in Lambda, I ruled-out the use of FURL or HTTP API Gateway:

- With FURL, while you can setup Origin Access Control (OAC) with CloudFront, at the time of writing, [PUT and POST operations require the client to sign the request payload](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-lambda.html#create-oac-overview-lambda).

- HTTP API Gateway does not support WAF. An alternative solution would involve creating a Lambda@Edge to verify the presence of the custom header in the request.

To improve security, the custom verification header can be stored in secrets manager with [rotation](https://github.com/aws-samples/amazon-cloudfront-waf-secretsmanager) enabled so that the header can be updated as well as the origin WAF and the CloudFront distribution configurations.

## Let’s see the code

Here are the relevant parts of the solution:

### 1- Deploying fastify Web app on Lambda using LWA

Creating a new fastify project is a breeze, I generally go with [typescript](https://fastify.dev/docs/latest/Reference/TypeScript/); for the purpose of this article, I will create one super basic api:

```
    import fastify from "fastify";

    const server = fastify();

    server.get("/health", async () => {
      return "yup ! I am healthy";
    });

    server.get("/where-are-you-deployed", async () => {
      return {
        "i-am-deployed-on": process.env.DEPLOYED_ON,
      };
    });

    server.listen({ host: "0.0.0.0", port: 8080 }, (err, address) => {
      if (err) {
        console.error(err);
        process.exit(1);
      }
      console.log(`Server listening at ${address}`);
    });
```

I will deploy the Lambda function using zip archive and in order to use LWA as a Lambda extension, we’ll need to:

- Attach the LWA layer to the function

- Set the handler to the startup command [run.sh](https://github.com/ziedbentahar/flexible-deployment-with-lambda-web-adapter/blob/main/src/run.sh) script. This script starts the fastify Web app. It will be added to the zip package after the code bundling.

- And lastly, define the `AWS_LAMBDA_EXEC_WRAPPER` environment variable to `/opt/bootstrap`

  {% gist https://gist.github.com/ziedbentahar/9ccc6411065f5aeb73a05a4ebb9d8449.js %}

  The [RestApi](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_apigateway.RestApi.html) CDK construct simplifies exposition of the Lambda function:

  {% gist https://gist.github.com/ziedbentahar/80fe6510291f29eaaa3414b65b8b8fbb.js %}

  After deployment, you will be able to view in the console the layer associated with the function:

![Lambda with LWA layer configured](https://cdn-images-1.medium.com/max/6112/1*l2-x_V0rcVbsZMb1_MXIkQ.png)

### 2- Defining the alternative deployment on ECS Fargate

CDK offers an [L3 construct](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ecs_patterns.ApplicationLoadBalancedFargateService.html) to deploy a load balanced ECS service. What I find interesting about this construct is that it hides all the complexity and verbosity of defining such a deployment, while allowing a level of flexibility. Another neat feature is that it can build and push [our container](https://github.com/ziedbentahar/flexible-deployment-with-lambda-web-adapter/blob/main/src/Dockerfile) image.

We’ll make sure to enable HTTPS, for that we’ll create a certificate and associated to the load balancer:

{% gist https://gist.github.com/ziedbentahar/628a0809af13da168c78b08e80ced691.js %}

Here, I am using the default configuration, but you will want to adapt it to your own requirements.

Once deployed, the ECS service looks like this on the AWS console

![Solution overview](https://cdn-images-1.medium.com/max/6132/1*HxmV9l15tk2gNoajI1iJIg.png)

You can find the full definition of the construct [here](https://github.com/ziedbentahar/flexible-deployment-with-lambda-web-adapter/blob/main/infra/lib/WebAppOnECSFargate.ts).

### 3- Handling the two different deployment strategies

The important bit, the CDK construct that enables flexible deployments:

{% gist https://gist.github.com/ziedbentahar/d3c48de9153b6fa1d5d96baf72e28352.js %}

This construct handles two deployment strategies `Lambda` or `ECSFargate`. 
For each strategy, we’ll need to provide a factory function that creates the required resources. These two functions need to be injected from a parent construct and they are lazily evaluated given the selected strategy.

We’ll also make sure that the distribution cache policy is disabled for both of these two origins.

As an example, the origin of the distribution, should end up looking like this when you select Lambda deployment strategy

![Configured origin on “Lambda + RestAPI” mode](https://cdn-images-1.medium.com/max/5172/1*kSi63nG4AUSV0HmjDg345Q.png)

And finally, let’s see how to define the WAF WebACL with a rule that checks the x-origin-header verification header:

{% gist https://gist.github.com/ziedbentahar/697faf8f9293c2fd5265c18a2e86194f.js %}

You can follow [this link](https://github.com/ziedbentahar/flexible-deployment-with-lambda-web-adapter/blob/main/infra/lib/WebAppDeployment.ts) for the complete WebAppDeployment construct definition

## Flexible deployment in action

Before wapping up, let’s call where-are-you-deployed endpoint defined in the sample web app for each strategy.

![calling the “where-are-you-deployed” endpoint from Postman](https://cdn-images-1.medium.com/max/6162/1*Fe8iQkPEecXnjLdudm_V2A.png)

## Wrapping up

Lambda web adapter is certainly not the only tool that helps running full-fledged web apps on Lambda functions, but it simplifies their deployment while supporting architectural evolution.

In this article we have seen how to build a CDK construct that offers a way to deploy the same web app on two distinct platforms, we can choose Lambda function or ECS Fargate by specifying a configuration during design time. We can extend this further by enabling the system to automatically redeploy itself, during runtime, on a specific target based on some events or CloudWatch alarms !

As always, you can find the full code source, ready to be adapted and deployed here:
[**GitHub - ziedbentahar/flexible-deployments-with-lambda-web-adapter**](https://github.com/ziedbentahar/flexible-deployments-with-lambda-web-adapter)

Thanks for reading and hope you enjoyed it !

## Resources

[**Restricting access to Application Load Balancers**](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/restrict-access-to-load-balancer.html)

[**GitHub - awslabs/aws-lambda-web-adapter: Run web applications on AWS Lambda**](https://github.com/awslabs/aws-lambda-web-adapter)

[**GitHub - aws-samples/amazon-cloudfront-waf-secretsmanager: Enhance Amazon CloudFront Origin…**](https://github.com/aws-samples/amazon-cloudfront-waf-secretsmanager)