🚀

BigQuery で特定アカウントが特定データセットにアクセスするのを拒否するタグを利用した設定をTerraformで表現してみる

2025/02/21に公開

はじめに

前提

BigQuery のデータセットを、 Terraform の google_bigquery_dataset_iam_bindinggoogle_bigquery_dataset_iam_member もしくは google_bigquery_dataset のなどで、データセット単位での個別のアクセス管理をしていない状態。

やりたかったこと

上記前提があり google_project_iam_member で、 roles/bigquery.dataViewer などプロジェクト全体へのアクセス許可しているケースで、そのユーザが特定データセットにアクセスさせたくない場合、どうしたらよいか?

王道メソッド(今回見送り)

  • 他のプロジェクトでは、google_bigquery_dataset_iam_binding もしくは、google_bigquery_dataset でのデータセット単位での個別のアクセス管理を導入していたので、それを今回のプロジェクトでも導入する。
    • 対象プロジェクトが大きく、また検証も困難事情があり、絶対無理ではないが作業コストが大きそうなため、この案は一旦見送った。

今回採用したメソッド

  • 該当アカウントのアクセスを拒否したいデータセットに、「タグ」を付与する。
  • その特定「タグ」が付与されているデータセットに対しては、該当アカウントのアクセスをiam condition で拒否する。

Terraform 設定サンプル


local "project_id" {
  description = ""
  type        = string
  default     = "foobar-project"
}

resource "google_service_account" "test_user" {
  project      = local.project_id
  account_id   = "test-user"
  display_name = "Service Account"
}

local "test_user_roles" {
  type        = list(string)
  description = "test_user roles"
  default = [
    "roles/bigquery.jobUser",
    "roles/bigquery.readSessionUser",
  ]
}

resource "google_project_iam_member" "test_user" {
  project = local.project_id
  count   = length(local.test_user_roles)
  role    = element(local.test_user_roles, count.index)
  member  = "serviceAccount:${google_service_account.test_user.email}"
}

local "access_denied_target_dataset" {
  type        = list(string)
  description = "Access denied target dataset"
  default = [
    "a_dataset",
    "b_dataset"
  ]
}

resource "google_bigquery_dataset" "datasets" {
  for_each   = toset(local.access_denied_dataset)
  dataset_id = each.key
  location   = "asia-northeast1"
}

resource "google_tags_tag_key" "target_tag_key" {
  short_name = "access"
  parent     = "projects/${local.project_id}"
}

resource "google_tags_tag_value" "target_tag_value" {
  short_name = "target_restricted"
  parent     = google_tags_tag_key.target_tag_key.id
}

resource "google_tags_location_tag_binding" "tag_bindings" {
  for_each = toset(local.access_denied_target_dataset)

  parent    = "//bigquery.googleapis.com/projects/${local.project_id}/datasets/${each.value}"
  tag_value = google_tags_tag_value.target_tag_value.id
  location  = "asia-northeast1"
}

resource "google_project_iam_member" "deny_service_account_access" {
  for_each = toset(local.access_denied_target_dataset)

  project = local.project_id
  role    = "roles/bigquery.dataViewer"
  member  = "serviceAccount:${google_service_account.test_user.email}"

  condition {
    title       = "deny access"
    description = "deny access"
    expression  = "!resource.matchTag('${local.project_id}/access', 'target_restricted')"
  }
}

設定の解説

  • サービスアカウント test-user を作成
  • BigQuery のジョブ実行 (jobUser) などの IAM ロールを付与(dataViewer は削除)
  • a_dataset, b_dataset に target_restricted タグを付与
  • タグ付きデータセットには test-user の dataViewer 権限を拒否

ポイントは以下の部分で、google_project_iam_member の condition.expression を使い、特定タグにマッチした場合はアクセス拒否されないが、マッチした場合、アクセス拒否される。

resource "google_project_iam_member" "deny_service_account_access" {
  for_each = toset(local.access_denied_target_dataset)

  project = local.project_id
  role    = "roles/bigquery.dataViewer"
  member  = "serviceAccount:${google_service_account.test_user.email}"

  condition {
    title       = "deny access"
    description = "deny access"
    expression  = "!resource.matchTag('${local.project_id}/access', 'target_restricted')"
  }
}

所感

  • タグを利用した、BigQueryのアクセス制御は初めてやってみたが、柔軟性があるなと感じた。
    • とはいえ、基本的には、データセット単位でのメンバーを定義したアクセス制御を導入するのが望ましいとは思うが...。
  • ネットに転がっているタグを使った例が、何故か組織レベルのものが多かったが、別にプロジェクトレベルのタグでも問題ない。

参考になったもの

Discussion