特定のサイトにリダイレクトするだけのページをサーバーレスで構築する方法(HTTPS対応)

サービスのドメイン変更などの理由によって、特定のドメインへのアクセスをすべて別ドメインにリダイレクトしたくなったとします。
この記事ではこれをサーバーレスで構築してみたいと思います。

# お題
- 特定のドメインへのアクセスをすべて他のページにリダイレクトする
- 管理コストを低減するためにサーバーレスで構築したい
- 常時HTTPSが当たり前の時代なので、HTTPS対応をしたい
- インフラコストを低く抑えたい(awsの無料利用枠の範囲内くらいにしたい)

# 出来上がったもの
これらのページにアクセスをすると弊社社員である遠藤のfacebookにリダイレクトされます。

http://endo.farm/
https://endo.farm/

# 構成
<img width="708" alt="スクリーンショット 2018-12-02 21.41.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/74551/c2fd90bb-30c9-e4b4-6193-002d145b9231.png">

まず、構築してみたシステムの構成を紹介します。
リダイレクトを行っているのはS3で、その手前にはHTTPS終端のためにCloudFrontを配置してあります。
ACMでSSL証明書を取得することで無料でHTTPSを利用することができます。
また、Route53のALIASレコード機能を使うことでZone Apexに対しても名前解決を行うことができます。

# 説明
ではここから、これらの設定をCloudFormationのテンプレートファイルを利用して説明します。
なお、完全なCloudFormationのテンプレートファイルは以下のリポジトリにあります。

https://github.com/shio-phys/endo-farm

## Route53
まずは、Route53のHosted Zoneがないと何も始まらないので作ります。
なお、Route53を使ってドメインの登録をしている場合は自動的にHosted Zoneが作られているので、これは不要です。

```yaml
Parameters:
  NakedDomain:
    Type: 'String'
    Default: 'endo.farm'
Resources:
  Route53HostedZoneEndoFarm:
    Type: "AWS::Route53::HostedZone"
    Properties:
      Name: !Ref NakedDomain
```

## ACM
ACMを使って証明書を取得します。
なお、CloudFrontにこの証明書をアタッチするために、必ず`us-east-1`リージョンで証明書を取得して下さい。

```yaml
Parameters:
  NakedDomain:
    Type: 'String'
    Default: 'endo.farm'

Resources:
  CertificateManagerCertificateEndoFarm:
    Type: AWS::CertificateManager::Certificate
    Properties:
      DomainName: !Ref NakedDomain
      ValidationMethod: 'DNS'
```

テンプレートの適用をしている最中の画面で、このDNSレコードを登録して下さいという旨のメッセージが出るのでそれに従って、CNAMEレコードの登録を行います。

```yaml
Resources:
  Route53RecordSetCNAMENakedDomain:
    Type: 'AWS::Route53::RecordSet'
    Properties:
      HostedZoneId: !Ref Route53HostedZoneEndoFarm
      Name: <画面に表示されたドメイン名>
      ResourceRecords:
        - <画面に表示されたドメイン名>
      TTL: 60
      Type: 'CNAME'
```

DNSレコードが登録されたあとに暫く待つと、証明書が使えるようになります。

## S3 + CloudFront
次にS3とCloudFrontの設定を行います。
S3のWebsiteConfigurationを利用してリダイレクトを設定します。
そして、その前段にCloudFrontを配置します。
このときの注意点はOriginを指定する時にS3のバケットを直に指定するのではなく、そのwebSiteURLを指定することです。
この設定にしないとCloudFrontはS3バケットに設定したWebsiteConfigurationを無視します。

```yaml
Parameters:
  AcmCertificateArnEndoFarm:
    Type: 'String'
    Default: <SSL証明書のARN>

Resources
  S3BucketEndoFarm:
    Type: 'AWS::S3::Bucket'
    Properties:
      AccessControl: Private
      BucketName: !Ref NakedDomain
      WebsiteConfiguration:
        IndexDocument: 'index.html'
        RoutingRules:
          - RedirectRule:
              HostName: 'www.facebook.com'
              ReplaceKeyWith: 'mari.endou.75'
              HttpRedirectCode: '302'

  CloudFrontDistributionEndoFarm:
    Type: 'AWS::CloudFront::Distribution'
    Properties:
      DistributionConfig:
        Enabled: true
        Origins:
          - Id: !Sub S3-${NakedDomain}
            DomainName: !Select [1, !Split ['://', !GetAtt S3BucketEndoFarm.WebsiteURL] ]
            CustomOriginConfig:
              OriginProtocolPolicy: 'http-only'
        Aliases:
          - !Ref NakedDomain
        DefaultCacheBehavior:
          DefaultTTL: 0
          ForwardedValues:
            QueryString: false
          MaxTTL: 0
          MinTTL: 0
          TargetOriginId: !Sub S3-${NakedDomain}
          ViewerProtocolPolicy: 'allow-all'
        HttpVersion: 'http2'
        IPV6Enabled: true
        ViewerCertificate:
          AcmCertificateArn: !Ref AcmCertificateArnEndoFarm
          MinimumProtocolVersion: 'TLSv1.1_2016'
          SslSupportMethod: 'sni-only'
```

## ZoneApexに対するAレコード
最後にZoneApexに対してAレコードを設定すれば完成です。
ZoneApexに対するCNAMEは設定できないので、必ずAliasTargetを利用してAレコードを登録して下さい。

```yaml
Resources:
  Route53RecordSetANativeDomain:
    Type: 'AWS::Route53::RecordSet'
    Properties:
      HostedZoneId: !Ref Route53HostedZoneEndoFarm
      Name: !Ref NakedDomain
      AliasTarget:
        DNSName: !GetAtt CloudFrontDistributionEndoFarm.DomainName
        HostedZoneId: !Ref Route53HostedZoneEndoFarm
      Type: 'A'
```

# 費用
さて、この構成の費用はいくらになるでしょうか？
アクセスが十分に少なく、aws無料利用枠の範囲内に入っている場合は月額$1.5( + ドメイン取得費用)で運用することができます。
なお、この$1.5はRoute53のHostedZoneに対する課金額です。

# まとめ
特定のサイトにリダイレクトするだけのページをサーバーレスで構築することができました。