GCSに配置したCSVファイルなどをBigQueryの外部表として設定するときに、列レベルセキュリティを使って列単位で閲覧制限をかけたい場合の設定方法を説明します。
GCS外部表の2つの形式
具体的な設定を説明する前に、GCSに配置されたオブジェクトをBigQueryの外部表として参照するには2つの方法があることを説明します。
1つ目がCloud Storage external tableで、2つめがCloud Storage BigLake tableです。
この2つはできることがほとんど同じなため、違いを意識せずに使っている人も多いかと思います。
実際に自分も1年前くらい前までは違いがわかりませんでした。
しかし、列レベルセキュリティを使う場合はこれらの違いを理解することが重要です。
前者のCloud Storage external tableは昔からある方法です。
BigQueryでSELECT文を実行したとき、実行者自信がGCSのオブジェクトを読み出す権限を持っている必要があります。
そのため、ファイル単位でしか権限管理ができず、ファイル内の特定の列だけを読み出す権限を与えるということはできません。
これはGCSの権限の最小単位がファイルである以上しかたないです。
一方で後者のCloud Storage BigLake tableはBigQueryからGCSを読み出すためのリソースであるConnectionというものを作成し、このConnectionに紐づくService AccountにGCSの読み出し権限を与えます。
そのため、SELECT文の実行者にはGCSの権限を一切付与しなくても良いです。
権限管理はBigQueryテーブルでのみ行うために、こちらの方法では列単位で権限を与えることもできます。
Terraform
先程の議論を踏まえて、terraformでCloud Storage BigLake tableを作成していきます。
まずはConnectionを作成します。
resource "google_bigquery_connection" "gcs-resource" {
connection_id = "gcs_resource"
location = "US"
description = "Connection for GCS Resource"
cloud_resource {}
}
そして、ConnectionのServiceAccountがGCSからデータを読み出せるような権限を付与します。
cloud_resource
が配列になっているのがやや気持ち悪いですが、そういうものだと思いながら [0]
します。
resource "google_storage_bucket_iam_member" "static-data-table-connection-grant" {
bucket = "バケット名"
role = "roles/storage.objectViewer"
member = format("serviceAccount:%s", google_bigquery_connection.gcs-resource.cloud_resource[0].service_account_id)
}
最後に以下のようにスキーマを記述したJSONファイルを指定してテーブルを作成します。
どの列に列レベルセキュリティをいれるかはJSONファイル中で指定します。
複数個のPolicyTagを指定できそうな雰囲気をしていますが、2つ以上指定するとエラーになってしまいます。
resource "google_bigquery_table" "static-data-table" {
project = "プロジェクトID"
dataset_id = "データセット名"
table_id = "テーブル名"
schema = jsonencode(file("スキーマを記述したJSONファイルのパス"))
deletion_protection = false
external_data_configuration {
# このあたりのオプションは各自の環境で変える
source_format = "CSV"
compression = "NONE"
autodetect = false
connection_id = google_bigquery_connection.gcs-resource.id
source_uris = [
"読み込みたいファイルのパス"
]
}
# Ignore connection_id because the difference will appear even though the connection_id has not changed.
# https://github.com/hashicorp/terraform-provider-google/issues/12386
lifecycle {
ignore_changes = [external_data_configuration[0].connection_id]
}
}
[
{
"name": "col1",
"type": "STRING",
"mode": "NULLABLE"
},
{
"name": "col2",
"type": "INTEGER",
"mode": "NULLABLE",
"policyTags": { "names": ["ポリシータグ"] }
}
]