はじめに
SapeetでSWEをやっている林です。
前回の記事「Claude Codeで爆速開発!S3→BigQueryデータ転送システムを1時間で設計した話」では、データ同期システムのアーキテクチャと設計について紹介しました。
今回は、その続編として、Claude Codeを使ってTerraformのインフラコードを生成し、実際にAWS環境にデプロイするまでの過程を共有します。その過程で得た知識やつまづいたポイントも併せて解説していきます。
この記事で学べること
- 生成されたTerraformコードをAWSにデプロイするまでの実践フロー
-
実際に遭遇した3つの問題とClaude Codeによる自動修正
- Terraformのディレクトリ構成の理解(root main.tfの役割)
- AWS認証設定の落とし穴(backend.tf vs provider block)
- terraform plan構文エラーの自動修正テクニック
AWS事前設定
以下は事前に生成したsetup-guide.mdに従って設定済みであることを前提とします:
-
Terraform Backend用リソース
- S3バケット(stateファイル保存用、バージョニング・暗号化有効)
- DynamoDBテーブル(stateロック用)
-
GCP認証情報
- BigQueryサービスアカウント作成済み
- サービスアカウントキーをAWS Systems Manager Parameter Storeに保存済み
プロジェクト構成
terraform/
├── main.tf # ← ここが重要!
├── variables.tf
├── outputs.tf
├── modules/
│ ├── vpc/
│ ├── ecs/
│ ├── s3/
│ ├── iam/
│ ├── ecr/
│ ├── cloudwatch/
│ └── eventbridge/
└── environments/
├── dev/
│ ├── main.tf # ← rootモジュールを呼び出すだけ
│ ├── backend.tf
│ ├── variables.tf
│ └── terraform.tfvars
└── prod/
└── ...
Claude Codeでインフラ構築する全体フロー
前回の記事で、Claude Codeに要件を伝え、Terraformコードの生成タスクをtask-breakdown.mdとして作成してもらいました。
今回は、そのタスクを実際に実行してもらうところからスタートします。
Step 1: task-breakdown.mdに基づいた段階的実装
Claude Codeに「task-breakdown.mdのタスクを順次実行してください」と依頼すると、自動的に以下のステップを進めてくれました:
生成されたタスク一覧:
1. プロジェクト構造の作成
2. VPCモジュールの実装
3. IAMモジュールの実装
4. S3モジュールの実装
5. ECRモジュールの実装
6. CloudWatchモジュールの実装
7. ECSモジュールの実装
8. EventBridgeモジュールの実装
9. ルートモジュールの作成
10. Dev環境の設定
約5分で、全モジュールのコードが生成されました。
task-breakdown.mdは、Claude Codeが自動生成するタスク管理ファイルです。大きなタスクを小さなステップに分割し、進捗を追跡できます。各ステップが完了すると、自動的にチェックマークが付きます。
Step 2: Backend設定とterraform init
まず、S3バケットとDynamoDBテーブルを手動で作成し、backend設定を行いました:
# environments/dev/backend.tf
terraform {
backend "s3" {
bucket = "simple-data-sync-terraform-state-${timestamp}"
key = "dev/terraform.tfstate"
region = "ap-northeast-1"
encrypt = true
dynamodb_table = "simple-data-sync-terraform-lock"
profile = "your-aws-profile" # AWSプロファイル
}
}
$ cd terraform/environments/dev
$ terraform init
✅ 結果: 初回initは成功!stateファイルがS3に保存されました。
Step 3: Claude Codeに terraform plan 実行を依頼
さあ、Backend設定も完了したので、Claude Codeに以下のプロンプトを投げました:
terraform/environments/devの方にinitしたので、planを実行し、エラーをなくすようにお願いします。
すると、Claude Codeが terraform plan を実行し、以下の3つのエラーを検出しました:
エラー1: AWS認証エラー
❌ Error: No valid credential sources found
エラー2: EventBridgeの構文エラー
❌ Error: Unsupported argument
on ../../modules/eventbridge/main.tf line 43:
43: maximum_event_age = 3600
エラー3: S3ライフサイクルの警告
⚠️ Warning: Invalid Attribute Combination
No attribute specified when one (and only one) of
[rule[0].filter, rule[0].prefix] is required
Step 4: Claude Codeによる自動エラー修正
ここでClaude Codeの真価を発揮!
上記のプロンプト(「planを実行し、エラーをなくすようにお願いします」)を投げただけで、エラーがなくなるまで自動的に修正し続けてくれました。
Claude Codeの対応:
-
terraform planを実行 - エラーを検出・分析
- 該当ファイルを自動的に読み込み
- 修正を適用
-
terraform validateで検証 - 再度
terraform planを実行 - エラーがあれば 2 に戻る
このサイクルを自動的に3回繰り返し、約10分ですべてのエラーを解決しました。
Step 5: terraform apply でデプロイ完了
$ terraform plan
Plan: 45 to add, 0 to change, 0 to destroy.
$ terraform apply
Apply complete! Resources: 45 added, 0 changed, 0 destroyed.
✅ 成功! 約15分でAWSインフラのデプロイが完了しました。
デプロイされたリソース:
- VPC(サブネット、セキュリティグループ)
- IAMロール 3つ(ECS Task、Task Execution、EventBridge)
- S3バケット(暗号化、バージョニング有効)
- ECRリポジトリ
- ECSクラスターとタスク定義
- EventBridgeスケジュールルール
- CloudWatchロググループとメトリクスアラーム
所要時間: Backend設定5分 + エラー修正10分 + デプロイ15分 = 約30分
遭遇した問題と解決方法
上記のStep 3で遭遇した問題について、詳しく解説します。
問題1: Terraform root main.tfの役割を理解する
最初に遭遇したのは、Terraformのディレクトリ構成に関する誤解でした。
誤解していたこと
当初、私はTerraformのディレクトリ構成について以下のように誤解していました:
❌ 間違った理解
environments/dev/main.tf に全モジュールを直接呼び出す
environments/prod/main.tf に全モジュールを直接呼び出す
→ 各環境で同じモジュール呼び出しコードを重複して書く
正しい構成パターン
実際には、以下のような構成が推奨されます:
terraform/main.tf(rootモジュール)
# すべてのモジュールをここで組み合わせる
provider "aws" {
region = var.aws_region
profile = var.aws_profile
default_tags {
tags = merge(
{
Project = var.project_name
Environment = var.environment
ManagedBy = "Terraform"
},
var.tags
)
}
}
module "vpc" {
source = "./modules/vpc"
# VPC設定
}
module "ecs" {
source = "./modules/ecs"
# VPCモジュールの出力を使用
vpc_id = module.vpc.vpc_id
# ...
}
# 他のモジュールも同様に定義
environments/dev/main.tf(環境固有の設定)
# rootモジュール全体を1回呼び出すだけ
module "data_sync" {
source = "../../" # rootモジュールを参照
# 環境固有の変数を渡す
project_name = var.project_name
environment = "dev"
aws_region = var.aws_region
aws_profile = var.aws_profile
# dev環境固有の設定
ecs_task_cpu = 512
ecs_task_memory = 1024
enable_nat_gateway = false
# ...
}
この構成のメリット
✅ DRY原則の遵守: モジュール間の依存関係を一箇所(root main.tf)で管理
✅ 保守性の向上: モジュール構成の変更が各環境に自動的に反映される
✅ 環境間の一貫性: 同じインフラ構成を異なるパラメータで再利用
問題2: AWS認証設定の落とし穴(backend.tf vs provider)
これは最も混乱したポイントでした。backend.tf にプロファイルを設定していたのに認証エラーが発生したのです。
遭遇した問題
backend.tf にAWSプロファイルを設定していたのに、terraform plan で認証エラーが発生しました。
# environments/dev/backend.tf
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "dev/terraform.tfstate"
region = "ap-northeast-1"
profile = "your-aws-profile" # ← これは設定済み
}
}
$ terraform init
✅ Success! # backend設定は正常に動作
$ terraform plan
❌ Error: No valid credential sources found
原因の理解
Terraformには2種類の認証設定が存在します:
| 用途 | 設定場所 | 使用されるタイミング |
|---|---|---|
| Backend認証 |
backend "s3" block |
terraform init でstate管理用S3にアクセスする時 |
| Provider認証 |
provider "aws" block |
terraform plan/apply でAWSリソースを操作する時 |
つまり、backend.tfのprofileはstateファイルの保存先へのアクセスにのみ使用され、実際のリソース操作には使われないのです。
解決策
rootモジュールのprovider blockにもプロファイル設定を追加します。
Step 1: 変数を定義
# terraform/variables.tf
variable "aws_profile" {
description = "AWS CLI profile to use for authentication"
type = string
default = null
}
Step 2: Provider blockに追加
# terraform/main.tf
provider "aws" {
region = var.aws_region
profile = var.aws_profile # ← これを追加
default_tags {
tags = merge(
{
Project = var.project_name
Environment = var.environment
ManagedBy = "Terraform"
},
var.tags
)
}
}
Step 3: 環境ごとにプロファイルを設定
# environments/dev/variables.tf
variable "aws_profile" {
description = "AWS CLI profile to use for authentication"
type = string
default = "your-aws-profile"
}
Step 4: rootモジュールに渡す
# environments/dev/main.tf
module "data_sync" {
source = "../../"
aws_region = var.aws_region
aws_profile = var.aws_profile # ← これを追加
# ...
}
認証方法の整理
参考までに、AWS Provider の認証方法の優先順位は以下の通りです:
- Provider block内の
access_key/secret_key(非推奨) - Provider block内の
profile(✅ 推奨) - 環境変数
AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY - 環境変数
AWS_PROFILE - デフォルトプロファイル (
~/.aws/credentialsの[default])
本番環境ではIAMロールの使用が推奨されますが、開発環境ではプロファイルを明示的に指定する方が管理しやすいです。
問題3: terraform plan で発生した構文エラー
Claude Codeが生成したコードでも、いくつかの構文エラーが発生しました。しかし、これらは全てClaude Codeによる自動修正で解決できました。
エラー例1: EventBridgeのパラメータ名誤り
Error: Unsupported argument
on ../../modules/eventbridge/main.tf line 43:
43: maximum_event_age = 3600
An argument named "maximum_event_age" is not expected here.
修正内容:
# terraform/modules/eventbridge/main.tf
retry_policy {
- maximum_event_age = 3600
+ maximum_event_age_in_seconds = 3600
maximum_retry_attempts = 2
}
AWS EventBridgeのリトライポリシーでは、maximum_event_age ではなく maximum_event_age_in_seconds が正しいパラメータ名でした。
Claude Codeによる自動修正の流れ
上記のエラーに対して、私がやったことはシンプルな1行のプロンプトだけです:
terraform/environments/devの方にinitしたので、planを実行し、エラーをなくすようにお願いします。
これだけで、Claude Codeが自動的に:
-
terraform planを実行 - エラー原因を分析
- 該当ファイルを読み込み
- 正しい修正を適用
-
terraform validateで検証 - 再度
terraform planを実行(エラーがあれば2に戻る)
このサイクルを自動的に繰り返し、すべてのエラーを解決してくれました。
ポイント: エラーメッセージを手動でコピペする必要はなく、「エラーをなくすまで修正してください」と目標を伝えるだけで、Claude Codeが自律的に問題解決してくれます。
Claude Code活用のポイント
今回の実践で分かった、Claude Codeを効率的に使うコツをまとめます:
推奨
- 目標を明確に伝える: 「エラーをなくすまで修正してください」と指示することで、Claudeが自律的に問題解決サイクルを回してくれる
-
Claudeにコマンド実行を任せる:
terraform planの実行もClaudeに任せることで、エラー検出→修正→再検証を自動化できる - TodoListで進捗を確認: 複数のエラーがある場合、どのエラーを修正中かが可視化される
-
検証はClaudeに任せる:
terraform validateやterraform planの実行もClaude Codeが自動で行ってくれる -
ガードレールの設定:
.claude/rulesでterraform applyの自動実行を防止するルールを定義し、誤削除やコスト超過を防ぐ(参考)
非推奨
- エラーメッセージを手動でコピペする(Claudeに直接コマンド実行させる方が効率的)
- 途中で人間が介入しすぎる(Claude Codeの自律的な問題解決を信頼する)
- 曖昧な指示を出す(「エラーを修正して」ではなく「planが成功するまで修正して」と具体的に)
効果的なワークフロー
前回の記事(コード生成依頼)から今回の記事(デプロイ)までの全体フローです:
まとめ
今回、Claude Codeが生成したTerraformコードを実際にAWSにデプロイし、約30分で完了することができました。
得られた知見
- Terraformのディレクトリ構成: root main.tfでモジュールを一元管理し、環境ごとに変数を渡すパターン
- AWS認証の2層構造: backend.tf(state保存)とprovider block(リソース操作)は別物
- Claude Codeの自律的な問題解決能力: 「エラーをなくすまで修正してください」という目標を伝えるだけで、コマンド実行→エラー検出→修正→再検証のサイクルを自動的に回してくれる
人間が判断すべきポイント
Claude Codeは非常に強力ですが、以下の点は必ず人間が確認・判断する必要があります:
- セキュリティ設定の妥当性(IAMポリシー、セキュリティグループ)
- コスト最適化の判断(NAT Gateway vs VPC Endpoints など)
- 環境固有の要件(リージョン、命名規則、タグ戦略)
- コンプライアンス要件(データ暗号化、ログ保持期間など)
Next Action
次回は、このTerraformで構築したインフラ上で動作するデータ同期アプリケーション(Python + boto3 + BigQuery Client)の実装と疎通確認について紹介する予定です。