When Less is More: Serverless NAT Gateway - Part 1
Tags: javascript, webdev, programming, serverless
Published: 2022-02-10 11:39:49

Serverless architecture offers developers a variety of advantages that prove attractive in the development of large, scalable applications. Let's go over 3 top benefits:

- It offers the ability to write code and deploy to the cloud without worrying about the infrastructure.
- It enhances the economic sense of paying for what you use or execution-only billing.
- The ability to write an application in a language/framework of your choosing with a fast turnaround to production-ready setup.

Integration of third-party services is an inevitable part of the development lifecycle. If you do work with security-conscious third-party services then a common requirement that arises is whitelisting of an IP to avail these services.

In this two-part tutorial series, we will walk through the **creation of an AWS lambda function** with some additional AWS resources that will allow you to **provide third-party services with a static IP for whitelisted communication**

Let’s begin with Part 1 of this tutorial where you will:

- Use the serverless framework with webpack to create a serverless application and all the necessary AWS resources that go with it.
- Integrate a NAT Gateway with Elastic IP for static IP.

In the next part (i.e. Part 2 ) of this series, you will,

- Use GitHub Actions as a CD pipeline to verify and deploy to AWS.

##The Architecture
This tutorial assumes you have an expert level understanding of the following AWS services:

- [NAT Gateway](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html)
- [VPC]
(https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Subnets.html)
- [Public/Private Subnet]
(https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Scenario2.html)
- [Internet Gateway](https://docs.aws.amazon.com/vpc/latest/userguide/egress-only-internet-gateway.html)
- [Route Tables]
(https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Route_Tables.html)
- [Lambda]
(https://docs.aws.amazon.com/lambda/latest/dg/getting-started-create-function.html)

We will also be using the serverless framework to create, set up, test locally and deploy the application. The serverless framework is a great tool to get started with serverless architecture and systems.

Please visit the link https://www.serverless.com/framework/docs to learn more.

Our setup will look like this:

![Setup](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xuplr2wz87cltobdpor3.png)

In this tutorial, we will help you get through the deployment of a Lambda function with the proper connections to have an elastic IP associated.

### Let's start building

####Starter Project
Just a quick intro to what all we have. The most important part of the setup is the **serverless.yml** file. In it you will find:

1. Service name: Currently, it reads from the env file. Feel free to use one of your choosing.

2. Plugins:
- [serverless-webpack](https://www.npmjs.com/package/serverless-webpack) plugin for bundling the functions, the dependencies and more. Check out the [webpack](https://github.com/wednesday-solutions/sls-nat-gateway-eip-starter/blob/sls-starter/webpack.config.js) config here.
- [serverless-offline](https://www.npmjs.com/package/serverless-offline) plugin for testing locally.  
We’ll break this part of the tutorial into two sections:
- Create the AWS resources
- Addition of Lambda functions

## Creating the AWS Resources

### Step 1 - Update the serverless file
Add the following lines below the last lines of the serverless.yml file:

 

```
...
functions: ${file(./resources/functions.yml)}
resources:
  - ${file(./resources/iam.yml)}
  - ${file(./resources/vpc.yml)}
  - ${file(./resources/security-groups.yml)}
  - ${file(./resources/internet-gateway.yml)}
  - ${file(./resources/elastic-ip.yml)}
  - ${file(./resources/nat-gateway.yml)}
  - ${file(./resources/route-private.yml)}
  - ${file(./resources/route-public.yml)}
```


Here we are pointing to the functions (lambdas) and the resources (AWS infrastructure) that we will need to setup all this. We will add these files along the way. Exciting much?

```
`YAML syntax maybe problematic for some people.
Please take the help of lint plugin or a service 
like http://www.yamllint.com/`
```


### Step 2 - Adding the VPC
 

```

vi resources/vpc.yml
```

Let's add the resources. First, create a **vpc.yml** file in the resources folder. This is where you will create an AWS vpc resource. Copy and paste the following code into the **vpc.yml.** Don’t forget to check the indentation and change the names, tags as you want.

```
Resources:
  ServerlessVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: "10.0.0.0/16"
      Tags:
        - Key: 'Name'
          Value: 'ServerlessVPC'
```

‍
Pretty basic stuff, right? We have a resource type and a CIDR block (a range of IP addresses).

We will need to come back to this file in a bit. Let's move on.

### Step 3 - Adding the Elastic IP and Internet Gateway
We will create two files called **internet-gateway.yml** and **elastic-ip.yml** in the resources folder. Add the below resources to the files as mentioned in the **elastic-ip.yml**

 
```vi resources/elastic-ip.yml```

```
## Elastic IP
Resources:
  ElasticIpLambda:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc
```

Now let's create the **internet-gateway.yml** file.

```
 vi touch resources/internet-gateway.yml
```

```
## Elastic IP
Resources:
  ElasticIpLambda:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc
```

‍
We created two more resources. An Internet Gateway that allows us to connect to the outside internet from AWS VPC and an Elastic IP is the pubic static IP that will be given to third parties as our service IP address. The domain is a field that indicates whether the Elastic IP address is for use with instances in a VPC (which it is!).

At this point your folder structure will look like this:

![folder structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e6vtnr369lxmy4n01l32.png)

###Step 4 - Adding a NAT Gateway Resource and Subnets
 

```
vi touch resources/nat-gateway.yml
```

Create a **nat-gateway.yml** file in the resources. Add the following resources.

```
#nat-gateway.yml
Resources:
  ServerlessNatGateway:
    Type: AWS::EC2::NatGateway
    Properties: 
      AllocationId:
        Fn::GetAtt:
         - ElasticIpLambda
         - AllocationId
      SubnetId:
        Ref: ServerlessPublicSubnet1
  ServerlessPublicSubnet1: 
    DependsOn: 
      - ServerlessVPC
    Type: AWS::EC2::Subnet
    Properties:
      VpcId:
        Ref: ServerlessVPC
      CidrBlock: '10.0.2.0/24'
      AvailabilityZone: ${self:provider.region}a
      Tags:
        - Key: Name
          Value: ServerlessPublicSubnet1
  ServerlessPrivateSubnet1:
    DependsOn: 
      - ServerlessVPC
    Type: AWS::EC2::Subnet
    Properties:
      VpcId:
        Ref: ServerlessVPC
      CidrBlock: '10.0.1.0/24'
      AvailabilityZone: ${self:provider.region}a
      Tags:
        - Key: Name
          Value: ServerlessPrivateSubnet1

```

The NAT gateway is the star of the show. NAT is a service that allows instances inside your vpc to connect to outside resources or systems but external connections to internal vpc systems are prohibited. AllocationId is a function that gets the AllocationId of the Elastic IP resource we created. The Nat has a public subnet that it connects to. Look at the figure for the architecture.

The other resources are subnets. A private one that connects to resources in the vpc. A public one that will reroute and connect to Internet Gateway. Read more about the subnet [here](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Subnets.html#subnet-basics).

### Step 5 - Route Tables
We will have two route tables as part of this setup. One for the **private subnet** and another for the **public subnet**. Create two files **route-private.yml** and **route-public.yml** and add the following resources correctly. 
Let's work on the route-private first

```
 vi resources/route-private.yml
```

```
#route-private.yml
Resources:
  DefaultPrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: ServerlessVPC
      Tags:
        - Key: Name
          Value: DefaultPrivateRouteTable
  DefaultPrivateRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId:
        Ref: DefaultPrivateRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId:
        Ref: ServerlessNatGateway
  SubnetRouteTableLambdaAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: DefaultPrivateRouteTable
      SubnetId:
        Ref: ServerlessPrivateSubnet1

```

Now the route public file and resources

```
vi resources/route-public.yml
```

```
#route-public.yml
Resources:
  DefaultPublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: ServerlessVPC
      Tags:
        - Key: Name
          Value: DefaultPublicRouteTable
  DefaultPublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId:
        Ref: DefaultPublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: 
        Ref: SlsTutorialIGW
  IGWRouteTableLambdaAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: DefaultPublicRouteTable
      SubnetId:
        Ref: ServerlessPublicSubnet1
```

‍

A route table is a set of rules that establishes where network traffic is directed. It's can be associated with a subnet. It has a destination and target gateway. For the private route table, we add a route table rule that routes all traffic through the NAT Gateway. We also create an association between the route table and our private subnet.

![route table](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m1bmlhhuk9t5teqsigps.png)

This is how a route table looks after creation. Don't worry we will reach there.

The public route table also follows mostly the same pattern. The only difference is that its association is with the IG we created in Step 2.

###Step 6 - VPC Gateway attachment
Now let's get back to the vpc resource down the line. It's that time. Go back to the **vpc.yml** file and add the following lines

 

```
vi resources/vpc.yml

```
```
#vpc.yml
.
.
.
ServerlessVPCGA:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId:
        Ref: ServerlessVPC
      InternetGatewayId:
        Ref: SlsTutorialIGW
```
 

This attaches the Internet Gateway to the vpc enabling communication with the internet through the vpc.

Remember this is a new resource within the vpc file. I know some people don’t like code pictures, but I wanted to try carbon. So **vpc.yml** will look like this:

![code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s09qp99v2l9m7omp409b.png)


### Step 7 - Adding an IAM resource


```
vi resources/iam.yml
```

```
Resources:
  TestRoleForSLSNATGateway:
    Type: AWS::IAM::Role
    Properties:
      Description: This is an example role for SLS NAT Gateway
      RoleName: ${self:service.name}-nat-gateway-role
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      ManagedPolicyArns:
          - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
```


We are adding an IAM role that will allow us to access the lambda and the associated lambda logs in CloudWatch. Now that we have all the resources. All the file structures should look like this.


![lambda logs ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i102xifi2bpq543jsmyd.png)


‍

### Step - 8: Add a security group
We will add a security group for our architecture setup.

 

```
vi resources/security-groups.yml
```

```
#security-groups.yml
Resources:
  ServerlessSecurityGroup:
    DependsOn:
      - ServerlessVPC
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: SecurityGroup for Serverless Functions
      VpcId:
        Ref: ServerlessVPC
      Tags:
        - Key: 'Name'
          Value: 'sls-tutorial-sg'
```

We add a security group and add it to the VPC we created using the VpcId key in the template. This is important to control traffic to and from the subnets. By default, it has a set of rules associated with the traffic that flows through the group.

##Adding the functions

### Step 1: Add a function resource
We will add a function or a lambda in this step. Create a file called functions.yml in the resources folders and add the following code to it. This just points to a function that we will add, nothing fancy.

```
vi resources/functions.yml
```

```
#functions.yml
slsNatTutorialFunction:
  handler: functions/tutorial-function/index.handler
  role: TestRoleForSLSNATGateway
  events:
    - http
        method: GET
        path: /say-hello
        cors: true

```


We have the name of the function and the handler it points to.

### Step 2 Add the function
Inside the functions folder, create a folder called tutorial-function and an **index.js.** Add the following function to the handler

 	

```
mkdir -p functions/tutorial-function
vi touch functions/tutorial-function/index.js
```

 	

```
import { apiSuccess, apiFailure } from '@utils';
import axios from 'axios';

exports.handler = async (event, context, callback) => {
	console.log(JSON.stringify(event));
	try {
		const response = await axios.get('https://httpbin.org/ip');
		const data = response.data;
		console.log(data);
		return apiSuccess(callback, data);
	} catch (error) {
		return apiFailure(callback, error);
	}
};
```

This function is very basic with the idea being to just hit an outside service that returns the IP of the server from which the request was made. This will help us see that we have assigned a NAT Gateway Elastic IP to the lambda.

### Step 3 - Attaching the resources to the function
This is where it all comes together. We have created a lot of resources, we need to piece it all together so that the lambda we created has these resources attached. We do that in the **serverless.yml** file.

```
vi serverless.yml
```

```
 	
.
.
.
versionFunctions: false
vpc:
    securityGroupIds:
      - Fn::GetAtt:
          - ServerlessSecurityGroup
          - GroupId
    subnetIds:
      - Ref: ServerlessPrivateSubnet1

```


We should add the lines starting from the vpc to the file. Make sure you get the indent correct. We are attaching our Lambda functions with the vpc, security group, and the private subnet. Remember the Lambda rests in the private subnet.

### Step 4 - Testing this locally
Now as part of this set up, we do have a very interesting way to test our functions locally. We have added a plugin called serverless-offline to get this started up locally rather easily.

To get started, go to your working directory with your setup and run the following command.
 	

```
yarn start-offline
```

This should start up a server using webpack, that exposes the following API’s.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ehjd9dhqe6moucm5don3.png)

You can see a GET method exposed by the server. To verify the API’s you could now just go to a API testing resource like postman and try to hit this endpoint.

 	

```
#here is a cURL for you to copy paste.
curl --location --request GET 
'http://localhost:3000/local/say-hello' 

```
‍

The result should be something like this.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g7k47taa4je17qft0si0.png)

Nice right? Now let’s get this deployed to AWS so that the whole world can say hello to your API. That was bad. Onwards we go.

### Where to go from here?
This got a bit long, didn’t it ? If at any point you get stuck or want to refer to the resources, please feel free to refer to the finished setup available [here](https://github.com/wednesday-solutions/sls-nat-gateway-eip)

We have certainly made great strides in creating all these resources and linking all of them. If you reached all the way to the end here, great job!

Let’s wrap this up and deploy all the resources we created using GitHub Actions in Part 2 of this tutorial series. Will see you there!

Liked what you see? Found it helpful? Feel Free to share it.
We'd love to hear what you think, Tweet at us [here.](https://twitter.com/wednesdaysol
)

‍Originally appeared on https://www.wednesday.is/writing-tutorials/when-less-is-more-serverless-nat-gateway-part-1

#### 
About the Author
Vishnu Prasad is a `Software Engineer` at[ Wednesday Solutions](https://www.wednesday.is/). If not thinking about creating great
experiences on the web he is probably re-watching episodes of the Office or listening to 90's Malayalam Music