AWS入門したての新卒がTerraformで挑戦！Next.jsサイトをS3に爆速デプロイ

こんにちは！新卒エンジニアのあきともです。
「AWS？難しそう...」
「AWS使ってみたけど、ボタンが多くてよく分からない...」
「Terraform？聞いたことない...」
実は、つい最近までそう思っていました。そんな私でも、Terraformを使いWEBサイトを構築できました。本記事ではAWSとTerraformを使ってNext.jsサイトをS3にデプロイした内容をお話しします！
# 必要なもの
- Terraform
- Node.js
- AWS CLI
# Next.jsプロジェクト作成＆ビルド
まずデプロイするプロジェクトを作成します。
```bash
$ npx create-next-app@latest
Need to install the following packages:
create-next-app@14.2.4
Ok to proceed? (y) y

✔ What is your project named? … frontend
✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like to use `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to customize the default import alias (@/*)? … No / Yes
Creating a new Next.js app in /app/frontend.
```
`next.config.js`を以下に修正してください。
```js
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: "export",
  trailingSlash: true,
};

export default nextConfig;
```
`package.json`のscriptsを以下に修正してください。
```json
"scripts": {
    "dev": "next dev",
    "build": "next build && next export",
    "start": "next start",
    "lint": "next lint"
  }
```
ビルドしてoutフォルダを作成してください。
```
npm run build
```
この出来上がったoutフォルダをTerraformでS3にアップロードします。

# TerraformでAWSのリソース設定
```main.tf
provider "aws" {
  region = "ap-northeast-1"
}

resource "aws_s3_bucket" "web_hosting_bucket" {
  bucket = "test-deployed-by-terraform"

  force_destroy = true
}

resource "aws_s3_bucket_public_access_block" "web_hosting_bucket_public_access_block" {
  bucket = aws_s3_bucket.web_hosting_bucket.id

  block_public_acls       = false
  block_public_policy     = false
  ignore_public_acls      = false
  restrict_public_buckets = false
}

resource "aws_s3_bucket_policy" "bucket_policy" {
  bucket = aws_s3_bucket.web_hosting_bucket.id
  policy = data.aws_iam_policy_document.policy_document.json
  depends_on = [
    aws_s3_bucket_public_access_block.web_hosting_bucket_public_access_block, # パブリックアクセスブロックの設定を待たないとエラーが発生する
  ]
}

data "aws_iam_policy_document" "policy_document" {
  statement {
    sid    = "Statement1"
    effect = "Allow"
    principals {
      type        = "*"
      identifiers = ["*"]
    }
    actions = [
      "s3:GetObject"
    ]
    resources = [
      "${aws_s3_bucket.web_hosting_bucket.arn}/*"
    ]
  }
}

resource "aws_s3_bucket_website_configuration" "web_hosting_bucket_config" {
  bucket = aws_s3_bucket.web_hosting_bucket.id

  index_document {
    suffix = "index.html"
  }

  error_document {
    key = "error.html"
  }
}

module "template_files" {
  source   = "hashicorp/dir/template"
  base_dir = "../frontend/out" # ビルドフォルダを指定
}

resource "aws_s3_object" "bucket_object" {
  for_each     = module.template_files.files
  bucket       = aws_s3_bucket.web_hosting_bucket.id
  key          = each.key
  source       = each.value.source_path
  content_type = each.value.content_type
  etag         = filemd5(each.value.source_path)
}
```
## 解説
- AWS providerを設定し、東京リージョン（ap-northeast-1）を指定
- S3バケットを作成
    - バケット名: "test-deployed-by-terraform"
    - force_destroy: trueで、中身があっても削除可能
- バケットのパブリックアクセス設定を全て許可
- バケットポリシーを設定
    - 全てのユーザーにGetObject権限を付与
- バケットをウェブサイトホスティング用に設定
    - インデックスドキュメント: index.html
    - エラードキュメント: error.html
- テンプレートファイルモジュールを使用してローカルファイルを読み込み
- S3オブジェクトリソースを作成
    - テンプレートファイルモジュールで読み込んだファイルをS3にアップロード
    - コンテンツタイプとETAG（アップロードファイルの変更点検知）を設定


# Terraformを実行
```bash
terraform init   # 初期化
terraform plan   # プランの確認
terraform apply -auto-approve # 実際にリソースを作成
```
# デプロイ確認
AWS Console→S3の詳細画面→プロパティ→静的ウェブサイトホスティングのバケットウェブサイトエンドポイントにアクセスすると以下の画面が現れると思います。
これでデプロイ完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2005049/93170b66-2368-839c-6e04-b6b42d749dfa.png)

# おわりに
いかがでしょうか？私は個人的に、AWSのコンソール画面を手動で操作するよりも、Terraformでインフラをコードとして管理し実行する方が簡単だと実感しました。おかげで、AWSを使うハードルが下がったと感じています。（AWSのコンソール画面は機能が多すぎて、どこを操作すればいいか分かりにくいですからね...）
# ソースコード
https://github.com/akitomonam/terraform-nextjs-s3-deploy
# 参考
https://zenn.dev/ym5754n/scraps/248ec6c4cb0d63

https://qiita.com/curlneko/items/b9c70d6d6da2559531c9