A basic guide to connecting a AWS Lambda function to MongoDB in EC2 via VPC Peering

Kavita Nambissan Ganguli
11 min readApr 15, 2019

--

Recently for a project I had to set up an AWS Lambda function that received an input from an Amazon APIGateway request and had to write data to a MongoDB database, set up in an EC2 instance. The Lambda function and the MongoDB EC2 instance reside in two different VPCs, so I decided to set up a VPC Peering connection to allow communication between the two.

Why would I place the Lambda function in a separate VPC you ask? There is no real reason for me to do so, but most organizations have several VPCs in place, in different regions, managing different services in their own secure environments. VPC peering is common practice in such scenarios. So, I decided to use VPC Peering mainly to learn how to do it, and here’s what I learnt.

Do take a moment to go through the official documentation on VPC Peering.

Before we begin, the first part of this article covers the basics of setting up a VPC with a public subnet. If you’re a extremely comfortable with AWS VPC basics you can skip this till the second section, where we do the actual VPC peering.

Let’s get started with a look at our setup:

Basic AWS architecture

As you can see, my Lambda expects a request from my WebSocket API Gateway, which I will not be dealing with in this article. It will then store some information (connection IDs) into the MongoDB database nestled within an EC2 instance, that I set up in the default VPC.

We’ll begin by setting up our Lambda in it’s own VPC. So start by creating a new VPC, as shown below:

Create VPC screen

One important thing to note here is the IPv4 CIDR block, you have to ensure it is not the same as the VPC you intend to form a peering connection with. You cannot create a VPC peering connection between VPCs with matching or overlapping IPv4 CIDR blocks. This includes any CIDR blocks you may not even want to connect with. Also note that I will not be dealing with IPv6 CIDR blocks in this article.

Here are all the unsupported VPC peering configurations, make sure you don’t fall into any of these categories.

Once your VPC is set up, you will reach the VPC Dashboard. Now if haven’t created other VPCs before this, you will see one other VPC listed, the default one. Let’s ignore it for now, don’t worry we’ll come back to it in a bit.

Below are a few key components of every VPC, this is not an exhaustive list, I’m only mentioning components we need for our setup:

  • Subnets — IP address blocks that allow you to launch AWS resources as private (no internet access) or public (with internet access) within an availability zone.
  • Route Tables — Every subnet needs to be associated with a Route table which sets the rules for outbound traffic for that subnet. Each VPC has a main Route table as well, though you can create separate tables for your subnets.
  • Elastic IP — If you intend to launch EC2 instances in a public subnet, i.e. allow internet access to your EC2 instance, then you need to attach an Elastic IP to it. This will ensure the public IP of your EC2 instance does not change every time you start — stop — start your instance.
  • Elastic Network Interface (ENI) — As the name suggests, an ENI is mainly used to map private / public IP addresses, Elastic IPs and more to an instance. This handles network traffic for the attached instance.
  • Internet Gateway — Required to provide AWS resources in your VPC with access to the Internet. This includes any NAT Gateways you may set up for your private subnets.
  • NAT Gateway — These gateways allow you to set up outbound internet access to your private subnets, while allowing only requested internet traffic inwards.

Now that our VPC is set up, let’s get some subnets in place. I am creating a private subnet, more on that in a bit, in each availability zone. So fill out the Create Subnet form, tag your newly created VPC, assign the IPv4 CIDR block you wish to use for this subnet and you’re done. The diagram below should tell you what you currently have set up so far. The region will of course vary depending on your own account.

Progress so far

But why is my subnet private? Simply because I do not intend to route any traffic to an Internet Gateway. I want my lambda to remain in a private subnet, with traffic only coming in and out to trusted sources. Of course, my lambda also needs to connect to the Internet since I need to POST a request to my API Gateway eventually, but we’ll solve that soon.

So once you have created your private subnets in each availability zone, let’s move on to our Route tables. Take a look at the diagram below to get an idea of where our VPC currently stands with route tables.

Having the main route table is great and all, but we want some custom rules in place. So let’s go ahead and make a route table for our subnets. All I want to add in my Route table for now is the default local route. So we map our VPC CIDR blocks and set them to local targets. Once that is done we then do the all important step of associating our subnets to this route table.

Select the Subnet Associations tab to Edit Subnet Associations

Now my VPC looks something like this:

With this setup we have a handle on local traffic within our VPC, so let’s create our Lambda function next. I’m not going to go into the details of creating a lambda here, you can checkout the complete code for my Lambda in this Git repository.

I’m going to assume you have created the Lambda function, and then assigned it to this VPC. While doing so you must have encountered an error when no security group was chosen, you probably chose the default security group that comes with your VPC, which is fine for now. Don’t worry we’ll discuss security groups in detail soon enough.

Another point I’m going to talk about is the Role you assign to your Lambda.

Policies attached to my Lambda

As you can see, I’ve provided my lambda with VPC Access, as well as the basic Execution role, since I want it to write logs to CloudWatch and have access to resources within an EC2 instance. The policy, AmazonAPIGatewayInvokeFullAccess is required to allow it to POST requests to my WebSocket APIGateway. If you don’t plan to use your lambda for API Gateway access and only to access an EC2 instance in another VPC, just leave this one out.

Now normally you might not associate a VPC with your Lambda, as this causes it to lose connectivity to the Internet. But in my case, I need to provide it Internet connectivity in order to make a POST request to the API Gateway.

So let’s open up our VPC to allow internet access to our lambda, which we do with an Internet Gateway. In order to allow a resource internet access all you need to do is add a rule in your route table that links destination “0.0.0.0/0” to your Internet gateway. This rule makes a route table public, and any subnet attached to it, public as well.

But we want to use private subnets for our Lambda, thereby preventing the Internet from initiating communication.

This is where our NAT Gateway comes in. The NAT Gateway will allow our private subnet to connect to the Internet, but disallow any communication initiated from the Internet. This is a secure state for a lot of instances, which might need Internet access for updating packages etc. but don’t want to allow access from the outside. In order to add a NAT Gateway to your VPC, you will need a public subnet. This mainly because the NAT Gateway needs to connect to the Internet Gateway to access the internet, so it cannot be placed in a private subnet. For the private subnet to use it, all you need to do is add a route to it’s route table that points internet-bound traffic, 0.0.0.0/0, to the NAT gateway. Here’s a look at our architecture now:

Great! Now our lambda function can connect to the internet. There’s just one more thing to do, and that is to configure our security group. Now you have to remember that security groups work at the instance level and not at the subnet level. So you will need to assign each of your instances a security group depending on your traffic rules. Let’s take a look at the rules for my Lambda:

For my lambda I’m allowing all traffic from within my security group, ensuring any resource within the same security group can communicate with it. In order to allow internet access, I’m allowing all traffic outwards to 0.0.0.0/0 as well. This is the traffic that will eventually get directed to my NAT gateway. There is one additional rule here, which we will get to once my EC2 instance is set up in the second VPC. With this, our Lambda is pretty much set up.

I’ve used this VPC for my lambda function, but you can place any resource in there that requires this setup.

Now we move onto my EC2 instance, which contains my MongoDB database. I set up this EC2 instance in the default VPC that was created with my account. Remember that the default VPC comes with a default subnet in each availability zone. Since we’ve already been through the VPC setup process in quite a bit of detail, I’ll just quickly explain our VPC set up here without going into too much detail.

VPC 2 containing my EC2 instance

As I’m using the default VPC, I already have subnets in all my availability zones, and I’ve kept them private for now using a private route table that only allows local traffic. If you wish to give your EC2 instance internet access, or be able to access the MongoDB database from the internet you can either attach an Internet Gateway to the VPC and create a public route table that your subnets are associated with, or retain privacy of your subnet with a NAT Gateway as shown in the VPC we set up earlier.

Moving on, I created a new EC2 instance in one of the subnets, and installed MongoDB in it. This is a standard t2.micro, Amazon Linux instance, all within Free Tier. I added a security group to the instance, enabled SSH access for my IP, so that I could install MongoDB in the instance. We’ll revisit this security group once again after the VPC peering connection is set up.

Now our two VPCs are set up and ready to go.

We begin setting up our VPC peering connection in the VPC service dashboard. Select the Peering Connections option and click on Create Peering Connection.

Enter your details here

When you create a peering connection request, the account owning the accepter VPC has to accept the request. Since we own both VPCs you need to navigate to the Peering Connections tab again and you will see the peering connection we created in the pending state. Select the connection and click on the Actions button, which let’s you select ‘Accept Request’. After you click on the confirmation button, you will then be asked to modify your route tables to set up flow of traffic to/from the AWS resources to the peering connection.

The modification to the route tables is simply an addition of the peering connection and destination in both VPC resources. Let’s look at the AWS Lambda function route table first:

All traffic directed to CIDR block 172.31.0.0/16, from my lambda VPC, goes to the peering connection

I’ve added a rule that takes all the traffic heading to 172.31.0.0/16, which is the CIDR block assigned to the VPC holding my EC2 instance, to the peering connection. The peering connection then routes this to the other VPC.

I need to add the same rule, but in the other direction, for my EC2 VPC as well.

Route table in VPC 2 — containing my EC2 instance

But you’re not done yet! This step routes traffic correctly between your peered VPCs, but we have not yet configured our security groups to allow this traffic. In fact our security groups only allow local inbound traffic. Let’s fix this, by adding the security groups in our peer VPCs in our inbound/outbound rules.

Inbound rules of both VPC security groups

I’m allowing inbound traffic from the security groups of each VPC into the other. If your VPCs are in different accounts you will need to prepend the security group IDs with your account number. If your VPC is in a different region you cannot use security groups IDs, you will need to use the CIDR block of your VPC instead.

Similarly my outbound rules look like this:

With that we have finally connected all the dots and your VPCs are now set up to communicate with each other. To put it visually, here is a final picture of our AWS setup:

The final AWS set up of our VPCs

An important point to remember here is that VPC peering connections use private IPv4 or IPv6 addresses. So when my lambda wants to connect to the peer VPC EC2 instance, the host is the private IP address of my EC2 instance. The choice of resources in this setup is incidental to my requirements, you could set this up for any AWS resource that you wish to use.

I’m still working my way through the complexities of AWS architecture, and this article was just an attempt to share what I have learnt. If you have any thoughts on how something could be done better, or a point I may have missed, please feel free to leave a comment.

--

--