🔐

bq コマンドは成功するのに Python で 403?GCP 認証の違いと注意点

に公開

これはなに?

BigQuery を Python で使おうとしたら「403 Forbidden」エラーが出たんですが、bq コマンドでは問題なく動く。同じアカウントなのに挙動が違う、という状況に遭遇しました。

調べてみたところ、Google Cloud SDK(CLI)と Python SDK(ライブラリ)では使われる認証方式が異なるため、設定や権限の扱いに差が出ることがわかりました。同じような問題で困っている人向けに、仕組みと原因、解決策をまとめます。


問題の状況

# bq コマンドは成功
$ bq query "SELECT COUNT(*) FROM \`my-project.production.users\`"
+-------+
| count |
+-------+
| 12500 |
+-------+
# Python は失敗
from google.cloud import bigquery

client = bigquery.Client(project='my-project')
df = client.query("SELECT COUNT(*) FROM production.users").to_dataframe()

# エラー内容
google.api_core.exceptions.Forbidden: 403 POST https://bigquery.googleapis.com/...
Caller does not have required permission to use project my-project.
Grant the caller the roles/serviceusage.serviceUsageConsumer role...

結論だけ知りたい人向け

原因の概要

CLI ツール(gcloud / bq)と Python SDK では、内部的に使う認証情報の種類が違います。
そのため、「どのプロジェクトの quota(使用枠)を消費するか」という設定がずれていると、API 側で 403 Forbidden が発生します。

認証方式 主な利用ツール 認証情報の保存場所 quota の指定方法
CLI認証 gcloud, bq, gsutil ~/.config/gcloud/credentials.db gcloud config set billing/quota_project
ADC(Application Default Credentials) Python SDK, Node.js SDK など ~/.config/gcloud/application_default_credentials.json quota_project_id フィールド

対応方法

まず ADC の quota project を明示的に設定します。

gcloud auth application-default set-quota-project my-project

このとき、my-project に対して serviceusage.services.use 権限(通常は roles/serviceusage.serviceUsageConsumer)が必要です。

それでも解決しない場合は、一時的な回避策として CLI トークンを使う方法があります。

import subprocess
from google.cloud import bigquery
from google.oauth2 import credentials as oauth2_credentials

def get_bigquery_client(project):
    token = subprocess.run(
        ['gcloud', 'auth', 'print-access-token'],
        capture_output=True, text=True, check=True
    ).stdout.strip()
    creds = oauth2_credentials.Credentials(token=token)
    return bigquery.Client(project=project, credentials=creds)

GCP の認証方式を整理する

1. CLI 認証(gcloud auth login

CLI ツール(bq など)で利用される方式で、認証情報は ~/.config/gcloud/credentials.db に保存されます。

gcloud auth login

この方式では、gcloud config get-value billing/quota_project の設定値が quota project として利用されます。
設定していない場合は Google が管理するデフォルトプロジェクトが使われることもあります。


2. ADC(Application Default Credentials)

Python や Node.js の SDK が使う認証方式です。

gcloud auth application-default login

認証情報は ~/.config/gcloud/application_default_credentials.json に保存されます。

{
  "client_id": "1234567890-...",
  "quota_project_id": "my-project",
  "refresh_token": "1//0e...",
  "type": "authorized_user"
}

quota_project_id が空のままだと、API 側で quota 消費先を判断できず、エラーや警告が出る場合があります。
Google Cloud の公式ドキュメントにも「クォータプロジェクトを設定していない場合、API リクエストが失敗する可能性がある」と記載があります。


quota_project_id の意味

クォータプロジェクトとは、「この API 呼び出しでどのプロジェクトの使用枠(quota)を使うか」を指定する仕組みです。
課金先プロジェクトとは別の概念で、quota を利用するためにも権限が必要です。

quota_project_id を設定するには
↓
serviceusage.services.use 権限が必要
↓
roles/serviceusage.serviceUsageConsumer ロールを付与

この権限がない場合、ADC の認証を通してリクエストすると 403 Forbidden が返ることがあります。


CLI と SDK で挙動が異なる理由

CLI(bq)と SDK(google-cloud-bigquery)は同じ API を呼びますが、使う認証情報のソースが異なるため、
結果的に quota project や権限チェックの対象が違うことがあります。

CLI では gcloud の設定が自動で quota project に反映され、問題が表面化しにくい一方、
ADC は application_default_credentials.json の設定をそのまま使うため、欠けているとエラーになります。


安定した運用方法

開発環境では CLI のトークンを使う方法(gcloud auth print-access-token)が手軽です。一方、CI/CD や本番環境ではサービスアカウントキーを利用し、環境変数 GOOGLE_APPLICATION_CREDENTIALS で指定することをおすすめします。また、クォータプロジェクトは明示しておくと安心です。


まとめ

今回の 403 Forbidden エラーは「クォータプロジェクトの不整合」が原因でした。ADC では明示設定がないと厳密な権限チェックに引っかかります。

なお、403 エラーには API 未有効化、IAM 権限不足、クォータ超過など他にも複数の原因があるため、実際のトラブルシュートではそれらも合わせて確認してください。


参考資料

GitHubで編集を提案

Discussion