Last active
April 11, 2019 10:21
-
-
Save revmischa/38c77e225e7334ceffbca74ba779b3c3 to your computer and use it in GitHub Desktop.
Serverless CloudFormation to provide private RDS and lambda networking with internet access in VPC.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
plugins: | |
- serverless-pseudo-parameters | |
provider: | |
name: aws | |
runtime: python3.7 | |
stage: ${opt:stage, 'dev'} | |
environment: | |
SQLALCHEMY_DATABASE_URI: # DB DSN | |
"Fn::Join": ['', ['postgresql://', {"Fn::Join": ['', ['{{resolve:secretsmanager:', !Ref RDSInstanceSecret, ':SecretString:username}}']]}, ':', {"Fn::Join": ['', ['{{resolve:secretsmanager:', !Ref RDSInstanceSecret, ':SecretString:password}}']]}, '@',{ "Fn::Join": [":", ["Fn::GetAtt": [ServerlessRDSCluster, Endpoint.Address], "Fn::GetAtt": [ServerlessRDSCluster, Endpoint.Port]]] }, "/${self:custom.db.dbname}"]] | |
resources: | |
Resources: | |
ServerlessVPC: | |
Type: AWS::EC2::VPC | |
Properties: | |
Tags: | |
- Key: "Name" | |
Value: "#{AWS::StackName}" | |
EnableDnsSupport: true # disable for production | |
EnableDnsHostnames: true # disable for production | |
CidrBlock: "10.0.0.0/16" | |
ServerlessSubnetA: | |
DependsOn: ServerlessVPC | |
Type: AWS::EC2::Subnet | |
Properties: | |
Tags: | |
- Key: Name | |
Value: "#{AWS::StackName} Lambda" | |
VpcId: | |
Ref: ServerlessVPC | |
AvailabilityZone: ${self:provider.region}a | |
CidrBlock: "10.0.3.0/24" | |
ServerlessSubnetB: | |
DependsOn: ServerlessVPC | |
Type: AWS::EC2::Subnet | |
Properties: | |
Tags: | |
- Key: Name | |
Value: "#{AWS::StackName} Lambda" | |
VpcId: | |
Ref: ServerlessVPC | |
AvailabilityZone: ${self:provider.region}b | |
CidrBlock: "10.0.1.0/24" | |
ServerlessSubnetC: | |
DependsOn: ServerlessVPC | |
Type: AWS::EC2::Subnet | |
Properties: | |
Tags: | |
- Key: Name | |
Value: "#{AWS::StackName} Lambda" | |
VpcId: | |
Ref: ServerlessVPC | |
AvailabilityZone: ${self:provider.region}c | |
CidrBlock: "10.0.2.0/24" | |
DBSubnetA: | |
DependsOn: ServerlessVPC | |
Type: AWS::EC2::Subnet | |
Properties: | |
Tags: | |
- Key: Name | |
Value: "#{AWS::StackName} DB" | |
VpcId: | |
Ref: ServerlessVPC | |
AvailabilityZone: ${self:provider.region}a | |
CidrBlock: "10.0.6.0/24" | |
DBSubnetB: | |
DependsOn: ServerlessVPC | |
Type: AWS::EC2::Subnet | |
Properties: | |
Tags: | |
- Key: Name | |
Value: "#{AWS::StackName} DB" | |
VpcId: | |
Ref: ServerlessVPC | |
AvailabilityZone: ${self:provider.region}b | |
CidrBlock: "10.0.7.0/24" | |
DBSubnetC: | |
DependsOn: ServerlessVPC | |
Type: AWS::EC2::Subnet | |
Properties: | |
Tags: | |
- Key: Name | |
Value: "#{AWS::StackName} DB" | |
VpcId: | |
Ref: ServerlessVPC | |
AvailabilityZone: ${self:provider.region}c | |
CidrBlock: "10.0.8.0/24" | |
ServerlessSubnetPublic: | |
DependsOn: ServerlessVPC | |
Type: AWS::EC2::Subnet | |
Properties: | |
VpcId: | |
Ref: ServerlessVPC | |
AvailabilityZone: ${self:provider.region}a | |
CidrBlock: "10.0.10.0/24" | |
ServerlessSecurityGroup: | |
DependsOn: ServerlessVPC | |
Type: AWS::EC2::SecurityGroup | |
Properties: | |
GroupDescription: SecurityGroup for Serverless Functions | |
VpcId: | |
Ref: ServerlessVPC | |
ServerlessStorageSecurityGroup: | |
DependsOn: ServerlessVPC | |
Type: AWS::EC2::SecurityGroup | |
Properties: | |
GroupDescription: Ingress for RDS Instance | |
VpcId: | |
Ref: ServerlessVPC | |
SecurityGroupIngress: | |
- IpProtocol: tcp | |
FromPort: '5432' | |
ToPort: '5432' | |
SourceSecurityGroupId: | |
Ref: ServerlessSecurityGroup | |
ServerlessPrivateSubnetGroup: | |
Type: AWS::RDS::DBSubnetGroup | |
Properties: | |
DBSubnetGroupDescription: "NAT-ed" | |
SubnetIds: | |
- Ref: ServerlessSubnetA | |
- Ref: ServerlessSubnetB | |
- Ref: ServerlessSubnetC | |
DBSubnetGroup: | |
Type: AWS::RDS::DBSubnetGroup | |
Properties: | |
DBSubnetGroupDescription: "Database" | |
SubnetIds: | |
- Ref: DBSubnetA | |
- Ref: DBSubnetB | |
- Ref: DBSubnetC | |
NATIP: # IP for public NAT | |
Type: AWS::EC2::EIP | |
Properties: | |
Domain: vpc | |
NatGateway: # NAT GW in public subnet | |
Type: AWS::EC2::NatGateway | |
DependsOn: | |
- NATIP | |
- ServerlessSubnetPublic | |
Properties: | |
AllocationId: !GetAtt NATIP.AllocationId | |
SubnetId: !Ref ServerlessSubnetPublic | |
PrivateRouteTable: # Route table for internal routing | |
Type: AWS::EC2::RouteTable | |
Properties: | |
VpcId: !Ref ServerlessVPC | |
Tags: | |
- Key: Name | |
Value: "#{AWS::StackName} Private" | |
DefaultPrivateRoute: # Route to provide internet access for private subnet (TODO: provide only for lambda, not RDS etc) | |
Type: AWS::EC2::Route | |
DependsOn: | |
- NatGateway | |
- PrivateRouteTable | |
Properties: | |
RouteTableId: !Ref PrivateRouteTable | |
DestinationCidrBlock: 0.0.0.0/0 | |
NatGatewayId: !Ref NatGateway | |
SubARouteTableAssociation: # link private subnets to PrivateRouteTable | |
Type: AWS::EC2::SubnetRouteTableAssociation | |
Properties: | |
RouteTableId: !Ref PrivateRouteTable | |
SubnetId: !Ref ServerlessSubnetA | |
SubBRouteTableAssociation: | |
Type: AWS::EC2::SubnetRouteTableAssociation | |
Properties: | |
RouteTableId: !Ref PrivateRouteTable | |
SubnetId: !Ref ServerlessSubnetB | |
SubCRouteTableAssociation: | |
Type: AWS::EC2::SubnetRouteTableAssociation | |
Properties: | |
RouteTableId: !Ref PrivateRouteTable | |
SubnetId: !Ref ServerlessSubnetC | |
DBARouteTableAssociation: # link DB subnets to routes | |
Type: AWS::EC2::SubnetRouteTableAssociation | |
Properties: | |
RouteTableId: !Ref PublicRouteTable # change to private to wall off from internet | |
SubnetId: !Ref DBSubnetA | |
DBBRouteTableAssociation: | |
Type: AWS::EC2::SubnetRouteTableAssociation | |
Properties: | |
RouteTableId: !Ref PublicRouteTable # see above | |
SubnetId: !Ref DBSubnetB | |
DBCRouteTableAssociation: | |
Type: AWS::EC2::SubnetRouteTableAssociation | |
Properties: | |
RouteTableId: !Ref PublicRouteTable | |
SubnetId: !Ref DBSubnetC | |
IGW: # Internet GateWay | |
Type: AWS::EC2::InternetGateway | |
ServerlessIGWAttachment: # Connect IGW to Serverless VPC | |
Type: AWS::EC2::VPCGatewayAttachment | |
Properties: | |
InternetGatewayId: !Ref IGW | |
VpcId: !Ref ServerlessVPC | |
PublicRouteTable: # Route table for routing to internet | |
Type: AWS::EC2::RouteTable | |
Properties: | |
VpcId: !Ref ServerlessVPC | |
Tags: | |
- Key: Name | |
Value: "#{AWS::StackName} Public" | |
DefaultPublicRoute: # Route to provide internet access from NAT to IGW | |
Type: AWS::EC2::Route | |
Properties: | |
RouteTableId: !Ref PublicRouteTable | |
DestinationCidrBlock: 0.0.0.0/0 | |
GatewayId: !Ref IGW | |
PublicRouteTableAssociation: # connect public subnet (with NAT GW) to IGW | |
Type: AWS::EC2::SubnetRouteTableAssociation | |
Properties: | |
RouteTableId: !Ref PublicRouteTable | |
SubnetId: !Ref ServerlessSubnetPublic | |
# DB | |
ServerlessRDSCluster: | |
DependsOn: ServerlessStorageSecurityGroup | |
Type: AWS::RDS::DBInstance | |
Properties: | |
DeletionProtection: false # enable this for production! | |
PubliclyAccessible: true # disable this for production? | |
Engine: Postgres | |
DBName: ${self:custom.db.dbname} | |
MasterUsername: !Join ['', ['{{resolve:secretsmanager:', !Ref RDSInstanceSecret, ':SecretString:username}}' ]] | |
MasterUserPassword: !Join ['', ['{{resolve:secretsmanager:', !Ref RDSInstanceSecret, ':SecretString:password}}' ]] | |
DBInstanceClass: ${self:custom.db.instance_class} | |
AllocatedStorage: 10 | |
VPCSecurityGroups: | |
- "Fn::GetAtt": ServerlessStorageSecurityGroup.GroupId | |
DBSubnetGroupName: !Ref DBSubnetGroup | |
# DB login secret | |
RDSInstanceSecret: | |
Type: AWS::SecretsManager::Secret | |
Properties: | |
Description: 'RDS master admin' | |
GenerateSecretString: | |
SecretStringTemplate: '{"username": "dbadmin"}' | |
GenerateStringKey: 'password' | |
PasswordLength: 16 | |
ExcludeCharacters: '"@/\%' | |
SecretRDSInstanceAttachment: | |
Type: AWS::SecretsManager::SecretTargetAttachment | |
Properties: | |
SecretId: | |
Ref: RDSInstanceSecret | |
TargetId: | |
Ref: ServerlessRDSCluster | |
TargetType: AWS::RDS::DBInstance |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment