Last updated: Jan 26, 2024
Reading timeยท5 min
We are going to provision a Lambda function in a VPC and enable it to access the internet.
There are a couple of things we have to do to give Lambda functions created in a VPC internet access:
Let's create the VPC and lambda function:
import * as ec2 from 'aws-cdk-lib/aws-ec2'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import * as cdk from 'aws-cdk-lib'; import * as path from 'path'; export class CdkStarterStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props?: cdk.StackProps) { super(scope, id, props); const vpc = new ec2.Vpc(this, 'my-cdk-vpc', { ipAddresses: ec2.IpAddresses.cidr('10.0.0.0/16'), natGateways: 1, maxAzs: 3, subnetConfiguration: [ { name: 'private-subnet-1', subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS, cidrMask: 24, }, { name: 'public-subnet-1', subnetType: ec2.SubnetType.PUBLIC, cidrMask: 24, }, ], }); const lambdaFunction = new lambda.Function(this, 'lambda-function', { runtime: lambda.Runtime.NODEJS_18_X, // ๐ place lambda in the VPC vpc, // ๐ place lambda in Private Subnets vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS, }, memorySize: 1024, timeout: cdk.Duration.seconds(5), handler: 'index.main', code: lambda.Code.fromAsset(path.join(__dirname, '/../src/my-lambda')), }); } }
Let's go over the code snippet.
PUBLIC
and a PRIVATE_WITH_EGRESS
subnet groups.
We will launch our Lambda function in private subnets. Note that the VPC
will provision 1 NAT Gateway, which will allow our Lambda to access the
internet from a PRIVATE
subnet$0.045
in the us-east-1
region.PRIVATE_WITH_EGRESS
.Lambda functions provisioned in a PUBLIC subnet don't get assigned a public IP address and don't have access to the internet.
We didn't add a role to the Lambda function or edit its default role.
Lambda creates an Elastic network interface (virtual network card) for each (private) subnet in the VPC, so it has to have the necessary permissions to create, describe and delete network interfaces.
The necessary permissions are included in the
AWSLambdaVPCAccessExecutionRole
managed policy, which CDK attaches to lambdas
launched in a VPC automatically.
We haven't explicitly provided a security group to the lambda function, so CDK will also create a default security group for us.
The default security group has a single inbound rule:
Type | Protocol | Port | Source |
---|---|---|---|
All Traffic | All | All | default-SG-id |
And the following outbound rule:
Type | Protocol | Port | Source |
---|---|---|---|
All Traffic | All | All | 0.0.0.0/0 |
The default security group allows all outbound traffic. Since security groups are stateful, an outbound request that we've made to the internet, automatically gets permissions for the response from the other direction.
In our case, the default security group will do. If you want to read more about creating security groups in CDK, check out my other article - Security Group Example in AWS CDK - Complete Guide.
Let's add the code for the lambda function at src/my-lambda/index.js
:
const fetch = require('node-fetch'); async function main(event) { try { const res = await fetch('https://randomuser.me/api'); const resJson = await res.json(); console.log('api response ๐', JSON.stringify(resJson, null, 4)); return {body: JSON.stringify(resJson), statusCode: 200}; } catch (error) { return {body: JSON.stringify({error})}; } } module.exports = {main};
The function makes a request to an API and returns the response. We've used
the node-fetch
package, so we have to install it:
cd src/my-lambda npm init -y npm install node-fetch cd ../../
Let's deploy the Lambda function and the VPC and test if our function has access to the internet:
npx aws-cdk deploy
After deployment, we can see that the Lambda has been launched in a VPC, and is associated with private subnets only.
The route tables associated with our private subnets have a route that points all non-intra-VPC traffic out to our NAT Gateway:
CDK has also automatically attached the AWSLambdaVPCAccessExecutionRole
managed policy to our lambda's role:
Our public subnets route all non-intra-VPC traffic to the Internet Gateway:
Everything seems to be in place for our Lambda function launched in a VPC to have internet access. Let's test the function via the Lambda management console:
Our Lambda function successfully queried the remote API and got the response.
The CloudWatch logs are a bit more readable:
Either way, we successfully provisioned a Lambda function that can access the internet in a VPC.
The function is created in PRIVATE_WITH_EGRESS
subnets of the VPC. The route
tables associated with our private subnets have a route that points to a NAT
Gateway, which enables our Lambda to access the internet.
CDK did quite a bit of the heavy lifting for us:
allowPublicSubnet
to true
on the Function
constructor to acknowledge the limitation. Placing your VPC Lambda in a public subnet without setting this prop to true
returns a validation error.To delete the resources we've provisioned, issue the destroy
command:
npx aws-cdk destroy
I've also written an article on how to provision an AWS Lambda function outside a VPC in CDK.
You can learn more about the related topics by checking out the following tutorials: