Skip to content

Instantly share code, notes, and snippets.

@reinhrst
Created January 5, 2022 13:40
Show Gist options
  • Save reinhrst/c4c726407c70a1c7a0c493b407428520 to your computer and use it in GitHub Desktop.
Save reinhrst/c4c726407c70a1c7a0c493b407428520 to your computer and use it in GitHub Desktop.
CloudFormation stack for website with react-routing and api-reverse-proxy
AWSTemplateFormatVersion: 2010-09-09
Description: Creates an https website
Metadata: {}
Parameters:
HostedZoneName:
Description: The HostedZoneName of the zone that you want to host the website on. This has to be in the same account, and has to be active (i.e. used as the DNS server for this domain).
Type: String
HostedZoneId:
Description: The ID for the HostedZoneName
Type: String
Hostname:
Description: The hostname to host the website on (in the HostedZoneId). This should not exist yet within the HostedZoneId.
Type: String
AllowedPattern: ^[a-z0-9][a-z0-9-]*$
Default: www
PriceClass:
Type: String
Description: The CloudFront distribution price class
AllowedValues:
- 'PriceClass_100'
- 'PriceClass_200'
- 'PriceClass_All'
Mappings: {}
Conditions: {}
Resources:
Certificate:
Type: AWS::CertificateManager::Certificate
Properties:
DomainName: !Sub "${Hostname}.${HostedZoneName}"
DomainValidationOptions:
- DomainName: !Sub "${Hostname}.${HostedZoneName}"
HostedZoneId: !Ref HostedZoneId
ValidationMethod: DNS
Bucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
Properties:
AccessControl: Private
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
BucketName: !Sub "${Hostname}.${HostedZoneName}"
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
VersioningConfiguration:
Status: Enabled
BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref Bucket
PolicyDocument:
Statement:
- Action:
- s3:GetObject
Effect: Allow
Resource: !Sub "arn:aws:s3:::${Bucket}/website/*"
Principal:
CanonicalUser: !GetAtt CloudFrontOriginAccessIdentity.S3CanonicalUserId
ApiSourceOriginRequestPolicy:
Type: AWS::CloudFront::OriginRequestPolicy
Properties:
OriginRequestPolicyConfig:
Name: ApiSourceOriginRequestPolicy
CookiesConfig:
CookieBehavior: none
QueryStringsConfig:
QueryStringBehavior: all
HeadersConfig:
HeaderBehavior: none
ApiSourceCachePolicy:
Type: AWS::CloudFront::CachePolicy
Properties:
CachePolicyConfig:
DefaultTTL: 0
MinTTL: 0
MaxTTL: 1
Name: ApiSourceCachePolicy
ParametersInCacheKeyAndForwardedToOrigin:
EnableAcceptEncodingGzip: true
EnableAcceptEncodingBrotli: true
CookiesConfig:
CookieBehavior: none
QueryStringsConfig:
QueryStringBehavior: all
HeadersConfig:
HeaderBehavior: whitelist
Headers:
- Authorization
- Accept
- Content-Type
RoutingRedirectFunction:
Type: AWS::CloudFront::Function
Properties:
AutoPublish: true
FunctionCode: |
function handler(event) {
var request = event.request;
if (request.uri === "/OAuthResponse"
|| request.uri.startsWith("/division/")) {
request.uri = "/index.html"
}
return request;
}
FunctionConfig:
Comment: Redirects the paths used by the React Router
Runtime: cloudfront-js-1.0
Name: RoutingRedirectFunction
CloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Aliases:
- !Sub "${Hostname}.${HostedZoneName}"
DefaultCacheBehavior:
CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # caching disabled
Compress: true
ForwardedValues:
QueryString: false
FunctionAssociations:
- EventType: viewer-request
FunctionARN:
!GetAtt RoutingRedirectFunction.FunctionMetadata.FunctionARN
TargetOriginId: !Sub "S3-${Hostname}.${HostedZoneName}"
ViewerProtocolPolicy: redirect-to-https
CacheBehaviors:
- AllowedMethods: [GET, HEAD, OPTIONS, PUT, PATCH, POST, DELETE]
CachePolicyId: !Ref ApiSourceCachePolicy
Compress: true
OriginRequestPolicyId: !Ref ApiSourceOriginRequestPolicy
PathPattern: /api/v1/*
TargetOriginId: apisource
ViewerProtocolPolicy: https-only
- AllowedMethods: [GET, HEAD, OPTIONS, PUT, PATCH, POST, DELETE]
CachePolicyId: !Ref ApiSourceCachePolicy
Compress: true
OriginRequestPolicyId: !Ref ApiSourceOriginRequestPolicy
PathPattern: /legacyApi/
TargetOriginId: apisource
ViewerProtocolPolicy: https-only
- AllowedMethods: [GET, HEAD, OPTIONS, PUT, PATCH, POST, DELETE]
CachePolicyId: !Ref ApiSourceCachePolicy
Compress: true
OriginRequestPolicyId: !Ref ApiSourceOriginRequestPolicy
PathPattern: /legacyApi2/
TargetOriginId: apisource
ViewerProtocolPolicy: https-only
DefaultRootObject: index.html
Enabled: true
HttpVersion: http2
Origins:
- DomainName: !Sub "${Bucket}.s3.amazonaws.com"
OriginPath: /website
Id: !Sub "S3-${Hostname}.${HostedZoneName}"
S3OriginConfig:
OriginAccessIdentity:
!Sub "origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity}"
- DomainName: api.apisource.org
Id: apisource
OriginPath: ""
CustomOriginConfig:
OriginProtocolPolicy: match-viewer
Logging:
Bucket: !Sub "${Bucket}.s3.amazonaws.com"
IncludeCookies: False
Prefix: cloudfront-logs/
PriceClass: !Ref PriceClass
ViewerCertificate:
AcmCertificateArn: !Ref Certificate
MinimumProtocolVersion: TLSv1
SslSupportMethod: sni-only
Tags:
- Key: Domain
Value: !Sub "${Hostname}.${HostedZoneName}"
CloudFrontOriginAccessIdentity:
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment: !Sub 'CloudFront OAI for ${Hostname}.${HostedZoneName}'
DNSEntry:
Type: AWS::Route53::RecordSet
Properties:
Name: !Sub "${Hostname}.${HostedZoneName}."
Type: A
AliasTarget:
DNSName: !GetAtt CloudFrontDistribution.DomainName
HostedZoneId: Z2FDTNDATAQYW2 # hardcoded CloudFront zone id
HostedZoneName: !Sub "${HostedZoneName}."
Outputs:
{}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment