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 hidden or 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