Registering AWS Lambda Function as the target for AWS ALB using serverless framework
Tags: aws, serverless, node
Published: 2023-07-17 16:34:53



AWS Elastic Load Balancer (ELB) are used to distribute traffic across multiple targets such as EC2 Instances, Lambda Functions, Private IP (private servers) address, in one or more Availability Zones.

AWS ELB is of three types:

1. Application Load Balancer (ALB)
2. Network Load Balancer (NLB)
3. Gateway Load Balancer (GLB)




**Application Load Balancer**

An Application Load Balancer functions at the application layer, the seventh layer of the Open Systems Interconnection (OSI) model. After the load balancer receives a request, it evaluates the listener rules in priority order to determine which rule to apply, and then selects a target from the target group for the rule action. You can configure listener rules to route requests to different target groups based on the content of the application traffic. Routing is performed independently for each target group, even when a target is registered with multiple target groups.

The targets of the load balancer can be changed as per requirements, without disrupting the overall flow of requests to your application.

Health checks can be configured on the load balancer. It monitors the health of the registered targets. This ensures that the traffic is only redirected to healthy targets without compromising on the overall application availability for the users.



**AWS Lambda Function**

AWS Lambda is a Function as a Service(FaaS), in which users are only responsible for the working code and are not concerned with the overload of provisioning or managing the servers. All this is taken care by the lambda function itself internally.

The users need to submit the code in one of the language runtimes supported by Lambda function.The Lambda service runs your function only when needed and scales automatically. You only pay for the compute time that you consume i.e. there is no charge when your code is not running.



**Configuring Lambda Function as the target for ALB**

In order to register Lambda function as the target for your ALB using serverless framework, there are 2 ways. One is using the default VPC provided by AWS for deploying the resources. The other is creating a custom VPC & deploying the ALB in that VPC. Depending on your requirements, either of one can be followed.


**Using Default VPC**

The AWS provided default VPC can be leveraged for deploying the ALB and the lambda functions in a particular region. All you need is to provide the vpc, subnets and security group information (allowing inbound/outbound rules) in the provider section of the serverless.yml file.

```
custom:
  vpc:
    id: 
      - vpc-0xxxxxxxxxxxxxxxb
    subnetIds: 
      - subnet-0xxxxxxxxxxxxxxxc
      - subnet-0xxxxxxxxxxxxxxx9
      - subnet-0xxxxxxxxxxxxxxx7
    securityGroupIds: 
      - sg-0xxxxxxxxxxxxxxxa
```
Replace with the actual default values provided by AWS. Like this, constants can be created first in the custom section of the serverless.yml & further referred in the provider section as

```
 vpc:
    securityGroupIds: ${self:custom.vpc.securityGroupIds}
    subnetIds: ${self:custom.vpc.subnetIds}
```


**Using custom VPC**

If the requirement is to create a custom VPC, it can be done by creating the required resources in the resources section of the serverless.yml file


**Creating VPC**

```
VPC:
      Type: AWS::EC2::VPC
      Properties:
        CidrBlock: 12.2.1.0/24
        EnableDnsHostnames: true
        EnableDnsSupport: true
```

Define the CidrBlock according to your requirements (IP/16, IP/24, etc). EnableDnsHostnames is enabled to provide DNS names to the resources launched in this VPC. EnableDnsSupport is enabled to allow AWS provided DNS service to resolve public DNS names to IP addresses.


**Creating Public Subnets:**
Public subnets can be created for the VPC created above. In this article, two public subnets each having 125 host configuration is created using the snippet:

```
SubnetOne:
      Type: AWS::EC2::Subnet
      Properties:
        VpcId: { Ref: VPC }
        CidrBlock: 12.2.1.0/25
        MapPublicIpOnLaunch: true
        AvailabilityZone: !Select 
          - 0
          - Fn::GetAZs: {Ref: 'AWS::Region'}
SubnetTwo:
      Type: AWS::EC2::Subnet
      Properties:
        VpcId: { Ref: VPC }
        MapPublicIpOnLaunch: true
        CidrBlock: 12.2.1.128/25
        AvailabilityZone: !Select 
          - 1
          - Fn::GetAZs: { Ref: 'AWS::Region'}
```
Provide the logical name of the VPC in the VPCId. 

> Ref is a AWS Cloud Formation intrinsic function that returns the value of the parameter or the resource. In this example, the resource logical name is passed. Hence it returns the physical ID of the referenced resource.

The MapPublicIpOnLaunch assigns a public IPV4 address to the resources launched under this subnet. To define the availability zone for the subnet, the cloud formation intrinsic functions can be leveraged or a hard-coded AZ can be mentioned as well.

> AWS::Region is an AWS Cloud Formation intrinsic function that returns the region in which the resource will be deployed.

In order for the VPC to be connected to internet, it requires an Internet Gateway. Hence, create the Internet Gateway & VPCGatewayAttachement for connecting the Internet Gateway with the VPC.


```
InternetGateway:
      Type: AWS::EC2::InternetGateway
    AttachGateway:
      Type: AWS::EC2::VPCGatewayAttachment
      Properties:
        VpcId: { Ref: VPC }
        InternetGatewayId: { Ref: InternetGateway }
```

With this resources in place, a VPC with 2 Subnets & an Internet Gateway will be created. But still the subnet isn't having any Public routes defined for it. In order to do so, create a Route Table, define a public route in the route table & associate the subnets with the Route table.


```
PublicSubnetRouteTable:
      Type: AWS::EC2::RouteTable
      Properties:
        VpcId: { Ref: VPC }
PublicSubnetRoute:
      Type: AWS::EC2::Route
      DependsOn: AttachGateway
      Properties:
        RouteTableId: { Ref: PublicSubnetRouteTable }
        DestinationCidrBlock: 0.0.0.0/0
        GatewayId: { Ref: InternetGateway }
 PublicSubnetRouteTableAssociationOne:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        RouteTableId: { Ref: PublicSubnetRouteTable }
        SubnetId: { Ref: SubnetOne }
 PublicSubnetRouteTableAssociationTwo:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        RouteTableId: { Ref: PublicSubnetRouteTable }
        SubnetId: { Ref: SubnetTwo }
```


The DestinationCidrBlock: 0.0.0.0/0 creates a public route to internet.

Until this, the environment is created with all the configurations required. The next step is to deploy a Load balancer in the Subnet.



**Creating a Load Balancer**

```
LoadBalancer:
      Type: AWS::ElasticLoadBalancingV2::LoadBalancer
      Properties:
        Name: alb-lambda
        Type: 'application'
        IpAddressType: 'ipv4'
        Scheme: 'internet-facing'
        Subnets:
          - { Ref: SubnetOne }
          - { Ref: SubnetTwo }
        SecurityGroups:
          - { Ref: SecurityGroupALB }
```
The above snippet will create an Application Load Balancer with an internet-facing scheme i.e. targets of this load balancer will have public IP address & hence clients over the internet can send request to it. The other scheme can be internal where the targets will have a private IP address.

> Only one subnet can be specified per AZ and an ALB must contain subnets from at least 2 AZs.


In order for the load balancer to perform path based routing, it requires an HTTP Listener that can listen on specific port & protocol.


```
 HTTPListener:
      Type: AWS::ElasticLoadBalancingV2::Listener
      Properties:
        DefaultActions:
          - Type: fixed-response
            FixedResponseConfig:
              ContentType: application/json
              MessageBody: 'Page Not Found'
              StatusCode: '404'
        LoadBalancerArn:
          Ref: LoadBalancer
        Port: 80
        Protocol: HTTP
```
The DefaultActions property specifies that in case no conditions are matched to redirect it to specific target, some default actions will be executed. In this case a "404 - Page Not Found" response will be returned. This can be customised as per the requirements.

Next step is to create a Lambda Function with an alb event configured on it. Hence, whenever the request comes to ALB & based on the conditions defined in the alb event, the serverless framework internally creates an http listener rule & target group & attaches the lambda function as the target in the target group. In case the conditions (path & method) matches, the logic / handler defined in the lambda function is executed.



**Creating a Lambda Function**

```
functions:
  functionOne:
    handler: handlers/functionOneHandler.handler
    events:
      - alb:
          listenerArn: { Ref: HTTPListener }
          priority: 1
          conditions:
            path: /hello
            method: GET
```
Define the function in the functions section of serverless.yml file.

> Note: Currently the serverless framework only support Ref or direct Arn for the listenerArn field.


The priority defines the execution preference for the http listener. ( sorted as low to high)

With this Infrastructure As a Code (IaC) in place as well as appropriate iamRoleStatements in the provider section of serverless.yml file, required custom/environment variables and the runtime information, the resources can be deployed in the AWS cloud for the specified region.

You can checkout the Serverless Land pattern for this integration - [Lambda function as target for ALB](https://serverlessland.com/patterns/alb-lambda-sls-framework)

Found helpful? Follow us for more such contents. Subscribe, Like & Share among the serverless community.

References - [https://docs.aws.amazon.com/](https://docs.aws.amazon.com/)