Cloud Frontから特定のS3バケットにある画像にアクセスできるようにする（cache distributionパターン）

## やりたかったこと
s3にuploadした画像を [cache distributionパターン](http://aws.clouddesignpattern.org/index.php/CDP:Cache_Distribution%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3)で管理しアクセスできるようにしたい。
Infrastructure as Code100%を目指しているので、リソースの作成はterraformで！
※ 独自ドメインでアクセスできるようにしたいのでroute53の設定もしますが、SSL証明書は発行してある前提です。Cloud Frontはus-eastで発行しないと作成できないので要注意です！（罠）

## 構成
![Untitled Diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137322/3a541428-3cbc-7521-5cfd-d56e76314f92.png)

## 作成手順
上の構成図の下から順に作っていこうと思います👍
全てのterraformコードを載せています。

### S3の作成
```s3.tf
resource "aws_s3_bucket" "image-bucket" {
  bucket = "image-bucket"
  acl    = "private"
  region = "ap-northeast-1"
}
```

まず上記のコードのみapplyしてみましょう。
そしてコンソールからimage-bucketにimageをuploadし、 `https://s3-ap-northeast-1.amazonaws.com/image-bucket/image.png` にアクセス。
privateバケットなのでアクセスできないはずですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137322/2756124d-4b54-0953-dce5-15cbe9c9110e.png)

### Origin Access Identityの作成
上記の通りS3に直接アクセスすることはできませんが、Cloud Frontからはアクセスできるようにするため、Origin Access Identityを作成します。あとでS3のPolicyに紐づけることで、Cloud Frontからのアクセスを許可できます。

```cloudfront.tf
resource "aws_cloudfront_origin_access_identity" "origin_access_identity" {
  comment = "origin access identity for s3"
}
```

### S3のPolicyを作成
principalsのidentifiersで先ほど作成したOrigin Access Identityを指定し、**Cloud Front　→ S3 bucket** の一部アクションを許可をします。

```iam.tf
data "aws_iam_policy_document" "cf_to_s3_policy" {
  statement {
    actions = ["s3:GetObject", "s3:ListBucket"]

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

    principals {
      type        = "AWS"
      identifiers = ["${aws_cloudfront_origin_access_identity.origin_access_identity.iam_arn}"]
    }
  }
}
```
そしてこのpolicyをs3に繋げてあげます。

```iam.tf
resource "aws_s3_bucket_policy" "cf-to-s3" {
  bucket = "${aws_s3_bucket.image-bucket.id}"
  policy = "${data.aws_iam_policy_document.cf_to_s3_policy.json}"
}
```

### Cloud Frontの作成
#### よしなに指定する箇所
- origin/domain_name
    - S3バケットのドメイン名
- s3_origin_config/origin_access_identity
    - Origin Access Identityのパス
- aliases
    - 名前解決させたい独自のドメイン名
- viewer_certificate/acm_certificate_arn
    - 独自ドメインを発行したacmのarn（us-eastである必要があります）

#### 他パラメータの補足
- default_cache_behavior
    - cacheサーバーの細かい設定
- price_class
    - cacheサーバーとして利用する地域のクラスを選択
- restrictions
    - ユーザーリクエストの地域制限を設定、white listとblack listを登録できる

```cloudfront.tf
resource "aws_cloudfront_distribution" "s3_distribution" {
  origin {
    domain_name = "${aws_s3_bucket.image-bucket.bucket_regional_domain_name}"
    origin_id   = "${aws_s3_bucket.image-bucket.id}"

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

  enabled         = true
  is_ipv6_enabled = false
  comment         = "comment"

  logging_config {
    include_cookies = false
    bucket          = "${aws_s3_bucket.image-bucket.bucket_domain_name}"
    prefix          = "prefix"
  }

  aliases = ["sample.com"]

  default_cache_behavior {
    allowed_methods  = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "${aws_s3_bucket.image-bucket.id}"

    forwarded_values {
      query_string = false

      cookies {
        forward = "none"
      }
    }

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

  price_class = "PriceClass_200"

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  tags = {
    Environment = "dev"
  }

  viewer_certificate {
    acm_certificate_arn = "arn:aws:acm:us-east-1:xxx:certificate/xxx"
    ssl_support_method  = "vip"
  }
}
```

Cloud Frontの設定は、us-eastしか受け付けない問題から始まり、restrictionsをサーバーの話かと思いwhite listに日本（JP）を入れずに設定してしまったりと（自分もアクセスできなくなるw）ちょっと詰まりました・・・。

さて、Cloud Front→S3まではできたのでapplyしてみましょう。
（Cloud Frontの作成は30minほどかかります😓）

Cloud Frontが無事に作成されたら、domain nameを確認しアクセスしてみましょう。リクエストが成功し、S3にuploadした画像をみることができました🎉
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137322/2fe98fd5-b171-928b-0a45-108f136fd2ac.png)

### route53のRecordの作成
最後に独自ドメインからもアクセスできるようにします。

#### よしなに指定する箇所
- zone_id
    - HostedZoneのid
- name
    - アクセスできるようにするドメイン名
- records
    - cnameの値＝先ほど作成したCloud Front Distributionのドメイン名（xxx.cloudfront.net）

```route53.tf
resource "aws_route53_record" "cname" {
  zone_id = "xxx"
  name    = "sample.com"
  type    = "CNAME"
  ttl     = "300"
  records = ["${aws_cloudfront_distribution.s3_distribution.domain_name}"]
}
```

これでapplyしてみましょう！
`sample.com/image.png` にアクセスして画像が表示されました🎉
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137322/25d5310d-a69a-e410-cf29-783ea1622ea7.png)