CloudFront + S3のWebサイトをTerraformで構築する

2021.05.25

概要

S3にホスティングされた静的コンテンツをCloudFront経由で配信してみようと思います。 今回はTerraformを用いてシステムの構築を行います。

CloudFrontとは

CloudFrontはAWSが提供するCDNで、世界中に配置されたエッジローケーションと呼ばれるデータセンターからコンテンツの配信を行うことでレイテンシーを抑えることができます。

S3だけでも静的コンテンツのホスティングは可能ですが、CloudFrontを用いることでより高いパフォーマンスで配信することが可能となります。 というのも、CloudFrontにはキャッシングやルーティングの最適化などが施されているためです。

詳しく知りたい方は以下の記事がおすすめです。

以下の流れ

今回使用するファイルは以下の3つです。

  • main.tf
  • s3.tf
  • cloud_front.tf

それぞれを順に見ていきましょう。

AWS Providerの設定

main.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.0"
    }
  }
}

provider "aws" {
  region  = "ap-northeast-1"
}

ここではAWSをTerraformで使用するための設定を書いています。 これは自分の環境に合わせて書いてください。

S3

s3.tf

resource "aws_s3_bucket" "bucket" {
  bucket_prefix = "static-www"
  acl = "private"

  website {
    index_document = "index.html"
    error_document = "error.html"
  } 
}

resource "aws_s3_bucket_policy" "bucket" {
    bucket = aws_s3_bucket.bucket.id
    policy = data.aws_iam_policy_document.static-www.json
}

data "aws_iam_policy_document" "static-www" {
  statement {
    sid = "Allow CloudFront"
    effect = "Allow"
    principals {
        type = "AWS"
        identifiers = [aws_cloudfront_origin_access_identity.static-www.iam_arn]
    }
    actions = [
        "s3:GetObject"
    ]

    resources = [
        "${aws_s3_bucket.bucket.arn}/*"
    ]
  }
}

ここではコンテンツ(今回はHTML)を置いておくためのS3バケットの設定を行っています。個人的に重要だと思ったポイントは以下の通りです

  • ACLはprivate
    • 後述のAmazon CloudFront Origin Accecc Identityによってアクセスするのでprivateでも問題ないです
  • websiteでホスティングに対応
    • インデックスとエラーページを設定できます
  • aws_cloudfront_origin_access_identityで公開する
    • 極端に言うとCloudFrontのみがアクセスできればページは公開できます
    • aws_cloudfront_origin_access_identityはCloudFrontが使用する認証情報です
    • GetObjectさえできればCloudFrontはユーザーに渡すコンテンツを入手できます

今回はバケットにコンテンツをアップロードすることを考えていませんが、適切に権限を設定した対象を作成し、バケットポリシーに追加することで可能となります。 また、Terraformを用いてオブジェクトをアップロードすることも可能です。

contents.tf

resource "aws_s3_bucket_object" "index_page" {
  bucket = aws_s3_bucket.bucket.id
  key = "index.html"
  source = "www/index.html"
  content_type = "text/html"
  etag = filemd5("www/index.html")
}

ここではindex用のHTMLをバケットに追加しています。ファイルはお好きなHTMLファイルを指定してください。content_type は目的によって適切なMIME Typeを設定しましょう。例えばPNGならimage/pngなどです。

CloudFront

cloudfront.tf

resource "aws_cloudfront_distribution" "static-www" {
    origin {
        domain_name = aws_s3_bucket.bucket.bucket_regional_domain_name
        origin_id = aws_s3_bucket.bucket.id
        s3_origin_config {
          origin_access_identity = aws_cloudfront_origin_access_identity.static-www.cloudfront_access_identity_path
        }
    }

    enabled =  true

    default_root_object = "index.html"

    default_cache_behavior {
        allowed_methods = [ "GET", "HEAD" ]
        cached_methods = [ "GET", "HEAD" ]
        target_origin_id = aws_s3_bucket.bucket.id
        
        forwarded_values {
            query_string = false

            cookies {
              forward = "none"
            }
        }

        viewer_protocol_policy = "redirect-to-https"
        min_ttl = 0
        default_ttl = 3600
        max_ttl = 86400
    }

    restrictions {
      geo_restriction {
          restriction_type = "whitelist"
          locations = [ "JP" ]
      }
    }
    viewer_certificate {
        cloudfront_default_certificate = true
    }
}

resource "aws_cloudfront_origin_access_identity" "static-www" {}

ここではCloudFrontの設定を行っています。個人的に重要だと思ったポイントは以下の通りです。

  • originで配信元を設定
    • ドメインで配信元を指定する。ここではS3のバケットのドメインになっている
    • s3_origin_config内でS3ににアクセスする認証情報を指定
  • default_cache_behaviorでキャッシングを設定
    • allowed_methodsで取り扱うHTTPメソッドを制御
    • cached_methodsでキャッシュするメソッドを制御
    • forarded_valuesでクッキー、ヘッダー、クエリパラメータの転送を制御
    • viewer_protocol_policyでアクセス方法を制御。今回はHTTPSにリダイレクトする
    • ttlでキャッシュの有効期限を設定
  • restrictionで配信地域を制御
  • viewer_certificateで証明書を管理
    • 今回はcloud_frontのドメインを使用しているので、cloudfront_default_certificateで設定可能
    • ACMを用いて証明書を設定することもできる

ここでは紹介しきれなかったCloudFrontの機能がたくさんあるので、Terraformのリファレンスで確認してみてください。

感想

CloudFront + S3という構成でWebページをホスティングすることができました。 両者ともフルマネージドサービスなので、サーバーの管理が不要なのがいいと思いました。 CloudFrontは多機能なのでこれからも勉強していきたいです。