🛠️

GCSの複数バケットを対象としたイベントをトリガーするcloud functionsをterraformで作る

2022/12/06に公開

Cloud FunctinsにはGCS(Google Cloud Storage)の各種イベントを受け取って起動する機能があります。

https://cloud.google.com/functions/docs/calling/storage?hl=ja

特にオブジェクトのファイナライズをトリガーとしたFunctionsなんかは、GCSを使ったパイプラインを構築する上ではよく使います。

従来のGCSイベントでは単一バケットのイベントしか受けとれない

GCSのイベントを利用したFunctionsはとても便利ですが、基本的にFunctionsとバケットが一対一で結びつくような形になるため、複数のバケットを対象にすることができませんでした。

GCSのイベント駆動ではなくHTTPリクエスト起動のFunctionsを作れば可能ではありますが・・・

Cloud Audit Logのエントリ駆動のFunctionsを使う

従来のGCSイベントはPub/Subを介してFunctionsを起動していましたが、Cloud Audit LogのエントリをトリガーとするFunctionsを作成することで、複数のGCSイベントをトリガーにすることができます。

Cloud Audit Logは文字通りGCPの監査ログの仕組みですね。
https://cloud.google.com/logging/docs/audit?hl=ja

GCSのAudit Logを有効にする

GCSのAudit Logはデフォだと無効になってるので有効化しないと使えません。ダッシュボードからだとAudit Logのページから編集できます。

Terraformだと以下のように書けば反映されます。

resource "google_project_iam_audit_config" "this" {
  project = var.project_id
  service = "storage.googleapis.com"
  audit_log_config {
    log_type = "DATA_WRITE"
  }
}

今回はデータ書き込みだけ有効にしてます。

必要な権限を持ったService Accountを作成

こんな感じので必要な権限を持ったService Accountを作成します。

resource "google_service_account" "this" {
  account_id   = var.name
  display_name = var.name
}

resource "google_project_iam_member" "this" {
  for_each = toset([
    "roles/pubsub.publisher",
    "roles/eventarc.eventReceiver",
    "roles/run.invoker",
  ])
  project = var.project_id
  role    = each.value
  member  = "serviceAccount:${google_service_account.this.email}"
}

Audit Logのエントリ通知はEventarc経由となるため、eventarc.eventReceiverが必要です。また、これから作成するCloud Functions(第二世代)は内部的にはCloudRunなので、run.invokerも必要になります。pubsub.publisherが必要だったかどうはイマイチ覚えてないので念のため書いてますが、もしかしたら必要ないかもです。

Cloud Functions(第二世代)を作成

Audit Logをトリガーとするには第二世代のCloud Functionを使う必要があります。ソースコードを配置するGCSバケットは既に作成している前提で進めます。

一応source artifactの部分も載せます。

data "archive_file" "this" {
  type        = "zip"
  source_dir  = var.source_dir # ソースコードのディレクトリ
  output_path = "${var.source_dir}.zip"
}

resource "google_storage_bucket_object" "this" {
  name   = "functions/${var.name}/${data.archive_file.this.output_md5}.zip"
  bucket = var.source_bucket_name # ソースをアップするバケット名
  source = data.archive_file.this.output_path
}

上記のsourceを使ったFunctionsを作成します。

resource "google_cloudfunctions2_function" "this" {
  name     = var.name
  location = var.region

  build_config {
    runtime     = "ランタイムを入れる"
    entry_point = "エントリポイント"
    source {
      storage_source {
        bucket = var.source_bucket_name # ソースをアップするバケット名
        object = google_storage_bucket_object.this.name
      }
    }
  }

  service_config {
    ingress_settings               = "ALLOW_INTERNAL_ONLY"
    all_traffic_on_latest_revision = true
    service_account_email          = google_service_account.this.email
  }

  event_trigger {
    trigger_region        = var.region
    event_type            = "google.cloud.audit.log.v1.written"
    retry_policy          = "RETRY_POLICY_RETRY"
    service_account_email = google_service_account.this.email
    event_filters {
      attribute = "serviceName"
      value     = "storage.googleapis.com"
    }
    event_filters {
      attribute = "methodName"
      value     = "storage.objects.create"
    }
    event_filters {
      attribute = "resourceName"
      value     = var.resource_path_pattern
      operator  = "/projects/_/buckets/hogehoge-*/objects/**"
    }
  }
}

重要なのはevent_triggerの部分ですね。event_typeで、先ほど有効化したAudit LogのGCSイベントログを指定しています。
ちなみにgoogle.cloud.audit.log.v1.writtenには以下のイベントが含まれるっぽいです。

その後、3つのevent_filtersでさらに具体的なトリガー対象を指定しています。
1つ目のevent_filtersはGCSのLogであることを示し、2つ目はstorage.objects.createイベントであることを示します。そして3つ目のevent_filtersで対象とするバケットをパスパターンで指定しています。上記の例だと「バケット名がhogehoge-で始まるバケットの全ファイル」が対象になります。パスパターンに関しては以下のドキュメントに詳しく書いてあります。
https://cloud.google.com/eventarc/docs/path-patterns

最後に

使い方に若干クセがありますが、Audit Logを使うことで複数バケットを対象とするFunctionsのトリガーを作成できます。用途次第では有効そうですね。Audit LogをトリガーとしたFunctionsはGCS以外にも応用できそうなので、他にも色々試したみたいです。

Discussion