Terraformを使ってみたかった人が取り敢えずS3 + CloudFrontで静的サイトを公開してみた(備忘録)

#はじめに
こんにちは。普段業務でAWSのマネジメントコンソールを使用している者です。
そんな僕がプライベートでTerraformを使ってAWSの何かしらの環境を構築してみたかったので、ちょっとだけ試してみました。
それを備忘録がてら記事にしたいと思います。

そのため本記事は以下のような対象者に向けた記事となりますので予めご了承ください。

* Terraformを大雑把に知りたい人
* Terraformに興味はあるが使ったことのない人



#まずTerraformとは...?
IaC（インフラストラクチャ定義ツール） と呼ばれるツールの一つです。
インフラの構成をソースコードとして管理できる為、例えば以下のようなメリットが享受できます。

* 何度でも同じ構成を再現できる
* ヒューマンエラーを減らせる
* バージョン管理ができる

などなど...

このIacの中で最も認知度が高いのがTerraformです。

#今回作る環境

CloudFrontを経由しS3にホスティングした静的サイトにアクセスできる環境を構築します。

CloudFrontをかます理由としては高速化も勿論あるのですが、独自ドメインでSSL/TSL 経由でコンテンツを配信しようと思った時に必須になるからです。
S3のみで独自ドメインを利用するとSSL/TSL 経由での配信ができませんが、CloudFront を利用すれば、ACM (AWS Certificate Manager) で作成した証明書をディストリビューションに設定することができるので、独自ドメインでSSL/TSL経由での配信が可能になります。

そのため静的サイトをS3にホスティングするときはこのパターンがオーソドックスだと思われます。
ただ今回は簡略化のため、独自ドメインの設定や、ACMの作成/設定は省略したいと思います。


#実践
Terraformをインストールしている前提です。

##.tfファイルを作成する
インフラの構成を記述します。

下記はTerraformの公式ドキュメントなのですが、S3をオリジンにもつCloudFrontを作成する際のサンプルになります。
非常に分かりやすく参考になりました。

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_distribution

```main.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.0"
    }
  }
}

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

```s3.tf
# バケットの作成
resource "aws_s3_bucket" "test-takeyabu-web-site" {
    bucket = "test-takeyabu-web-site"
    acl    = "private"
    # バケットポリシーの指定
    policy = data.aws_iam_policy_document.test-takeyabu-web-site-bucket_policy.json

    versioning {
        enabled = true
    }

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

}

# バケットポリシーの作成
data "aws_iam_policy_document" "test-takeyabu-web-site-bucket_policy" {
    statement {
        sid    = ""
        effect = "Allow"
        principals {
             type        = "AWS"
             identifiers = ["*"]
        }

        actions = [
            "s3:GetObject"
        ]
        resources = [
            "arn:aws:s3:::test-takeyabu-web-site",
            "arn:aws:s3:::test-takeyabu-web-site/*"
        ]
    }
}

```

```cloudfront.tf
# OriginTarget用の変数作成
locals {
    s3-origin-id-test-takeyabu-web-site = "s3-origin-id-test-takeyabu-web-site"
}

# OriginAccessIdentityの作成
resource "aws_cloudfront_origin_access_identity" "test-takeyabu-web-site-idntity" {
    comment = "access-identity-test-takeyabu-web-site.s3.amazonaws.com"
}

# Distributionの作成
resource "aws_cloudfront_distribution" "test-takeyabu-web-site-dst" {
    # Originの作成。
    origin {
        domain_name = aws_s3_bucket.test-takeyabu-web-site.bucket_regional_domain_name
        origin_id   = local.s3-origin-id-test-takeyabu-web-site
        s3_origin_config {
            origin_access_identity = aws_cloudfront_origin_access_identity.test-takeyabu-web-site-idntity.cloudfront_access_identity_path
        }
    }

    enabled             = true
    is_ipv6_enabled     = true
    comment             = "test-takeyabu-web-site"
    default_root_object = "index.html"

    default_cache_behavior {
        allowed_methods  = ["GET", "HEAD"]
        cached_methods   = ["GET", "HEAD"]
        target_origin_id = local.s3-origin-id-test-takeyabu-web-site
        compress         = true
        viewer_protocol_policy = "allow-all"
        default_ttl = 3600
        max_ttl = 86400
        min_ttl = 0

        forwarded_values {
            query_string = false
            cookies {
                forward = "none"
            }
        }
    }

    restrictions {
        geo_restriction {
          restriction_type = "none"
        }
    }
　　 #ACMを使用する場合はここを指定する
    viewer_certificate {
        acm_certificate_arn            = ""
        cloudfront_default_certificate = true
        ssl_support_method             = ""
    }

}

```

##初期化
```
$ terraform init
```
成功すると
.terraform
.terraform.lock.hcl
が生成されます。

##定義内容をチェック
```
$ terraform plan
```
実行されるプランの内容が出力されるので、
定義内容が意図した通りになっているかを確認します。

##実行
```
$ terraform apply
```
正常に実行できたらマネジメントコンソールで、
意図した通りにリソースが作成されているかを確認します。

##アクセス確認
S3にindex.htmlを配置します。
中身はなんでもokです。

その後、CloudFrontのディストリビューションドメイン名にアクセスし
index.htmlの内容が表示されれば成功です。