Claude Code を Vertex AI 経由で最新モデルで使う & settings.json をセキュアに整える
はじめに
先日(2026 年 5 月 28 日)、Anthropic から最新モデルの Claude Opus 4.8 が公開されました。
せっかくなので、この Claude Opus 4.8 を Claude Code から Vertex AI 経由で動かしつつ、settings.json を Terraform / Python 開発向けにセキュアへ整えてみました。その過程でいくつかハマったので、その記録としてまとめます。
想定読者
- Claude Code を Vertex AI(GCP)バックエンドで使いたい
- 最新の Claude モデルを指定して使いたい
- 破壊操作を防ぎつつ、安全に LLM を利用したい
- オートフォーマットを効かせたい(Terraform / Python)
TL;DR
{
"env": {
"CLAUDE_CODE_USE_BEDROCK": "0",
"CLAUDE_CODE_USE_VERTEX": "1",
"ANTHROPIC_MODEL": "claude-opus-4-8",
"ANTHROPIC_VERTEX_PROJECT_ID": "your-gcp-project-id",
"CLOUD_ML_REGION": "global",
"FORCE_PROMPT_CACHING_5M": "1",
"CLAUDE_CODE_MAX_OUTPUT_TOKENS": "10000"
},
"permissions": {
"allow": [
"Bash(terraform fmt*)",
"Bash(terraform validate*)",
"Bash(terraform plan*)",
"Bash(terraform init*)",
"Bash(terraform show*)",
"Bash(terraform output*)",
"Bash(terraform state list*)",
"Bash(terraform state show*)",
"Bash(tflint*)",
"Bash(uv run*)",
"Bash(uv sync*)",
"Bash(uv lock*)",
"Bash(ruff *)",
"Bash(mypy *)",
"Bash(pytest*)",
"Bash(git status*)",
"Bash(git diff*)",
"Bash(git log*)"
],
"ask": [
"Bash(terraform apply*)",
"Bash(terraform destroy*)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(git push --force*)",
"Bash(curl *)",
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Read(./**/*.tfstate)",
"Read(./**/*.tfstate.backup)"
]
},
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path // .tool_response.filePath // empty' | { read -r f; [ -n \"$f\" ] || exit 0; case \"$f\" in *.tf|*.tfvars) command -v terraform >/dev/null 2>&1 && terraform fmt \"$f\" ;; *.py) if command -v ruff >/dev/null 2>&1; then ruff format \"$f\"; elif command -v uv >/dev/null 2>&1; then uv run --quiet ruff format \"$f\"; elif command -v black >/dev/null 2>&1; then black -q \"$f\"; fi ;; esac; } >/dev/null 2>&1 || true",
"statusMessage": "Formatting (terraform fmt / ruff)..."
}
]
}
]
},
"statusLine": {
"type": "command",
"command": "bash ~/.claude/statusline.sh"
},
"fileCheckpointingEnabled": true,
"language": "Japanese"
}
#!/usr/bin/env bash
input=$(cat)
dir=$(printf '%s' "$input" | jq -r '.workspace.current_dir // .cwd // empty')
model=$(printf '%s' "$input" | jq -r '.model.display_name // empty')
[ -n "$dir" ] && cd "$dir" 2>/dev/null
base=$(basename "${dir:-$PWD}")
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
out="📁 $base"
[ -n "$branch" ] && out="$out 🌿 $branch"
[ -n "$model" ] && out="$out 🤖 $model"
printf '%s' "$out"
1. Vertex AI 経由で最新 Claude を使う
~/.claude/settings.json の env でバックエンドとモデルを指定します。
{
"env": {
"CLAUDE_CODE_USE_BEDROCK": "0",
"CLAUDE_CODE_USE_VERTEX": "1",
"ANTHROPIC_MODEL": "claude-opus-4-8",
"ANTHROPIC_VERTEX_PROJECT_ID": "your-gcp-project-id",
"CLOUD_ML_REGION": "global",
"FORCE_PROMPT_CACHING_5M": "1",
"CLAUDE_CODE_MAX_OUTPUT_TOKENS": "10000"
}
}
それぞれの項目は以下のとおりです。
-
CLAUDE_CODE_USE_VERTEX=1で Vertex AI バックエンドを有効化(Bedrock は0で明示的にオフ) -
ANTHROPIC_MODELにモデル IDclaude-opus-4-8を指定 -
ANTHROPIC_VERTEX_PROJECT_IDはモデルをデプロイした GCP プロジェクト ID -
gcloud auth application-default loginを済ませておく -
FORCE_PROMPT_CACHING_5M=1でプロンプトキャッシュの TTL を 5 分に固定する -
CLAUDE_CODE_MAX_OUTPUT_TOKENS=10000で 1 回の出力トークン上限を抑える(後述)
プロンプトキャッシュは 5 分 TTL を明示する
Claude Code はプロンプトキャッシュの TTL を認証方式に応じて自動で選びます。Claude のサブスクリプション利用ならデフォルトが 1 時間 TTL(追加費用なし)ですが、Vertex AI / Bedrock / API キーのような従量課金(per-token)バックエンドでは、安い 5 分 TTL がデフォルトです。1 時間 TTL は便利な反面、キャッシュ書き込みの単価が 5 分 TTL より割高になります。
そのため従量課金で使う場合は、ENABLE_PROMPT_CACHING_1H(1 時間へオプトイン)ではなく、FORCE_PROMPT_CACHING_5M=1 で 5 分 TTL に固定するのがおすすめです。FORCE_PROMPT_CACHING_5M は他の TTL 設定(ENABLE_PROMPT_CACHING_1H を含む)を上書きして 5 分に強制するため、上位スコープに 1 時間設定が紛れていても安全に 5 分へ寄せられます。
"FORCE_PROMPT_CACHING_5M": "1"
💡 連続して作業していれば 5 分ごとにキャッシュが更新され続けるので、5 分 TTL でも十分ヒットする。長時間の離席を挟む断続的な使い方が多い場合に限り、1 時間 TTL を検討する。
出力トークンの上限を決めておく
CLAUDE_CODE_MAX_OUTPUT_TOKENS は 1 リクエストあたりの出力トークン上限です。長文の出力が暴走してトークンを浪費するのを抑えられます。
ここでは控えめに 10000 から始めています。Opus 4.8 の出力上限は 128,000 トークンあるので 10000 はかなり絞った値ですが、通常のコーディング作業では 1 回の出力がこれを超えることは多くありません。まずは小さく始めて、困ったら上げるのが安全です。
"CLAUDE_CODE_MAX_OUTPUT_TOKENS": "10000"
💡 この値を大きくすると、auto-compaction が走るまでの実効コンテキストが狭くなる。逆に、大きなファイルの一括生成や大規模リファクタで出力が途中で切れるようなら 段階的に上げる。
バックエンドの有効・無効は両方を明示する
Vertex を使いたいだけなら、Bedrock の設定は書かなくてもよさそうに見えます。ですが、ここでハマりました。
Claude Code の settings.json は、上書きではなく合体される挙動のようです。ユーザー設定(~/.claude/settings.json)とプロジェクト設定(.claude/settings.json)の env がマージされるため、上位スコープの設定が残ったまま効いてしまいます。
自分の場合、ユーザー設定側に "CLAUDE_CODE_USE_BEDROCK": "1" が入っていました。そのため、プロジェクト設定で CLAUDE_CODE_USE_VERTEX=1 を足しただけでは Bedrock の設定が生き残ってしまい、プロジェクト設定側で "0" に上書きするまで Vertex に切り替わりませんでした。
別スコープの設定が紛れていると、設定したはずなのに効かない、という状態になりがちです。そこで片方だけでなく、CLAUDE_CODE_USE_VERTEX=1(使う側)と CLAUDE_CODE_USE_BEDROCK=0(使わない側)の両方を書いておくのがおすすめです。こうしておけば、上位スコープに何が入っていても意図したバックエンドに固定でき、安全です。
CLOUD_ML_REGION の指定を忘れない
以前のバージョンでは、この設定が無くても動きました。ですが今回 Opus 4.8 を使おうとしたところ、CLOUD_ML_REGION を指定していないとモデル呼び出しが通りませんでした。Vertex 経由ではリージョン指定が必須で、これが無い、もしくは合っていないと弾かれてしまいます。
"CLOUD_ML_REGION": "global"
💡 モデルにアクセスできない、404 / NOT_FOUND が返る、というときは、まず
CLOUD_ML_REGIONとモデル ID の組み合わせを疑う。
2. permissions でセキュアに使う
Claude Code はコマンド実行のたびに確認を出せますが、毎回確認するのは手間です。かといって全部許可するのは危険です。
そこで allow / ask / deny の 3 段に振り分けて、安全なものは自動で実行し、危険なものだけ確認または禁止するようにしました。
"permissions": {
"allow": [
"Bash(terraform fmt*)",
"Bash(terraform validate*)",
"Bash(terraform plan*)",
"Bash(terraform init*)",
"Bash(terraform show*)",
"Bash(terraform output*)",
"Bash(terraform state list*)",
"Bash(terraform state show*)",
"Bash(tflint*)",
"Bash(uv run*)",
"Bash(uv sync*)",
"Bash(uv lock*)",
"Bash(ruff *)",
"Bash(mypy *)",
"Bash(pytest*)",
"Bash(git status*)",
"Bash(git diff*)",
"Bash(git log*)"
],
"ask": [
"Bash(terraform apply*)",
"Bash(terraform destroy*)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(git push --force*)",
"Bash(curl *)",
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Read(./**/*.tfstate)",
"Read(./**/*.tfstate.backup)"
]
}
| 区分 | 入れるもの | 理由 |
|---|---|---|
allow |
plan / validate / fmt / テスト / lint / git 参照系 |
読み取り・検証が中心で副作用が小さい。毎回の確認は不要 |
ask |
terraform apply / destroy
|
インフラを変更・破壊する。どの権限モードでも必ず確認 |
deny |
rm -rf / git push --force / .env / *.tfstate 読み取り |
取り返しがつかない、または機密。実行も閲覧もさせない |
3. hooks で Claude の編集後にオートフォーマット
ローカルでフォーマットをかけ忘れて CI でこける、というのは地味によくあります。これを防ぐ手段はエディタの format on save や pre-commit が王道ですが、Claude にどんどん書かせる運用だと、Claude の出力が未整形のまま diff に乗りがちです。そこで Claude が編集した直後に hook でフォーマットを掛けておくと、かけ忘れを減らせます。
hooks を使うと、ツール実行の前後に任意のコマンドを差し込めます。hook には、実行前に走る PreToolUse(ガードや入力の書き換え向き)と、実行後に走る PostToolUse(フォーマットやテスト向き)の 2 種類があります。フォーマットは書き込み後に走らせたいので、PostToolUse を使います。
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path // .tool_response.filePath // empty' | { read -r f; [ -n \"$f\" ] || exit 0; case \"$f\" in *.tf|*.tfvars) command -v terraform >/dev/null 2>&1 && terraform fmt \"$f\" ;; *.py) if command -v ruff >/dev/null 2>&1; then ruff format \"$f\"; elif command -v uv >/dev/null 2>&1; then uv run --quiet ruff format \"$f\"; elif command -v black >/dev/null 2>&1; then black -q \"$f\"; fi ;; esac; } >/dev/null 2>&1 || true",
"statusMessage": "Formatting (terraform fmt / ruff)..."
}
]
}
]
}
やっていることは以下のとおりです。
- hook には対象ファイルの情報が JSON で stdin に流れてくるので、
jqでパスを取り出す - 拡張子で処理を振り分ける
-
*.tf/*.tfvarsはterraform fmt -
*.pyはruff format(無ければuv run ruff→blackにフォールバック)
-
-
command -vでツールが存在するときだけ実行し、最後に|| trueを付けて、フォーマッタが未導入でも処理を止めないようにする
hook を作るときは、実際に流れてくる JSON を echo '{...}' | <command> でパイプして単体テストしておくと、事故りにくいです。
(応用)PreToolUse でガードする例
実行前に止めたい、もしくは書き換えたいときは PreToolUse を使います。例えば prompt 型の hook で「この Bash は危険か」を LLM に判定させる、といった使い方もできます。
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "jq -r '.tool_input.command' >> ~/.claude/bash-log.txt" }
]
}
]
}
上の例は、全 Bash コマンドをログに残すだけのものです。
4. おまけ
複数のリポジトリやブランチを行き来していると、今どのブランチ・どのモデルで作業しているのかを見失いがちです。statusLine を使うと、フッターに作業ディレクトリ・git ブランチ・モデルを常に表示でき、こうした認識違いを防げます。
"statusLine": {
"type": "command",
"command": "bash ~/.claude/statusline.sh"
},
"fileCheckpointingEnabled": true
-
statusLineはフッターに📁 ディレクトリ 🌿 git ブランチ 🤖 モデルを表示する -
fileCheckpointingEnabledは編集前にスナップショットを取り、/rewindで巻き戻せるようにする
statusline.sh の中身は以下です。
#!/usr/bin/env bash
input=$(cat)
dir=$(printf '%s' "$input" | jq -r '.workspace.current_dir // .cwd // empty')
model=$(printf '%s' "$input" | jq -r '.model.display_name // empty')
[ -n "$dir" ] && cd "$dir" 2>/dev/null
base=$(basename "${dir:-$PWD}")
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
out="📁 $base"
[ -n "$branch" ] && out="$out 🌿 $branch"
[ -n "$model" ] && out="$out 🤖 $model"
printf '%s' "$out"
実際に起動すると、こんな感じで表示されます。ヘッダーに claude-opus-4-8 · Google Vertex AI、フッターに作業ディレクトリ・ブランチ・モデルが出ていれば、ここまでの設定が効いている状態です。

なお、フッターの 📁 zenn 🌿 main 🤖 claude-opus-4-8 はあくまで自分の環境での表示例です。
まとめ
- Vertex 経由で最新モデルを使うには
CLAUDE_CODE_USE_VERTEX=1とANTHROPIC_MODELを指定する。CLOUD_ML_REGION(globalが安定)を忘れない - 設定はマージされるので、使わないバックエンドは
CLAUDE_CODE_USE_BEDROCK=0と書いて明示的にオフへ切り替える - 従量課金(Vertex / Bedrock)ではプロンプトキャッシュは安い 5 分 TTL がデフォルト。
FORCE_PROMPT_CACHING_5M=1で 5 分に固定し、割高な 1 時間 TTL を避ける。CLAUDE_CODE_MAX_OUTPUT_TOKENSで出力トークンの上限も決めておく -
allow(検証系)/ask(apply・destroy)/deny(破壊・機密)の 3 段で振り分ける。*.tfstateと.envは読ませない -
PostToolUseで Claude の編集後にオートフォーマットを掛ける。format on save の本命はエディタ / pre-commit / CI で、hook はその補完。command -vガードを入れておくと未導入環境でも壊れない -
statusLineでブランチを表示し、fileCheckpointingEnabledで/rewindを使えるようにする
設定変更後、Claude Code を再起動してください。
Discussion