# Copyright 2022 Wyatt Zacharias

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

AWSTemplateFormatVersion: "2010-09-09"
Description: Template to deploy a cloudfront distribution with an accompanying S3 bucket

Parameters:
  DomainName:
    Type: String
    Description: The primary domain for the cloudfront distribution
  HostedZoneId:
    Type: String
    Description: The zone ID of the authoritative route53 public hosted zone for the domain

Resources:
  WebsiteBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub cloudfront-${DomainName}-${AWS::AccountId}-${AWS::Region}
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: 'AES256'
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true

  WebsiteBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref WebsiteBucket
      PolicyDocument:
        Statement:
          - Sid: Allow cloudfront read only
            Effect: Allow
            Action: s3:GetObject
            Resource: !Sub arn:aws:s3:::${WebsiteBucket}/*
            Principal:
              CanonicalUser: !GetAtt WebsiteBucketOAI.S3CanonicalUserId

  WebsiteBucketOAI:
    Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
    Properties: 
      CloudFrontOriginAccessIdentityConfig:
        Comment: !Ref WebsiteBucket

  BucketRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Path: /
      RoleName: ec2-cloudfront-role
      Description: !Sub Allow ec2 to upload to ${WebsiteBucket}
      ManagedPolicyArns:
        - !Ref BucketRolePolicy

  BucketRolePolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      Path: /
      ManagedPolicyName: !Sub ec2-upload-cloudfront
      Description: !Sub Policy to allow uploads to ${WebsiteBucket}
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action: 's3:*'
            Resource: 
              - !Sub arn:aws:s3:::${WebsiteBucket}
              - !Sub arn:aws:s3:::${WebsiteBucket}/*

  BucketRoleProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      InstanceProfileName: ec2-cloudfront
      Roles:
        - !Ref BucketRole

  WebsiteDistribution:
    Type: AWS::CloudFront::Distribution
    Properties: 
      DistributionConfig: 
        Aliases: 
          - !Ref DomainName
          - !Sub www.${DomainName}
        DefaultCacheBehavior:
          AllowedMethods:
            - GET
            - HEAD
          Compress: true
          DefaultTTL: 60
          ForwardedValues:
            QueryString: false
          TargetOriginId: S3BaseOrigin
          ViewerProtocolPolicy: redirect-to-https
        DefaultRootObject: index.html
        Enabled: true
        HttpVersion: http2
        IPV6Enabled: true
        Origins:
          - DomainName: !GetAtt WebsiteBucket.DomainName
            Id: S3BaseOrigin
            S3OriginConfig:
              OriginAccessIdentity: !Sub origin-access-identity/cloudfront/${WebsiteBucketOAI}
        PriceClass: PriceClass_100
        ViewerCertificate:
          AcmCertificateArn: !Ref Certificate
          MinimumProtocolVersion: TLSv1.2_2018
          SslSupportMethod: sni-only

  Certificate:
    Type: AWS::CertificateManager::Certificate
    Properties:
      DomainName: !Ref DomainName
      ValidationMethod: DNS
      DomainValidationOptions:
        - DomainName: !Ref DomainName
          HostedZoneId: !Ref HostedZoneId
        - DomainName: !Sub www.${DomainName}
          HostedZoneId: !Ref HostedZoneId
      SubjectAlternativeNames:
        - !Sub www.${DomainName}

  WebsiteBaseDnsA:
    Type: AWS::Route53::RecordSet
    Properties:
      AliasTarget:
        DNSName: !GetAtt WebsiteDistribution.DomainName
        HostedZoneId: Z2FDTNDATAQYW2
      HostedZoneId: !Ref HostedZoneId
      Name: !Ref DomainName
      Type: A

  WebsiteBaseDnsAAAA:
    Type: AWS::Route53::RecordSet
    Properties:
      AliasTarget:
        DNSName: !GetAtt WebsiteDistribution.DomainName
        HostedZoneId: Z2FDTNDATAQYW2
      HostedZoneId: !Ref HostedZoneId
      Name: !Ref DomainName
      Type: AAAA

  WebsiteWwwDnsA:
    Type: AWS::Route53::RecordSet
    Properties:
      AliasTarget:
        DNSName: !GetAtt WebsiteDistribution.DomainName
        HostedZoneId: Z2FDTNDATAQYW2
      HostedZoneId: !Ref HostedZoneId
      Name: !Sub www.${DomainName}
      Type: A

  WebsiteWwwDnsAAAA:
    Type: AWS::Route53::RecordSet
    Properties:
      AliasTarget:
        DNSName: !GetAtt WebsiteDistribution.DomainName
        HostedZoneId: Z2FDTNDATAQYW2
      HostedZoneId: !Ref HostedZoneId
      Name: !Sub www.${DomainName}
      Type: AAAA
