[駄場の部屋] S3バケットへのアクセスをCloudFront経由にしました

概要

自分が管理するS3バケットのうち、いくつかPublicになっているものがあります。
今回の記事では、このサイトで使用しているS3バケットについてCloudFrontを適用したので、その設定方法を備忘録として残しておこうと思います。

このサイトでは、XOPSのアドオン自作楽曲を公開しています。
それらのファイルはS3バケットに格納されており、任意のユーザーに対するGetObjectを許可しているため、ブラウザからリンクをクリックするとファイルをダウンロードできるわけです。

最近Qiitaで以下の記事を読み、S3バケットを直接インターネットに公開するのはよくないな、と思ったため、CloudFrontの設定を行いました。
まあ自分の場合、引落口座に200万円も入っていないので、支払いができなくてアカウント停止が関の山でしょうね…。
https://qiita.com/miruky/items/b996e374c91923141178

今回はまずAWS管理コンソールからCloudFrontの設定を行い、その後Terraformコードを作成しました。
その二つの手順をこの記事にまとめておきます。

AWS管理コンソールから設定

AWS管理コンソールからの設定は非常に簡単で、自分の場合は15分くらいで完了したと思います。

  • S3バケットは既存のものを使用
  • DNSサービスとしてRoute 53を使用

CloudFrontは従量課金しかないと思っていましたが、いつの間にか定額プランが登場していたので、今回はFreeプランを選択しました。

Freeプランだと毎月100万リクエストまたは100 GBのデータ転送が利用できるようです。
このサイトでの利用であれば十分な値ですね。

Distributionの名前を入力します。

ドメイン名を入力します。
ファイルをfiles.daba-no-heya.comで公開したいので、サブドメインを入力します。
このスクリーンショットは記事作成のために後から撮影したもので、実際の作業時には先にdistributionを作成して後からサブドメインの設定を行いました。

Origin typeで"Amazon S3″を選択し、S3 originにS3バケットのURLを設定します。

“Allow private S3 bucket access to CloudFront"にチェックしておくと、S3バケットのポリシーが自動で更新されます。

WAFも追加料金なしで一緒に作成してくれます。

ACMに証明書を作成する必要がありますが、この画面の"Create certificate"を押下すると自動で作成してくれます。
自分はすでに証明書があるので選択肢にfiles.daba-no-heya.comが表示されています。

最後の"Review and create"の画面で設定内容を確認し、問題なければ"Create distribution"を押下します。

作業は以上になります。
簡単ですね。

Terraformコードに落とし込む

この作業がなかなか厄介で、AWS管理コンソールから作成するなら15分くらいで終わる作業が、Terraformにしようとすると5時間くらいかかりました。
なので、今回の作業については無理にTerraform化せず、Markdownか何かで手順書を作成しておく方がいいかもしれません。
自分は今後開発環境を立ち上げたりするときに、コマンドを実行するだけでインフラを作成できるようにしたかったので、Terraformコードを作成しましたが…。

  • 多数の環境に同じ内容を適用する予定がある
  • 必要なときだけインフラを立ち上げて利用しなくなったら削除する

のようなユースケースであればTerraform化するのがいいと思いますが、そうでなければ別に手順書があればいいと思いますね。

作業の流れとしては、

  1. resource定義を作成する
  2. importブロックを作成する
  3. Plan結果を確認し、差分をなくすようにresource定義を更新する

となります。
順を追って紹介していきます。

それと一つ注意点なのですが、この記事の作成時点(2026-05-01)だと、CloudFrontの定額プランをTerraformから作成することはできないようです。
この件に関するGitHubのIssueを見つけました。
https://github.com/hashicorp/terraform-provider-aws/issues/45450

自分がインフラ管理に使用しているTerraformコードは非公開なので、一部のみ抜粋して紹介しますね。

resource定義を作成する

リソースをTerraformの管理下に取り込むにあたって、取込先のresource定義が必要なので、あらかじめ作成しておきます。
ここでは、aws_cloudfront_distributionのインポート作業を例に挙げます。

awsプロバイダーのドキュメントに記載の例を参考にしながらresource定義を作成します。
各必須パラメータにはわかる範囲で値をセットします。
どのような値になっているかわからないものについては適当な値をセットしておきます。
この後の作業で各パラメータの値を修正していくので、ここでは必須パラメータの指定が足りていない旨のエラーが出ない状態になれば問題ありません。

importブロックを作成する

terraform importで一つずつインポートしてもいいですが、リソースの数が多い場合はimportブロックを使ったほうが便利です。

以下のようなimportブロックを用意して適当なファイルに保存します。

import {
  to = module.daba_no_heya.module.cloudfront.aws_cloudfront_distribution.main
  id = "CloudFront DistributionのID"
}

Terraformの公式ドキュメントによると、importブロックを記載するのはそれ専用のimports.tfか、各リソース定義の近くがいいとのことです。
自分はTerraformを実行するディレクトリにimports.tfを作成しました。

toにはリソース定義のアドレスを指定します。

idにはリソースのIDを指定します。
awsプロバイダーの場合、idはARNであったり各リソース固有のIDであったりします。
CloudFront distributionの場合はdistributionのIDを指定します。
distributionのIDはAWS管理コンソールから確認できます。

Plan結果を確認し、差分をなくすようにresource定義を更新する

terraform planを実行すると、インポート対象のリソースについて、実際のAWSリソースとTerraformコードの記載の差分が表示されます。
Plan結果から実際のAWSリソースに設定されているパラメータがわかるので、それをもとにしてresourceブロックを更新します。
完全に差分が出ない状態にするのか、あるいはサービスに影響のないパラメータ(tagとかdescriptionとか)の差分は許容するのか、そのあたりは状況に応じて決めてください。

自分の場合、最終的なresourceブロックは以下のようになりました。

data "aws_cloudfront_cache_policy" "caching_optimized" {
  name = "Managed-CachingOptimized"
}

resource "aws_cloudfront_distribution" "main" {
  origin {
    domain_name              = var.s3_bucket_regional_domain_name
    origin_access_control_id = aws_cloudfront_origin_access_control.main.id
    origin_id                = var.origin_id
  }

  enabled         = true
  is_ipv6_enabled = true

  aliases = [
    "files.${var.domain_name}"
  ]

  web_acl_id = var.web_acl_arn

  default_cache_behavior {
    allowed_methods        = ["GET", "HEAD"]
    cached_methods         = ["GET", "HEAD"]
    cache_policy_id        = data.aws_cloudfront_cache_policy.caching_optimized.id
    compress               = true
    target_origin_id       = var.origin_id
    viewer_protocol_policy = "redirect-to-https"
    min_ttl                = 0
    default_ttl            = 0
    max_ttl                = 0
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
      locations        = []
    }
  }

  viewer_certificate {
    acm_certificate_arn      = var.certificate_arn
    ssl_support_method       = "sni-only"
    minimum_protocol_version = "TLSv1.2_2021"
  }

  tags = {
    Name = "daba-no-heya"
  }
}

この手順を他のリソースについても実施します。
Plan実行時に表示される差分がなくなったら、terraform applyを実行してください。
Applyを実行すると、AWSリソースがTerraformの管理下に取り込まれます。

AWS,Terraform

Posted by maeda6uiui