この記事は、 HashiCorp Japan Advent Calendar 2025 の 16 日目の記事です!
はじめに
「terraform plan の結果、毎回目で追うの辛くないですか?」
あ、それはですね〜、HCP Terraformのご利用で読みやすくなります
数千行に及ぶ Plan 結果から、セキュリティリスクやコストの増加を見落とさずにチェックするのは至難の業です。
そこで今回は、2025 年 9 月に Amazon Bedrock で利用可能になった Claude 4.5 Sonnet を使い、「Plan 結果をパイプで渡すだけで、S 級アーキテクト並みのレビューを返してくれるツール」 を試せるようにしましたよ。良い時代ですね。
単なる差分チェックではありません。
「そのインスタンスサイズ、開発環境にしては高すぎませんか?」
「ポート 22 全開放はやっちゃいけないんですよ」
「バックアップのことまで当然考えたよな?」
「Terraform っていうのは、こうやって書くんだよ!!あ!?」
と、まるでマサカリ優しい先輩のように具体的に怒ってくれるツールを、ワンライナーで実行できるように実装します。
今回作るもの
アーキテクチャ
仕組みは非常にシンプルです。
-
terraform planを実行し、バイナリを出力。 -
terraform show -jsonで JSON に変換。 - Python スクリプトで JSON を解析し、重要部分を抽出。
- AWS Bedrock (Claude 4.5 Sonnet) にプロンプトと共に送信。
- AI が Markdown 形式でレビューレポートを出力。
手順4は、会社でご利用いただく場合、
会社としてクラウドに Plan 内容を送信していいかどうかはよくよくご確認くださいね!
利用するモデル
なんでもよかったんですが、お使いの人が多そうなモデルを選択。
-
Model ID:
us.anthropic.claude-sonnet-4-5-20250929-v1:0 - 特徴: 以前のモデルに比べ、IaC (Infrastructure as Code) の文脈理解と推論能力が圧倒的に向上しています。
1. 準備:レビュー対象の「ダメな」コード
AI の実力を試すために、あえてツッコミどころ満載の Terraform コード (main.tf) を用意します。
あ、これ、絶対 apply しちゃだめですからね!!
ネタフリじゃないですよ!
# main.tf
provider "aws" {
region = "ap-northeast-1"
}
# 【問題点 1】S3 バケットのパブリックアクセスブロックを無効化している
resource "aws_s3_bucket" "data_bucket" {
bucket = "company-sensitive-data-2025-test"
}
resource "aws_s3_bucket_public_access_block" "data_bucket" {
bucket = aws_s3_bucket.data_bucket.id
block_public_acls = false
block_public_policy = false
ignore_public_acls = false
restrict_public_buckets = false
}
# 💰【問題点 2】開発環境なのにハイスペックすぎるインスタンス
resource "aws_instance" "dev_server" {
ami = "ami-0d52744d6551d851e" # Amazon Linux 2023
instance_type = "c7g.4xlarge" # 高額!!!
tags = {
Name = "dev-test-server"
Env = "development" # 開発環境というタグがある
}
}
# 【問題点 3】セキュリティグループ全開放
resource "aws_security_group" "allow_all" {
name = "allow_all_traffic"
description = "Allow all inbound traffic"
ingress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"] # 危険!
}
}
さあ、釣りエサの用意まで完了しました!
2. 実装:AI レビュアーの本体 (analyze_tf_plan.py)
ここが心臓部です。Bedrock の API にアクセスして結果を解析します。
ポイント:
-
System Prompt の作り込み: 単なる指摘ではなく、「なぜ危険か」「いくら損するか」を具体的に語らせるよう指示しています。ここの
get_system_prompt()をいじれば好きなキャラにもできますし、分析方針も変えられますよ! - API 仕様のハマりどころ: 最新の Bedrock API の仕様に沿って実装してください!変更するときはよく document 確認の上で!
import sys
import json
import boto3
from botocore.exceptions import ClientError
# 最新の Claude 4.5 Sonnet を指定 (リージョンは US East 1 等を利用)
MODEL_ID = "us.anthropic.claude-sonnet-4-5-20250929-v1:0"
REGION = "us-east-1"
def get_system_prompt():
return """
あなたは、AWS認定ソリューションアーキテクト・プロフェッショナルであり、セキュリティスペシャリストです。
Terraform初心者〜中級者のエンジニアに対して、コードレビューを行うメンターの役割を担ってください。
提供されたTerraform PlanのJSONを分析し、**「具体的」かつ「教育的」**なレポートを作成してください。
抽象的な指摘は避け、必ず「なぜ修正が必要か」「修正するとどうなるか」を論理的に説明してください。
## レビューのガイドライン
### 1. 🚨 セキュリティ (最優先)
単に「全開放はダメ」と言うのではなく、攻撃者の視点でリスクを説明してください。
- 悪い例: 「セキュリティグループが広すぎます」
- 良い例: 「ポート22が 0.0.0.0/0 で開放されています。これは全世界からSSHのブルートフォース攻撃を受け続ける状態です。また、ボットネットのスキャン対象となり、数分で侵害されるリスクがあります。」
### 2. 💰 コスト (インパクト重視)
リソースタイプ(例: c7g.4xlarge)から、概算の月額コストを予測し、適正な代替案を提示してください。
- 良い例: 「開発環境に c7g.4xlarge (約$500/月) はオーバースペックです。t4g.small (約$12/月) に変更すれば、コストを約98%削減できます。」
### 3. 🛠️ 具体的な修正案 (Terraformコード)
指摘事項に対して、修正後のTerraformコードスニペットを提示してください。
### 4. 🏷️ 命名規則とベストプラクティス
リソース名から環境(dev/prod)や役割が読み取れるか、タグ付けは適切かを確認してください。
## 出力フォーマット (Markdown)
# 🛡️ Terraform インフラレビュー結果
## 📊 総合評価: [ S / A / B / C / D ] (理由をひとことで)
## 🚨 クリティカルな指摘事項 (修正必須)
**(ここにはセキュリティリスクや重大な設定ミスを記載)**
### 1. [タイトル]
- **現状**: [具体的な設定値]
- **リスク**: [攻撃シナリオや具体的な弊害]
- **修正案**:
```hcl
[修正後のコードスニペット]
"""
def analyze_plan(plan_json):
client = boto3.client("bedrock-runtime", region_name=REGION)
context_data = {
"resource_changes": plan_json.get("resource_changes", []),
"variables": plan_json.get("variables", {}),
"terraform_version": plan_json.get("terraform_version", "unknown")
}
prompt_content = f"以下のTerraform Plan変更内容をレビューしてください:\n\n{json.dumps(context_data, ensure_ascii=False)}"
payload = {
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 8192,
"system": [
{
"type": "text",
"text": get_system_prompt()
}
],
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": prompt_content
}
]
}
],
"temperature": 0.1
}
try:
response = client.invoke_model(
modelId=MODEL_ID,
body=json.dumps(payload)
)
result = json.loads(response.get("body").read())
return result["content"][0]["text"]
except ClientError as e:
return f"AWS Error: {e}"
except Exception as e:
return f"Unknown Error: {e}"
if __name__ == "__main__":
try:
input_str = sys.stdin.read()
if not input_str:
print("Error: Input is empty. Pipe the json output to this script.")
sys.exit(1)
plan_data = json.loads(input_str)
print(f"🚀 Claude 4.5 Sonnet ({MODEL_ID}) is analyzing your infrastructure...\n")
print("--- Analysis Report ---\n")
review = analyze_plan(plan_data)
print(review)
except json.JSONDecodeError:
print("Error: Invalid JSON format. Make sure to run 'terraform show -json'")
except Exception as e:
print(f"Error: {e}")
3. ローカル実行環境の構築 (setup.sh)
記事を読んだ皆さんが「Python の環境構築めんどくさい」とならないよう、仮想環境 (venv) の作成からライブラリ (boto3) のインストールまで全自動で行うシェルスクリプトを用意しました。
実行環境整ってる人は読まなくて大丈夫です!
#!/bin/bash
# エラーが発生したら即停止する設定
set -e
echo "Setting up the environment..."
# 1. 仮想環境 (.venv) がなければ作成する
if [ ! -d ".venv" ]; then
echo "Creating Python virtual environment..."
python3 -m venv .venv
fi
# 2. 仮想環境をアクティベートする
source .venv/bin/activate
# 3. 必要なライブラリ (boto3) が入っているか確認し、なければ入れる
if ! python -c "import boto3" 2>/dev/null; then
echo "Installing dependencies (boto3)..."
pip install --upgrade pip
pip install boto3
fi
echo "✅ Environment ready."
4. Terraform 初期化
テストプロジェクトでみなさん試されてるとおもうので、
今のファイル郡をおいてあるところで、 terraform init 実行しておきます。
必要ないよ、という人は飛ばしてください。
terraform init
5. 実行環境の自動化 (run_analysis.sh)
さて、 Blog タイトルどおりの自動分析を毎回実行するためのスクリプトを用意していきます。
python の実行環境を activate して環境を呼び出しつつ、
terraform plan した内容を出力して、それをうけとって bedrock 経由で分析してもらうスクリプトになっています。
実際はこれを繰り返し実行していく感じですね。
#!/bin/bash
source .venv/bin/activate
terraform plan -out=tfplan.binary && \
terraform show -json tfplan.binary | python analyze_tf_plan.py
実行権を渡しておいてください。
chmod +x run_analysis.sh
6. いざ実行!驚きの結果へ
さて、やっと実行です。
以下のコマンドを実行します。わーい。
./run_analysis.sh
すると、数秒後に Claude 4.5 Sonnet から以下のようなレビューが返ってきました。
実際の結果です
いや、この紹介記事より長いじゃんよ・・・・
実行結果はこちら
--- Analysis Report ---
# 🛡️ Terraform インフラレビュー結果
## 📊 総合評価: **D (危険)**
**理由**: セキュリティ設定が致命的に不足しており、コストも最適化されていません。本番環境への適用は絶対に避けてください。
---
## 🚨 クリティカルな指摘事項 (修正必須)
### 1. 【重大】S3バケットが完全に公開状態
- **現状**: `aws_s3_bucket_public_access_block` で全てのパブリックアクセス制御が `false`
- **リスク**:
- バケット内の全データが**インターネット上に公開される可能性**があります
- 過去の事例では、顧客情報や機密データの漏洩により数億円規模の損害賠償が発生しています
- AWSの自動スキャンツールやボットが数時間以内にこのバケットを発見し、データを収集します
- GDPR違反で最大2000万ユーロまたは全世界売上の4%の制裁金が科される可能性があります
- **修正案**:
```hcl
resource "aws_s3_bucket_public_access_block" "data_bucket" {
bucket = aws_s3_bucket.data_bucket.id
block_public_acls = true # パブリックACLを完全ブロック
block_public_policy = true # パブリックバケットポリシーをブロック
ignore_public_acls = true # 既存のパブリックACLを無視
restrict_public_buckets = true # パブリックアクセスを完全制限
}
```
### 2. 【重大】S3バケットの暗号化が未設定
- **現状**: `server_side_encryption_configuration` が設定されていない
- **リスク**:
- データが平文で保存され、AWS内部での不正アクセスやディスク廃棄時のデータ漏洩リスクがあります
- コンプライアンス要件(PCI-DSS, HIPAA等)を満たせません
- **修正案**:
```hcl
resource "aws_s3_bucket_server_side_encryption_configuration" "data_bucket" {
bucket = aws_s3_bucket.data_bucket.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256" # またはKMSを使用: "aws:kms"
}
bucket_key_enabled = true # コスト削減のため有効化
}
}
```
### 3. 【重大】S3バケットのバージョニングが未設定
- **現状**: バージョニングが有効化されていない
- **リスク**:
- 誤削除や上書きが発生した場合、データを復元できません
- ランサムウェア攻撃でデータが暗号化された場合、復旧不可能です
- **修正案**:
```hcl
resource "aws_s3_bucket_versioning" "data_bucket" {
bucket = aws_s3_bucket.data_bucket.id
versioning_configuration {
status = "Enabled"
}
}
```
### 4. 【重大】EC2インスタンスのセキュリティグループが未指定
- **現状**: `vpc_security_group_ids` が未設定 (デフォルトSGが適用される)
- **リスク**:
- デフォルトセキュリティグループは通常、同一SG内の全通信を許可しており、意図しない通信が発生します
- 攻撃者が同じVPC内に侵入した場合、横展開が容易になります
- **修正案**:
```hcl
resource "aws_security_group" "dev_server" {
name = "dev-server-sg"
description = "Security group for development server"
vpc_id = var.vpc_id
# 必要最小限のインバウンドルールのみ設定
ingress {
description = "SSH from corporate VPN only"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"] # 社内ネットワークのみ
}
egress {
description = "Allow all outbound"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "dev-server-sg"
Env = "development"
}
}
resource "aws_instance" "dev_server" {
# ... 他の設定 ...
vpc_security_group_ids = [aws_security_group.dev_server.id]
}
```
---
## 💰 コスト最適化の指摘事項
### 5. 【高】開発環境に過剰スペックのインスタンス
- **現状**: `c7g.4xlarge` (16vCPU, 32GB RAM) を使用
- **コスト影響**:
- **東京リージョンでの月額コスト: 約 $520 (約78,000円)**
- 24時間稼働の場合、年間 $6,240 (約936,000円)
- **推奨**: 開発環境であれば、以下のインスタンスで十分です
- `t4g.medium` (2vCPU, 4GB): 約 $24/月 (約3,600円) → **96%削減**
- `t4g.large` (2vCPU, 8GB): 約 $48/月 (約7,200円) → **91%削減**
- **修正案**:
```hcl
resource "aws_instance" "dev_server" {
ami = "ami-0d52744d6551d851e"
instance_type = "t4g.medium" # 開発環境に適したサイズ
# ... 他の設定 ...
tags = {
Name = "dev-test-server"
Env = "development"
}
}
```
### 6. 【中】EC2インスタンスの自動停止設定がない
- **現状**: 開発サーバーが24時間稼働する設定
- **コスト影響**: 夜間・週末の停止で約60%のコスト削減が可能
- **推奨**: AWS Instance Schedulerまたはタグベースの自動停止を導入
```hcl
resource "aws_instance" "dev_server" {
# ... 他の設定 ...
tags = {
Name = "dev-test-server"
Env = "development"
Schedule = "office-hours" # 平日9-18時のみ稼働
}
}
```
---
## 🏷️ 命名規則とベストプラクティス
### 7. 【中】S3バケット名にリージョン情報がない
- **現状**: `company-data-bucket-2025`
- **問題点**:
- グローバルで一意である必要があるS3バケット名に、リージョンや環境情報がありません
- 他の環境(staging, production)との区別が困難です
- **推奨命名規則**:
```hcl
resource "aws_s3_bucket" "data_bucket" {
bucket = "company-data-dev-apne1-2025" # {組織}-{用途}-{環境}-{リージョン}-{年}
# または
# bucket = "company-dev-data-bucket-ap-northeast-1"
tags = {
Name = "company-data-dev-apne1-2025"
Environment = "development"
ManagedBy = "terraform"
Purpose = "application-data-storage"
}
}
```
### 8. 【低】タグの不足
- **現状**: EC2にはタグがあるが、S3バケットにタグが未設定
- **問題点**:
- コスト配分レポートで費用の追跡ができません
- リソースの所有者や目的が不明確です
- **推奨**:
```hcl
resource "aws_s3_bucket" "data_bucket" {
bucket = "company-data-dev-apne1-2025"
tags = {
Name = "company-data-dev-apne1-2025"
Environment = "development"
ManagedBy = "terraform"
Owner = "dev-team@company.com"
CostCenter = "engineering"
Project = "project-x"
}
}
```
---
## ✅ 追加で実装すべき設定
### 9. S3バケットのライフサイクルポリシー
```hcl
resource "aws_s3_bucket_lifecycle_configuration" "data_bucket" {
bucket = aws_s3_bucket.data_bucket.id
rule {
id = "archive-old-versions"
status = "Enabled"
noncurrent_version_transition {
noncurrent_days = 30
storage_class = "STANDARD_IA" # 低頻度アクセス
}
noncurrent_version_transition {
noncurrent_days = 90
storage_class = "GLACIER" # アーカイブ
}
noncurrent_version_expiration {
noncurrent_days = 365 # 1年後に削除
}
}
}
```
### 10. EC2インスタンスのIMDSv2強制化
```hcl
resource "aws_instance" "dev_server" {
# ... 他の設定 ...
metadata_options {
http_endpoint = "enabled"
http_tokens = "required" # IMDSv2を強制
http_put_response_hop_limit = 1
instance_metadata_tags = "enabled"
}
}
```
### 11. S3バケットのアクセスログ記録
```hcl
resource "aws_s3_bucket" "log_bucket" {
bucket = "company-logs-dev-apne1-2025"
tags = {
Name = "company-logs-dev-apne1-2025"
Env = "development"
}
}
resource "aws_s3_bucket_logging" "data_bucket" {
bucket = aws_s3_bucket.data_bucket.id
target_bucket = aws_s3_bucket.log_bucket.id
target_prefix = "s3-access-logs/"
}
```
---
## 📋 修正優先順位まとめ
| 優先度 | 項目 | 影響 | 修正時間 |
|--------|------|------|----------|
| 🔴 最優先 | S3パブリックアクセスブロック | セキュリティ | 5分 |
| 🔴 最優先 | S3暗号化設定 | セキュリティ | 5分 |
| 🔴 最優先 | セキュリティグループ設定 | セキュリティ | 15分 |
| 🟠 高 | インスタンスタイプ変更 | コスト | 5分 |
| 🟠 高 | S3バージョニング | データ保護 | 5分 |
| 🟡 中 | 命名規則の統一 | 運用性 | 10分 |
| 🟡 中 | タグの追加 | 運用性 | 10分 |
| 🟢 低 | ライフサイクルポリシー | コスト | 15分 |
---
## 🎓 学習ポイント
1. **セキュリティはデフォルトで閉じる**: AWSのデフォルト設定は必ずしも安全ではありません。明示的に制限を設定しましょう。
2. **コストは積み重なる**: 月$500の無駄は年間$6,000です。開発環境こそコスト意識が重要です。
3. **タグは資産管理の基本**: 後から追加するのは困難です。最初から体系的に設定しましょう。
**次のステップ**: 上記の修正を適用後、`terraform plan` を再実行し、変更内容を確認してください。
7. まとめ
HCP Terraform 使えば見やすい terraform plan の結果も、
分析部分を AI に任せることでより深い考察が可能ですね!
今回はローカルの実行環境から実行しましたが、以下のような応用が考えられます。
例えば、HCP Teraform の Run Tasks から Webhook 経由で実行して結果を得て、より見やすい分析にできます!その場合は、 lambda にホストしたり、 Railway とかで気軽に Webhook をホストをすると、より簡単に組み合わせやすいのでご検討ください!
他にも、 Github Actions と連携するアイデアもあります。
例えば、お使いのリポジトリの .github/workflows/ci.yml にこんな感じで設定して Github Actions を実行できます。
# GitHub Actions Workflow 例
steps:
- name: Terraform Plan
run: terraform plan -out=tfplan.binary
- name: Terraform Show
run: terraform show -json tfplan.binary > plan.json
- name: AI Review
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
# ...
run: |
cat plan.json | python analyze_tf_plan.py
# ここで AI が警告したら exit 1 させる処理を入れるなどなど・・・
PR コメントに連携させれば、さらに発展した使い方ができますね!
2026 年のインフラ運用は、AI とペアプログラミング(ペア運用)するのが当たり前になりそうですね!
それではまた!
Tips
- この記事のコードはそのままコピペで動作します(要 AWS 環境)。
- ぜひご自身の環境の
tfplanを食わせてみて、どんな指摘が来るか試してみてください!