ness
Version:
✪ No-effort static sites deployed to your AWS account.
828 lines (815 loc) • 28.1 kB
YAML
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
AccessLogStack:
Description: 'Optional stack name of s3 stack to store access logs.'
Type: String
Default: ''
WAFStack:
Description: 'Optional stack name of WAF stack.'
Type: String
Default: ''
DomainName:
Description: 'Optional domain.'
Type: String
Default: ''
SubDomainNameWithDot:
Description: 'Primary name that is used to create the DNS entry with trailing dot. Leave blank for naked (or apex and bare) domain.'
Type: String
Default: ''
RedirectSubDomainNameWithDot:
Description: 'Optional sub domain name redirecting to DomainName (e.g. www.).'
Type: String
Default: ''
DefaultRootObject:
Description: 'Optional name of the index document for the website (e.g., index.html).'
Type: String
Default: 'index.html'
DefaultErrorObject:
Description: 'Optional name of the error document for the website (e.g. error.html).'
Type: String
Default: ''
DefaultErrorResponseCode:
Description: 'The HTTP status code that you want to return along with the error page (requires DefaultErrorObject).'
Type: String
Default: '404'
AllowedValues: ['200', '404']
ContentSecurityPolicy:
Description: 'Optional content-security-policy to be served with the site. Defaults to HTTPS only.'
Type: String
Default: 'default-src https:; script-src https:; style-src https:'
ExistingCertificate:
Description: 'Optional ACM Certificate ARN.'
Type: String
Default: ''
IncludeCloudFrontAlias:
Description: 'Whether to include the domain as an alias for the CloudFront distribution. If an existing distribution already has the Alias, we need to delete it first.'
Type: String
Default: 'true'
AllowedValues: ['true', 'false']
LambdaBucket:
Description: 'Bucket for deploying lambdas.'
Type: String
Default: ''
DefaultCachePolicyArn:
Description: 'Default cache policy ARN.'
Type: String
Default: ''
ImageCachePolicyArn:
Description: 'Image cache policy ARN.'
Type: String
Default: ''
StaticCachePolicyArn:
Description: 'Static cache policy ARN.'
Type: String
Default: ''
ImageOriginRequestPolicyArn:
Description: 'Image origin request policy ARN.'
Type: String
Default: ''
IsNextJs:
Description: 'Whether this is a Next.js deployment.'
Type: String
Default: 'false'
AllowedValues: ['true', 'false']
NextJsDefaultLambdaKey:
Description: 'Next.js default lambda key.'
Type: String
Default: ''
NextJsImageLambdaKey:
Description: 'Next.js image lambda key.'
Type: String
Default: ''
NextJsApiLambdaKey:
Description: 'Next.js api lambda key.'
Type: String
Default: ''
NextJsRegenerationLambdaKey:
Description: 'Next.js regeneration lambda key.'
Type: String
Default: ''
NextJsImagePath:
Description: 'Next.js image path.'
Type: String
Default: ''
NextJsDataPath:
Description: 'Next.js data path.'
Type: String
Default: ''
NextJsBasePath:
Description: 'Next.js base path.'
Type: String
Default: ''
NextJsStaticPath:
Description: 'Next.js static path.'
Type: String
Default: ''
NextJsApiPath:
Description: 'Next.js api path.'
Type: String
Default: ''
LogsRetentionInDays:
Description: 'Specifies the number of days you want to retain log events in the specified log group.'
Type: Number
Default: 14
AllowedValues: [1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653]
Conditions:
HasDomain: !Not [!Equals [!Ref DomainName, '']]
IsNextJs: !Equals [!Ref IsNextJs, 'true']
HasDomainOrIsNextJs: !Or [!Condition HasDomain, !Condition IsNextJs]
NoDomain: !Not [!Condition HasDomainOrIsNextJs]
HasRedirectDomainName: !Not [!Equals [!Ref RedirectSubDomainNameWithDot, '']]
HasBucket: !Not [!Equals [!Ref AccessLogStack, '']]
HasWAF: !Not [!Equals [!Ref WAFStack, '']]
HasDefaultRootObject: !Not [!Equals [!Ref DefaultRootObject, '']]
HasDefaultErrorObject: !Not [!Equals [!Ref DefaultErrorObject, '']]
HasExistingCertificate: !Not [!Equals [!Ref ExistingCertificate, '']]
ShouldIncludeCloudFrontAlias: !Equals [!Ref IncludeCloudFrontAlias, 'true']
HasNextJsImagePath: !Not [!Equals [!Ref NextJsImagePath, '']]
HasNextJsDataPath: !Not [!Equals [!Ref NextJsDataPath, '']]
HasNextJsBasePath: !Not [!Equals [!Ref NextJsBasePath, '']]
HasNextJsStaticPath: !Not [!Equals [!Ref NextJsStaticPath, '']]
HasNextJsApiPath: !Not [!Equals [!Ref NextJsApiPath, '']]
HasNextJsRegenerationLambda: !Not [!Equals [!Ref NextJsRegenerationLambdaKey, '']]
Resources:
Bucket:
Type: 'AWS::S3::Bucket'
UpdateReplacePolicy: Delete
DeletionPolicy: Delete
Properties:
AccelerateConfiguration:
AccelerationStatus: Enabled
WebsiteConfiguration:
!If [
NoDomain,
{
IndexDocument: !If [HasDefaultRootObject, !Ref DefaultRootObject, !Ref 'AWS::NoValue'],
ErrorDocument:
!If [HasDefaultErrorObject, !Ref DefaultErrorObject, !Ref 'AWS::NoValue'],
},
!Ref 'AWS::NoValue',
]
BucketPolicyPublic:
Condition: NoDomain
Type: 'AWS::S3::BucketPolicy'
Properties:
Bucket: !Ref Bucket
PolicyDocument:
Statement:
- Action: 's3:GetObject'
Effect: Allow
Resource: !Sub 'arn:aws:s3:::${Bucket}/*'
Principal: '*'
BucketPolicy:
Condition: HasDomainOrIsNextJs
Type: 'AWS::S3::BucketPolicy'
Properties:
Bucket: !Ref Bucket
PolicyDocument:
Statement:
- Action: 's3:GetObject'
Effect: Allow
Resource: !Sub 'arn:aws:s3:::${Bucket}/*'
Principal:
CanonicalUser: !GetAtt CloudFrontOriginAccessIdentity.S3CanonicalUserId
- Sid: AllowSSLRequestsOnly # AWS Foundational Security Best Practices v1.0.0 S3.5
Effect: Deny
Principal: '*'
Action: 's3:*'
Resource:
- !GetAtt 'Bucket.Arn'
- !Sub '${Bucket.Arn}/*'
Condition:
Bool:
'aws:SecureTransport': false
NextJsDefaultLambdaRole:
Condition: IsNextJs
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
- edgelambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: S3Policy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:GetObject*
- s3:GetBucket*
- s3:List*
- s3:DeleteObject*
- s3:PutObject*
- s3:Abort*
Resource: !Sub 'arn:aws:s3:::${Bucket}/*'
- PolicyName: SQSPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- sqs:SendMessage
- sqs:GetQueueAttributes
- sqs:GetQueueUrl
Resource:
- !If
- HasNextJsRegenerationLambda
- !GetAtt NextJsRegenerationQueue.Arn
- '*'
- PolicyName: InvokePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource:
- !If
- HasNextJsRegenerationLambda
- !GetAtt NextJsRegenerationLambda.Arn
- '*'
NextJsDefaultLambda:
Condition: IsNextJs
Type: AWS::Lambda::Function
Properties:
Code:
S3Bucket: !Ref LambdaBucket
S3Key: !Ref NextJsDefaultLambdaKey
Role: !GetAtt NextJsDefaultLambdaRole.Arn
Description: Default Lambda@Edge function for Next.js
Handler: index.handler
Runtime: nodejs12.x
Timeout: 10
MemorySize: 512
DependsOn:
- NextJsDefaultLambdaRole
NextJsDefaultLambdaCurrentVersion:
Condition: IsNextJs
Type: AWS::Lambda::Version
Properties:
FunctionName: !Ref NextJsDefaultLambda
UpdateReplacePolicy: Delete
DeletionPolicy: Delete
NextJsImageLambdaRole:
Condition: HasNextJsImagePath
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
- edgelambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: S3Policy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:PutObject
Resource: !Sub 'arn:aws:s3:::${Bucket}/*'
NextJsImageLambda:
Condition: HasNextJsImagePath
Type: AWS::Lambda::Function
Properties:
Code:
S3Bucket: !Ref LambdaBucket
S3Key: !Ref NextJsImageLambdaKey
Role: !GetAtt NextJsImageLambdaRole.Arn
Description: Image Lambda@Edge function for Next.js
Handler: index.handler
Runtime: nodejs12.x
Timeout: 10
MemorySize: 512
DependsOn:
- NextJsImageLambdaRole
NextJsImageLambdaCurrentVersion:
Condition: HasNextJsImagePath
Type: AWS::Lambda::Version
Properties:
FunctionName: !Ref NextJsImageLambda
UpdateReplacePolicy: Delete
DeletionPolicy: Delete
NextJsImageLambdaCurrentVersionEventInvokeConfig:
Condition: HasNextJsImagePath
Type: AWS::Lambda::EventInvokeConfig
Properties:
FunctionName: !Ref NextJsImageLambda
Qualifier: !GetAtt NextJsImageLambdaCurrentVersion.Version
MaximumRetryAttempts: 1
NextJsApiLambdaRole:
Condition: HasNextJsApiPath
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
- edgelambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: S3Policy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:PutObject
Resource: !Sub 'arn:aws:s3:::${Bucket}/*'
NextJsApiLambda:
Condition: HasNextJsApiPath
Type: AWS::Lambda::Function
Properties:
Code:
S3Bucket: !Ref LambdaBucket
S3Key: !Ref NextJsApiLambdaKey
Role: !GetAtt NextJsApiLambdaRole.Arn
Description: API Lambda@Edge function for Next.js
Handler: index.handler
Runtime: nodejs12.x
Timeout: 10
MemorySize: 512
DependsOn:
- NextJsApiLambdaRole
NextJsApiLambdaCurrentVersion:
Condition: HasNextJsApiPath
Type: AWS::Lambda::Version
Properties:
FunctionName: !Ref NextJsApiLambda
UpdateReplacePolicy: Delete
DeletionPolicy: Delete
NextJsApiLambdaCurrentVersionEventInvokeConfig:
Condition: HasNextJsApiPath
Type: AWS::Lambda::EventInvokeConfig
Properties:
FunctionName: !Ref NextJsApiLambda
Qualifier: !GetAtt NextJsApiLambdaCurrentVersion.Version
MaximumRetryAttempts: 1
NextJsRegenerationQueue:
Condition: HasNextJsRegenerationLambda
Type: AWS::SQS::Queue
DeletionPolicy: Delete
Properties:
QueueName: !Sub '${Bucket}.fifo'
FifoQueue: true
NextJsRegenerationLambdaRole:
Condition: HasNextJsRegenerationLambda
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: S3Policy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:GetObject*
- s3:GetBucket*
- s3:List*
- s3:DeleteObject*
- s3:PutObject*
- s3:Abort*
Resource:
- !Sub 'arn:aws:s3:::${Bucket}'
- !Sub 'arn:aws:s3:::${Bucket}/*'
- PolicyName: SQSPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- sqs:ReceiveMessage
- sqs:ChangeMessageVisibility
- sqs:GetQueueUrl
- sqs:DeleteMessage
- sqs:GetQueueAttributes
Resource: !GetAtt NextJsRegenerationQueue.Arn
NextJsRegenerationLambda:
Condition: HasNextJsRegenerationLambda
Type: AWS::Lambda::Function
Properties:
Code:
S3Bucket: !Ref LambdaBucket
S3Key: !Ref NextJsRegenerationLambdaKey
Role: !GetAtt NextJsRegenerationLambdaRole.Arn
Description: Regeneration Lambda function for Next.js
Handler: index.handler
Runtime: nodejs14.x
Timeout: 30
MemorySize: 512
DependsOn:
- NextJsRegenerationLambdaRole
NextJsRegenerationLambdaCurrentVersion:
Condition: HasNextJsRegenerationLambda
Type: AWS::Lambda::Version
Properties:
FunctionName: !Ref NextJsRegenerationLambda
UpdateReplacePolicy: Delete
DeletionPolicy: Delete
NextJsRegenerationLambdaEventSourceMapping:
Condition: HasNextJsRegenerationLambda
Type: AWS::Lambda::EventSourceMapping
Properties:
Enabled: true
EventSourceArn: !GetAtt NextJsRegenerationQueue.Arn
FunctionName: !GetAtt NextJsRegenerationLambda.Arn
ViewerRequestRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- 'lambda.amazonaws.com'
- 'edgelambda.amazonaws.com'
Action: 'sts:AssumeRole'
ViewerRequestLambdaPolicy:
Type: 'AWS::IAM::Policy'
Properties:
PolicyDocument:
Statement:
- Effect: Allow
Action:
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
Resource: !GetAtt 'ViewerRequestLogGroup.Arn'
PolicyName: lambda
Roles:
- !Ref ViewerRequestRole
ViewerRequestLambdaEdgePolicy:
Type: 'AWS::IAM::Policy'
Properties:
PolicyDocument:
Statement:
- Effect: Allow
Action: 'logs:CreateLogGroup'
Resource: !Sub 'arn:${AWS::Partition}:logs:*:${AWS::AccountId}:log-group:/aws/lambda/us-east-1.${ViewerRequestFunction}:log-stream:'
- Effect: Allow
Action:
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
Resource: !Sub 'arn:${AWS::Partition}:logs:*:${AWS::AccountId}:log-group:/aws/lambda/us-east-1.${ViewerRequestFunction}:log-stream:*'
PolicyName: 'lambda-edge'
Roles:
- !Ref ViewerRequestRole
ViewerRequestFunction:
Type: 'AWS::Lambda::Function'
Properties:
Code:
ZipFile: !Sub |
const regex = /\.[A-Za-z0-9]+$/;
const indexDocument = '${DefaultRootObject}';
const domainName = '${DomainName}'.toLowerCase();
const redirectSubDomainNameWithDot = '${RedirectSubDomainNameWithDot}'.toLowerCase();
const redirectDomainName = redirectSubDomainNameWithDot ? `${!redirectSubDomainNameWithDot}${!domainName}` : '';
exports.handler = async function(event) {
const cf = event.Records[0].cf;
if (cf.request.headers.host[0].value.toLowerCase() === redirectDomainName) {
return {
status: '301',
statusDescription: 'Moved Permanently',
headers: {
location: [{
key: 'Location',
value: `https://${!domainName}${!cf.request.uri}`,
}],
}
};
}
if (cf.request.uri.endsWith('/')) {
return Object.assign({}, cf.request, {uri: `${!cf.request.uri}${!indexDocument}`});
}
if (cf.request.uri.endsWith(`/${!indexDocument}`)) {
return {
status: '302',
statusDescription: 'Found',
headers: {
location: [{
key: 'Location',
value: cf.request.uri.substr(0, cf.request.uri.length - indexDocument.length),
}],
}
};
}
if (!regex.test(cf.request.uri)) {
return {
status: '302',
statusDescription: 'Found',
headers: {
location: [{
key: 'Location',
value: `${!cf.request.uri}/`,
}],
}
};
}
return cf.request;
};
Handler: 'index.handler'
MemorySize: 128
Role: !GetAtt 'ViewerRequestRole.Arn'
Runtime: 'nodejs12.x'
Timeout: 5
ViewerRequestVersion:
Type: 'AWS::Lambda::Version'
Properties:
FunctionName: !Ref ViewerRequestFunction
ViewerRequestLogGroup:
Type: 'AWS::Logs::LogGroup'
Properties:
LogGroupName: !Sub '/aws/lambda/${ViewerRequestFunction}'
RetentionInDays: !Ref LogsRetentionInDays
ViewerResponseFunction:
Type: 'AWS::CloudFront::Function'
Properties:
AutoPublish: true
Name: !Sub '${AWS::StackName}-CfFunction'
FunctionConfig:
Comment: CloudFront Function to add security headers to objects
Runtime: cloudfront-js-1.0
FunctionCode: !Sub |
function handler(event) {
var response = event.response;
var headers = response.headers;
// Set HTTP security headers
// Since JavaScript doesn't allow for hyphens in variable names, we use the dict["key"] notation
headers['strict-transport-security'] = { value: 'max-age=63072000; includeSubdomains; preload'};
headers['content-security-policy'] = { value: "${ContentSecurityPolicy}"};
headers['x-content-type-options'] = { value: 'nosniff'};
headers['x-frame-options'] = {value: 'DENY'};
headers['x-xss-protection'] = {value: '1; mode=block'};
headers['referrer-policy'] = {value: 'same-origin'};
// Return the response to viewers
return response;
}
CloudFrontOriginAccessIdentity:
Condition: HasDomainOrIsNextJs
Type: 'AWS::CloudFront::CloudFrontOriginAccessIdentity'
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment: !Sub
- '${SubDomainNameWithDot}${DomainName}'
- SubDomainNameWithDot: !Ref SubDomainNameWithDot
DomainName: !Ref DomainName
CloudFrontDistribution:
Condition: HasDomainOrIsNextJs
Type: 'AWS::CloudFront::Distribution'
Properties:
DistributionConfig:
Aliases: !If
- ShouldIncludeCloudFrontAlias
- !If
- HasRedirectDomainName
- - !Sub
- '${SubDomainNameWithDot}${DomainName}'
- SubDomainNameWithDot: !Ref SubDomainNameWithDot
DomainName: !Ref DomainName
- !Sub
- '${RedirectSubDomainNameWithDot}${DomainName}'
- RedirectSubDomainNameWithDot: !Ref RedirectSubDomainNameWithDot
DomainName: !Ref DomainName
- - !Sub
- '${SubDomainNameWithDot}${DomainName}'
- SubDomainNameWithDot: !Ref SubDomainNameWithDot
DomainName: !Ref DomainName
- !Ref 'AWS::NoValue'
Comment: Created by Ness
CustomErrorResponses: !If
- HasDefaultErrorObject
- - ErrorCode: 403 # 403 from S3 indicates that the file does not exists
ResponseCode: !Ref DefaultErrorResponseCode
ResponsePagePath: !Sub
- '/${DefaultErrorObject}'
- DefaultErrorObject: !Ref DefaultErrorObject
- []
CacheBehaviors:
- !If
- HasNextJsImagePath
- AllowedMethods:
- GET
- HEAD
- OPTIONS
- PUT
- PATCH
- POST
- DELETE
CachePolicyId: !Ref ImageCachePolicyArn
CachedMethods:
- GET
- HEAD
- OPTIONS
Compress: true
LambdaFunctionAssociations:
- EventType: origin-request
LambdaFunctionARN: !Ref NextJsImageLambdaCurrentVersion
OriginRequestPolicyId: !Ref ImageOriginRequestPolicyArn
PathPattern: !Ref NextJsImagePath
TargetOriginId: s3origin
ViewerProtocolPolicy: redirect-to-https
- !Ref AWS::NoValue
- !If
- HasNextJsDataPath
- AllowedMethods:
- GET
- HEAD
- OPTIONS
CachePolicyId: !Ref DefaultCachePolicyArn
CachedMethods:
- GET
- HEAD
- OPTIONS
Compress: true
LambdaFunctionAssociations:
- EventType: origin-request
IncludeBody: true
LambdaFunctionARN: !Ref NextJsDefaultLambdaCurrentVersion
- EventType: origin-response
LambdaFunctionARN: !Ref NextJsDefaultLambdaCurrentVersion
PathPattern: !Ref NextJsDataPath
TargetOriginId: s3origin
ViewerProtocolPolicy: redirect-to-https
- !Ref AWS::NoValue
- !If
- HasNextJsBasePath
- AllowedMethods:
- GET
- HEAD
- OPTIONS
CachePolicyId: !Ref StaticCachePolicyArn
CachedMethods:
- GET
- HEAD
- OPTIONS
Compress: true
PathPattern: !Ref NextJsBasePath
TargetOriginId: s3origin
ViewerProtocolPolicy: redirect-to-https
- !Ref AWS::NoValue
- !If
- HasNextJsStaticPath
- AllowedMethods:
- GET
- HEAD
- OPTIONS
CachePolicyId: !Ref StaticCachePolicyArn
CachedMethods:
- GET
- HEAD
- OPTIONS
Compress: true
PathPattern: !Ref NextJsStaticPath
TargetOriginId: s3origin
ViewerProtocolPolicy: redirect-to-https
- !Ref AWS::NoValue
- !If
- HasNextJsApiPath
- AllowedMethods:
- GET
- HEAD
- OPTIONS
- PUT
- PATCH
- POST
- DELETE
CachePolicyId: !Ref DefaultCachePolicyArn
CachedMethods:
- GET
- HEAD
- OPTIONS
Compress: true
LambdaFunctionAssociations:
- EventType: origin-request
IncludeBody: true
LambdaFunctionARN: !Ref NextJsApiLambdaCurrentVersion
PathPattern: !Ref NextJsApiPath
TargetOriginId: s3origin
ViewerProtocolPolicy: redirect-to-https
- !Ref AWS::NoValue
DefaultCacheBehavior:
AllowedMethods:
- GET
- HEAD
- OPTIONS
CachePolicyId: !Ref DefaultCachePolicyArn
CachedMethods:
- GET
- HEAD
- OPTIONS
Compress: true
LambdaFunctionAssociations: !If
- IsNextJs
- - EventType: origin-request
IncludeBody: true
LambdaFunctionARN: !Ref NextJsDefaultLambdaCurrentVersion
- EventType: origin-response
LambdaFunctionARN: !Ref NextJsDefaultLambdaCurrentVersion
- - EventType: viewer-request
LambdaFunctionARN: !Ref ViewerRequestVersion
FunctionAssociations:
- EventType: viewer-response
FunctionARN: !GetAtt ViewerResponseFunction.FunctionMetadata.FunctionARN
TargetOriginId: s3origin
ViewerProtocolPolicy: redirect-to-https
DefaultRootObject: !If [HasDefaultRootObject, !Ref DefaultRootObject, !Ref 'AWS::NoValue']
Enabled: true
HttpVersion: http2
IPV6Enabled: true
Logging:
!If [
HasBucket,
{
Bucket: {'Fn::ImportValue': !Sub '${AccessLogStack}-BucketDomainName'},
Prefix: !Ref 'AWS::StackName',
},
!Ref 'AWS::NoValue',
]
Origins:
- DomainName: !GetAtt 'Bucket.RegionalDomainName'
Id: s3origin
S3OriginConfig:
OriginAccessIdentity: !Sub 'origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity}'
PriceClass: 'PriceClass_All'
ViewerCertificate:
!If [
HasExistingCertificate,
{
AcmCertificateArn: !Ref ExistingCertificate,
MinimumProtocolVersion: 'TLSv1.1_2016',
SslSupportMethod: 'sni-only',
},
!Ref 'AWS::NoValue',
]
WebACLId: !If
- HasWAF
- {'Fn::ImportValue': !Sub '${WAFStack}-WebACL'}
- !Ref 'AWS::NoValue'
Outputs:
StackName:
Description: 'Stack name.'
Value: !Sub '${AWS::StackName}'
BucketName:
Description: 'Name of the S3 bucket storing the static files.'
Value: !Ref Bucket
Export:
Name: !Sub '${AWS::StackName}-BucketName'
URL:
Description: 'URL to static website.'
Value: !If
- HasDomain
- !Sub
- 'https://${SubDomainNameWithDot}${DomainName}'
- SubDomainNameWithDot: !Ref SubDomainNameWithDot
DomainName: !Ref DomainName
- !If
- IsNextJs
- !Sub
- 'https://${DomainName}'
- DomainName: !GetAtt 'CloudFrontDistribution.DomainName'
- !GetAtt 'Bucket.WebsiteURL'
Export:
Name: !Sub '${AWS::StackName}-URL'
DistributionId:
Condition: HasDomainOrIsNextJs
Description: 'CloudFront distribution ID'
Value: !Ref CloudFrontDistribution
Export:
Name: !Sub '${AWS::StackName}-DistributionId'
DistributionDomainName:
Condition: HasDomainOrIsNextJs
Description: 'CloudFront distribution domain name'
Value: !GetAtt 'CloudFrontDistribution.DomainName'
Export:
Name: !Sub '${AWS::StackName}-DistributionDomainName'