Skip to content

Instantly share code, notes, and snippets.

@instantiator
Last active April 26, 2024 15:50
Show Gist options
  • Save instantiator/fc433a98bb6a58234602d67a69b3cda7 to your computer and use it in GitHub Desktop.
Save instantiator/fc433a98bb6a58234602d67a69b3cda7 to your computer and use it in GitHub Desktop.
AWS CloudFormation/SAM templates for an app that places lambdas inside a VPC, and restores their internet access

Sometimes you need to move your application lambdas inside VPC subnets (eg. if you need them to be able to access an RDS cluster, or another service that doesn't mesh perfectly with serverless). Doing so removes them from the default VPC, and with that they lose internet access.

The templates included here show how you can deploy lambdas inside a VPC, and create a route for them back to the internet.

You can still use API Gateway and trigger your lambdas with events, but now they'll be able to reach the internet, too.

template.yaml creates the VPC, which is then used by all the other resources.

template-nat.yaml creates:

  • An Internet Gateway for the VPC
  • 3x private subnets (for your lambdas, or for RDS, or other resources...)
  • 1x public subnet with a NAT Gateway
  • Routing tables and routes...
    • A routing table for the private subnets, routing internet traffic to the NAT Gateway
    • A routing table for the public subnet, routing internet traffic to the Internet Gateway

A security group for the lambdas, and 3x private subnets appear in the outputs from template-nat.yaml. They can be applied to other resources (eg. the sample lambda in template.yaml).

Parameters:
VpcId:
Type: String
Tag:
Type: String
Resources:
VpcInternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-IGW"
AttachInternetGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VpcId
InternetGatewayId: !Ref VpcInternetGateway
PublicSubnet:
Type: "AWS::EC2::Subnet"
Properties:
VpcId: !Ref VpcId
AvailabilityZone: !Select [ 0, !GetAZs ]
CidrBlock: "10.0.6.0/24"
NatElasticIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
NatGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatElasticIP.AllocationId
SubnetId: !Ref PublicSubnet
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VpcId
AvailabilityZone: !Select [ 0, !GetAZs ]
CidrBlock: "10.0.4.0/24"
PrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VpcId
AvailabilityZone: !Select [ 1, !GetAZs ]
CidrBlock: "10.0.5.0/24"
PrivateSubnet3:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VpcId
AvailabilityZone: !Select [ 2, !GetAZs ]
CidrBlock: "10.0.7.0/24"
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VpcId
Tags:
- Key: Name
Value: !Sub "${Tag}-PrivateRouteTable"
DefaultPrivateRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway
PrivateSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable
SubnetId: !Ref PrivateSubnet1
PrivateSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable
SubnetId: !Ref PrivateSubnet2
PrivateSubnet3RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable
SubnetId: !Ref PrivateSubnet3
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VpcId
Tags:
- Key: Name
Value: !Sub "${Tag}-PublicRouteTable"
DefaultPublicRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref VpcInternetGateway
PublicSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet
LambdaSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: Security Group for lambdas
GroupDescription: Lambda traffic
VpcId: !Ref VpcId
SecurityGroupIngress:
- IpProtocol: "-1"
CidrIp: "0.0.0.0/0"
SecurityGroupEgress:
- IpProtocol: "-1"
CidrIp: "0.0.0.0/0"
Outputs:
PublicSubnet:
Value: !Ref PublicSubnet
PrivateSubnet1:
Value: !Ref PrivateSubnet1
PrivateSubnet2:
Value: !Ref PrivateSubnet2
PrivateSubnet3:
Value: !Ref PrivateSubnet3
LambdaSecurityGroup:
Value: !Ref LambdaSecurityGroup
Resources:
# Create the VPC for the whole application
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: "10.0.0.0/16"
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-VPC"
# For each function:
# Give them the AWSLambdaVPCAccessExecutionRole policy
# Give them the security group created by your NAT stack
# Put them in the private subnets created by your NAT stack
SampleFunction:
Type: AWS::Serverless::Function
Properties:
Description: A function inside the VPC with internet access
CodeUri: ./src/SampleFunctions/
Handler: AssemblyName::Namespace.Namespace.Class::Method
Policies: AWSLambdaVPCAccessExecutionRole
VpcConfig:
SecurityGroupIds:
- !GetAtt NatStack.Outputs.LambdaSecurityGroup
SubnetIds:
- !GetAtt NatStack.Outputs.PrivateSubnet1
- !GetAtt NatStack.Outputs.PrivateSubnet2
- !GetAtt NatStack.Outputs.PrivateSubnet3
# Deploy the NAT stack as a nested stack
NatStack:
Type: AWS::Serverless::Application
Properties:
Location: template-nat.yaml
Parameters:
VpcId: !Ref VPC
Tag: !Sub "${AWS::StackName}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment