【速報】TerraformがCloudFrontに対応しました

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

こんにちは、中山です。

先程Terraformのv0.6.15がリリースされましたね

CHANGELOGはこちらです。さまざまなアップデートが含まれるのですが、やはりCloudFrontへの対応に目を引かれました。 aws_cloudfront_distributionaws_cloudfront_origin_access_identity がCloudFront用のリソースです。以前のバージョンでもaws_cloudformation_stackリソースを使用すればCloudFrontの作成も可能でしたが、やはりリソースという形で対応してくれるとうれしいですね。

という訳で、早速使ってみたのでレポートしてみます。

インストール

まずはTerraformをインストールしましょう。

$ brew update
$ brew cask install terraform

バージョンを確認します。

$ terraform version
Terraform v0.6.15

TerraformでCache Distributionパターンを実装する

S3にオリジンの静的コンテンツを置いてCloudFrontでそのキャッシュを配信してみましょう。

コード

以下のコードを作成してください。

  • main.tf
variable "name"       { default = "ap-northeast-1-cloudfront-resource-demo" }
variable "region"     { default = "ap-northeast-1" }
variable "access_key" { }
variable "secret_key" { }

variable "acl"         { default = "public-read" }
variable "policy_file" { default = "policy.json.tpl" }
variable "index"       { default = "index.html" }

provider "aws" {
  region     = "${var.region}"
  access_key = "${var.access_key}"
  secret_key = "${var.secret_key}"
}

resource "aws_cloudfront_origin_access_identity" "origin_access_identity" {
  comment = "${var.name}"
}

resource "template_file" "s3_policy" {
  template = "${file(concat(path.module, "/", var.policy_file))}"

  vars {
    bucket_name            = "${var.name}"
    origin_access_identity = "${aws_cloudfront_origin_access_identity.origin_access_identity.id}"
  }
}

resource "aws_s3_bucket" "s3" {
  bucket        = "${var.name}"
  acl           = "${var.acl}"
  force_destroy = true
  policy        = "${template_file.s3_policy.rendered}"

  website {
    index_document = "${var.index}"
  }
}

resource "aws_s3_bucket_object" "s3" {
  bucket       = "${aws_s3_bucket.s3.bucket}"
  key          = "${var.index}"
  source       = "${concat(path.module, "/", var.index)}"
  content_type = "text/html"
}

resource "aws_cloudfront_distribution" "cf" {
  enabled             = true
  comment             = "${var.name}"
  default_root_object = "${var.index}"
  price_class         = "PriceClass_200"
  retain_on_delete    = true

  origin {
    domain_name = "${concat(aws_s3_bucket.s3.id, ".s3.amazonaws.com")}"
    origin_id   = "${var.name}"

    s3_origin_config {
      origin_access_identity = "${aws_cloudfront_origin_access_identity.origin_access_identity.cloudfront_access_identity_path}"
    }
  }

  default_cache_behavior {
    allowed_methods  = ["GET", "HEAD"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "${aws_s3_bucket.s3.id}"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "whitelist"
      locations        = ["US", "CA", "GB", "DE", "JP"]
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

output "s3_website_endpoint"    { value = "${aws_s3_bucket.s3.website_endpoint}" }
output "cloudfront_domain_name" { value = "${aws_cloudfront_distribution.cf.domain_name}" }
  • policy.json.tpl
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadForGetBucketObjets",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${origin_access_identity}"
      },
      "Action": ["s3:GetObject"],
      "Resource": ["arn:aws:s3:::${bucket_name}/*"]
    }
  ]
}

コードの解説

1. aws_cloudfront_origin_access_identity

今回はS3のエンドポイントへ直接アクセスさせない設定にするので、オリジンアクセスアイデンティティを作成します。オリジンアクセスアイデンティティを利用したS3バケットへのアクセス制限は、弊社甲木が書いた以下のエントリが参考になります。

2. template_file

バケットポリシーをテンプレートから作成しています。Terraform上の変数を ${variable} という形式でテンプレートに埋め込み、それをレンダリングすることで変数の展開が可能です。

3. aws_s3_bucket

S3にバケットを作成します。Static Website Hosting機能を有効にさせ、ACLは public-read に、バケットポリシーは作成するバケットに対して s3:GetObject を許可させます。CloudFrontからのみコンテンツにアクセスさせたいのでPrincipleにはオリジンアクセスアイデンティティのIDを指定します。

4. aws_s3_bucket_object

作成したバケットにindex.htmlをアップロードします。

5. aws_cloudfront_distribution

最後にCloudFrontを作成します。詳細はドキュメントに詳しいですが、マネジメントコンソールでCloudFrontを作成した場合との対応表を以下に記述します。

Terraform上の設定 Terraform上の値 マネジメントコンソール上の設定 マネジメントコンソール上の値
enabled true Distribution State Enabled
comment ap-northeast-1-cloudfront-resource-demo Comment ap-northeast-1-cloudfront-resource-demo
default_root_object index.html Default Root Object index.html
price_class PriceClass_200 Price Class Use Only US, Europe and Asia
retain_on_delete true N/A(Terraform固有の機能) N/A
origin - domain_name ap-northeast-1-cloudfront-resource-demo.s3.amazonaws.com Origin Domain Name ap-northeast-1-cloudfront-resource-demo.s3.amazonaws.com
origin - origin_id ap-northeast-1-cloudfront-resource-demo Origin ID ap-northeast-1-cloudfront-resource-demo
origin - s3_origin_config - origin_access_identity origin-access-identity/cloudfront/ABC123 Origin Access Identity origin-access-identity/cloudfront/ABC123
default_cache_behavior - allowed_methods ["GET", "HEAD"] Allowed HTTP Methods GET, HEAD
default_cache_behavior - cached_methods ["GET", "HEAD"] Cached HTTP Methods GET, HEAD (Cached by default)
default_cache_behavior - target_origin_id ap-northeast-1-cloudfront-resource-demo Origin ap-northeast-1-cloudfront-resource-demo
default_cache_behavior - forwarded_values - query_string false Forward Headers None (Improves Caching)
default_cache_behavior - forwarded_values - cookies - forward none Forward Cookies None (Improves Caching)
default_cache_behavior - viewer_protocol_policy allow-all Viewer Protocol Policy HTTP and HTTPS
default_cache_behavior - min_ttl 0 Minimum TTL 0
default_cache_behavior - default_ttl 3600 Default TTL 3600
default_cache_behavior - max_ttl 86400 Maximum TTL 86400
restrictions - geo_restriction - restriction_type whitelist Restriction Type Whitelist
restrictions - geo_restriction - locations ["US", "CA", "GB", "DE", "JP"] Countries CA -- CANADA
DE -- GERMANY
JP -- JAPAN
GB -- UNITED KINGDOM
US -- UNITED STATES
viewer_certificate - cloudfront_default_certificate true SSL Certificate Default CloudFront Certificate (*.cloudfront.net)

実行する

まずオリジンコンテンツを設置します。今回はテスト目的なので簡易的なHTMLを設置するだけにします。

$ echo 'Hello, World!' > index.html

Terraformを実行します。 <access_key><secret_key> に自分のAWSクレデンシャルを指定してください。

$ terraform plan -var access_key=<access_key> -var secret_key=<secret_key>
$ terraform apply -var access_key=<access_key> -var secret_key=<secret_key>

CloudFrontのStateがdeployedになるまで15分程度時間が掛かります。deployedになったらCloudFrontのドメインにアクセスしてみましょう。 <cloudfront-domain-name> は自分の環境に合うよう修正してください。以下のようにHTTP/HTTPS両方でアクセスできたら成功です。

$ curl http://<cloudfront-domain-name>
Hello World!
$ curl https://<cloudfront-domain-name>
Hello World!

逆に、以下のようにS3のエンドポイントへ直接アクセスして403が返ったら、バケットポリシーが意図したとおりに動作していると確認できます。

$ curl <s3-website-endpoint>
<html>
<head><title>403 Forbidden</title></head>
<body>
<h1>403 Forbidden</h1>
<ul>
<li>Code: AccessDenied</li>
<li>Message: Access Denied</li>
<li>RequestId: ABC123</li>
<li>HostId: ABC123</li>
</ul>
<hr/>
</body>
</html>

まとめ

いかがだったでしょうか。

Terraformがどんどん便利になってうれしいですね。今後もアップデートを追いかけて行こうと思います。

本エントリがみなさんの参考になったら幸いに思います。