Claude CodeでGCP Cloud Functionsを作る: HTTP関数、Secret Manager、Cloud Loggingまで
Claude CodeでCloud Run functionsを作る。HTTP関数、Secret Manager、Cloud Logging、デプロイ確認を実例で解説。
Google Cloud Functionsは、現在のGoogle Cloudドキュメントでは Cloud Run functions として案内される場面が増えています。昔の「小さな関数を置くだけ」の印象で止まっていると少し混乱しますが、2026年時点の実務では「ソースコードからCloud Runサービスとしてビルドされる関数」と考えると理解しやすいです。
この記事では、Claude CodeでGCP Cloud Functionsを作るときの実装手順を、HTTP関数、Secret Manager、Cloud Logging、デプロイ確認までまとめます。対象はNode.jsのHTTP関数と、Cloud StorageイベントをEventarc経由で受けるCloudEvent関数です。Eventarcは、Google Cloud内のイベントをCloud Run functionsへ届ける仕組みです。Functions Frameworkは、ローカルでもクラウドでも同じ関数の形で呼び出せるようにする足場です。
MasaがClaudeCodeLabで使い分ける基準は単純です。Webhook、軽い通知、CSV取り込みの起点、社内バッチの呼び出し口のように「入口が1つで、処理も1つ」に閉じるならCloud Run functionsが向いています。一方、複数ルートを持つAPI、Next.jsやExpressの本格バックエンド、WebSocket、独自Dockerfile、長い処理時間、細かいCPU制御が必要ならCloud Runを選びます。判断に迷う場合は、先に Claude CodeでGCP Cloud Runを扱う記事 も確認してください。
Claude Codeに丸投げしてはいけない領域もあります。IAM、Secret Manager、再試行、冪等性、ログ設計です。冪等性とは、同じリクエストやイベントが2回届いても結果が壊れない性質です。Pub/SubやEventarcの世界では「少なくとも1回届く」前提で設計するため、重複処理を避ける仕組みをコード側に持たせます。
向いているユースケース
| ユースケース | 入口 | 実務で確認する点 |
|---|---|---|
| Stripe、GitHub、社内ツールのWebhook受信 | HTTP関数 | 署名検証、Bearerトークン、再送対策、Secret Manager |
| Cloud Storageに置かれたCSVの取り込み | Eventarc + CloudEvent関数 | イベント重複、バケットとリージョン、ファイル名ルール |
| 問い合わせフォームの通知処理 | HTTP関数またはPub/Sub連携 | すぐ200を返す設計、後段キュー、レート制限 |
| 夜間集計や同期処理の起動口 | Cloud Scheduler + HTTP関数 | OIDC認証、タイムゾーン、タイムアウト |
この4つはClaude Codeとの相性が良いです。理由は、入力、検証、ログ、失敗時の扱いをチェックリスト化しやすいからです。逆に「1つの関数に複数の責務を詰める」「関数内で長いループを回す」「ユーザー向けAPI全体を1関数にする」形は、レビューも運用も難しくなります。
比較記事や設計メモを作る場合は、Claude Codeのコードレビュー運用 と Claude Codeでシークレット管理を見直す記事 も内部リンクとして合わせると、読者が次に取る行動を作りやすくなります。
最小プロジェクト構成
ここではビルド手順なしで動くCommonJSのNode.js例にします。公式ドキュメントではNode.jsのFunctions Frameworkを使ってHTTP関数を登録します。ローカル実行でも同じ仕組みを使えるので、Claude Codeに生成させたコードをすぐcurlで試せます。
functions-demo/
index.js
package.json
package.json は次の内容です。
{
"name": "claude-code-gcp-functions-demo",
"version": "1.0.0",
"private": true,
"main": "index.js",
"scripts": {
"start:http": "functions-framework --target=handleAction --port=8080",
"start:event": "functions-framework --target=handleStorageObject --signature-type=cloudevent --port=8081"
},
"dependencies": {
"@google-cloud/firestore": "^7.11.0",
"@google-cloud/functions-framework": "^3.4.6"
}
}
npm install
HTTP関数とCloudEvent関数
次の index.js は、そのまま貼り付けて構文チェックできます。HTTP側は Authorization: Bearer ... を検証し、Idempotency-Key があればそれを重複防止キーにします。Storageイベント側はCloudEventのIDをFirestoreに保存し、同じイベントが再配達されてもジョブを二重作成しないようにしています。
const functions = require("@google-cloud/functions-framework");
const { Firestore } = require("@google-cloud/firestore");
const crypto = require("node:crypto");
const db = new Firestore();
function jsonLog(severity, message, extra = {}) {
console.log(JSON.stringify({ severity, message, ...extra }));
}
function requireBearerToken(req) {
const expected = process.env.API_TOKEN;
const header = req.get("Authorization") || "";
return Boolean(expected && header === `Bearer ${expected}`);
}
function stableHash(value) {
return crypto.createHash("sha256").update(value).digest("hex");
}
functions.http("handleAction", async (req, res) => {
if (req.method !== "POST") {
res.status(405).json({ ok: false, error: "POST only" });
return;
}
if (!requireBearerToken(req)) {
res.status(401).json({ ok: false, error: "invalid token" });
return;
}
const body = req.body || {};
if (typeof body.userId !== "string" || typeof body.action !== "string") {
res.status(400).json({ ok: false, error: "userId and action are required" });
return;
}
const idempotencyKey =
req.get("Idempotency-Key") ||
stableHash(`${body.userId}:${body.action}:${body.requestedAt || ""}`);
const requestRef = db.collection("function_requests").doc(idempotencyKey);
const logRef = db.collection("action_logs").doc(idempotencyKey);
try {
let duplicate = false;
await db.runTransaction(async (tx) => {
const existing = await tx.get(requestRef);
if (existing.exists) {
duplicate = true;
return;
}
tx.create(requestRef, {
userId: body.userId,
action: body.action,
createdAt: new Date(),
source: "handleAction"
});
tx.set(logRef, {
userId: body.userId,
action: body.action,
createdAt: new Date()
});
});
jsonLog("INFO", "action accepted", { userId: body.userId, duplicate });
res.status(200).json({ ok: true, duplicate, idempotencyKey });
} catch (error) {
jsonLog("ERROR", "action failed", { error: String(error) });
res.status(500).json({ ok: false, error: "internal error" });
}
});
functions.cloudEvent("handleStorageObject", async (cloudEvent) => {
const data = cloudEvent.data || {};
const bucket = data.bucket;
const name = data.name;
if (!bucket || !name) {
jsonLog("WARNING", "storage event missing bucket or name", { eventId: cloudEvent.id });
return;
}
const eventRef = db.collection("processed_storage_events").doc(cloudEvent.id);
const jobRef = db.collection("storage_import_jobs").doc(stableHash(`${bucket}/${name}`));
await db.runTransaction(async (tx) => {
const existing = await tx.get(eventRef);
if (existing.exists) {
jsonLog("INFO", "duplicate storage event ignored", { eventId: cloudEvent.id });
return;
}
tx.create(eventRef, {
bucket,
name,
eventType: cloudEvent.type,
createdAt: new Date()
});
tx.set(jobRef, {
bucket,
name,
status: "queued",
updatedAt: new Date()
}, { merge: true });
});
jsonLog("INFO", "storage import job queued", { bucket, name, eventId: cloudEvent.id });
});
console.log(JSON.stringify(...)) にしているのは、Cloud Loggingで検索しやすくするためです。Cloud Runのログは標準出力と標準エラーを自動的にCloud Loggingへ送ります。文字列だけのログより、severity、message、eventId、userId を入れた構造化ログのほうが、障害調査で絞り込みやすくなります。
ローカルで動作確認する
HTTP関数を起動します。Windows PowerShellなら setx ではなく、そのシェルだけに効く $env:API_TOKEN="local-token" を使うと安全です。
export API_TOKEN="local-token"
npm run start:http
別ターミナルから送ります。
curl -X POST http://localhost:8080 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer local-token" \
-H "Idempotency-Key: local-001" \
-d '{"userId":"user-123","action":"login","requestedAt":"2026-06-03T00:00:00Z"}'
CloudEvent関数もローカルで再現できます。
npm run start:event
curl -X POST http://localhost:8081 \
-H "Content-Type: application/json" \
-H "ce-id: local-event-001" \
-H "ce-specversion: 1.0" \
-H "ce-type: google.cloud.storage.object.v1.finalized" \
-H "ce-source: //storage.googleapis.com/projects/_/buckets/demo-bucket" \
-d '{"bucket":"demo-bucket","name":"inbox/sample.csv","metageneration":"1"}'
ローカルでFirestoreへ接続する場合は gcloud auth application-default login が必要です。本番データに向けたくない場合は、検証用プロジェクトかFirestore Emulatorを使ってください。Claude Codeには、最初から「本番プロジェクトを使わないローカル確認手順も出して」と頼むのが安全です。
Secret ManagerとIAMを用意する
秘密情報を .env やソースコードに置くと、レビューやログで漏れます。APIトークンはSecret Managerに保存し、実行サービスアカウントだけが読めるようにします。
PROJECT_ID="$(gcloud config get-value project)"
REGION="asia-northeast1"
RUNTIME_SA="functions-runtime@${PROJECT_ID}.iam.gserviceaccount.com"
gcloud iam service-accounts create functions-runtime \
--display-name="Functions runtime service account"
printf "replace-with-real-token" | gcloud secrets create api-token \
--replication-policy="automatic" \
--data-file=-
gcloud secrets add-iam-policy-binding api-token \
--member="serviceAccount:${RUNTIME_SA}" \
--role="roles/secretmanager.secretAccessor"
gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
--member="serviceAccount:${RUNTIME_SA}" \
--role="roles/datastore.user"
落とし穴は、デプロイする人の権限と関数が実行時に使う権限を混ぜることです。デプロイ担当者にはCloud Run functionsを作る権限、サービスアカウントを指定するための iam.serviceAccountUser、必要に応じてEventarcやSecret Managerを設定する権限が必要です。一方、実行サービスアカウントには、処理に必要な最小権限だけを付けます。Secret Managerを読むのはデプロイ担当者ではなく、関数の実行サービスアカウントです。
gcloud runでデプロイする
公式の新しい流れでは、Cloud Run functionsを gcloud run deploy でデプロイできます。HTTP関数は公開Webhookでなければ、まず認証必須にしておきます。
gcloud run deploy handle-action \
--source . \
--function handleAction \
--base-image nodejs24 \
--region "${REGION}" \
--service-account "${RUNTIME_SA}" \
--no-allow-unauthenticated \
--memory 512Mi \
--timeout 60s \
--max-instances 20
gcloud run services update handle-action \
--region "${REGION}" \
--update-secrets=API_TOKEN=api-token:latest
Storageイベント用の関数は、まずCloud Run serviceとしてデプロイし、その後にEventarcトリガーを作ります。
BUCKET="your-import-bucket"
EVENTARC_SA="eventarc-invoker@${PROJECT_ID}.iam.gserviceaccount.com"
gcloud iam service-accounts create eventarc-invoker \
--display-name="Eventarc trigger invoker"
gcloud run deploy storage-import \
--source . \
--function handleStorageObject \
--base-image nodejs24 \
--region "${REGION}" \
--service-account "${RUNTIME_SA}" \
--no-allow-unauthenticated \
--memory 512Mi \
--timeout 120s \
--max-instances 10
gcloud run services add-iam-policy-binding storage-import \
--region "${REGION}" \
--member="serviceAccount:${EVENTARC_SA}" \
--role="roles/run.invoker"
gcloud eventarc triggers create storage-finalized-to-function \
--location="${REGION}" \
--destination-run-service=storage-import \
--destination-run-region="${REGION}" \
--event-filters="type=google.cloud.storage.object.v1.finalized" \
--event-filters="bucket=${BUCKET}" \
--service-account="${EVENTARC_SA}"
Eventarcトリガーは作成直後に数分待つことがあります。Cloud Storageバケットのロケーションとトリガーのロケーションが合っていないと、イベントが届かない原因になります。Cloud Run serviceもリージョン単位なので、ユーザーや連携先サービスに近い場所を選びます。日本向けなら asia-northeast1 が候補になりますが、料金階層、データ所在地、FirestoreやStorageの場所も一緒に見ます。
ログとデプロイ確認
デプロイ後はURLが返るだけで安心しないほうがよいです。HTTP関数は認証、Secret Manager、Firestore書き込み、Cloud Loggingまで確認します。
SERVICE_URL="$(gcloud run services describe handle-action \
--region "${REGION}" \
--format='value(status.url)')"
curl -X POST "${SERVICE_URL}" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer replace-with-real-token" \
-H "Idempotency-Key: prod-smoke-001" \
-d '{"userId":"smoke-user","action":"deploy-check","requestedAt":"2026-06-03T00:00:00Z"}'
gcloud run services logs read handle-action \
--region "${REGION}" \
--limit 20
gcloud logging read \
'resource.type="cloud_run_revision" AND resource.labels.service_name="handle-action" AND jsonPayload.message="action accepted"' \
--limit 20 \
--format json
Cloud Runはリクエストログ、コンテナログ、システムログをCloud Loggingへ送ります。console.error に例外を出すだけでなく、ユーザーID、イベントID、ファイル名、重複判定、外部APIのレスポンスコードなど、調査で必要な値を安全な範囲で入れます。トークン、メール本文、個人情報をログに出すのは避けます。
失敗例と落とし穴
1つ目は、再試行を有効にしたのに冪等性を入れないことです。EventarcやPub/Subのイベントは再配達される前提で設計します。請求、メール送信、在庫更新、CSV取り込みでは、イベントIDや業務IDをFirestoreなどに保存し、同じ仕事を二重実行しないようにします。
2つ目は、Secret Managerの権限をデプロイ担当者にだけ付けることです。実行時にシークレットを読むのは関数のサービスアカウントです。デプロイが成功しても、本番リクエストで Permission denied になるなら、まず実行サービスアカウントを疑います。
3つ目は、公開HTTP関数を安易に --allow-unauthenticated にすることです。Webhookで公開が必要な場合でも、署名検証、IP制限、API Gateway、Cloud Armor、レート制限を検討します。社内バッチやCloud Schedulerから呼ぶだけなら、OIDC認証つきの非公開呼び出しを基本にします。
4つ目は、Cloud Functionsに大きすぎる責務を持たせることです。複数ルート、長時間処理、独自OSパッケージ、常時CPUを使う処理、GPUや細かいコンテナ制御が必要ならCloud Run、Cloud Run jobs、Workflowsへ分けます。Cloud Run functionsは「入口の薄い処理」に使うほど安定します。
5つ目は、コストとリージョンを後回しにすることです。リクエストがない間の実行料金だけで判断せず、Artifact Registry、Cloud Build、Cloud Storage、Eventarc、Cloud Loggingのログ量も見ます。検証環境は削除コマンドまでClaude Codeに出させ、放置リソースを減らします。
Claude Codeにレビューさせるプロンプト
実装後は、Claude Codeに次の観点でレビューさせると抜けを見つけやすいです。
このCloud Run functions実装をレビューしてください。
観点:
- Functions Frameworkの登録名、gcloud run deployの--function、package.jsonのtargetが一致しているか
- HTTP関数の認証、入力検証、エラーレスポンスが安全か
- Eventarcから同じイベントが再配達されても二重処理しないか
- Secret Managerの値をログや例外に出していないか
- 実行サービスアカウントに必要最小限のIAMだけを付けているか
- Cloud Loggingで障害調査できる構造化ログになっているか
- Cloud Runに分けたほうがよい責務をCloud Functionsへ押し込んでいないか
問題があれば、重大度、理由、修正コード、確認コマンドを出してください。
ClaudeCodeLabでは、こうした実務チェックリストを 教材・テンプレート集 と チーム向け研修 にも落とし込んでいます。Webhookやサーバーレス基盤をチームで標準化したい場合は、記事だけで終わらせず、レビュー観点をテンプレート化しておくと運用が楽になります。
公式ドキュメント
- Cloud Run functions documentation
- Deploy a Cloud Run function
- Deploy a Cloud Run function using the gcloud CLI
- Write Cloud Run functions
- Local functions development
- Trigger functions from Cloud Storage using Eventarc
- Configure secrets for Cloud Run services
- Logging and viewing logs in Cloud Run
- Compare Cloud Run functions
- Cloud Run locations
まとめ
Claude CodeでGCP Cloud Functionsを作るときは、関数コードだけでなく、Secret Manager、IAM、Cloud Logging、Eventarc、デプロイ確認まで1セットにします。2026年のGoogle CloudではCloud Run functionsとして扱う場面が多いため、gcloud functions deploy の古い手癖だけでなく、gcloud run deploy --source . --function ... --base-image nodejs24 の流れも押さえておくと迷いにくくなります。
実際に試した結果、Node.js 24のCloud Run functions、Functions FrameworkのローカルHTTP実行、CloudEventのcurl再現、Secret Managerの環境変数注入、Firestoreを使った冪等性チェック、Cloud Loggingの構造化ログ検索までを小さな検証プロジェクトで確認できました。本番に載せる前に、公開範囲、実行サービスアカウント、ログに出す値、リージョンとコストを人間がレビューすることが、Claude Code時代のサーバーレス運用で一番効く安全策です。
無料PDF: Claude Code はじめてのチートシート
まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。
スパムは送りません。登録情報は厳重に管理します。
Claude Codeを仕事で使える形にしませんか?
無料PDFで基礎を固めたあと、すぐ使えるテンプレート集で試し、必要なら業務自動化や導入相談まで進められます。
この記事を書いた人
Masa
Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。
関連書籍・参考図書
この記事のテーマに関連する書籍を楽天ブックスで探せます。
※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。
関連記事
ObsidianメモをCLAUDE.mdに変えるClaude Code運用: 文脈を毎回説明しない仕組み
Obsidianの作業メモからCLAUDE.md用の運用ノートを作り、Claude Codeに安定した文脈を渡す方法。
Claude Code Revenue CTA Routing: 記事からPDF、Gumroad、相談へ送る設計
PVだけで終わらせず、読者の状態に合わせて無料PDF、Gumroad教材、導入相談へ分岐するCTA設計です。
Claude Codeチーム引き継ぎルール: レビュー、権限、収益導線まで渡す実務手順
Claude Codeの作業をチームで渡すための証拠、権限、ロールバック、無料PDF/Gumroad/相談導線の実務ルール。