![[小ネタ]dbtのgrantsとSnowflakeのCOPY GRANTS・FUTURE GRANTSの関係を検証してみた](https://images.ctfassets.net/ct0aopd36mqt/wp-refcat-img-a0f70de29a00d596aa50512303131e72/2fcbfee571e6db80cd904c3c64f7cecc/dbt-1200x630-1.jpg?w=3840&fm=webp)
[小ネタ]dbtのgrantsとSnowflakeのCOPY GRANTS・FUTURE GRANTSの関係を検証してみた
かわばたです。
dbtのgrants機能を検証する中で、SnowflakeのCOPY GRANTSやFUTURE GRANTSと組み合わせた場合に権限がどのように変化するのか、いくつかのパターンについて挙動を確認しました。
【dbt grantsを使用してアクセス制御を行ってみた】
対象読者
- dbtのアクセス制御について確認したい方
検証環境と事前準備
検証環境
- dbt CloudのEnterprise版
- SnowflakeトライアルアカウントのEnterprise版
事前準備
dbtのサンプルデータであるjaffle_shopを活用しています。
また、Snowflakeにデータベース・スキーマ・ロールを作成しています。
-- martデータベース作成必要(dbtで加工したマート保存用)
create database KAWABATA_MART_DB;
-- -- スキーマ作成(本番用スキーマ)
create schema KAWABATA_MART_DB.PROD_SCHEMA;
-- ロールの作成
-- 分析者のロール
CREATE ROLE IF NOT EXISTS ANALYST_ROLE;
-- BI使用者のロール
CREATE ROLE IF NOT EXISTS BI_USER;
-- 誰も使用しないロール
CREATE ROLE IF NOT EXISTS unused;
検証内容
- COPY GRANTSと- grantsは併用可能なのか
- SnowflakeのFUTURE GRANTSとgrantsの関係はどうなるのか
実際に試してみた
COPY GRANTS
概要
COPY GRANTSはSnowflakeのCREATE TABLEのオプションです。
CREATE OR REPLACE TABLEなどのクエリが起こった際に、元のテーブルからのアクセス権限を保持することを指定します。
dbtでは下記のようにdbt_project.ymlで定義することで、このオプションを設定することができます。
models:
  +copy_grants: true
【公式ドキュメント】
【Snowflakeドキュメント】
検証
dbtでCOPY GRANTSとgrantsを併用した場合どうなるのか検証してみました。
まずはCOPY GRANTSをコメントアウトし、テーブルレベルの権限をgrantsで付与します。
name: jaffle_shop
version: '1.9.0' # dbt プロジェクトのバージョン
profile: 'default'
seed-paths: ["seeds"]
model-paths: ["models"]
macro-paths: ["macros"]
# clean-targets:
#   - "target"
#   - "dbt_packages"
seeds:
  # Builds seeds into '<your_schema_name>_raw'
  jaffle_shop:
    +schema: raw
models:
  ## copy_grantsの有効化
  # +copy_grants: true
  jaffle_shop:
    # Materialize staging models as views, and marts as tables
    staging:
      +materialized: view
    marts:
      +materialized: table
      ## martsフォルダ内のANALYST_ROLEとBI_USERへ権限を付与
      +grants:
        # target.nameが 'prod' の場合
        delete: "{{ 'BI_USER' if target.name == 'prod' else 'ANALYST_ROLE' }}"
        # target.nameが 'prod' ではない場合 (開発環境の場合)
        insert: "{{ 'ANALYST_ROLE' if target.name != 'prod' else 'unused' }}"
        update: "{{ 'ANALYST_ROLE' if target.name != 'prod' else 'unused' }}"
flags:
  require_generic_test_arguments_property: true
今回は開発環境で行うので、martsフォルダ内のモデルから作成されたテーブルは下記権限が付与されるはずです。
- ANALYST_ROLE
- delete,insert,update
 
実際に確認してみます。
-- ロール確認
SHOW GRANTS ON TABLE kawabata_mart_db.DBT_TKAWABATA.LOCATIONS;

想定通りの権限が付与されていることが確認できました。
続いて、COPY GRANTSをコメントアウトを外して実行しますが、事前にSnowflake側でselect権限を付与します。
-- select権限付与
GRANT SELECT ON TABLE kawabata_mart_db.DBT_TKAWABATA.LOCATIONS TO ROLE ANALYST_ROLE;

無事に権限が付与されたことを確認しました。
name: jaffle_shop
version: '1.9.0' # dbt プロジェクトのバージョン
profile: 'default'
seed-paths: ["seeds"]
model-paths: ["models"]
macro-paths: ["macros"]
# clean-targets:
#   - "target"
#   - "dbt_packages"
seeds:
  # Builds seeds into '<your_schema_name>_raw'
  jaffle_shop:
    +schema: raw
models:
  ## copy_grantsの有効化
  +copy_grants: true
  jaffle_shop:
    # Materialize staging models as views, and marts as tables
    staging:
      +materialized: view
    marts:
      +materialized: table
      ## martsフォルダ内のANALYST_ROLEとBI_USERへ権限を付与
      +grants:
        # target.nameが 'prod' の場合
        delete: "{{ 'BI_USER' if target.name == 'prod' else 'ANALYST_ROLE' }}"
        # target.nameが 'prod' ではない場合 (開発環境の場合)
        insert: "{{ 'ANALYST_ROLE' if target.name != 'prod' else 'unused' }}"
        update: "{{ 'ANALYST_ROLE' if target.name != 'prod' else 'unused' }}"
flags:
  require_generic_test_arguments_property: true
上記のように、COPY GRANTSのコメントアウトを外して実行しました。

上記のとおり、select権限が削除されていることからCOPY GRANTSによって引き継がれた権限が、dbtのgrantsの設定によって上書きされていることが確認できました。
grantsをコメントアウトし、COPY GRANTSが機能しているか確認してみます。
下記クエリを実行し、select権限のみ残しています。
-- DELETE,INSERT,UPDATE権限削除
REVOKE DELETE ON ALL TABLES IN SCHEMA kawabata_mart_db.DBT_TKAWABATA FROM ROLE ANALYST_ROLE;
REVOKE INSERT ON ALL TABLES IN SCHEMA kawabata_mart_db.DBT_TKAWABATA FROM ROLE ANALYST_ROLE;
REVOKE UPDATE ON ALL TABLES IN SCHEMA kawabata_mart_db.DBT_TKAWABATA FROM ROLE ANALYST_ROLE;
-- select権限付与
GRANT SELECT ON TABLE kawabata_mart_db.DBT_TKAWABATA.LOCATIONS TO ROLE ANALYST_ROLE;
name: jaffle_shop
version: '1.9.0' # dbt プロジェクトのバージョン
profile: 'default'
seed-paths: ["seeds"]
model-paths: ["models"]
macro-paths: ["macros"]
# clean-targets:
#   - "target"
#   - "dbt_packages"
seeds:
  # Builds seeds into '<your_schema_name>_raw'
  jaffle_shop:
    +schema: raw
models:
  ## copy_grantsの有効化
  +copy_grants: true
  jaffle_shop:
    # Materialize staging models as views, and marts as tables
    staging:
      +materialized: view
    marts:
      +materialized: table
      ## martsフォルダ内のANALYST_ROLEとBI_USERへ権限を付与
      # +grants:
      #   # target.nameが 'prod' の場合
      #   delete: "{{ 'BI_USER' if target.name == 'prod' else 'ANALYST_ROLE' }}"
      #   # target.nameが 'prod' ではない場合 (開発環境の場合)
      #   insert: "{{ 'ANALYST_ROLE' if target.name != 'prod' else 'unused' }}"
      #   update: "{{ 'ANALYST_ROLE' if target.name != 'prod' else 'unused' }}"
flags:
  require_generic_test_arguments_property: true

上記のとおり、COPY GRANTSが機能してselect権限が残っていることが分かります。
検証したところ、動作の順序としてCOPY GRANTSが先に機能し、その後にgrantsが適用されることが分かりました。
FUTURE GRANTS
概要
SnowflakeにはFUTURE GRANTSのようにスキーマやデータベース内に将来新しく作成されるオブジェクト(テーブル、ビューなど)に対して、あらかじめ権限を自動的に付与しておくことができる機能です。
【公式ドキュメント】
検証
FUTURE GRANTSが適用されている場合、dbtのgrantsとの関係を調べます。
Snowflakeで下記のように設定しselect権限を付与するようにしました。
-- FUTURE
GRANT SELECT ON FUTURE TABLES IN SCHEMA kawabata_mart_db.DBT_TKAWABATA TO ROLE ANALYST_ROLE;
付与されているか確認します。
-- 特定のスキーマのFUTURE GRANTSを確認
SHOW FUTURE GRANTS IN SCHEMA kawabata_mart_db.DBT_TKAWABATA;

権限が付与されていることが分かります。
確認する対象のテーブルは下記のように、OWNERSHIP権限以外付与していない状態です。

dbt_project.ymlは下記のように定義しています。
name: jaffle_shop
version: '1.9.0' # dbt プロジェクトのバージョン
profile: 'default'
seed-paths: ["seeds"]
model-paths: ["models"]
macro-paths: ["macros"]
# clean-targets:
#   - "target"
#   - "dbt_packages"
seeds:
  # Builds seeds into '<your_schema_name>_raw'
  jaffle_shop:
    +schema: raw
models:
  ## copy_grantsの有効化
  # +copy_grants: true
  jaffle_shop:
    # Materialize staging models as views, and marts as tables
    staging:
      +materialized: view
    marts:
      +materialized: table
      # martsフォルダ内のANALYST_ROLEとBI_USERへ権限を付与
      +grants:
        # target.nameが 'prod' の場合
        delete: "{{ 'BI_USER' if target.name == 'prod' else 'ANALYST_ROLE' }}"
        # target.nameが 'prod' ではない場合 (開発環境の場合)
        insert: "{{ 'ANALYST_ROLE' if target.name != 'prod' else 'unused' }}"
        update: "{{ 'ANALYST_ROLE' if target.name != 'prod' else 'unused' }}"
flags:
  require_generic_test_arguments_property: true

上記が結果となります。
dbtのgrantsで設定した権限(DELETE, INSERT, UPDATE)に加え、SnowflakeのFUTURE GRANTSで設定したSELECT権限も付与されていることが確認できました。
この結果から、FUTURE GRANTSとdbtのgrantsは競合せず、それぞれ独立して機能することがわかります。
最後に
今回の検証から、dbtの権限管理機能の使い分けについて下記のようなパターンがあるかと思います。
- 
権限管理をSnowflake側に寄せたい場合: 
 dbtでは+copy_grants: trueを設定し、権限付与はSnowflake側で手動またはFUTURE GRANTSで行う形。
- 
権限管理をdbt側に寄せたい(GitOps化したい)場合: 
 dbtの+grants設定を積極的に活用し、コードとして管理する形。
 ただし、Snowflake側で個別に追加した権限はdbt runのたびにリセットされる可能性があるため注意が必要です。
- 
ハイブリッドで管理する場合: 
 FUTURE GRANTSでベースとなる読み取り権限(例:SELECT)を広範囲に付与し、dbtのgrantsでモデルごとに書き込み権限(例:INSERT)などを個別に追加する形。
この記事が何かの参考になれば幸いです!












