【AWS】CloudFormtionでブログサービスを構築してみた(03.S3,CloudFront編)

# 01 はじめに
[【AWS】CloudFormtionでブログサービスを構築してみた(02.EC2,RDS,ALB編)](https://qiita.com/1k5/items/5acca0ee627ec37b56da)の続きである。
過去に公開した記事は下記の通りである。
・[【AWS】CloudFormtionでブログサービスを構築してみた(概要編)](https://qiita.com/1k5/items/4f6d61373e80f824bd9a)
・[【AWS】CloudFormtionでブログサービスを構築してみた(01.Network編)](https://qiita.com/1k5/items/d268697691ef2ef3dd0e)
・[【AWS】CloudFormtionでブログサービスを構築してみた(02.EC2,RDS,ALB編)](https://qiita.com/1k5/items/5acca0ee627ec37b56da)

`この記事で扱うものはコンポーネントは下記である。`
1.S3
2.CloudFront

# 02 構成図
下記のオレンジ枠を作成する。
![構成図2-S3,CloudFront.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1050566/09ee34d0-045d-19ae-03f3-1aebc2a10d35.png)



作成後、手動でS3にファイルをアップロードする。

# 03.S3-CloudFrontの設計
1.OAIを実装して、S3エンドポイントにはCloudFrontを経由した場合のみアクセス可能にする。
　(S3エンドポイントには直接アクセス不可にする)

2.S3バケットポリシーのプリンシパルを
`Principal: "*"`
にしているが、Route53の兼ね合いでこの設定にしている。
PrincipalをCloudFrontのみ許可の設定にしたら
独自ドメインでCloudFrontに通信を流したらアクセスできなかったため。
もしこの設定以外で回避できるのであれば試してみたい。

3.httpアクセスしたらCloudFrontでhttpsにリダイレクトさせる

4.設定変更した際にすぐ反映が確認できるようにTTLは60にしている。

# 04.テスト方法
設定変更後は下記観点でテストする
1.CloudFrontから発行されたドメインにhttpsアクセスしてSorryページが表示されるか確認する。

2.S3エンドポイントに直接アクセスしてアクセス不可なことも確認する。

3.画像を差し替える。差し替え後少し経ったら再アクセスして差し替えた画像が表示されるか確認する

4.httpアクセスがhttpsにリダイレクトされるか確認する。

# 05.事前準備
CloudFrontに証明書をアタッチするためバージニアリージョンで証明書を取得する。

# 06.テンプレートファイル
```yaml:S3-CludFront.yml
AWSTemplateFormatVersion: 2010-09-09
Description: CF-S3,CloudFront
# ------------------------------------------------------------#
#  パラメータ
# ------------------------------------------------------------#
Parameters:
  #バケット名を指定
  S3BucketName:
    Type: String
    Description: Type of this BacketName.
  
  #バージニアリージョンで取得した証明書のARNを指定
  ACMarn:
    Type: String
    Description: Type of this ACM-ARN at us-east-1

  #Route53で設定するFQDNを指定
  Aliases:
    Type: String
    Description: Type of this FQDN 

# ------------------------------------------------------------#
#  リソース
# ------------------------------------------------------------#
Resources:
# ------------------------------------------------------------#
# S3
# ------------------------------------------------------------#
  s3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      AccessControl: Private
      BucketName:  !Sub ${S3BucketName}
      #外部アクセスの許可はしない
      PublicAccessBlockConfiguration:
        BlockPublicAcls: "true"
        BlockPublicPolicy: "true"
        IgnorePublicAcls: "true"
        RestrictPublicBuckets: "true"
      Tags:
        - Key: Name
          Value: CF-S3

# ------------------------------------------------------------#
# S3バケットポリシーの設定
# ------------------------------------------------------------#
  BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref "s3Bucket"
      PolicyDocument:
        Id: !Sub '${AWS::StackName}-BucketPolicy'
        Statement:
          - Sid: AddPerm
            Effect: Allow
            Principal: "*"
            Action:
              - s3:GetObject
            #下記2つのファイルをあげる予定なのでそれ以外は拒否する
            Resource:
              - !Sub 'arn:aws:s3:::${s3Bucket}/index.html'
              - !Sub 'arn:aws:s3:::${s3Bucket}/image/image.png'
            Condition:
            #CloudFrontとCloudFormationからのアクセスを許可する
              StringEquals:
                aws:UserAgent: Amazon CloudFront
              StringNotEquals:
                "aws:CalledVia":
                  - "cloudformation.amazonaws.com"

# ------------------------------------------------------------#
# CloudFront-OAI
# ------------------------------------------------------------#
  OriginAccessIdentity:
    Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment: !Sub
          - "OAI-${S3Bucket}"
          - S3Bucket: !Ref s3Bucket

# ------------------------------------------------------------#
# CloudFrontDistribution
# ------------------------------------------------------------#
  CloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    DependsOn: s3Bucket
    Properties:
      DistributionConfig:
        Origins:
          - Id: CustomOrigin
            DomainName: !Sub "${S3BucketName}.s3.ap-northeast-1.amazonaws.com"
            CustomOriginConfig:
              HTTPPort: 80
              OriginProtocolPolicy: http-only
        Enabled: true
        #最安値のものを使用
        PriceClass: PriceClass_100
        DefaultRootObject: index.html
        CustomErrorResponses:
          - ErrorCachingMinTTL: 10
            ErrorCode: 403
            ResponseCode: 503
            ResponsePagePath: /index.html
        Comment: !Sub '${AWS::StackName}-distribution'
        DefaultCacheBehavior:
          TargetOriginId: CustomOrigin
          ForwardedValues:
            QueryString: false
          DefaultTTL: 60
          MaxTTL: 60
          MinTTL: 60
          #httpsにリダイレクトさせる
          ViewerProtocolPolicy: redirect-to-https
        Aliases:
        - !Sub '${Aliases}'
        ViewerCertificate:
          SslSupportMethod: sni-only
          AcmCertificateArn: !Ref  ACMarn
      Tags:
        - Key: CloudFormationArn
          Value: !Sub '${AWS::StackName}'


Outputs:
  #CloudFrontのDNS名の定義(Route53で使う)
  CloudFrontDNS:
      Value: !GetAtt CloudFrontDistribution.DomainName
      Export:
        Name: CloudFront-DNS-Outputs
```



# 07.テストアクセス
S3にSorryページ用の資材をアップロードする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1050566/b22bb046-1041-9de8-675b-d3d9965d94d6.png)

CloudFrontのドメイン名は下記
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1050566/b34791a3-d1c8-67af-cf32-2890a97fbcb7.png)

1.CloudFrontから発行されたドメインにhttpsアクセスしてSorryページが表示されるか確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1050566/c559c576-997f-dd35-ff0b-72e0c594324e.png)
→想定通り

2.S3エンドポイントに直接アクセスしてアクセス不可なことも確認する

今回は下記ドメインにした
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1050566/193a4f76-05a8-49df-3623-cc5da2d236b1.png)
アクセスができないことを確認する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1050566/5908f41e-94b3-88eb-367b-91ecc95287c5.png)
→想定通り

3.画像を差し替える。差し替え後少し経ったら再アクセスして差し替えた画像が表示されるか確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1050566/a97fb37e-9bbf-f0fc-4477-a2bc129fcd46.png)
→想定通り

4.httpアクセスがhttpsにリダイレクトされるか確認する。

```
>curl http://dpsicsc0t98gg.cloudfront.net -o /dev/null -w '%{http_code}\n' -s
'301
'
>
→想定通り
```
# 参考文献
https://dev.classmethod.jp/articles/cfn-s3-webhosting-cloudfront/

https://awstut.com/2022/05/16/s3-content-delivery-via-cloudfront-oai/

https://qiita.com/okubot55/items/8d059a48bb5f22d3278b

★次回はこちら
[【AWS】CloudFormtionでブログサービスを構築してみた(04.Route53編)](https://qiita.com/1k5/items/96c7874050e4bb9a5127)