【Terraform】GCSバケットをトリガーにするCloud RunをTerraformで作成する時には注意してね、という話

【Terraform】GCSバケットをトリガーにするCloud RunをTerraformで作成する時には注意してね、という話

2025.11.01

データ事業本部の川中子(かわなご)です。

以前、Terraformを使ってGoogle CloudのCloud Storage(GCS)をトリガーとする、
Cloud Run Functionsをデプロイしたときに、権限エラーで困ったことがありました。

まっさらなプロジェクトに上記のような資材を作成する際に発生しやすいエラーなので、
Google Cloudの仕様とともに、私の躓きポイントをご紹介したいと思います。

検証の背景

今回の検証で作成する資材のイメージは以下になります。

1761713092860

  • Terraformで以下の処理をGoogle Cloudに対して実行
    • 各種サービスのAPIを有効化
    • GCSのバケットを作成
    • バケットへのファイル作成をトリガーとしたCloud Run Functionsを作成
  • デプロイ実行時にエラーが発生するかを検証
  • エラー確認後、必要な権限付与のステップを追加して正常動作確認

GCSバケットへのファイル格納をトリガーにCloud Run Functionsを作成する場合、
内部的にはPub/Subのトピックとサブスクリプションが自動で作成されます。

https://cloud.google.com/eventarc/docs/retry-events?hl=ja#how_retries_work

このPub/Subアセットの作成は、GCSのサービスエージェントが実行主体となりますが、
GCSのサービスエージェントはデフォルトでPub/Sub関連の権限を持っていないため、
明示的にroles/pubsub.publisherの権限を付与する必要があります。

https://docs.cloud.google.com/eventarc/standard/docs/run/create-trigger-storage-gcloud?hl=ja

またEventarcのトリガーを作成する場合、Compute Engineのサービスアカウントに対して、
roles/eventarc.eventReceiverの権限を事前に付与しておく必要があります。

なおCompute Engineのサービスアカウントの仕様については、
組織の作成日によって一部異なるため、詳細は以下のドキュメントを参照してください。
私の検証環境では、Compute Engineのサービスアカウントに十分な権限が付与されています。

組織のポリシーの構成によっては、デフォルトのサービス アカウントに、プロジェクトに対する編集者のロールが自動的に付与される場合があります。iam.automaticIamGrantsForDefaultServiceAccounts 組織ポリシー制約を適用して、自動的なロール付与を無効にすることを強くおすすめします。2024 年 5 月 3 日以降に組織を作成した場合、この制約はデフォルトで適用されます。

https://cloud.google.com/compute/docs/access/service-accounts?hl=ja#default_service_account

今回は、権限が不足している状態でどのようなエラーが発生するのか、
そしてどのように解決すればよいのかを、Terraformを使って検証してみました。

検証

今回は2つのパターンで検証していきます。

  • パターン1:GCSサービスエージェントに権限を付与せずに関数を作成 → エラー
  • パターン2:事前に権限を付与してから関数を作成 → 正常動作

パターン1:Terraformのコードについて

パターン1のTerraformコードは以下のような構成になっています。

# 必要なAPI有効化
resource "google_project_service" "storage_api" {
  service = "storage.googleapis.com"
}

resource "google_project_service" "cloudfunctions_api" {
  service = "cloudfunctions.googleapis.com"
}

# ... その他のAPI有効化 ...

# トリガー用バケット作成
resource "google_storage_bucket" "trigger_bucket" {
  name     = var.bucket_name
  location = var.region
  # ...
}

# Cloud Run Functions作成
resource "google_cloudfunctions2_function" "gcs_function" {
  name     = var.function_name
  location = var.region

  build_config {
    runtime     = "python311"
    entry_point = "hello_gcs"
    source {
      storage_source {
        bucket = google_storage_bucket.function_bucket.name
        object = google_storage_bucket_object.function_source.name
      }
    }
  }

  event_trigger {
    event_type     = "google.cloud.storage.object.v1.finalized"
    trigger_region = var.region
    event_filters {
      attribute = "bucket"
      value     = var.bucket_name
    }
  }
}

このコードでは、大まかに以下のようなステップで処理が進みます。
GCSサービスエージェントへの権限付与は含まれていません。

  • 各種サービスのAPIを有効化
  • トリガー用のGCSバケットを作成
  • バケットをトリガーとするCloud Run Functionsを作成

パターン1:実行してみる

Planを実行して問題ないことを確認し、Applyを実行します。
APIやバケットの作成の後に、以下のエラーが発生しました。

Error: Error waiting to create function: Error waiting for Creating function:
Error code 7, message: Creating trigger failed:

The Cloud Storage service account for your bucket is unable to publish to Cloud Pub/Sub topics
in the specified project.

To use GCS CloudEvent triggers, the GCS service account requires the
Pub/Sub Publisher (roles/pubsub.publisher) IAM role in the specified project.
(See https://cloud.google.com/eventarc/docs/run/quickstart-storage#before-you-begin):
permission denied

エラーメッセージを見ると、バケットからPub/Subトピックへのパブリッシュができないこと、
そして roles/pubsub.publisher権限が必要であることが明確に示されています。

パターン2:追加修正部分について

次に、エラーを解決するための処理を追加してみます。

なおGCSのサービスエージェントは、関連サービスを利用するタイミング、
またはエージェント名をリクエストするタイミングで初めて有効化されます。

https://cloud.google.com/storage/docs/projects?hl=ja#service-agents

今回はエージェント名をリクエストする処理を追加することで、
GCSのサービスエージェントを有効化した後に、必要な権限の付与を行います。

GCSサービスエージェントの取得

以下の処理で、GCSサービスエージェントのメールアドレスを取得します。
このタイミングでサービスエージェントが有効化されます。
(私の環境では既にサービスエージェントは有効化されています)

data "google_storage_project_service_account" "gcs_account" {
  project = var.project_id

  depends_on = [
    google_project_service.storage_api
  ]
}

Pub/Sub Publisher権限の付与

サービスエージェントに対して、roles/pubsub.publisher権限を付与します。

resource "google_project_iam_member" "gcs_pubsub_publisher" {
  project = var.project_id
  role    = "roles/pubsub.publisher"
  member  = "serviceAccount:${data.google_storage_project_service_account.gcs_account.email_address}"
}

Cloud Run Functionsの依存関係追加

Cloud Run Functionsの関数作成のdepends_onに権限付与のステップを追加し、
GCSサービスエージェントへの権限が付与された後に、関数が作成されるようにします。

resource "google_cloudfunctions2_function" "gcs_function" {
  name     = var.function_name
  location = var.region
  # ... その他の設定 ...

  depends_on = [
    google_storage_bucket.trigger_bucket,
    google_storage_bucket_object.function_source,
    google_project_iam_member.gcs_pubsub_publisher,  # 追加
    google_project_service.cloudfunctions_api,
    google_project_service.cloudbuild_api,
    google_project_service.run_api,
    google_project_service.eventarc_api,
    google_project_service.pubsub_api
  ]
}

パターン2:実行してみる

パターン1で作成された資材は全て削除したうえで、再度Applyを実行してみます。
以下は一部ステップのログ出力となります。

サービスエージェント取得

サービスエージェントのメールアドレスが取得されています。

data.google_storage_project_service_account.gcs_account: Reading...
data.google_storage_project_service_account.gcs_account: Read complete after 1s
  [id=service-YOUR_PROJECT_NUMBER@gs-project-accounts.iam.gserviceaccount.com]

Pub/Sub Publisher権限付与

取得したサービスエージェントにroles/pubsub.publisherが付与されました。

google_project_iam_member.gcs_pubsub_publisher: Creating...
google_project_iam_member.gcs_pubsub_publisher: Creation complete after 9s
  [id=YOUR_PROJECT_ID/roles/pubsub.publisher/serviceAccount:service-YOUR_PROJECT_NUMBER@gs-project-accounts.iam.gserviceaccount.com]

Cloud Run Functions作成

正常にCloud Run Functions関数の作成まで実行できました。

google_cloudfunctions2_function.gcs_function: Creating...
google_cloudfunctions2_function.gcs_function: Still creating... [10s elapsed]
google_cloudfunctions2_function.gcs_function: Still creating... [20s elapsed]
...
google_cloudfunctions2_function.gcs_function: Creation complete after 1m14s
  [id=projects/YOUR_PROJECT_ID/locations/asia-northeast1/functions/tf-gcs-sa-err-bucket-func-fixed]

GCSのサービスエージェントにroles/pubsub.publisherが付与されることで、
関数の作成までが想定通りに実行されることが確認できました。

まとめ

今回の検証ポイントを簡単にまとめてみます。

  • GCSバケットをトリガーとしたCloud Runを作成する場合は以下が必要
    • GCSのサービスエージェントにroles/pubsub.publisher権限を付与
    • Compute Engineのサービスアカウントにroles/eventarc.eventReceiver権限を付与
      • 2024/05/03以降に作成した組織では基本的に必須
  • GCSのサービスエージェントは、関連サービスの使用またはエージェント名の取得時に有効化される
  • プロジェクト作成直後に、Terraformで上記アセットをデプロイする際には注意が必要

各ポイントはGoogle Cloudの公式ドキュメントにも記載がある内容ですが、
特定の条件下では躓きやすいポイントだと思うので、誰かの参考になれば幸いです。

最後まで記事を閲覧頂きありがとうございました。

採用情報

私の所属するデータ事業本部では、以下の職種で積極採用を行っています。

  • データ分析基盤構築エンジニア
  • プロジェクトマネージャー
  • 機械学習エンジニア
  • データアナリスト

また定期的に開催している会社説明会では、各職種の業務内容やプロジェクト事例などをご紹介しています。
会社説明会の開催情報や現在の募集職種の詳細については、以下の関連ページをご確認ください。

▼ 会社説明会スケジュール
https://careers.classmethod.jp/jobfair/

▼ 募集職種の詳細
https://careers.classmethod.jp/requirements/

▼ 過去の説明会動画(アーカイブ)
https://www.youtube.com/playlist?list=PLL6J8djFsPBV20gfYn9A8vXMA3k3QKKx8

この記事をシェアする

FacebookHatena blogX

関連記事