🚢

Terraform未経験からのLLMとつくるインフラ構築

に公開

はじめに

個人サービスを今までに何個か作成していて、IaCを利用してのインフラ構築は経験がなく、いつも手動で構築していました。
今回はCursorの力を借りながら、Terraformを用いたインフラ構築に挑戦してみました。
この記事では実際に動かした構成の紹介をしていきます。

成果物:

https://github.com/ta-toshio/terraform-aws-network-presets/tree/main

Terraformを選択した理由

インフラ構成をコードで管理する手段としては、AWS CDKやPulumiなど複数の選択肢があります。その中で私がTerraformを選んだ理由は、以下の2点です。

1つ目は、マルチクラウド対応の柔軟性です。将来的にAWS以外にGoogle Cloud(GCP)を使ってみたく、AWS専用のCDKではなく、GCPや他クラウドサービスでも再利用しやすいシステムから。

2つ目は、学習リソースを既に持っていたことです。以前購入していたUdemyのTerraform講座があったため、教材を活用しながら取り組みやすいと考えました。

購入した講座は以下です。内容はとても良かったです。Github Actionsからのデプロイもカバーしています。

https://www.udemy.com/course/devops-deployment-automation-terraform-aws-docker/

仕様書から始めるインフラ設計

Terraformのコードを書き始める前に、まずはインフラの設計書を作ることから始めました。ChatGPT-4oとCursor内で対話しながらアーキテクチャ設計書を作成しました。

https://github.com/ta-toshio/terraform-aws-network-presets/blob/main/aws-architecture.md

仕様書には以下の内容が含まれています:

  • Amplify + ECS + RDSを中心とした構成
  • ALBあり/なし構成、Public/Private構成をTerraformのworkspaceで切り替え可能
  • コスト最適化とセキュリティの両立
  • ALBなし構成の場合のDNS更新を自動化するLambda設計
  • 開発・ステージング・本番の環境分離とCI/CD戦略

ALBあり/なし、WebサーバーはPublic/Privateサブネットどちらに置くかを選択できるような形にしています。コスト気にしなければALBありPrivateサブネットに配置構成にするのですが、NATやVPCエンドポイントが本来無料で運用したい勢には割と高く、とりあえずはいろいろ試してみたく切り替え可能な構成で作成する形で行きました。

実装・構成

インフラ設計書をもとに、Terraformで実際にAWSインフラをコード化しました。cursor、claude-3.7-sonnetを利用してのトライ&エラーで作成していきました。

ディレクトリ構成と責務の分離

Terraformコードは、以下の3層に整理しています。

  • setup/: 初回だけ実行する初期セットアップ(S3バケットやIAMユーザーなど)
  • deploy/{env}/: dev/staging/productionごとのデプロイ(環境固有設定)
  • modules/: 各AWSサービスごとの再利用可能なモジュール(ネットワーク、ECS、RDSなど)
/
├── setup/           # 1回のみ実行。状態管理・認証のための土台
├── deploy/dev/      # 環境別定義(他に staging/、production/ あり)
├── modules/         # 共通モジュール(VPC, ALB, ECS, RDS など)
└── README.md        # 使用方法とCI/CD構成の解説

全体

.
├── .env.dev                # ローカル開発用環境変数テンプレート
├── docker-compose.yml      # 開発時にTerraformをコンテナで動かす定義
├── setup/                  # 一度だけ実行する初期セットアップ用
│   ├── main.tf             # S3バケットやDynamoDBロック、IAMユーザー、ECRリポジトリ作成
│   └── variables.tf
├── deploy/                 # 各環境(dev/staging/production)ごとのデプロイ定義
│   ├── dev/
│   ├── staging/
│   └── production/
│       ├── main.tf         # backend設定、workspaceマトリクス、モジュール呼び出し
│       ├── variables.tf
│       └── outputs.tf
├── modules/                # 環境共通で使う再利用可能なモジュール群
│   ├── network/            # VPC、サブネット、IGW、NAT、VPCエンドポイント
│   ├── security/           # セキュリティグループ定義
│   ├── alb/                # ALB+証明書+ターゲットグループ
│   ├── ecs/                # ECS Fargate クラスター・タスク・サービス・Autoscaling
│   ├── rds/                # RDS PostgreSQL インスタンス/サブネットグループ
│   ├── monitoring/         # CloudWatch アラーム・ダッシュボード・SNS
│   ├── amplify/            # AWS Amplify (Next.js)ホスティング
│   └── route53/            # Route 53 レコード作成
└── README.md               # 利用手順とCI/CDの説明

Terraform Workspace による構成バリエーションの管理

特に変わった設計としては public-with-albprivate-no-albなど4パターンの構成をひとつのmain.tfで切り替えられる設計です。

# publicサブネット + ALBありの構成
terraform workspace select public-with-alb
terraform apply
locals {
  config = {
    "public-with-alb" = {
      use_public_subnet  = true
      use_alb            = true
      enable_nat_gateway = false
    }
    ...
  }

  current_config = lookup(local.config, terraform.workspace)
  use_alb = local.current_config.use_alb
}

このようにWorkspace名に応じて構成のON/OFFを切り替えることで、開発/本番で構成を柔軟に調整可能です。

成果物を構成図で可視化

リソース 内容
Route53 独自ドメインでの名前解決
AWS Amplify Next.jsフロントエンドのホスティングとCI/CD
ALB + ACM HTTPS終端とWebサーバーへのルーティング
ECS Fargate (Web) バックエンドAPIコンテナ(ALB経由でアクセス)
ECS Fargate (CLI) マイグレーションやバッチ用に必要時のみ実行されるタスク
RDS PostgreSQL データ永続化(privateサブネット、セキュリティグループで防御)
CloudWatch モニタリングとアラート通知

public-with-albの図

private-with-albの図

ハマった点

amplify

amplifyのCNAMEは自前で作成しなくてよいみたいです。自動で作成してくれました。以下を最初自前で作成していましたが、不必要でした。

# # Amplify用CNAMEレコード - メインドメイン
# resource "aws_route53_record" "amplify" {
#   count   = local.create_amplify_records && local.use_clerk ? 1 : 0
#   zone_id = var.zone_id
#   name    = var.domain_name
#   type    = "CNAME"
#   ttl     = 300
#   records = [var.amplify_domain]
  
#   lifecycle {
#     create_before_destroy = true
#   }
# }

# # Amplify用CNAMEレコード - wwwサブドメイン
# resource "aws_route53_record" "amplify_www" {
#   count   = local.create_amplify_records && local.use_clerk ? 1 : 0
#   zone_id = var.zone_id
#   name    = "www.${var.domain_name}"
#   type    = "CNAME"
#   ttl     = 300
#   records = [var.amplify_domain]
  
#   lifecycle {
#     create_before_destroy = true
#   }
# }

あとamplifyの画面に行くと警告が出力されます。一度「移行を開始」で認可すれば警告はでなくなるはずです。移行開始しなくても動いていたので、何回目からか気にせず利用してしまっています。

おわりに

terraform destroy/apply コマンドで作り直しが簡単にできる体験は、学習体験としてはかなりよかったです。
またLLM開発の相性はよいのではないでしょうか。アプリ開発の流れとほぼ同じように、作成するのものを明確にしたあと、AIエディタのエージェントに作成依頼。理解できない箇所があれば、解説を頼み、必要があれば別のLLMにも質問してみる、の流れで行えました。

Terraformの習得は、環境構築の理解とコードの文法理解の両方が求められるため、初心者には非常に負荷の高い学習です。しかし、AIを活用することで

  • エラーの原因をすばやく特定できる
  • よくある設計パターンを自然言語で聞き出せる
  • 自分が何をしたいかを対話的に整理できる

というメリットを享受できます。

AIエディタ(LLM) x terraformの資料は以下のようなものもあり興味深く、学習・成果物が捗るかもしれません。今後も活用していこうと思います。

Discussion