Google Cloud Storage の blob を読み取るとき、バケットの権限が過剰になりがちです。
たとえば Google Cloud の公式ドキュメントのコードを抜粋して説明します。 https://cloud.google.com/storage/docs/downloading-objects?hl=ja#storage-download-object-python
不要なコメントを削除し、説明用のコメントを追加しています。
このコードは指定の Cloud Storage に置かれたオブジェクトをダウンロードするコードです。
from google.cloud import storage
def download_blob(bucket_name, source_blob_name, destination_file_name):
"""Downloads a blob from the bucket."""
bucket_name = "your-bucket-name"
storage_client = storage.Client()
bucket = storage_client.bucket(bucket_name) # ※1
blob = bucket.blob(source_blob_name)
blob.download_to_filename(destination_file_name)
print(
"Downloaded storage object {} from bucket {} to local file {}.".format(
source_blob_name, bucket_name, destination_file_name
)
)
このコードは、実行時に storage.objects.get だけでなく storage.buckets.get の権限を必要とします。試しにStorage オブジェクト閲覧者(roles/storage.objectViewer)のロールに所属した状態で実行すると、次のような storage.buckets.get を要求するメッセージに遭遇します。
does not have storage.buckets.get access to the Google Cloud Storage Bucket
ちなみに storage.buckets.get の権限は公式ドキュメントにて 次のように説明されています。
バケットのメタデータ(IAM ポリシーを除く)を読み取る。バケットの Pub/Sub 通知構成の一覧表示または読み取る。
今回の用途では不要な権限です。ストレージのレガシーバケット(roles/storage.legacyBucketOwner)のロールを付与すれば、エラーはでなくなりますが過剰な権限を付与することになります。
サンプルコードの問題
サンプルコードの書き方では、必ず storage.buckets.get が必要です。
しかし、コードを書き換えることでStorage オブジェクト閲覧者のロール、ひいては storage.objects.get の権限のみでオブジェクトにアクセス可能となります。
問題の箇所は※1です。 bucket メソッドで storage.buckets.get が必要なリクエストが飛んでしまいます。
次のように Bucket インスタンスを作成すればバケットへのアクセスリクエストが飛びません。
# from google.cloud.storage import Blob, Bucket
blob = Blob(source_blob_name, Bucket(storage_client, name=bucket_name))
blob.download_to_filename(destination_file_name)
書き換えたコード
全体像は、このようになります。
from google.cloud import storage
from google.cloud.storage import Blob, Bucket
def download_blob(bucket_name, source_blob_name, destination_file_name):
"""Downloads a blob from the bucket."""
bucket_name = "your-bucket-name"
storage_client = storage.Client()
blob = Blob(source_blob_name, Bucket(storage_client, name=bucket_name))
blob.download_to_filename(destination_file_name)
print(
"Downloaded storage object {} from bucket {} to local file {}.".format(
source_blob_name, bucket_name, destination_file_name
)
)