Last active
July 22, 2020 11:16
-
-
Save MacksMind/c1dbc34d92aed1db892d23e758aa4637 to your computer and use it in GitHub Desktop.
CloudFormation template to deploy a static website to S3. Details at https://www.macksmind.io/aws/2020/07/20/deploy-static-website-to-amazon-s3.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
AWSTemplateFormatVersion: 2010-09-09 | |
Parameters: | |
GitHubOwner: | |
Type: String | |
Description: GitHub repo owner | |
MinLength: 1 | |
GitHubRepo: | |
Type: String | |
Description: GitHub repo name | |
MinLength: 1 | |
GitHubBranch: | |
Type: String | |
Default: master | |
Description: GitHub branch name | |
MinLength: 1 | |
GitHubSecret: | |
Type: String | |
Description: Reference to AWS Secrets Manager | |
AllowedPattern: "\\{\\{resolve:secretsmanager:.+:SecretString:.+\\}\\}" | |
Subdomain: | |
Type: String | |
Default: www | |
Description: Subdomain of URL (optional) | |
RootDomain: | |
Type: String | |
Description: Root domain of URL | |
IndexPage: | |
Type: String | |
Default: index.html | |
Description: Default page in any directory | |
MinLength: 1 | |
ErrorPage: | |
Type: String | |
Default: 404.html | |
Description: Page not found path (optional, but highly recommended) | |
Route53: | |
Type: String | |
Description: Automatically configure DNS in Route53? | |
AllowedValues: | |
- true | |
- false | |
Default: false | |
RedirectRoot: | |
Type: String | |
Description: Redirect RootDomain to Subdomain? | |
AllowedValues: | |
- true | |
- false | |
Default: false | |
JekyllEnv: | |
Type: String | |
Description: Jekyll build environment | |
Default: production | |
AllowedValues: | |
- production | |
- development | |
ChatbotSlackArn: | |
Type: String | |
Description: AWS Chatbot Channel ARN (optional) | |
Metadata: | |
AWS::CloudFormation::Interface: | |
ParameterGroups: | |
- Label: | |
default: GitHub Configuration | |
Parameters: | |
- GitHubOwner | |
- GitHubRepo | |
- GitHubBranch | |
- GitHubSecret | |
- Label: | |
default: Website Info | |
Parameters: | |
- Subdomain | |
- RootDomain | |
- IndexPage | |
- ErrorPage | |
- Label: | |
default: DNS Details | |
Parameters: | |
- Route53 | |
- RedirectRoot | |
Conditions: | |
HasSubdomain: | |
Fn::Not: | |
- Fn::Equals: | |
- '' | |
- Ref: Subdomain | |
HasErrorPage: | |
Fn::Not: | |
- Fn::Equals: | |
- '' | |
- Ref: ErrorPage | |
RedirectRoot: | |
Fn::And: | |
- Condition: HasSubdomain | |
- Fn::Equals: | |
- 'true' | |
- Ref: RedirectRoot | |
Route53: | |
Fn::Equals: | |
- 'true' | |
- Ref: Route53 | |
Route53Redirect: | |
Fn::And: | |
- Condition: Route53 | |
- Condition: RedirectRoot | |
Chatbot: | |
Fn::Not: | |
- Fn::Equals: | |
- '' | |
- Ref: ChatbotSlackArn | |
Resources: | |
Certificate: | |
Type: AWS::CertificateManager::Certificate | |
Properties: | |
DomainName: | |
Fn::If: | |
- HasSubdomain | |
- Fn::Sub: "${Subdomain}.${RootDomain}" | |
- Ref: RootDomain | |
DomainValidationOptions: | |
- DomainName: | |
Fn::If: | |
- HasSubdomain | |
- Fn::Sub: "${Subdomain}.${RootDomain}" | |
- Ref: RootDomain | |
ValidationDomain: | |
Ref: RootDomain | |
ValidationMethod: DNS | |
DeployBucket: | |
Type: AWS::S3::Bucket | |
Properties: | |
WebsiteConfiguration: | |
IndexDocument: | |
Ref: IndexPage | |
ErrorDocument: | |
Fn::If: | |
- HasErrorPage | |
- Ref: ErrorPage | |
- Ref: AWS::NoValue | |
DeployBucketPolicy: | |
Type: AWS::S3::BucketPolicy | |
Properties: | |
Bucket: | |
Ref: DeployBucket | |
PolicyDocument: | |
Statement: | |
- Action: | |
- s3:GetObject | |
Effect: Allow | |
Principal: "*" | |
Resource: | |
Fn::Sub: "${DeployBucket.Arn}/*" | |
Distribution: | |
Type: AWS::CloudFront::Distribution | |
Properties: | |
DistributionConfig: | |
Aliases: | |
- Fn::If: | |
- HasSubdomain | |
- Fn::Sub: "${Subdomain}.${RootDomain}" | |
- Ref: RootDomain | |
DefaultCacheBehavior: | |
AllowedMethods: | |
- GET | |
- HEAD | |
Compress: true | |
ForwardedValues: | |
QueryString: false | |
MinTTL: 86400 | |
TargetOriginId: S3Origin | |
ViewerProtocolPolicy: redirect-to-https | |
Enabled: true | |
Origins: | |
- DomainName: | |
Fn::Sub: "${DeployBucket}.s3-website-${AWS::Region}.amazonaws.com" | |
Id: S3Origin | |
CustomOriginConfig: | |
OriginProtocolPolicy: http-only | |
HttpVersion: http2 | |
PriceClass: PriceClass_100 | |
ViewerCertificate: | |
AcmCertificateArn: | |
Ref: Certificate | |
MinimumProtocolVersion: TLSv1.2_2018 | |
SslSupportMethod: sni-only | |
DistributionIP4: | |
Type: AWS::Route53::RecordSet | |
Condition: Route53 | |
Properties: | |
AliasTarget: | |
DNSName: | |
Fn::GetAtt: | |
- Distribution | |
- DomainName | |
HostedZoneId: Z2FDTNDATAQYW2 | |
HostedZoneName: | |
Fn::Sub: "${RootDomain}." | |
Name: | |
Fn::If: | |
- HasSubdomain | |
- Fn::Sub: "${Subdomain}.${RootDomain}" | |
- Ref: RootDomain | |
Type: A | |
DistributionIP6: | |
Type: AWS::Route53::RecordSet | |
Condition: Route53 | |
Properties: | |
AliasTarget: | |
DNSName: | |
Fn::GetAtt: | |
- Distribution | |
- DomainName | |
HostedZoneId: Z2FDTNDATAQYW2 | |
HostedZoneName: | |
Fn::Sub: "${RootDomain}." | |
Name: | |
Fn::If: | |
- HasSubdomain | |
- Fn::Sub: "${Subdomain}.${RootDomain}" | |
- Ref: RootDomain | |
Type: AAAA | |
Pipeline: | |
Type: AWS::CodePipeline::Pipeline | |
Properties: | |
ArtifactStore: | |
Location: | |
Ref: PipelineBucket | |
Type: S3 | |
RoleArn: | |
Fn::GetAtt: | |
- PipelineRole | |
- Arn | |
Stages: | |
- Actions: | |
- ActionTypeId: | |
Category: Source | |
Owner: ThirdParty | |
Provider: GitHub | |
Version: 1 | |
Configuration: | |
Owner: | |
Ref: GitHubOwner | |
Repo: | |
Ref: GitHubRepo | |
Branch: | |
Ref: GitHubBranch | |
OAuthToken: | |
Ref: GitHubSecret | |
PollForSourceChanges: false | |
Name: GitHubCommit | |
OutputArtifacts: | |
- Name: SourcePipe | |
Name: Source | |
- Actions: | |
- Name: JekyllDeploy | |
ActionTypeId: | |
Category: Build | |
Owner: AWS | |
Provider: CodeBuild | |
Version: 1 | |
Configuration: | |
ProjectName: | |
Ref: Project | |
InputArtifacts: | |
- Name: SourcePipe | |
Name: Build | |
PipelineBucket: | |
Type: AWS::S3::Bucket | |
Properties: | |
PublicAccessBlockConfiguration: | |
BlockPublicAcls: true | |
BlockPublicPolicy: true | |
IgnorePublicAcls: true | |
RestrictPublicBuckets: true | |
PipelineRole: | |
Type: AWS::IAM::Role | |
Properties: | |
AssumeRolePolicyDocument: | |
Statement: | |
- Action: sts:AssumeRole | |
Effect: Allow | |
Principal: | |
Service: codepipeline.amazonaws.com | |
Version: 2012-10-17 | |
PipelineRoleDefaultPolicy: | |
Type: AWS::IAM::Policy | |
Properties: | |
PolicyDocument: | |
Statement: | |
- Action: | |
- s3:GetObject* | |
- s3:GetBucket* | |
- s3:List* | |
- s3:DeleteObject* | |
- s3:PutObject* | |
- s3:Abort* | |
Effect: Allow | |
Resource: | |
- Fn::GetAtt: | |
- PipelineBucket | |
- Arn | |
- Fn::Sub: "${PipelineBucket.Arn}/*" | |
- Action: | |
- codebuild:BatchGetBuilds | |
- codebuild:StartBuild | |
- codebuild:StopBuild | |
Effect: Allow | |
Resource: | |
Fn::GetAtt: | |
- Project | |
- Arn | |
Version: 2012-10-17 | |
PolicyName: PipelineRoleDefaultPolicy | |
Roles: | |
- Ref: PipelineRole | |
PipelineWebhook: | |
Type: AWS::CodePipeline::Webhook | |
Properties: | |
Authentication: GITHUB_HMAC | |
AuthenticationConfiguration: | |
SecretToken: | |
Ref: GitHubSecret | |
Filters: | |
- JsonPath: "$.ref" | |
MatchEquals: refs/heads/{Branch} | |
TargetAction: GitHubCommit | |
TargetPipeline: | |
Ref: Pipeline | |
TargetPipelineVersion: | |
Fn::GetAtt: | |
- Pipeline | |
- Version | |
RegisterWithThirdParty: true | |
Project: | |
Type: AWS::CodeBuild::Project | |
Properties: | |
Name: | |
Ref: AWS::StackName | |
Artifacts: | |
Type: CODEPIPELINE | |
Cache: | |
Location: | |
Fn::Sub: "${PipelineBucket}/build_cache" | |
Type: S3 | |
Environment: | |
ComputeType: BUILD_GENERAL1_SMALL | |
Image: aws/codebuild/standard:4.0 | |
Type: LINUX_CONTAINER | |
ServiceRole: | |
Fn::GetAtt: | |
- ProjectRole | |
- Arn | |
Source: | |
BuildSpec: | |
Fn::Sub: | | |
version: 0.2 | |
phases: | |
install: | |
runtime-versions: | |
ruby: 2.7 | |
commands: | |
- mkdir -p _bundle_cache | |
- bundle config set path _bundle_cache | |
- bundle config set without 'development test' | |
- bundle install | |
build: | |
commands: | |
- JEKYLL_ENV=${JekyllEnv} bundle exec jekyll build | |
post_build: | |
commands: | |
- aws s3 sync --cache-control='no-cache' _site s3://${DeployBucket}/ --delete | |
- aws cloudfront create-invalidation --distribution-id ${Distribution} --paths '/*' | |
cache: | |
paths: | |
- '_bundle_cache/**/*' | |
Type: CODEPIPELINE | |
ProjectRole: | |
Type: AWS::IAM::Role | |
Properties: | |
AssumeRolePolicyDocument: | |
Statement: | |
- Action: sts:AssumeRole | |
Effect: Allow | |
Principal: | |
Service: codebuild.amazonaws.com | |
Version: 2012-10-17 | |
ProjectRoleDefaultPolicy: | |
Type: AWS::IAM::Policy | |
Properties: | |
PolicyDocument: | |
Statement: | |
- Action: | |
- s3:GetObject* | |
- s3:GetBucket* | |
- s3:List* | |
- s3:DeleteObject* | |
- s3:PutObject* | |
- s3:Abort* | |
Effect: Allow | |
Resource: | |
- Fn::GetAtt: | |
- PipelineBucket | |
- Arn | |
- Fn::Sub: "${PipelineBucket.Arn}/*" | |
- Action: | |
- s3:List* | |
- s3:DeleteObject* | |
- s3:PutObject* | |
- s3:Abort* | |
Effect: Allow | |
Resource: | |
- Fn::GetAtt: | |
- DeployBucket | |
- Arn | |
- Fn::Sub: "${DeployBucket.Arn}/*" | |
- Action: | |
- logs:CreateLogGroup | |
- logs:CreateLogStream | |
- logs:PutLogEvents | |
Effect: Allow | |
Resource: | |
- Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${Project} | |
- Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${Project}:* | |
- Action: cloudfront:CreateInvalidation | |
Effect: Allow | |
Resource: | |
Fn::Sub: arn:${AWS::Partition}:cloudfront::${AWS::AccountId}:distribution/${Distribution} | |
Version: 2012-10-17 | |
PolicyName: ProjectRoleDefaultPolicy | |
Roles: | |
- Ref: ProjectRole | |
ProjectNotification: | |
Type: AWS::CodeStarNotifications::NotificationRule | |
Condition: Chatbot | |
Properties: | |
DetailType: FULL | |
EventTypeIds: | |
- codebuild-project-build-state-failed | |
- codebuild-project-build-state-succeeded | |
- codebuild-project-build-state-in-progress | |
- codebuild-project-build-state-stopped | |
Name: | |
Fn::Sub: "${AWS::StackName}-notification" | |
Resource: | |
Fn::GetAtt: | |
- Project | |
- Arn | |
Targets: | |
- TargetAddress: | |
Ref: ChatbotSlackArn | |
TargetType: AWSChatbotSlack | |
CertificateRedirect: | |
Type: AWS::CertificateManager::Certificate | |
Condition: RedirectRoot | |
Properties: | |
DomainName: | |
Ref: RootDomain | |
DomainValidationOptions: | |
- DomainName: | |
Ref: RootDomain | |
ValidationDomain: | |
Ref: RootDomain | |
ValidationMethod: DNS | |
DeployBucketRedirect: | |
Type: AWS::S3::Bucket | |
Condition: RedirectRoot | |
Properties: | |
PublicAccessBlockConfiguration: | |
BlockPublicAcls: true | |
BlockPublicPolicy: true | |
IgnorePublicAcls: true | |
RestrictPublicBuckets: true | |
WebsiteConfiguration: | |
RedirectAllRequestsTo: | |
HostName: | |
Fn::Sub: "${Subdomain}.${RootDomain}" | |
Protocol: https | |
DistributionRedirect: | |
Type: AWS::CloudFront::Distribution | |
Condition: RedirectRoot | |
Properties: | |
DistributionConfig: | |
Aliases: | |
- Ref: RootDomain | |
DefaultCacheBehavior: | |
AllowedMethods: | |
- GET | |
- HEAD | |
ForwardedValues: | |
QueryString: false | |
TargetOriginId: S3OriginRedirect | |
ViewerProtocolPolicy: allow-all | |
Enabled: true | |
Origins: | |
- DomainName: | |
Fn::Sub: "${DeployBucketRedirect}.s3-website-${AWS::Region}.amazonaws.com" | |
Id: S3OriginRedirect | |
CustomOriginConfig: | |
OriginProtocolPolicy: http-only | |
HttpVersion: http2 | |
PriceClass: PriceClass_100 | |
ViewerCertificate: | |
AcmCertificateArn: | |
Ref: CertificateRedirect | |
MinimumProtocolVersion: TLSv1.2_2018 | |
SslSupportMethod: sni-only | |
DistributionIP4Redirect: | |
Type: AWS::Route53::RecordSet | |
Condition: Route53Redirect | |
Properties: | |
AliasTarget: | |
DNSName: | |
Fn::GetAtt: | |
- DistributionRedirect | |
- DomainName | |
HostedZoneId: Z2FDTNDATAQYW2 | |
HostedZoneName: | |
Fn::Sub: "${RootDomain}." | |
Name: | |
Ref: RootDomain | |
Type: A | |
DistributionIP6Redirect: | |
Type: AWS::Route53::RecordSet | |
Condition: Route53Redirect | |
Properties: | |
AliasTarget: | |
DNSName: | |
Fn::GetAtt: | |
- DistributionRedirect | |
- DomainName | |
HostedZoneId: Z2FDTNDATAQYW2 | |
HostedZoneName: | |
Fn::Sub: "${RootDomain}." | |
Name: | |
Ref: RootDomain | |
Type: AAAA |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment