個人的備忘録：S3 + CloudFront + Route53 をCloudFormationでまとめて構築テンプレート

## はじめに

本記事では、CloudFormation テンプレートを用いて、以下の3つのAWSサービスを連携させた静的ウェブサイトのホスティング構成を自動で構築する方法を紹介します。

- Amazon S3（静的コンテンツのホスティング）
- Amazon CloudFront（CDN 配信とHTTPS対応）
- Amazon Route 53（独自ドメインとの紐づけ）

**個人の備忘録程度の走り書きとなっておりますが、温かい目で見守っていただければ幸いです。**

## 書こうと思ったきっかけ

自身のブログサイトを高速かつセキュアに提供するために、S3とCloudFrontを用いた静的サイトホスティング構成を作成しました。

https://github.com/free-honda/honda-sr-office_20250323

設定内容をテンプレート化しておくことで、他プロジェクトにも簡単に再利用できると感じたため、備忘録としてまとめています。

## テンプレート構成のポイント

### 1. パラメータ設定
```yaml
Parameters:
  DomainName:
    Type: String
    Default: "xxx.blog"
    Description: "独自ドメイン名（例: xxx.blog）"
  HostedZoneId:
    Type: String
    Default: "ZXXXXXXXXXXXXXX"
    Description: "Route53のホストゾーンID"
```
ドメイン名とホストゾーンIDは、環境ごとにパラメータとして切り出すことで再利用性を高めています。

### 2. S3バケットの作成
```yaml
S3Bucket:
  Type: AWS::S3::Bucket
  Properties:
    BucketName: xxx-bucket
    AccessControl: Private
```
CloudFront経由でのみアクセスさせるため、バケットのアクセスは「Private」としています。

### 3. CloudFront OAI の設定
```yaml
CloudFrontOAI:
  Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
```
S3 バケットに対するアクセス制御のため、OAI（オリジンアクセスアイデンティティ）を作成します。

### 4. バケットポリシー設定
```yaml
S3BucketPolicy:
  Type: AWS::S3::BucketPolicy
  Properties:
    Bucket: !Ref S3Bucket
    PolicyDocument:
      Statement:
        - Effect: Allow
          Principal:
            CanonicalUser: !GetAtt CloudFrontOAI.S3CanonicalUserId
          Action: "s3:GetObject"
          Resource: !Sub "${S3Bucket.Arn}/*"
```
このポリシーにより、CloudFront OAI のみがS3にアクセスできます。

### 5. CloudFront ディストリビューション
```yaml
CloudFrontDistribution:
  Type: AWS::CloudFront::Distribution
  Properties:
    DistributionConfig:
      DefaultRootObject: index.html
      Origins:
        - OriginPath: /xxx-folder
```
CloudFrontのオリジンパスに特定のフォルダ（例：`/xxx-folder`）を設定しています。

### 6. ACM証明書とカスタムドメイン
```yaml
ViewerCertificate:
  AcmCertificateArn: arn:aws:acm:us-east-1:xxxxxxxxxxxx:certificate/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
```
us-east-1で発行したACM証明書を使用し、HTTPS配信に対応させています。

### 7. Route53 の A レコード設定
```yaml
Route53RecordSet:
  Type: AWS::Route53::RecordSet
  Properties:
    AliasTarget:
      DNSName: !GetAtt CloudFrontDistribution.DomainName
```
CloudFront のディストリビューションと独自ドメインを結びつけるために、Route53 の A レコードを作成しています。

## 全体のコード

```test.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: S3 + CloudFront + Route53

Parameters:
  DomainName:
    Type: String
    Default: "xxx.blog"
    Description: "独自ドメイン名（例: xxx.blog）"
  HostedZoneId:
    Type: String
    Default: "xxx"
    Description: "Route53のホストゾーンID（例: xxx）"

Resources:

  # S3 バケット
  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: honda-ehime
      AccessControl: Private
      Tags:
        - Key: Name
          Value: MyS3Bucket
        - Key: Environment
          Value: Production

  # CloudFront OAI
  CloudFrontOAI:
    Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment: OAI for S3 bucket

  # S3 バケットポリシー（OAI許可）
  S3BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref S3Bucket
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              CanonicalUser: !GetAtt CloudFrontOAI.S3CanonicalUserId
            Action: "s3:GetObject"
            Resource: !Sub "${S3Bucket.Arn}/*"

  # CloudFront ディストリビューション
  CloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Enabled: true
        Comment: CloudFront Distribution for honda-ehime with custom domain
        DefaultRootObject: index.html
        Origins:
          - Id: S3Origin
            DomainName: !GetAtt S3Bucket.RegionalDomainName
            OriginPath: /honda-sr-office_20250323  # ← 追加された部分
            S3OriginConfig:
              OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${CloudFrontOAI}"
        DefaultCacheBehavior:
          TargetOriginId: S3Origin
          ViewerProtocolPolicy: redirect-to-https
          AllowedMethods:
            - GET
            - HEAD
          CachedMethods:
            - GET
            - HEAD
          ForwardedValues:
            QueryString: false
            Cookies:
              Forward: none
        PriceClass: PriceClass_100
        Aliases:
          - !Ref DomainName
        ViewerCertificate:
          AcmCertificateArn: arn:aws:acm:us-east-1:xxx:certificate/xxx
          SslSupportMethod: sni-only
          MinimumProtocolVersion: TLSv1.2_2021
        Restrictions:
          GeoRestriction:
            RestrictionType: none
      Tags:
        - Key: Name
          Value: MyCloudFrontDistribution
        - Key: Environment
          Value: Production

  # Route53 Aレコード（CloudFrontへのAlias）
  Route53RecordSet:
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneId: !Ref HostedZoneId
      Name: !Ref DomainName
      Type: A
      AliasTarget:
        HostedZoneId: xxx
        DNSName: !GetAtt CloudFrontDistribution.DomainName
```

## まとめ

このテンプレートを使用することで、静的ウェブサイトを高速・安全にデプロイできる環境を一括で構築することができます。***オリジンパスやドメイン、ACMのARNなどは、用途に応じて柔軟に変更してくご活用ください！***