AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Parameters:
ResourcePrefix:
Type: String
Default: "api-r53"
Phase:
Type: String
Default: "1"
AllowedValues:
- "1"
- "2"
- "3"
Conditions:
Phase2: !Equals
- !Ref Phase
- "2"
Phase3: !Equals
- !Ref Phase
- "3"
AfterPhase2: !Or
- !Condition Phase2
- !Condition Phase3
Resources:
# ------------------------------------------------------------#
# Networking
# ------------------------------------------------------------#
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Sub ${ResourcePrefix}-vpc-{{resolve:ssm:${RandomIdParameter}}}
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.0.0/24
MapPublicIpOnLaunch: false
VpcId: !Ref VPC
AvailabilityZone: ap-northeast-1a
Tags:
- Key: Name
Value: !Sub ${ResourcePrefix}-public-subnet-{{resolve:ssm:${RandomIdParameter}}}
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.1.0/24
MapPublicIpOnLaunch: false
VpcId: !Ref VPC
AvailabilityZone: ap-northeast-1a
Tags:
- Key: Name
Value: !Sub ${ResourcePrefix}-private-subnet-{{resolve:ssm:${RandomIdParameter}}}
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${ResourcePrefix}-igw-{{resolve:ssm:${RandomIdParameter}}}
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId : !Ref InternetGateway
VpcId: !Ref VPC
RouteTableForPublicSubnet:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${ResourcePrefix}-public-route-table-{{resolve:ssm:${RandomIdParameter}}}
RouteForPublicSubnet:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref RouteTableForPublicSubnet
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
AssocciateRouteTableForPublicSubnet:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref RouteTableForPublicSubnet
SubnetId: !Ref PublicSubnet
Eip:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
Tags:
- Key: Name
Value: !Sub ${ResourcePrefix}-eip-{{resolve:ssm:${RandomIdParameter}}}
NatGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt Eip.AllocationId
SubnetId: !Ref PublicSubnet
Tags:
- Key: Name
Value: !Sub ${ResourcePrefix}-nat-gateway-{{resolve:ssm:${RandomIdParameter}}}
RouteTableForPrivateSubnet:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${ResourcePrefix}-private-route-table-{{resolve:ssm:${RandomIdParameter}}}
RouteForPrivateSubnet:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref RouteTableForPrivateSubnet
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway
AssocciateRouteTableForPrivateSubnet:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref RouteTableForPrivateSubnet
SubnetId: !Ref PrivateSubnet
# ------------------------------------------------------------#
# VPC Endpoint for API Gateway
# ------------------------------------------------------------#
VpcEndpoint:
Type: AWS::EC2::VPCEndpoint
Condition: AfterPhase2
Properties:
ServiceName: !Sub "com.amazonaws.${AWS::Region}.execute-api"
VpcEndpointType: Interface
PrivateDnsEnabled: true
VpcId: !Ref VPC
SubnetIds:
- !Ref PrivateSubnet
SecurityGroupIds:
- !Ref VpcEndpointSecurityGroup
VpcEndpointSecurityGroup:
Type: AWS::EC2::SecurityGroup
Condition: AfterPhase2
Properties:
GroupName: !Sub ${ResourcePrefix}-vpc-endpoint-sg-{{resolve:ssm:${RandomIdParameter}}}
GroupDescription: security group for vpc endpoint of api gateway
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: !GetAtt VPC.CidrBlock
SecurityGroupEgress:
- IpProtocol: "-1"
CidrIp: 0.0.0.0/0
# ------------------------------------------------------------#
# Route53 Resolver
# ------------------------------------------------------------#
ResolverRuleForEdgeApi:
Type: AWS::Route53Resolver::ResolverRule
Condition: Phase3
Properties:
DomainName: !Sub ${EdgeApi}.execute-api.${AWS::Region}.amazonaws.com
Name: !Sub ${ResourcePrefix}-edge-api-resolver-rule-{{resolve:ssm:${RandomIdParameter}}}
ResolverEndpointId: !Ref ResolverEndpoint
RuleType: FORWARD
TargetIps:
- Ip: 8.8.8.8
Port: 53
ResolverRuleAssociationForEdgeApi:
Type: AWS::Route53Resolver::ResolverRuleAssociation
Condition: Phase3
Properties:
Name: !Sub ${ResourcePrefix}-edge-api-resolver-rule-association-{{resolve:ssm:${RandomIdParameter}}}
ResolverRuleId: !GetAtt ResolverRuleForEdgeApi.ResolverRuleId
VPCId: !Ref VPC
ResolverRuleForRegionalApi:
Type: AWS::Route53Resolver::ResolverRule
Condition: Phase3
Properties:
DomainName: !Sub ${RegionalApi}.execute-api.${AWS::Region}.amazonaws.com
Name: !Sub ${ResourcePrefix}-regional-api-resolver-rule-{{resolve:ssm:${RandomIdParameter}}}
ResolverEndpointId: !Ref ResolverEndpoint
RuleType: FORWARD
TargetIps:
- Ip: 8.8.8.8
Port: 53
ResolverRuleAssociationForRegionalApi:
Type: AWS::Route53Resolver::ResolverRuleAssociation
Condition: Phase3
Properties:
Name: !Sub ${ResourcePrefix}-regional-api-resolver-rule-association-{{resolve:ssm:${RandomIdParameter}}}
ResolverRuleId: !GetAtt ResolverRuleForRegionalApi.ResolverRuleId
VPCId: !Ref VPC
ResolverEndpoint:
Type: AWS::Route53Resolver::ResolverEndpoint
Condition: Phase3
Properties:
Direction: OUTBOUND
IpAddresses:
# 2つ以上指定が必要
- SubnetId: !Ref PrivateSubnet
- SubnetId: !Ref PrivateSubnet
Name: !Sub ${ResourcePrefix}-resolver-endpoint-{{resolve:ssm:${RandomIdParameter}}}
SecurityGroupIds:
- !Ref ResolverEndpointSecurityGroup
ResolverEndpointSecurityGroup:
Type: AWS::EC2::SecurityGroup
Condition: Phase3
Properties:
GroupName: !Sub ${ResourcePrefix}-resolver-endpoint-sg-{{resolve:ssm:${RandomIdParameter}}}
GroupDescription: security group for route53 resolver endpoint
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 53
ToPort: 53
CidrIp: !GetAtt VPC.CidrBlock
- IpProtocol: udp
FromPort: 53
ToPort: 53
CidrIp: !GetAtt VPC.CidrBlock
SecurityGroupEgress:
- IpProtocol: "-1"
CidrIp: 0.0.0.0/0
# ------------------------------------------------------------#
# VPC Lambda
# ------------------------------------------------------------#
VpcLambda:
Type: AWS::Serverless::Function
DependsOn: VpcLambdaLog
Properties:
FunctionName: !Sub ${ResourcePrefix}-vpc-function-{{resolve:ssm:${RandomIdParameter}}}
InlineCode: |
import logging
import os
import socket
import requests
logger = logging.getLogger()
logger.setLevel(os.environ['LOG_LEVEL'])
def lambda_handler(event, context):
logger.info('\n'.join([
'',
'# Edge API',
get_output(url=os.environ['EDGE_API_URL']),
'',
'# Regional API Response',
get_output(url=os.environ['REGIONAL_API_URL']),
'',
'# Private API Response',
get_output(url=os.environ['PRIVATE_API_URL']),
]))
def get_output(url:str) -> str:
domain_name = url.split('/')[2]
output = '\n'.join([
f'IP: {get_ip(domain_name)}',
f'Response: {requests_get(url)}',
])
return output
def get_ip(domain_name:str) -> str:
try:
return socket.gethostbyname(domain_name)
except Exception as e:
return str(e)
def requests_get(url:str) -> str:
try:
return requests.get(url).content.decode('ascii')
except requests.exceptions.RequestException as e:
return str(e)
Handler: index.lambda_handler
Environment:
Variables:
LOG_LEVEL: INFO
EDGE_API_URL: !Sub https://${EdgeApi}.execute-api.${AWS::Region}.amazonaws.com/prod
REGIONAL_API_URL: !Sub https://${RegionalApi}.execute-api.${AWS::Region}.amazonaws.com/prod
PRIVATE_API_URL: !Sub https://${PrivateApi}.execute-api.${AWS::Region}.amazonaws.com/prod
Runtime: python3.8
Timeout: 10
MemorySize: 128
VpcConfig:
SecurityGroupIds:
- !Ref VpcLambdaSecurityGroup
SubnetIds:
- !Ref PrivateSubnet
Layers:
# "Python Toolkit" on https://github.com/mthenw/awesome-layers
- arn:aws:lambda:ap-northeast-1:770693421928:layer:Klayers-python38-requests:24
Policies:
- Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
Resource: !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*
- Effect: Allow
Action:
- logs:CreateLogStream
- logs:PutLogEvents
Resource: !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${ResourcePrefix}-vpc-function-{{resolve:ssm:${RandomIdParameter}}}:*
VpcLambdaSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub ${ResourcePrefix}-vpc-function-sg-{{resolve:ssm:${RandomIdParameter}}}
GroupDescription: security group for vpc lambda function
VpcId: !Ref VPC
SecurityGroupEgress:
- IpProtocol: "-1"
CidrIp: 0.0.0.0/0
VpcLambdaLog:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/lambda/${ResourcePrefix}-vpc-function-{{resolve:ssm:${RandomIdParameter}}}
RetentionInDays: 7
# ------------------------------------------------------------#
# API Gateway + Lambda
# ------------------------------------------------------------#
EdgeApi:
Type: AWS::Serverless::Api
Properties:
Name: !Sub ${ResourcePrefix}-edge-api-{{resolve:ssm:${RandomIdParameter}}}
StageName: prod
EndpointConfiguration:
Type: EDGE
RegionalApi:
Type: AWS::Serverless::Api
Properties:
Name: !Sub ${ResourcePrefix}-regional-api-{{resolve:ssm:${RandomIdParameter}}}
StageName: prod
EndpointConfiguration:
Type: REGIONAL
PrivateApi:
Type: AWS::Serverless::Api
Properties:
Name: !Sub ${ResourcePrefix}-private-api-{{resolve:ssm:${RandomIdParameter}}}
StageName: prod
EndpointConfiguration:
Type: PRIVATE
Auth:
ResourcePolicy:
IntrinsicVpcWhitelist:
- !Ref VPC
ApiLambda:
Type: AWS::Serverless::Function
DependsOn: ApiLambdaLog
Properties:
FunctionName: !Sub ${ResourcePrefix}-api-function-{{resolve:ssm:${RandomIdParameter}}}
InlineCode: |
def lambda_handler(event, context):
return {
'isBase64Encoded': False,
'statusCode': 200,
'headers': {},
'body': '{"message": "Hello from AWS Lambda"}'
}
Handler: index.lambda_handler
Runtime: python3.8
Timeout: 10
MemorySize: 128
Events:
EdgeApiEvent:
Type: Api
Properties:
Path: /
Method: get
RestApiId: !Ref EdgeApi
RegionalApiEvent:
Type: Api
Properties:
Path: /
Method: get
RestApiId: !Ref RegionalApi
PrivateApiEvent:
Type: Api
Properties:
Path: /
Method: get
RestApiId: !Ref PrivateApi
Policies:
- Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
Resource: !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*
- Effect: Allow
Action:
- logs:CreateLogStream
- logs:PutLogEvents
Resource: !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${ResourcePrefix}-api-function-{{resolve:ssm:${RandomIdParameter}}}:*
ApiLambdaLog:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/lambda/${ResourcePrefix}-api-function-{{resolve:ssm:${RandomIdParameter}}}
RetentionInDays: 7
# ------------------------------------------------------------#
# Supplemental Resources
# ------------------------------------------------------------#
RandomIdParameter:
Type: AWS::SSM::Parameter
Properties:
Name: !Join
- '/'
- - !Sub /${ResourcePrefix}
- !Select [ '0', !Split [ '-', !Select [ '2', !Split [ '/', !Ref AWS::StackId ] ] ] ]
- random-id
Type: String
Value: !Select [ '0', !Split [ '-', !Select [ '2', !Split [ '/', !Ref AWS::StackId ] ] ] ]