Files
PMTiles/serverless/aws/protomaps-template.yaml
Brandon Liu bcd5571ba5 Update CloudFormation template. (#435)
* Update CloudFormation template.

* Inline Lambda code into CloudFormation template.
read x-distribution-name header in TileJSON response if PUBLIC_HOSTNAME is not set.

* include both build-zip and build-cloudformation-stack in CI
2024-09-02 15:10:12 +08:00

185 lines
6.0 KiB
YAML

AWSTemplateFormatVersion: '2010-09-09'
Description: Serve Z/X/Y tiles through CloudFront + Lambda from an existing S3 bucket.
Parameters:
BucketName:
Description: 'Name of an existing S3 bucket with .pmtiles tilesets. Should be in the same region as your CloudFormation stack.'
Type: String
MinLength: 1
AllowedOrigins:
Description: 'Comma-separated list of domains (e.g. example.com) allowed by browser CORS policy, or * for all origins.'
Type: List<String>
Default: "*"
PublicHostname:
Description: 'Optional. Replace *.cloudfront.net in TileJSON with a custom hostname (e.g. example.com). See docs on how this value is cached.'
Type: String
Outputs:
CloudFrontDistributionUrl:
Description: 'URL of the CloudFront distribution'
Value: !Sub "https://${CloudFrontDistribution.DomainName}"
Export:
Name: !Sub "${AWS::StackName}-CloudFrontDistributionURL"
Conditions:
IsPublicHostnameProvided:
Fn::Not:
- Fn::Equals:
- Ref: PublicHostname
- ''
Resources:
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub "/aws/lambda/${AWS::StackName}"
RetentionInDays: 7
LambdaExecutionRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: !Sub "${AWS::StackName}-LambdaLoggingPolicy"
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogStream
- logs:PutLogEvents
Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${AWS::StackName}:log-stream:*"
- PolicyName: !Sub "${AWS::StackName}-LambdaS3AccessPolicy"
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: s3:GetObject
Resource: !Sub arn:aws:s3:::${BucketName}/*
LambdaFunctionUrl:
Type: 'AWS::Lambda::Url'
Properties:
AuthType: NONE
TargetFunctionArn: !GetAtt LambdaFunction.Arn
InvokeMode: BUFFERED
LambdaFunctionUrlPermission:
Type: 'AWS::Lambda::Permission'
Properties:
Action: lambda:InvokeFunctionUrl
FunctionName: !Ref LambdaFunction
Principal: '*'
FunctionUrlAuthType: NONE
ViewerRequestCloudFrontFunction:
Type: AWS::CloudFront::Function
Properties:
Name: !Sub "${AWS::StackName}-ViewerRequestCloudFrontFunction"
AutoPublish: true
FunctionCode: |
function handler(event) {
const request = event.request;
request.headers['x-distribution-domain-name'] = { value: event.context.distributionDomainName };
return request;
}
FunctionConfig:
Comment: 'Add x-distribution-domain header.'
Runtime: cloudfront-js-2.0
CloudFrontDistribution:
Type: 'AWS::CloudFront::Distribution'
DeletionPolicy: Delete
Properties:
DistributionConfig:
Origins:
- Id: LambdaOrigin
DomainName: !Select [2, !Split ["/", !GetAtt LambdaFunctionUrl.FunctionUrl]]
CustomOriginConfig:
OriginProtocolPolicy: https-only
DefaultCacheBehavior:
TargetOriginId: LambdaOrigin
ViewerProtocolPolicy: redirect-to-https
CachePolicyId: !Ref CachePolicyId
ResponseHeadersPolicyId: !Ref ResponseHeadersPolicyId
OriginRequestPolicyId: b689b0a8-53d0-40ab-baf2-68738e2966ac
FunctionAssociations:
Fn::If:
- IsPublicHostnameProvided
- !Ref "AWS::NoValue"
-
- EventType: viewer-request
FunctionARN: !GetAtt ViewerRequestCloudFrontFunction.FunctionARN
Enabled: true
HttpVersion: http2and3
Comment: "CloudFront Distribution"
PriceClass: PriceClass_All # Change this to save cost and distribute to fewer countries. Check https://aws.amazon.com/cloudfront/pricing/
CachePolicyId:
Type: 'AWS::CloudFront::CachePolicy'
Properties:
CachePolicyConfig:
Name: !Sub "${AWS::StackName}-CachePolicyConfig"
DefaultTTL: 86400
MaxTTL: 31536000
MinTTL: 0
ParametersInCacheKeyAndForwardedToOrigin:
EnableAcceptEncodingBrotli: true
EnableAcceptEncodingGzip: true
HeadersConfig:
HeaderBehavior: none
CookiesConfig:
CookieBehavior: none
QueryStringsConfig:
QueryStringBehavior: none
ResponseHeadersPolicyId:
Type: 'AWS::CloudFront::ResponseHeadersPolicy'
# DeletionPolicy: Delete
Properties:
ResponseHeadersPolicyConfig:
Name: !Sub "${AWS::StackName}-ResponseHeadersPolicyConfig"
CorsConfig:
AccessControlAllowOrigins:
Items: !Ref AllowedOrigins
AccessControlAllowHeaders:
Items:
- '*'
AccessControlAllowMethods:
Items:
- GET
- HEAD
- OPTIONS
AccessControlExposeHeaders:
Items:
- ETag
AccessControlAllowCredentials: false # Set to true if you want to include credentials
OriginOverride: true
Comment: 'CORS policy for Protomaps'
LambdaFunction:
Type: 'AWS::Lambda::Function'
Properties:
FunctionName: !Sub "${AWS::StackName}-LambdaFunction"
Runtime: nodejs20.x
Architectures: [arm64]
Role: !GetAtt LambdaExecutionRole.Arn
Handler: index.handler
MemorySize: 512
LoggingConfig:
LogGroup: !Ref LogGroup
Environment:
Variables:
BUCKET: !Ref BucketName
PUBLIC_HOSTNAME: !Ref PublicHostname
Code:
S3Bucket: !Ref BucketName
S3Key: lambda_function.zip