静的サイトの鉄板構成であるS3,Cloudfont,Route53をコマンド一発でデプロイするslsテンプレ

Route53・S3・Cloudfrontを連携させる設定はAWS初見の人では難しく感じると思います。私がそうでした。

今はSeverless Frameworkを使い、一発でデプロイするファイルを作成して運用しているので、公開します。
[Githubはこちら](https://github.com/umihico/sls-static-website)、[デモページはこちら](https://sls-static-website.umihi.co/)です。
テンプレは以下の環境で動作・デプロイされます。適宜読み替えてください。

- `umihi.co`というドメインをroute53で管理している。
- `us-east-1`のACMにて`umihi.co`と`*.umihi.co`を１つとして証明書を発行している。
- サブドメイン`sls-static-website.umihi.co`として静的サイトをデプロイする

以下のファイルを作成し、`AcmCertificateArn `と、ドメイン各所の書き換えを行い`sls deploy`でデプロイします。分かりやすさ重視でなるべくハードコーディングしています。

```yaml:serverless.yaml
service: sls-static-website

provider:
  name: aws
  runtime: provided
  stage: dev
  region: ap-northeast-1

resources:
  Resources:

    WebsiteBucket:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: sls-static-website.umihi.co
        WebsiteConfiguration:
          IndexDocument: index.html
          ErrorDocument: 404.html
      DeletionPolicy: Retain

    WebsiteCloudfront:
      Type: AWS::CloudFront::Distribution
      DependsOn:
      - WebsiteBucket
      Properties:
        DistributionConfig:
          Origins:
          - DomainName: sls-static-website.umihi.co.s3-website-ap-northeast-1.amazonaws.com
            Id: S3Origin
            CustomOriginConfig:
              HTTPPort: '80'
              HTTPSPort: '443'
              OriginProtocolPolicy: http-only
          Enabled: true
          HttpVersion: 'http2'
          DefaultRootObject: index.html
          Aliases:
          - sls-static-website.umihi.co
          DefaultCacheBehavior:
            AllowedMethods:
            - GET
            - HEAD
            Compress: true
            TargetOriginId: S3Origin
            ForwardedValues:
              QueryString: true
              Cookies:
                Forward: none
            ViewerProtocolPolicy: redirect-to-https
          PriceClass: PriceClass_All
          ViewerCertificate:
            AcmCertificateArn: arn:aws:acm:us-east-1:123456789:certificate/xxxxxxxx-yyyy-zzzz-aaaa-123456789 # must be in us-east-1
            SslSupportMethod: sni-only

    WebsiteDNSName:
      Type: AWS::Route53::RecordSetGroup
      Properties:
        HostedZoneName: umihi.co. # you need (.)period at the end
        RecordSets:
        - Name: sls-static-website.umihi.co. # you need (.)period at the end
          Type: A
          AliasTarget:
            HostedZoneId: Z2FDTNDATAQYW2 #  This is always the hosted zone ID when you create an alias record that routes traffic to a CloudFront distribution.
            # DNSName: ddddxxxxyyyyzzzzz.cloudfront.net # you can also hard-code like this
            DNSName:
              Fn::GetAtt: [ WebsiteCloudfront, DomainName ]

```

作成時にハマったのは

- route53の`HostedZoneName`や`RecordSets.Name`の値には末尾にピリオドが必要がある。
- S3にアップロード・更新しても、Cloudfrontがキャッシュを持つと反映されないので都度Invalidationする必要がある
- CloudfrontのOrigin設定の`DomainName`で補完で候補に表示される`sls-static-website.umihi.co.s3.amazonaws.com`ではなく、
Static Website Hostingのエンドポイントの`sls-static-website.umihi.co.s3-website-ap-northeast-1.amazonaws.com`を使わないと、この構成だとリダイレクトのループになった。

リダイレクト問題自体は解決せず分からなかったのですが仮に解決したら、どちらのエンドポイントを使うべきかは[こちら](https://qiita.com/ooxif/items/d28b7caf72bf5290588c)が参考になりました。

バケットにファイルを手動でアップロードすると、公開する作業が必要になります。[serverless-s3-sync](https://github.com/k1LoW/serverless-s3-sync)を使い設定すればデプロイ時にローカルファイルを常に同期することが可能になり、公開作業も不要になります。[Githubレポジトリの`serverless.yml`](https://github.com/umihico/sls-static-website/blob/master/serverless.yml)にはその設定も含まれているので、参考にしてください。

参考

- https://github.com/sjevs/cloudformation-s3-static-website-with-cloudfront-and-route-53/blob/master/s3-static-website-with-cloudfront-and-route-53.yaml
- https://gist.github.com/matalo33/fc2a9d8698c069e134b4b0b6640f0c84
- https://github.com/awslabs/aws-cloudformation-templates/blob/master/aws/services/S3/S3_Website_With_CloudFront_Distribution.yaml
- https://masaru-tech.hateblo.jp/entry/2018/03/27/111327
- https://qiita.com/NaokiIshimura/items/46994e67b712831c3016
- https://forum.serverless.com/t/unclear-how-to-reference-lambda-role-arn-in-serverless-yml/1147/5
- https://simonecarletti.com/blog/2016/08/redirect-domain-https-amazon-cloudfront/
- https://stackoverflow.com/questions/43835731/301-redirect-for-specific-page-with-cloudfront