🐘

もっともミニマルな構成でTerraformとAWS CLIによるIaC的なデプロイを体感してみる②

に公開

はじめに

前回の記事では「はじめてのデプロイ」を最小構成で越えるべく、TerraformとAWS CLIを使ってS3に1行だけのhtmlファイルをデプロイしてみました。
(詳しくは以下を参照してください)
https://zenn.dev/shikashi404/articles/8f7f8610a5eba1

ただ、上記のような構成でS3を直接インターネット全体に無制限で常時公開することはセキュリティ的なリスクが高すぎるため、本番環境ではまずなさそうです。

学習用とはいえ現実的な構成とあまりに違いがありすぎては実践に活かしづらいので、もう少しだけ実際の構成に近いものへと拡張してみたいと思います。

目指すゴール

前回の構成にCloudFrontを追加する。

なぜCloudFrontを足すのか

CloudFrontとはAWSが提供するCDNサービスですが、CloudFrontはそれ以外にもAWS Shield StandardというAWSが提供するDDoS攻撃対策の機能を標準で搭載していて、これをS3の手前に置くことで不正な大量リクエストの脅威を一定程度吸収することが出来ます。

また、それ以外にも下記のような理由でコスト面でもセキュリティ面でもS3を直接公開するよりは遥かに安心ができます。

CloudFrontを入れる嬉しさ
  • 毎月1000万件のリクエストまでは無料で捌いてくれる
  • S3から取得したコンテンツをキャッシュして配信することで、S3本体へのリクエストを激減させてくれる(=S3への従量課金額が減る)
  • S3→CloudFrontの間はデータ転送量にかかる課金が0
  • 必要に応じてWAF機能をアドオンすることもできる
  • S3がCloudFrontからのリクエストのみを受け付ける、という設定が簡単にできる
  • 独自ドメインや証明書の設定なしで簡単にHTTPSに対応できる

具体的に何をやるか

前回のmain.tfを修正し、S3とは別にCloudFrontのリソース定義を追加します。加えて、S3へのアクセスをCloudFrontからのリクエストのみに限定するOACの設定を付与したのち、新しい構成で改めてindex.htmlをデプロイします。

前提とすること

  • AWSへのユーザー登録が済んでいること
  • AWS CLIのインストールが済んでいること
  • AWS CLIへの鍵の登録(aws configure)が済んでいること
  • terraformのインストールが済んでいること

前提としないこと

  • デプロイ対象のソースコードが手元にある
  • CI/CDについて知見がある
  • GitHubにリポジトリをもっている

手順①:デプロイ対象のhtmlを用意する

前回同様、プロジェクトのフォルダを適当に用意し、直下にsrcディレクトリをつくったのち、その下に以下の1行だけのindex.htmlをつくります。

index.html
<div>CloudFront + S3</div>

手順②:main.tfファイルを用意する

main.tfファイルを下記の内容に修正します。

main.tf
provider "aws" {
  region = "ap-northeast-1"
}

# 1. HTMLを置く箱 (S3)
resource "aws_s3_bucket" "hogehoge_bucket" {
  bucket_prefix = "hoge-site-"
  force_destroy = true # 検証用: 中身ごと削除可能
}

# 2. S3を開けるための鍵 (OAC)
resource "aws_cloudfront_origin_access_control" "hoge_oac" {
  name                              = "hoge-oac"
  origin_access_control_origin_type = "s3"
  signing_behavior                  = "always"
  signing_protocol                  = "sigv4"
}

# 3. 配信設定 (CloudFront)
resource "aws_cloudfront_distribution" "hoge_distribution" {
  enabled             = true
  default_root_object = "index.html"

  # オリジン指定 (S3の場所と鍵をセット)
  origin {
    domain_name              = aws_s3_bucket.hogehoge_bucket.bucket_regional_domain_name
    origin_id                = "HogeHogeOrigin"
    origin_access_control_id = aws_cloudfront_origin_access_control.hoge_oac.id
  }

  # 通信の振る舞い (HTTPS強制・キャッシュ設定)
  default_cache_behavior {
    target_origin_id       = "HogeHogeOrigin"
    viewer_protocol_policy = "redirect-to-https"
    allowed_methods        = ["GET", "HEAD"]
    cached_methods         = ["GET", "HEAD"]
    # AWS推奨キャッシュポリシー (CachingOptimized)
    cache_policy_id        = "658327ea-f89d-4fab-a63d-7e88639e58f6"
  }

  # 地理制限 (必須項目のため "制限なし" で記述)
  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  # 証明書 (必須項目のため "デフォルト" で記述)
  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

# 4. ポリシーの設計図 (CloudFrontからのアクセスのみ許可)
data "aws_iam_policy_document" "hoge_s3_policy" {
  statement {
    actions   = ["s3:GetObject"]
    resources = ["${aws_s3_bucket.hogehoge_bucket.arn}/*"]
    principals {
      type        = "Service"
      identifiers = ["cloudfront.amazonaws.com"] // ここでCloudFrontサービスからのリクエストのみを許可するように設定
    }
    condition {
      test     = "StringEquals"
      variable = "AWS:SourceArn"
      values   = [aws_cloudfront_distribution.hoge_distribution.arn] // ここで自分が作成したCloudFrontからのリクエストのみを受け付けるように設定
    }
  }
}

# 5. ポリシーをS3に適用
resource "aws_s3_bucket_policy" "hogehoge_bucket_policy" {
  bucket = aws_s3_bucket.hogehoge_bucket.id
  policy = data.aws_iam_policy_document.hoge_s3_policy.json
}

# 6. 出力
output "hogehoge_bucket_name" {
  value = aws_s3_bucket.hogehoge_bucket.id
}

output "hoge_website_url" {
  value = "https://${aws_cloudfront_distribution.hoge_distribution.domain_name}"
}

下記コマンドを実行します

terraform apply

前回と違い、実行完了まで5分程度の時間がかかりましたが、無事以下のような表示がされリソース作成が完了しました。

手順③:作成したS3のバケットにindex.htmlを同期する

AWS CLIを使ってsrcディレクトリ内の内容をS3にアップロードします。

aws s3 sync ./src s3://hoge-site-bucket-123456 #ここは自分で指定したS3バケットのIDを指定

applyコマンド実行後に表示されていたURLにアクセスすると、実際にS3にアップした内容がCloudFrontにキャッシュされて表示されることが確認できます。

手順④: 作成したリソースを削除する

前回同様、後処理として作成したリソースを削除します。

前回はAWS CLIでバケット内のファイルを削除してからdestroyを行いましたが、今回はmain.tfのリソース定義の時点でforce_destroyの設定をしているので、s3にファイルが残っている状態のまま、以下を実行するだけで先ほどmain.tfでつくったリソースがすべて削除できます。

学習用に作って壊してが前提なら、はじめからこの設定を入れておくのが楽かもしれません。

terraform destroy

まとめ

前回の内容から少し構成を拡張して、S3の手前にCloudFrontを必ず噛ませる構成でデプロイを行ってみました。

前回も今回も記載しているTerraformのコードはGemini3.0と壁打ちしながら作成していますが、インフラ定義がコードで完結することで『まずつくってもらい、後から理解を追いつかせる』というコーディングの世界では最近増えてきた学習の流れをインフラ領域にまで拡張できるのはとても学習体験としてよいなと感じました。

次にやりたいこと

今回つくったS3+CloudFrontの構成にバックエンドを足してみようと考えています。今のところはLambdaを使ってみる予定です。

Discussion