In the last tutorial, we showed how to connect to an RDS using an RDS Proxy with a Lambda function. To do this, we needed to put the Lambda in the same VPC as the RDS and the RDS Proxy. This, of course, makes Lambda lose internet access. In this tutorial, I will show you how to fix this.

This tutorial will continue from where the last one left off and we will be building the example app.


First, we will create a Lambda function which we will use to test internet access. Add this in the serverless.yml file under the functions section:

1 testInternetAccess:
2  handler: lib/handlers/testInternetAccess.handler
3  events:
4    - http:
5        path: test-internet-access
6        method: get

This is how the testInternetAccess.js file looks like:

1const fetch = require('node-fetch')
2
3const testInternetAccess = async (event, context) => {
4  console.log(JSON.stringify({ event, context }))
5
6  let response
7  try {
8    console.log('Fetching data')
9    const res = await fetch('https://jsonplaceholder.typicode.com/posts/1')
10
11    if (!(res.status >= 200 && res.status < 300)) {
12      throw new Error('Unsuccessful API response')
13    }
14
15    response = {
16      statusCode: 200,
17      body: JSON.stringify({ 
18        internetStatus: 'Online',  
19        data: await res.json()
20      })
21    }
22  } catch (error) {
23    console.error(error)
24    response = {
25      statusCode: 500,
26      body: JSON.stringify({
27        internetStatus: 'Offline',  
28        error: error.message
29      })
30    }
31  }
32
33  console.log(response)
34  return response
35}
36
37module.exports.handler = testInternetAccess

You can deploy this function now by running:

1$ serverless deploy --stage dev

Send a GET request to the endpoint shown in the terminal after the deployment has finished. You’ll get a response which looks something like this: {"message": "Internal server error"}. If you check CloudWatch logs for the testInternetAccess Lambda, it should tell you that the Lambda had timed out after a certain amount of time. This is expected behavior if your Lambda doesn’t have internet access.

Now we’ll be starting setting up the resources which grant our Lambdas internet access. We’ll begin by creating two more Subnets in our VPC. One for each availability region which we’re already using. We’ll also create a NAT Gateway for one of the previously created Subnets. The Subnet which we choose must have an Internet Gateway in its Route Table. We’ll add the following code anywhere in the VpcResources.yml file:

1SubnetAPrivate:
2  Type: AWS::EC2::Subnet
3  Properties:
4    VpcId: !Ref VPC
5    AvailabilityZone: ${self:provider.region}a
6    CidrBlock: ${self:custom.VPC_CIDR}.0.2.0/24
7    Tags:
8      - Key: "Name"
9        Value: "SubnetAPrivate"
10
11SubnetBPrivate:
12  Type: AWS::EC2::Subnet
13  Properties:
14    VpcId: !Ref VPC
15    AvailabilityZone: ${self:provider.region}b
16    CidrBlock: ${self:custom.VPC_CIDR}.0.3.0/24
17    Tags:
18      - Key: "Name"
19        Value: "SubnetBPrivate"
20
21EIP:
22  Type: AWS::EC2::EIP
23  Properties:
24    Domain: vpc
25
26NATGateway:
27  Type: AWS::EC2::NatGateway
28  Properties:
29    AllocationId: !GetAtt EIP.AllocationId
30    SubnetId: !Ref SubnetA

Update the vpc section of the serverless.yml file to look like this:

1vpc:
2  securityGroupIds:
3    - !Ref LambdaSecurityGroup
4  subnetIds:
5    - !Ref SubnetAPrivate
6    - !Ref SubnetBPrivate

Next, we need to create a Route Table with a Route with a NAT Gateway and associate the newly created private Subnets to that Route Table. Add this code anywhere in the RoutingResources.yml file:

1RouteTablePrivate:
2  Type: AWS::EC2::RouteTable
3  Properties:
4    VpcId: !Ref VPC
5    Tags:
6      - Key: "Name"
7        Value: "RouteTablePrivate"
8
9RoutePrivate:
10  Type: AWS::EC2::Route
11  Properties:
12    DestinationCidrBlock: 0.0.0.0/0
13    NatGatewayId: !Ref NATGateway
14    RouteTableId: !Ref RouteTablePrivate
15    
16RouteTableAssociationSubnetAPrivate:
17  Type: AWS::EC2::SubnetRouteTableAssociation
18  Properties:
19    RouteTableId: !Ref RouteTablePrivate
20    SubnetId: !Ref SubnetAPrivate
21
22RouteTableAssociationSubnetBPrivate:
23  Type: AWS::EC2::SubnetRouteTableAssociation
24  Properties:
25    RouteTableId: !Ref RouteTablePrivate
26    SubnetId: !Ref SubnetBPrivate

That’s it. Now you can deploy your stack and test if your Lambdas have internet access using the testInternetAccess Lambda.

Suppose you have any deployment errors; check your stack in the AWS CloudFormation console. Go to the Events tab, and there you’ll see which resources failed, why, and when.