270 lines
9.0 KiB
YAML
270 lines
9.0 KiB
YAML
###############################################################################
|
|
### AWS Cloudformation Template
|
|
### InfluxData Documentation Website Hosting and Deployment
|
|
###############################################################################
|
|
AWSTemplateFormatVersion: 2010-09-09
|
|
Description: >
|
|
Cloudformation template to stand up the AWS resources for hosting the
|
|
InfluxData documentation static website created using Hugo. Cloudfront
|
|
distribution is used to cache requests to an S3 bucket configured as a static
|
|
website. A Lambda@Edge function rewrites requests with paths ending in
|
|
index.html and requests to old v1 docs endpoints, which reside in a second
|
|
bucket. Finally, a lambda is used to generate new versions of the docs using
|
|
the GitHub source based on event and webhook triggers.
|
|
|
|
###############################################################################
|
|
Parameters:
|
|
###############################################################################
|
|
|
|
AcmCertificateArn:
|
|
Type: String
|
|
Description: >
|
|
The ARN of the SSL certificate to use for the CloudFront
|
|
distribution.
|
|
|
|
DomainName:
|
|
Type: String
|
|
Description: The docs website domain name.
|
|
Default: dev.docs.influxdata.com
|
|
|
|
###############################################################################
|
|
Outputs:
|
|
###############################################################################
|
|
|
|
DocsProdBucketArn:
|
|
Description: The ARN of the S3 bucket hosting the static content.
|
|
Value: !GetAtt DocsV2Bucket.Arn
|
|
|
|
DocsCircleCIDeployAccessKeyId:
|
|
Description: The access key ID for CircleCI deployment to S3.
|
|
Value: !Ref DocsCircleCIDeployAccessKey
|
|
|
|
DocsCircleCIDeploySecretAccessKey:
|
|
Description: The secret access key for CircleCI deployment to S3.
|
|
Value: !GetAtt DocsCircleCIDeployAccessKey.SecretAccessKey
|
|
|
|
###############################################################################
|
|
Resources:
|
|
###############################################################################
|
|
|
|
DocsCloudFrontDistribution:
|
|
Type: AWS::CloudFront::Distribution
|
|
Description: The CDN for both V1 and V2 docs.
|
|
Properties:
|
|
DistributionConfig:
|
|
Aliases:
|
|
- !Ref DomainName
|
|
DefaultCacheBehavior:
|
|
Compress: true
|
|
ForwardedValues:
|
|
QueryString: false
|
|
TargetOriginId: !Ref DocsV2Bucket
|
|
ViewerProtocolPolicy: redirect-to-https
|
|
LambdaFunctionAssociations:
|
|
- EventType: origin-request
|
|
LambdaFunctionARN: !Ref DocsOriginRequestRewriteLambdaVersion
|
|
DefaultRootObject: index.html
|
|
CustomErrorResponses:
|
|
- ErrorCachingMinTTL: 300
|
|
ErrorCode: 403
|
|
ResponseCode: 404
|
|
ResponsePagePath: /404.html
|
|
Enabled: true
|
|
HttpVersion: http2
|
|
Origins:
|
|
- DomainName:
|
|
!Sub "${DocsV2Bucket}.s3.amazonaws.com"
|
|
Id: !Ref DocsV2Bucket
|
|
S3OriginConfig:
|
|
OriginAccessIdentity:
|
|
!Sub "origin-access-identity/cloudfront/${DocsCloudFrontOriginAccessIdentity}"
|
|
- DomainName:
|
|
!Sub "${DocsV1Bucket}.s3.amazonaws.com"
|
|
Id: !Ref DocsV1Bucket
|
|
S3OriginConfig:
|
|
OriginAccessIdentity:
|
|
!Sub "origin-access-identity/cloudfront/${DocsCloudFrontOriginAccessIdentity}"
|
|
PriceClass: PriceClass_100
|
|
ViewerCertificate:
|
|
AcmCertificateArn: !Ref AcmCertificateArn
|
|
MinimumProtocolVersion: TLSv1.1_2016
|
|
SslSupportMethod: sni-only
|
|
Tags:
|
|
- Key: Domain
|
|
Value: !Ref DomainName
|
|
|
|
DocsCloudFrontOriginAccessIdentity:
|
|
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
|
|
Properties:
|
|
CloudFrontOriginAccessIdentityConfig:
|
|
Comment: !Sub 'CloudFront Origin Access Identity for ${DomainName}'
|
|
|
|
DocsV2Bucket:
|
|
Type: AWS::S3::Bucket
|
|
Properties:
|
|
BucketEncryption:
|
|
ServerSideEncryptionConfiguration:
|
|
-
|
|
ServerSideEncryptionByDefault:
|
|
SSEAlgorithm: AES256
|
|
Tags:
|
|
- Key: Domain
|
|
Value: !Ref DomainName
|
|
|
|
DocsV2BucketPolicy:
|
|
Type: AWS::S3::BucketPolicy
|
|
Properties:
|
|
Bucket: !Ref DocsV2Bucket
|
|
PolicyDocument:
|
|
Statement:
|
|
-
|
|
Effect: Allow
|
|
Action:
|
|
- s3:GetObject
|
|
Resource: !Sub "arn:aws:s3:::${DocsV2Bucket}/*"
|
|
Principal:
|
|
CanonicalUser: !GetAtt DocsCloudFrontOriginAccessIdentity.S3CanonicalUserId
|
|
|
|
DocsV1Bucket:
|
|
Type: AWS::S3::Bucket
|
|
Properties:
|
|
BucketEncryption:
|
|
ServerSideEncryptionConfiguration:
|
|
-
|
|
ServerSideEncryptionByDefault:
|
|
SSEAlgorithm: AES256
|
|
Tags:
|
|
- Key: Domain
|
|
Value: !Ref DomainName
|
|
|
|
DocsV1BucketPolicy:
|
|
Type: AWS::S3::BucketPolicy
|
|
Properties:
|
|
Bucket: !Ref DocsV1Bucket
|
|
PolicyDocument:
|
|
Statement:
|
|
- Effect: Allow
|
|
Action:
|
|
- s3:GetObject
|
|
Resource: !Sub "arn:aws:s3:::${DocsV1Bucket}/*"
|
|
Principal:
|
|
CanonicalUser: !GetAtt DocsCloudFrontOriginAccessIdentity.S3CanonicalUserId
|
|
|
|
DocsOriginRequestRewriteLambda:
|
|
Type: AWS::Lambda::Function
|
|
Properties:
|
|
Description: Lambda function performing request URI rewriting.
|
|
Code:
|
|
ZipFile: |
|
|
const config = {
|
|
suffix: '.html',
|
|
appendToDirs: 'index.html',
|
|
removeTrailingSlash: false,
|
|
};
|
|
|
|
const regexSuffixless = /\/[a-z0-9]+([0-9\.]+)?$/; // e.g. "/some/page" but not "/", "/some/" or "/some.jpg"
|
|
const regexTrailingSlash = /.+\/$/; // e.g. "/some/" or "/some/page/" but not root "/"
|
|
|
|
exports.handler = function handler(event, context, callback) {
|
|
const { request } = event.Records[0].cf;
|
|
const { uri } = request;
|
|
const { suffix, appendToDirs, removeTrailingSlash } = config;
|
|
|
|
// Append ".html" to origin request
|
|
if (suffix && uri.match(regexSuffixless)) {
|
|
request.uri = uri + suffix;
|
|
callback(null, request);
|
|
return;
|
|
}
|
|
|
|
// Append "index.html" to origin request
|
|
if (appendToDirs && uri.match(regexTrailingSlash)) {
|
|
request.uri = uri + appendToDirs;
|
|
callback(null, request);
|
|
return;
|
|
}
|
|
|
|
// Redirect (301) non-root requests ending in "/" to URI without trailing slash
|
|
if (removeTrailingSlash && uri.match(/.+\/$/)) {
|
|
const response = {
|
|
// body: '',
|
|
// bodyEncoding: 'text',
|
|
headers: {
|
|
'location': [{
|
|
key: 'Location',
|
|
value: uri.slice(0, -1)
|
|
}]
|
|
},
|
|
status: '301',
|
|
statusDescription: 'Moved Permanently'
|
|
};
|
|
callback(null, response);
|
|
return;
|
|
}
|
|
|
|
// If nothing matches, return request unchanged
|
|
callback(null, request);
|
|
};
|
|
Handler: index.handler
|
|
MemorySize: 128
|
|
Role: !Sub ${DocsOriginRequestRewriteLambdaRole.Arn}
|
|
Runtime: nodejs8.10
|
|
Tags:
|
|
- Key: Domain
|
|
Value: !Ref DomainName
|
|
|
|
DocsOriginRequestRewriteLambdaVersion:
|
|
Type: AWS::Lambda::Version
|
|
Properties:
|
|
FunctionName: !Ref DocsOriginRequestRewriteLambda
|
|
Description: !Sub "URL rewriting for ${DomainName}"
|
|
|
|
DocsOriginRequestRewriteLambdaRole:
|
|
Type: AWS::IAM::Role
|
|
Properties:
|
|
AssumeRolePolicyDocument:
|
|
Version: 2012-10-17
|
|
Statement:
|
|
- Effect: Allow
|
|
Action: sts:AssumeRole
|
|
Principal:
|
|
Service:
|
|
- edgelambda.amazonaws.com
|
|
- lambda.amazonaws.com
|
|
ManagedPolicyArns:
|
|
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
|
|
|
|
DocsCircleCIDeployUser:
|
|
Type: AWS::IAM::User
|
|
Properties:
|
|
Policies:
|
|
- PolicyName: giveaccesstoupdatedocsbuckets
|
|
PolicyDocument:
|
|
Version: 2012-10-17
|
|
Statement:
|
|
- Effect: Allow
|
|
Action:
|
|
- s3:ListBucket
|
|
- s3:GetBucketLocation
|
|
Resource:
|
|
- !Sub "arn:aws:s3:::${DocsV2Bucket}"
|
|
- !Sub "arn:aws:s3:::${DocsV1Bucket}"
|
|
- Effect: Allow
|
|
Action:
|
|
- s3:PutObject
|
|
- s3:PutObjectAcl
|
|
- s3:DeleteObject
|
|
Resource:
|
|
- !Sub "arn:aws:s3:::${DocsV2Bucket}/*"
|
|
- !Sub "arn:aws:s3:::${DocsV1Bucket}/*"
|
|
- Effect: Allow
|
|
Action:
|
|
- cloudfront:GetDistribution
|
|
- cloudfront:CreateInvalidation
|
|
Resource: "*"
|
|
|
|
DocsCircleCIDeployAccessKey:
|
|
Type: AWS::IAM::AccessKey
|
|
Properties:
|
|
UserName: !Ref DocsCircleCIDeployUser
|