TerraformでCF+S3の静的ホスティング環境を作成する

terraformにてcloudfront+S3の静的ホスティング環境を構築してみます。

検索をかけると古いバージョンのterraform対応の記事はあるのですが、
微妙に最新版のterraformのコードと差分があり、そのままでは動かないことが多いので
ここでは、正式リリース版(1.0.7で動作確認済)に対応するコードをご紹介します。

# このコードで使われているterraformの機能の紹介

## 定数(tfvars)の参照
main.tfと同一階層に"terraform.tfvars"というファイル名でファイルを作成し、そこに定数を記述してください。
※読み込む定数設定ファイルを変更することもできますが、ここでは割愛します。

```tf
# [定数名] = [値]

# example
aws_access_key = "*****************"
aws_secret_key = "*****************"
aws_region     = "********"
```
main.tfなどで参照する際は

```tf
variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "aws_region" {}
```
という感じで参照する定数を定義する必要があります。


## ローカル定数の定義
下記の記述で、ローカル定数を定義することができます。
※ここではcloudfrontのdistibution設定時に利用するorigin_idを定数として定義しています。

```tf
# ローカル定数
locals {
  origin_id = "webOrigin"
}
```
参照する際はlocal.[キー]の記述で参照することができます。

## データリソースを使ったCacheポリシーの読込
データリソースは読み取り専用のリソースです。 すでにProvider上に存在するリソースの値を参照するために用います。
今回はキャッシュポリシーとしてawsがデフォルトで用意しているものを利用するため、データリソースの機能を使ってロードしておきます

```tf
# Cacheポリシー
data "aws_cloudfront_cache_policy" "CachingOptimized" {
  name = "Managed-CachingOptimized"
}
data "aws_cloudfront_cache_policy" "CachingDisabled" {
  name = "Managed-CachingDisabled"
}
```

# リソース作成のための記述

## Provioderの設定
```tf
provider "aws" {
  access_key = var.aws_access_key
  secret_key = var.aws_secret_key
  region     = var.aws_region
}
```
AWSの認証情報とデフォルトで作成対象となるregionを設定します。
アクセスキーIDとシークレットアクセスキー及び作成対象のregionの情報(東京リージョンであれば"ap-northeast-1")を設定してください。

直接、値を記述してもらってもかまいませんが、deploy先のアカウントが変更される可能性もあるため汎用性を考えるなら定数を参照する形が良いと思います。

## 各種S3bucketの作成
ここでは、

- s3のアクセスログ格納用
- cloudfrontのアクセスログ格納用
- 静的ホスティング用

の計３つのbucketを作成します。

S3のバケット名はaws全体でユニークである必要があります。ドメイン名などをbucket名とするのがベターかと思います。
ここではhost_bucket_nameとしてtfvarsにて定義しています。

```tf
# s3のアクセスログ格納用
resource "aws_s3_bucket" "s3LogBucket" {
  bucket = "${var.host_bucket_name}-s3-accesslog"
  acl    = "log-delivery-write"

}
# cloudfrontのアクセスログ格納用
resource "aws_s3_bucket" "cfLogBucket" {
  bucket = "${var.host_bucket_name}-cf-accesslog"
  acl    = "private"
}

```
s3のアクセスログの格納用のバケットについては、aclを"log-delivery-write"で作成してください。
これにより、静的ホスティング用のリソースのloggingのtarget_bucketに指定できるようになります。
cloudfrontのアクセスログ格納用バケットのaclはprivateで構いません。

```tf

## 静的ホスティング用
resource "aws_s3_bucket" "webBucket" {
  bucket = var.host_bucket_name
  acl    = "public-read-write"
  logging {
    target_bucket = aws_s3_bucket.s3LogBucket.id
    target_prefix = "s3log/"
  }
  website {
    index_document = "index.html"
  }

}
```
このバケットが実際に配信される領域になります。
公開状態とするため、aclはpublic-read-writeに設定。
静的websiteホスティングの設定の為、websiteのindex_documentを設定します。
また、loggingのtarget_bucketにs3のアクセスログ格納用のバケットを指定しています。

## bucket_policyの設定
静的サイトとして全リクエストを許可するためGetObjectの許可をBucketPolicyにて設定します。

```tf
resource "aws_s3_bucket_policy" "webBucketPolicy" {
  bucket = aws_s3_bucket.webBucket.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid       = "AllowGetPolicy"
        Effect    = "Allow"
        Principal = "*"
        Action    = "s3:GetObject"
        Resource  = "${aws_s3_bucket.webBucket.arn}/*"
      }
    ]
  })
}
```

## CloudFront Distributionの設定
今回はCloudFrontのデフォルトドメインでの設定になります。
注意点としてはorigin設定のdomain_nameを指定する際、domain_nameにbucket_regional_domain_nameを指定する際は同時にs3_origin_configにてOAIなどの設定を記述することになるのですが、この設定を行うとディレクトリインデックスが効かなくなります。
今回はディレクトリインデックスがサブディレクトリでも機能してほしいので、website_endpointを指定しています。
この場合、同時にcustom_origin_configを設定しないとエラーになってしまいますので注意してください。

```tf
resource "aws_cloudfront_distribution" "distribution" {
  # Origin設定
  origin {
    domain_name = aws_s3_bucket.webBucket.website_endpoint
    origin_id   = local.origin_id
    custom_origin_config {
      http_port                = 80
      https_port               = 443
      origin_keepalive_timeout = 5
      origin_protocol_policy   = "http-only"
      origin_read_timeout      = 30
      origin_ssl_protocols = [
        "TLSv1",
        "TLSv1.1",
        "TLSv1.2",
      ]
    }
  }
  enabled = true
  logging_config {
    include_cookies = false
    bucket          = aws_s3_bucket.cfLogBucket.bucket_domain_name
    prefix          = "cfprefix"

  }

  default_cache_behavior {
    allowed_methods  = ["GET", "HEAD", "OPTIONS"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = local.origin_id

    cache_policy_id = data.aws_cloudfront_cache_policy.CachingDisabled.id

    viewer_protocol_policy = "allow-all"
  }
  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

```


## 出力
最後にアクセス確認する為に、作成したDistributionのドメインを出力します。

```tf
# cloudfrontのドメインを出力
output "cloudfront_domain" {
  value = aws_cloudfront_distribution.distribution.domain_name
}
```

# まとめ

今回はterraformが正式リリースとなり、スクリプトの記述式が若干変わっており、
旧記事に惑わされて少してこずった部分があったので正式リリースのフォーマットに合わせた内容をまとめてみました。

次回は監視設定などを追加してみたいと思います。

※最終的なコードこちら。
[Github - terraform_cfs3](https://github.com/matsui-110/terraform_cfs3)