AWS Lambda VPC Networking
Tags: aws, lambda, vpc, nat
Published: 2019-06-25 21:36:02

Let’s face it, AWS networking isn’t exactly the most exciting thing in the world. Often, I’ve had a Lambda function that needs to connect to a database inside my VPC while still allowing connections to the internet. To put it kindly, configuring AWS networking to handle this common situation is quite involved.  This article is short manual to get it done.

  1. [Create a public subnet](#create-a-public-subnet)
  2. [Create a NAT gateway](#create-a-nat-gateway)
  3. [Create private subnets](#create-private-subnets)
  4. [Configure VPC gateway endpoints](#configure-vpc-gateway-endpoints)
  5. [Configure the Lambda function](#configure-the-lambda-function)

## Create a public subnet

Subnets divide the range of IP addresses available to a VPC into sub-sections, defined by [CIDR](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) blocks. For example, `172.31.0.0/16` is a CIDR block representing 65,536 addresses from `172.31.0.0` to `172.31.255.255`.

A subnet can be referred to as public or private. A *public subnet* has its traffic routed through an internet gateway, whereas a *private subnet* does not. An internet gateway is a VPC component that allows communication to the internet. The [default](https://docs.aws.amazon.com/vpc/latest/userguide/default-vpc.html) VPC, automatically created for your AWS account, comes pre-configured with a public subnet in each availability zone.

Subnets have a *route table* that describes how its network traffic is directed. The route table of a public subnet is shown below.

![Route table of a public subnet](https://thepracticaldev.s3.amazonaws.com/i/2fcn8xbs8er2rg3nvjc1.png)

In this example, all traffic destined to addresses within the VPC is kept local to the VPC. All other traffic (`0.0.0.0/0`) is sent to an internet gateway.

To create a public subnet:

  1. Navigate to the VPC service on the AWS console.
  2. Create a route table called `rt-to-igw`. Add a route to direct non-local traffic to an internet gateway, as in the example above.
  3. Create a new subnet called `lambda-public-subnet-1`. 
    - Choose the same VPC as that of the AWS resources you want to connect to.
    - Specify a CIDR block that the subnet will own, for example, `172.31.16.0/24` (256 addresses). There cannot be any overlap with the blocks owned by other subnets, so you may need to reallocate the addresses used elsewhere.
  4. After the subnet is created, change its route table to the one we created — `rt-to-igw`.

## Create a NAT gateway

EC2 instances can be assigned a public IP address. This is what allows you to, for example, SSH into it from your development machine, and to communicate with the outside internet. However, Lambda functions cannot be directly assigned a public IP address. Instead, we can use a *Network Address Translation* (NAT) gateway to enable a Lambda function to communicate with the internet.

  1. Navigate to the VPC service on the AWS console and choose "NAT Gateways".
  2. Create a new NAT gateway. Choose `lambda-public-subnet-1` for its subnet and assign it an elastic IP.
  3. After the NAT gateway is created, name it `lambda-nat-1`.
  4. Create a new route table called `rt-to-lambda-nat-1`. Add a route directing all non-local traffic — `0.0.0.0/0` — to the NAT gateway `lambda-nat-1`.

Be aware that a NAT gateway has associated [costs](https://aws.amazon.com/vpc/pricing/) — an hourly rate and a cost per GB of data sent through it.

## Create private subnets

The Lambda function will operate within a private subnet whose route table directs non-local traffic to the NAT gateway we created in the last section. AWS recommends that Lambda functions in a VPC be assigned to at least two subnets in different availability zones for higher availability. 

Next, create two private subnets which direct external traffic to the NAT gateway:

  1. Navigate to the VPC service on the AWS console and choose "Subnets"
  2. Create two new subnets called `lambda-private-subnet-1` and `lambda-private-subnet-2`.
      - Choose the same VPC as that of the AWS resources you want to connect to.
      - Specify a CIDR block for each subnet, for example, `172.31.17.0/24` and `172.31.18.0/24`
  3. After the subnets are created, change their route tables to `rt-to-lambda-nat-1`, which we created in the last section.


## Configure VPC gateway endpoints

This last step is optional, but recommended to reduce the NAT gateway's data-transfer costs.
The route table assigned to your private subnets should resemble the following:

![Private subnet route table](https://thepracticaldev.s3.amazonaws.com/i/s92ck2psvz7qmovyi8za.png)

It directs all external traffic to the NAT gateway, including any traffic the Lambda function sends to S3 or DynamoDB. Since the NAT gateway charges per-GB of data processed, it could be expensive to use these services with our current network configuration. However, we can use [VPC Gateway Endpoints](https://docs.aws.amazon.com/vpc/latest/userguide/vpce-gateway.html) to bypass the NAT on traffic going to S3 and DynamoDB. There is no charge for data passing through these endpoints, so your AWS bill will be lower.

  1. Navigate to the VPC service on the AWS console and choose “Endpoints".
  2. Create a new endpoint.
  3. Select the S3 service endpoint, whose name resembles `com.amazonaws.<REGION>.s3`.
  4. Associate this endpoint with the route table `rt-to-lambda-nat-1` (the private subnets' route table).
  5. Repeat steps 2–4 for the DynamoDB endpoint.

After completing these steps, the route table `rt-to-lambda-nat-1` should resemble the following:

![Route table with gateway endpoints for S3 and DynamoDB](https://thepracticaldev.s3.amazonaws.com/i/nbm8fri6rqnh28k8692v.png)


## Configure the Lambda function

Finally, configure the Lambda function to use the private subnets:

  1. Navigate to your Lambda function in the AWS console.
  2. Under the "Network" pane, choose the VPC that you have been using throughout this guide for the Lambda function.
  3. Choose the subnets `lambda-private-subnet-1` and `lambda-private-subnet-2`.
  4. For the security group, depending on your use-case, choose one which allows access to your RDS instance, Redshift cluster or ElastiCache instance. 


## Conclusion

Configuring a Lambda function to access resources in a VPC while still allowing it to talk to the internet is quite involved, as you have seen. But, this guide should provide you with a working solution that can be adapted to your needs. The VPC network that we configured is depicted below.

![VPC network diagram for the Lambda function](https://thepracticaldev.s3.amazonaws.com/i/i8o2p7xcq3h7mmdwk6i7.png)

In summary, the public subnet `lambda-public-subnet-1` directs traffic to an internet gateway through the route table `rt-to-igw`. We created a NAT gateway `lambda-nat-1` inside the public subnet. The private subnets `lambda-private-subnet-1` and `lambda-private-subnet-2` direct their external traffic to the NAT gateway through the route table `rt-to-nat-lambda-nat-1`. The Lambda function is configured to live within the private subnets.

Some further resources you may find helpful:

  - [VPC networking components docs](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Networking.html)
  - [How can I grant internet access to my VPC Lambda function?](https://aws.amazon.com/premiumsupport/knowledge-center/internet-access-lambda-function/)
  - [The Dark Art of AWS VPC Networking](https://www.verypossible.com/blog/aws-development-dark-art-of-vpc-networking)