BigQuery アクセス権設定まとめ & グループ設計例


:::note info
2021年時点でも、そこそこ参照されているため、BigQuery Advent Calendar 2021 25日目の記事としてアップデートします。
:::

https://qiita.com/advent-calendar/2021/bigquery


BigQuery リソースのアクセス権設定は難しいですが、データ資産の保護と活用のバランスを自由に設計できます。組織に合わせたアクセス権をうまく設定 & 設計して、データ資産を活かしていきたいですね。
本稿では、アクセス権の設定方法と、叩き台になりそうな具体的な設計例について述べます。


# アクセス権の設定方法
BigQuery リソースのアクセス権設定にあたり、覚えておく軸は 3 つです。
具体的な人間やアカウントを示す`プリンシパル`、権限範囲の対象（プロジェクトやデータセット）を示す`対象レイヤ`、具体的な一つ一つの`権限`、これらをおさえておけば、BigQuery の権限設定は安心です。

## プリンシパルは Google アカウントを筆頭に 7 種類
以下のプリンシパルに対して個別に役割を振ることができます。グループやドメインは自由に作成が可能です。

- Google アカウント
- サービス アカウント
- Google グループ
- Google Workspace ドメイン
- Cloud Identity ドメイン
- 認証済みのすべてのユーザー
- すべてのユーザー

すべてのユーザーへの権限付与は原則避けましょう。
この記事を読まなくても権限を理解できるようになったら、テーブルやデータセットの閲覧権限の配布から始めましょう。


## 対象レイヤの設定は 5 種類

用途に合わせて、以下の選択肢から適切な単位を選びましょう。

- 組織、フォルダ、プロジェクト単位
- BigQueryデータセット単位
- BigQueryテーブル、ビュー、関数単位
- BigQueryテーブルの行単位、列単位
- 実行時の条件（時刻など）

BigQueryに限らず、Google Cloudにおける権限は上位階層のものが移譲される点に注意が必要です。例えば、Aさんがプロジェクトのオーナーであった場合、そのプロジェクト配下のデータセットやテーブルを変更する権限を持ちます。また、Bさんがデータセットの読み取り権限を持っている場合、その下のテーブルへの読み取りを制限することは（基本的には）できません。


### 組織、フォルダ、プロジェクト単位

#### [Cloud IAM](https://cloud.google.com/iam/docs/overview)
プリンシパルに対して、組織、フォルダ、プロジェクトレベルの権限を付与します。

#### [VPC Service Controls](https://cloud.google.com/vpc-service-controls/docs/overview)
サービス境界を定義し、内外のデータ交換を制限します。

### データセット単位

#### [データセットへのアクセスの制御](https://cloud.google.com/bigquery/docs/dataset-access-controls)
プリンシパルに対して、データセットレベルの権限を付与します。
Web UIでは、データセットの詳細から「データセットの権限」を開くと、 Cloud IAM と同様のアクセス権限設定を行うことができます。

#### [承認済みデータセット](https://cloud.google.com/bigquery/docs/authorized-datasets)
データセットへの直接の参照権限付与なしに、承認したデータセット内のビューが、承認されたデータセット内のデータにアクセスすることを承認します。
通常のビューへのクエリは、参照するテーブルの権限も必要としますが、その権限を不要とします。

### テーブル、ビュー、関数単位

#### [テーブルへのアクセス制御](https://cloud.google.com/bigquery/docs/table-access-controls)
プリンシパルに対して、テーブルレベルのアクセス権を付与します。
設定は、テーブルの詳細から、テーブルの権限を選択すると Cloud IAM と同じアクセス権限設定を行うことができます。

#### [ビュー](https://cloud.google.com/bigquery/docs/views)
テーブル同様にビューの権限付与することもできますが、ビューを参照する際は、参照するテーブルやビュー、列への権限が必要です。依存するリソースの権限に移譲したい場合はビューも有効な手段になります。

#### [承認済みビュー](https://cloud.google.com/bigquery/docs/authorized-views)
ソースデータへのアクセスを許可することなく、ビューのクエリ結果を特定のユーザーやグループと共有できます。
集約関数を挟んだ結果を共有したいなど、依存するリソースと権限を分離したい場合に有効です。

ソースデータの振る舞いや利用用途によって、スケジュールされたクエリで実体化するなどの他の方法と検討します。

#### [承認済み関数](https://cloud.google.com/bigquery/docs/authorized-functions)
承認済みビューのUDF版です。ソースデータへのアクセスを許可することなく、関数の実行結果をユーザーに共有できます。

### BigQueryテーブルの行単位、列単位
#### [列レベルのセキュリティ](https://cloud.google.com/bigquery/docs/column-level-security)
列単位のアクセス制御ができます。
データカタログのポリシータグを利用し、機密性の高いアクセスを制御できます。

データカタログの「きめ細かい読み取り」権限を必須で要求し、データセットの閲覧権限を持っていても、列へのアクセスを制限することができます。

#### [Cloud KMS 鍵を使用した SQL の列レベルの暗号化](https://cloud.google.com/bigquery/docs/column-key-encrypt)

Cloud KMSとBigQueryのAEAD関数を使用して、列レベルの暗号化を行います。
暗号化した列の復号化にはCloud KMSの鍵へのアクセス権限が必要になるため、アクセス権限を分離できます。

#### [行レベルのセキュリティ](https://cloud.google.com/bigquery/docs/row-level-security-intro)
テーブル内の特定の行へのアクセスを許可できます。
クエリ実行者が担当者に割り当てられている行のみ見せたいなど、同じクエリで別な結果を返したい用途には便利です。

一方で、挙動の再現が難しいので、作り込まないことをお勧めします。

##### `セキュリティ関数`

SESSION_USER 関数を用いると、クエリを実行しているユーザーのメールアドレスを取得でき、ビューや行レベルセキュリティの中で使うことで、ユーザーごとに見せるテーブルを変えることができます。

### 実行時の条件

#### [Cloud IAM Conditions](https://cloud.google.com/iam/docs/conditions-overview)
Cloud IAM Conditions は Cloud IAM のアクセス権を `実行時の状態` によって切り替えることができます。対象テーブル名や実行時間などによって、権限を制限できます。終了が明確な場合の権限剥奪や、定時内のみ実行可能な権限を実現できます。

## 権限は大きく 9 種類
[事前定義された役割と権限](https://cloud.google.com/bigquery/docs/access-control) を参考に説明をつけて BigQuery で使用可能な権限を記載します。

### ジョブ（クエリを含む）
クエリを実行したり、データをコピーしたり、BigQuery のあらゆる実行権限を担います。
逆に言えば、これらの権限を与えなければ、**そのプロジェクトの配下で**一切のジョブを走らせることができなくなります。BigQueryはプロジェクトを跨いでクエリすることができるため、こちらはジョブの権限は純粋に課金され、ログが吐かれるプロジェクトとして権限を配る程度に考えておきましょう。

| 権限 | 説明 |
|:--|:--|
| bigquery.jobs.create | 新しいジョブを作成します。 |
| bigquery.jobs.get | ジョブのデータとメタデータを取得します。 |
| bigquery.jobs.list | すべてのジョブを一覧表示し、あらゆるユーザーが送信したあらゆるジョブのメタデータを取得します。他のユーザーが送信したジョブについては、詳細とメタデータが削除されます。 |
| bigquery.jobs.listAll | すべてのジョブを一覧表示し、あらゆるユーザーが送信したあらゆるジョブのメタデータを取得します。 |
| bigquery.jobs.delete | ジョブのメタデータを削除します。|
| bigquery.jobs.update | ジョブをキャンセルします。ジョブを作成すると、ジョブの作成者に、そのジョブの bigquery.jobs.get 権限および bigquery.jobs.update 権限に相当する権限が自動的に付与されます。 |


### データセット
データセットの CRUD に関する権限です。

| 権限 | 説明 |
|:--|:--|
| bigquery.datasets.create | 新しい空白のデータセットを作成します。 |
| bigquery.datasets.delete | データセットを削除します。 |
| bigquery.datasets.getIamPolicy | データセットの IAM 権限を読み取ります。 |
| bigquery.datasets.setIamPolicy | データセットの IAM 権限を変更します。 |
| bigquery.datasets.get | データセットに関するメタデータを取得します。 |
| bigquery.datasets.update | データセットのメタデータを更新します。 |
| bigquery.datasets.updateTag | データセットのタグを更新します。 |


### テーブル
テーブルの CRUD に関する権限です。

| 権限 | 説明 |
|:--|:--|
| bigquery.tables.create | 新しいテーブルを作成します。 |
| bigquery.tables.createSnapshot | 新しいテーブル スナップショットを作成します。 |
| bigquery.tables.delete | テーブルを削除します。 |
| bigquery.tables.deleteSnapshot | テーブル スナップショットを削除します。|
| bigquery.tables.export | BigQuery からテーブルのデータをエクスポートします。 |
| bigquery.tables.get | テーブルのメタデータを取得します。テーブルのデータを取得するには、bigquery.tables.getData が必要です。 |
| bigquery.tables.getData | テーブルのデータを取得します。この権限は、テーブルのデータを検索するために必要です。テーブルのメタデータを取得するには、bigquery.tables.get が必要です。 |
| bigquery.tables.getIamPolicy | テーブルの IAM ポリシーを読み取ります。 |
| bigquery.tables.list | テーブルとテーブルのメタデータをリスト表示します。 |
| bigquery.tables.restoreSnapshot | テーブル スナップショットを復元します。|
| bigquery.tables.setCategory | テーブル スキーマにポリシータグを設定します。|
| bigquery.tables.setIamPolicy | テーブルの IAM ポリシーを変更します。|
| bigquery.tables.update | テーブルのメタデータを更新します。テーブルのデータを更新するには、bigquery.tables.updateData が必要です。 |
| bigquery.tables.updateData | テーブルのデータを更新します。テーブルのメタデータを更新するには、bigquery.tables.update が必要です。 |
| bigquery.tables.updateTag | テーブルのタグを更新します。|

### 関数とストアド プロシージャ
関数とストアド プロシージャの CRUD に関する権限です。

| 権限 | 説明 |
|:--|:--|
| bigquery.routines.create | 新しいルーティン（関数とストアド プロシージャ）を作成します。 |
| bigquery.routines.list | ルーティンおよびルーティンのメタデータを一覧表示します。 |
| bigquery.routines.delete | ルーティンを削除します。 |
| bigquery.routines.get | ルーティンの定義とメタデータを取得します。 |
| bigquery.routines.update | ルーティンの定義とメタデータを更新します。 |
| bigquery.routines.updateTag | ルーティンのタグを更新します。|

### Model
BigQuery MLのモデルの CRUD に関する権限です。

| 権限 | 説明 |
|:--|:--|
| bigquery.models.create | 新しいモデルを作成します。 |
| bigquery.models.delete | モデルを削除します。 |
| bigquery.models.getData | モデルデータを取得します。モデル メタデータを取得するには、bigquery.models.getMetadata が必要です。 |
| bigquery.models.getMetadata | モデル メタデータを取得します。モデルデータを取得するには、bigquery.models.getData が必要です。 |
| bigquery.models.list | モデルとモデルのメタデータを一覧表示します。 |
| bigquery.models.updateData | モデルデータを更新します。モデル メタデータを更新するには、bigquery.models.updateMetadata が必要です。 |
| bigquery.models.updateMetadata | モデル メタデータを更新します。モデルデータを更新するには、bigquery.models.updateData が必要です。 |
| bigquery.models.export | モデルをエクスポートします。 |


### 行レベルのアクセス ポリシー
行レベルのアクセス ポリシーの CRUD に関する権限です。

| 権限 | 説明 |
|:--|:--|
| bigquery.rowAccessPolicies.create | テーブルに新しい行レベルのアクセス ポリシーを作成します。 |
| bigquery.rowAccessPolicies.delete | テーブルから行レベルのアクセス ポリシーを削除します。 |
| bigquery.rowAccessPolicies.getFilteredData | テーブルから、行レベルアクセス ポリシーの権限付与者リストにあるプリンシパルにのみ表示するデータを取得します。この権限は、行レベルのアクセス ポリシーにのみ付与することをおすすめします。 |
| bigquery.rowAccessPolicies.list | テーブルのすべての行レベルのアクセス ポリシーを表示します。 |
| bigquery.rowAccessPolicies.getIamPolicy | 行アクセス ポリシーの IAM 権限を取得します。 |
| bigquery.rowAccessPolicies.setIamPolicy | 行アクセス ポリシーの IAM 権限を設定します。 |
| bigquery.rowAccessPolicies.update | 行レベルのアクセス ポリシーを再作成します。 |

### BigQuery Data Transfer Service
BigQuery Data Transfer Service の実行に関する権限です。

| 権限 | 説明 |
|:--|:--|
| bigquery.transfers.get | 転送のメタデータを取得します。 |
| bigquery.transfers.update | 転送を作成、更新、削除します。 |


### 保存したクエリ
保存したクエリの CRUD に関する権限です。

| 権限 | 説明 |
|:--|:--|
| bigquery.savedqueries.create | 保存したクエリを作成します。 |
| bigquery.savedqueries.delete | 保存したクエリを削除します。 |
| bigquery.savedqueries.get | 保存したクエリのメタデータを取得します。 |
| bigquery.savedqueries.list | 保存したクエリを一覧表示します。 |
| bigquery.savedqueries.update | 保存したクエリを更新します。 |

### Storage Read API
Storage Read API 経由の読み取りに関する権限です。

| 権限 | 説明 |
|:--|:--|
| bigquery.readsessions.create | BigQuery Storage Read API を使用して新しい読み取りセッションを作成します。|
| bigquery.readsessions.getData | Storage Read API を使用して読み取りセッションからデータを読み取ります。|
| bigquery.readsessions.update | Storage Read API を使用して読み取りセッションを更新します。|

### 外部データソース
外部データソースの接続に関する権限です。

| 権限 | 説明 |
|:--|:--|
| bigquery.connections.create | プロジェクト内で新しい接続を作成します。 |
| bigquery.connections.delete | 接続を削除します。 |
| bigquery.connections.get | 接続のメタデータを取得します。認証情報は除外されます。 |
| bigquery.connections.list | プロジェクト内の接続を一覧表示します。 |
| bigquery.connections.update | 接続とその認証情報を更新します。 |
| bigquery.connections.updateTag | 接続のタグを更新します。 |
| bigquery.connections.use | 接続構成を使用して、リモート データソースに接続します。 |

### Reservations
Reservations（容量コミットメントの予約）に関する権限です。

| 権限 | 説明 |
|:--|:--|
| bigquery.capacityCommitments.create | プロジェクトに容量コミットメントを作成します。 |
| bigquery.capacityCommitments.delete | 容量コミットメントを削除します。 |
| bigquery.capacityCommitments.get | 容量コミットメントに関する詳細を取得します。 |
| bigquery.capacityCommitments.list | プロジェクト内の容量コミットメントすべてを一覧表示します。 |
| bigquery.capacityCommitments.update | プロジェクト内の容量コミットメントすべてを更新します。 |
| bigquery.reservations.create | プロジェクトに予約を作成します。 |
| bigquery.reservations.delete | 予約を削除します。 |
| bigquery.reservations.get | 予約の詳細を取得します。 |
| bigquery.reservations.list | プロジェクト内の全予約を一覧表示します。 |
| bigquery.reservations.update | 予約のプロパティを更新します。 |
| bigquery.reservationAssignments.create | 予約割り当てを作成します。所有者プロジェクトと割り当て先リソースには、この権限が必要です。予約割り当てを移動するには、新しい所有者プロジェクトと割り当て先リソースに bigquery.reservationAssignments.create が必要です。 |
| bigquery.reservationAssignments.delete | 予約割り当てを削除します。この権限は、所有者プロジェクトと割り当て先リソースに必要です。予約の割り当てを移動するには、古い所有者プロジェクトと割り当て先リソースに bigquery.reservationAssignments.delete が必要です。 |
| bigquery.reservationAssignments.list | プロジェクト内の予約割り当てすべてを一覧表示します。 |
| bigquery.reservationAssignments.search | 特定のプロジェクト、フォルダ、または組織の予約割り当てを検索します。 |

### BI Engine
BI Engine の予約に関する権限です。

| 権限 | 説明 |
|:--|:--|
| bigquery.bireservations.get | BI Engine の予約を読み取ります。 |
| bigquery.bireservations.update | BI Engine の予約を更新します。 |

### ポリシータグのきめ細かい読み取り

ポリシータグのきめ細かい読み取りの権限です。データカタログの権限であり、BigQueryのものではないですが、BigQueryの列レベルセキュリティを利用する際に必要になる権限です。

| 権限 | 説明 |
|:--|:--|
| datacatalog.categories.fineGrainedGet | ポリシータグによって制限された BigQuery 列のコンテンツにアクセスします |


# アクセス権の設計方法
ここまでにあげた、`プリンシパル`、`対象レイヤ`、`権限`をおさえておけば、BigQuery の権限設定は問題なくできるでしょう。その上で、組織のアクセス権設計はどうするべきでしょうか。ここでは、最小考慮事項として、リスクアセスメントの紹介と権限グループの例を示すに留めます。

## リスクアセスメントをしよう
ISO/IEC 27001, ISO/IEC 27002, ISO/IEC 27017、ISO/IEC 27018, JIS Q 15001, GDPR を確認して、組織ごとのリスクアセスメントを行いましょう。
やることは単純で、リスク分析と評価、コントロールです。個人情報は白黒決めにくい領域であることに加え、BigQuery ML のような機械学習の出力結果（個人情報でなくても）の倫理も問われることになる時代です。小さな母集団にフィットして出力自体が個人情報となる場合や、個人情報のうち性別以外全てをマスクして企業の採用可否を決めるような機械学習モデルを作ると偏見になる場合、多種多様なリスクを孕んでいます。

BigQuery の一部の情報が漏れたらどうなるか、全部が持ち出されたらどうなるか、モデルの中間出力は偏見を生まないか、リスクを思いつく限り洗い出し、リスクが回避可能か、予防低減可能か検討しましょう。リスクに関して思考停止せず、機密度合いに応じて権限を小さくできないか検討し続ける環境づくりができると良いです。

## グループを活用しよう
人にアクセス権が割り当てられている状態は、ライフサイクルが全く異なる物を同一管理しているため、健全な組織とは言い難いです。グループに権限を与え、ライフサイクルを切り離しましょう。グループにすることで、スケールアウトに強くなります。

1 つのグループに属すのではなく、複数のグループに跨って属するような運用を認めていただけると、エンジニア的に動きやすいです。プロジェクト A の管理者でありながら、プロジェクト B, C の分析担当者であったりといった縦横の組織構造がある場合に特に有効です。それ以外の場合でも、組織構造にそってグループを作って権限を与えると、権限管理者も例外処理が少なくてすみます。

### プロジェクト横断の階層構造の例
BigQuery の標準の役職ですが、組織の階層構造にそのまま当てはめることができます。管理者から順々に権限が減っていくので、Cloud IAM を用いて組織レベルやプロジェクト単位で設定するのがおすすめのレイヤになります。

| 役職 | 説明 |
|:--|:--|
| BigQuery 管理者 | プロジェクト内のすべてのリソースを管理する権限を提供します。プロジェクト内のすべてのデータを管理でき、プロジェクト内で実行されている他のユーザーのジョブもキャンセルできます。 |
| BigQuery データ編集者 | データセットのメタデータを読み取り、データセット内のテーブルを一覧表示する。データセットのテーブルを作成、更新、取得、削除する。プロジェクトまたは組織レベルで適用した場合、この役割は新しいデータセットを作成することもできます。 |
| BigQuery データオーナー | データセットを読み取り、更新、削除する。 データセットのテーブルを作成、更新、取得、削除する。プロジェクトまたは組織レベルで適用した場合、この役割は新しいデータセットを作成することもできます。 |
| BigQuery ユーザー | プロジェクト内でジョブ（クエリを含む）を実行する権限を付与します。ユーザーの役割は、自分が所有するジョブの列挙、自分が所有するジョブのキャンセル、プロジェクト内のデータセットの列挙ができます。また、プロジェクト内に新しいデータセットを作成することもできます。作成者には新しいデータセットに対する bigquery.dataOwner 役割が付与されます。 |
| BigQuery データ閲覧者 | データセットのメタデータを読み取り、データセット内のテーブルを一覧表示する。データセットのテーブルからデータとメタデータを読み取る。プロジェクトまたは組織レベルで適用した場合、この役割はプロジェクト内のすべてのデータセットを列挙することもできます。ただし、ジョブを実行するためには追加の役割が必要です。 |

### データセット横断の案件別アサイン構造の例
案件など組織の縦割りに当てはまるような構造です。管理者がいるわけではなく、個別のデータに対して、作成レベルと参照レベルの人がいるような構造です。データセットレイヤの権限設定のため 共有データセットを用いて設定するのがおすすめです。

| 担当案件 | 説明 |
|:--|:--|
| お客様 A の担当者 | プロジェクト A 内のデータセット、テーブルの一覧、取得の権限を持ちます。 |
| お客様 A の受注案件 X の担当者 | プロジェクト A のデータセット X 内のテーブルの作成、更新、削除の権限を持ちます。 |
| お客様 A の受注案件 Y の担当者 | プロジェクト A のデータセット Y 内のテーブルの作成、更新、削除の権限を持ちます。 |


### 利用者ごとに振る舞いを変える、データマスキングの例
役割横割りチームにフィットする構造です。データを触れる人間を少なくしたい時に使うイメージです。テーブルレイヤの権限設定のため 承認済みビューを用いて設定します。

| チーム | 説明 |
|:--|:--|
| プライバシ検証チーム | 組織内のテーブルの一覧、作成、更新、取得、削除の権限を持ちます。 |
| EL チーム | プライバシ検証、保護作業後の承認済みビューの一覧、取得の権限を持ちます。 |
| 分析チーム | EL 時の作業列を落とした、承認済みビューの一覧、取得の権限を持ちます。 |


### アクセス状態によって権限を変える、権限管理の例
時限式権限やリソース名などで実行時制約をかけたい時に使います。実行時レイヤの権限設定のため Cloud IAM Conditions を使って設定します。

| アカウント | 説明 |
|:--|:--|
| 社外監査アカウント | BigQuery データ閲覧者の権限を 1 ヶ月間、9 時から 17 時の間だけ有効にします。 |
| 社外連携アカウント | 案件の検証期間中、Storage の所定のディレクトリに BigQuery エクスポートジョブを投げる権限を持たせます。 |

### BigQuery アクセス状況に基づく権限管理の始め方
[JOBS_BY_* ビュー](https://qiita.com/na0/items/479307cc75b932c2731f)やCloud Audit Logsの集約シンクを用いることで、誰がいつ、どのテーブルを触ったのかがわかります。対応するリソースのみを運用している場合はBigQuery Data Lineageリポジトリの仕組みをデプロイして、Data Lineage構築も悪くないでしょう。

https://github.com/GoogleCloudPlatform/bigquery-data-lineage

誰がどのテーブルにクエリを実行しているか眺め、Cloud IAM と見比べて過剰な権限が付与されていないか、定常的に利用されているか確認しましょう。この際にも、ユーザーやサービスアカウントをグループに対応付けることで、個人よりもチームの目線でアクセス権限の整理ができます。

# まとめ
組織構造に合わせてグループを作成し、グループに対してアクセス権を振りましょう。このアクセス権は、対象レイヤに応じて 4 種類の設定の仕方があります。ぜひ、みんなでデータ利活用を進め、幸せになれる世界を作っていきましょう。