CloudFormation でS3 + CloudFrontをサクッと作成してみた

# はじめに
社内勉強会用途として、デモとして簡単なWebページを生成する必要性が出てきました。
構成としてはシンプルにS3 + CloudFrontで検討していました。
社内勉強会用途なのでサクッとIaCで作りたかったのですが、Google検索してみると意外とこれというCloudFormationコードが見つかりませんでした。(不必要なリソース作成まで含まれているケースが多かったです)

簡単なデモWebページを作りたいというニーズは世の中にもあるかと考えるので、コードを記事として記録しておきます。
また、AWS初学者の方向けに、CloudFormationのデプロイ手順も画面キャプチャ付きで説明します！

:::note info
今回、社内勉強会用にAmazon Q Developerを使ったCloudFormationのコード生成の例として本コードを用意しました。
結局、なかなかAmazon Q Developerで意図したコードが生成できなかったのですが、本筋とずれるのでその話はまた別の記事にでも出せればと思います。
:::

# 対象読者
- AWSを使った簡単なWebページをサクッと作りたい方
(デプロイ時間含め10分弱で作れます！)
- IaCとしてCloudFormationを使いたい方
- S3 + CloudFront構成のコードをとりあえず急ぎ確認したい方
- AWS初学者の方

# システム構成図
本記事のコードで作成可能なシステム構成は以下となっています。
とてもシンプルです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3784328/32c1231d-5bd3-46dc-a70e-eb180b4dabef.png)


# コード
S3 + CloudFrontが作成できるコードは以下の通りです。

大きく分けると、以下4つのAWSリソースを作成しています。
|No.| yamlブロック名 | 作成リソース |備考|
|:-|:-|:-|:-| 
|1.| Bucket  | S3バケット  | - |
|2.| BucketPolicy  | S3バケットポリシー | CloudFrontディストリビューションからのGetObject操作のみ許可しています
|3.| CloudFrontDistribution  | CloudFrontディストリビューション  | - |
|4.| OAC  | オリジンアクセスコントロール  | - |

```yaml:S3 ＋ CloudFront
AWSTemplateFormatVersion: 2010-09-09
Description: Static contents distribution using S3 and CloudFront.
 
Parameters:
  SiteName:
    Type: String
    Description: domain name e.g. test-site.com
 
Resources:
  Bucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Retain
    Properties:
      BucketName: !Sub ${SiteName}-s3-bucket
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: True
        BlockPublicPolicy: True
        IgnorePublicAcls: True
        RestrictPublicBuckets: True
      WebsiteConfiguration:
        IndexDocument: index.html
 
  BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref Bucket
      PolicyDocument:
        Statement:
          - Action: s3:GetObject
            Effect: Allow
            Resource: !Sub arn:aws:s3:::${Bucket}/*
            Principal:
              Service: "cloudfront.amazonaws.com"
            Condition:
              StringEquals:
                AWS:SourceArn:
                  - !Join
                    - ""
                    - - !Sub "arn:aws:cloudfront::${AWS::AccountId}:distribution/"
                      - !Ref CloudFrontDistribution
 
  CloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Origins:
          - Id: S3
            DomainName: !GetAtt Bucket.DomainName
            OriginAccessControlId: !GetAtt OAC.Id
            S3OriginConfig:
              OriginAccessIdentity: ""
        Enabled: true
        DefaultRootObject: index.html
        Comment: !Ref SiteName
        DefaultCacheBehavior:
          AllowedMethods:
            - HEAD
            - GET
          CachedMethods:
            - HEAD
            - GET
          DefaultTTL: 0
          MaxTTL: 0
          MinTTL: 0
          TargetOriginId: S3
          ForwardedValues:
            QueryString: false
          ViewerProtocolPolicy: redirect-to-https
        IPV6Enabled: false
 
  OAC:
    Type: AWS::CloudFront::OriginAccessControl
    Properties:
      OriginAccessControlConfig:
        Description: !Ref SiteName
        Name: !Ref SiteName
        OriginAccessControlOriginType: s3
        SigningBehavior: always
        SigningProtocol: sigv4
 
Outputs:
  CloudFrontDistributionDomain:
    Value: !GetAtt CloudFrontDistribution.DomainName
    Description: CloudFront Domain Name
    Export:
      Name: !Sub ${SiteName}-CloudFrontDomainName
```

# CloudFormationデプロイ手順

AWSマネジメントコンソールにログインし上部の検索窓で「CloudFormation」を検索します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3786376/bcd2ba19-6cb6-4299-becc-ec99b50a92d1.png)


左ペインの「スタック」ー 「スタックの作成」ー「新しいリソースを使用(標準)」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3786376/1b31adc0-ff93-46e4-b9a4-32edf409c1bc.png)

「既存のテンプレートを選択」- 「テンプレートファイルのアップロード」で
上記のコードをyamlファイルとしてアップロードし「次へ」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3786376/f64f323e-6068-4c6e-80f6-9afc3b8d12b7.png)

任意のスタック名を入力し「次へ」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3786376/c931fa0d-4b5b-4002-95e1-f320d4241383.png)

任意のスタックオプションを指定し「次へ」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3786376/b357ba0b-3bf9-4523-a9fa-ff38cf11e847.png)

最後に「送信」を押下

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3786376/5c87af96-633f-481a-84f9-c415f06fb520.png)


あとはS3にhtmlファイルをアップロードし、CloudFrontのURLを叩くだけでWebサイトが見えてくるはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3786376/624f1ffc-6916-4d79-a2e9-226630e7b129.png)

 ※社内のデモでは某ファーストフード店風のECサイトをイメージしてデモページを作りました。

# さいごに
さすがに本番構成でここまでシンプルなものを作ることはないかと思いますが
・簡単にデモとしてCloudFront ＋ OAC ＋ S3の環境を作りたい
・CloudFront ＋ OAC ＋ S3の動作の挙動を確認したい　検証したい
といった要望には使えるかなと思います。
しかもOACで接続制限しているのでCloudFront経由の接続のみ許可するセキュアな構成となっています。
機会があればお試しください！

# 弊社では一緒に働く仲間を募集中です！
現在、様々な職種を募集しております。
カジュアル面談も可能ですので、ご連絡お待ちしております！

募集内容等詳細は、是非採用サイトをご確認ください。
https://engineer.po-holdings.co.jp/