【Snowflake】FUTUREオプションを使ったらバルクロードできなくなった話

Snowflakeの便利な機能、[FUTUREオプション](https://docs.snowflake.com/ja/sql-reference/sql/grant-privilege)を使って思わぬ落とし穴にハマったときのことについての話です。
2024年末くらいの話なので時差があります。（最新の情報をご確認ください）

先にいうと単に仕様を見落としていただけなのですが、意外と気づかない人も多いのでは？と思いメモとして残します。
※少しややこしく難しいかもしれません😵

# FUTUREオプション/OWNERSHIP権限とは？
ロールや権限周りの話になりますので、そもそもSnowflakeのロールとは？という方は初めに以下の記事をよんでみてください！
[Snowflake オブジェクトへのアクセス制御](https://qiita.com/Hikaru_O/items/bc8ac8b91566485cc949)
[Snowflake ロール設計](https://qiita.com/Hikaru_O/items/a2853cfdbaa246bab074)

## FUTUREオプション
運用において、オブジェクトが増える度にその権限をロールに付与するのは手間がかかりますよね。
それを適切なロールに自動で付与されるようする設定が[FUTUREオプション](https://docs.snowflake.com/ja/sql-reference/sql/grant-privilege)です。
新規オブジェクトの権限をロールに自動付与することができます。
例えば次のようなコマンドで適応されます。
```
GRANT ALL ON FUTURE TABLES IN SCHEMA SC1 TO ROLE SCHEMAADMIN;
```
SC1スキーマ配下の新規テーブルのALL権限をSCHEMAADMINロールに自動付与するコマンドです。
スキーマ作成後テーブルを作成する度に権限を付与しなくて済むよう、事前にこのFUTUREオプションで設定をします。

メリット
・自動的に付与されるため運用にかかる工数が減る
・人手で権限を付与しないため、誤った付与先を指定することなどを防ぐ

## OWNERSHIP権限
[OWNERSHIP権限(所有権)](https://docs.snowflake.com/ja/user-guide/security-access-control-overview#securable-objects)は、そのオブジェクトに対する包括的な操作が可能であり、オブジェクトを削除できる唯一の権限です。
個々のオブジェクトにつきOWNERSHIP権限が一つのロールに付与されます。
デフォルトではオブジェクトを作成したロールに​OWNERSHIP権限が自動的に付与されます。​
例えば、単純にSYSADMINロールで全てのオブジェクトを作成した場合、それら全てのOWNERSHIP権限はSYSADMINロールにつきます。


## FUTUREオプションとOWNERSHIP権限による制御
上記の二つを活用すると以下のようなことができます。
権限の強いロールでオブジェクトを作成した場合でも、特定の階層下位ロールにOWNERSHIP権限を付与することができます。FUTUREプションを使用したオブジェクトはどのロールで作成してもこれが適用されるため作成時のロール考慮が不要になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635659/6354f91d-021b-46bd-81a6-572e9ea64116.png)

これを使わない手はないですよね！
ということで、これを活用しました！


# やりたかったこと

<details><summary>※登場オブジェクトが多いのでまとめました参考までに</summary>

・データレイク：今回はS3
・[ストレージ統合](https://docs.snowflake.com/ja/sql-reference/sql/create-storage-integration)：外部クラウドストレージ用に生成されたアクセス管理エンティティをストレージの場所（Amazon S3等）とともに格納するSnowflakeオブジェクト
・[外部ステージ](https://docs.snowflake.com/ja/user-guide/data-load-s3-create-stage)：Snowflakeからデータレイク（今回はS3）を参照するための窓のようなもの
・APPADMIN：ETLアプリ用に準備したロール
・SCHEMAADMIN：スキーマ管理者用に準備したロール
・[SYSADMIN](https://docs.snowflake.com/ja/user-guide/security-access-control-overview#system-defined-roles)：Snowflakeのシステム定義ロール
・[ステージのOWNERSHIP](https://docs.snowflake.com/ja/user-guide/security-access-control-privileges#stage-privileges)：ステージに対する包括的な制御を付与します
・[ステージのUSAGE](https://docs.snowflake.com/ja/user-guide/security-access-control-privileges#stage-privileges)：SQLステートメントで外部ステージオブジェクトを使用できるようにしREADとWRITE権限を含みます
・[ストレージ統合のUSAGE](https://docs.snowflake.com/ja/user-guide/security-access-control-privileges#integration-privileges)：統合を使用する他のコマンドを実行するときに統合を参照できるようにします
</details>

**1. ETLアプリ用のロールでデータレイクからSnowflakeへデータを一括ロードする
2. スキーマオブジェクトであるステージのOWNERSHIPがスキーマ管理者のロールに自動付与される**



この2点を実現できるように次のように設定を行いました。

>**1. ETLアプリ用のロールでデータレイクからSnowflakeへデータを一括ロードする**

→バルクロードをするため、外部ステージを作成しデータレイクとの[統合設定](https://docs.snowflake.com/ja/user-guide/data-load-s3-config-storage-integration)をする
　ETLアプリ用のロールに外部ステージのUSAGEを付与する（①にあたる。※バルクロードするための権限は外部ステージのUSAGEのみでOK　[参考](https://docs.snowflake.com/ja/user-guide/data-load-s3-config-storage-integration#step-6-create-an-external-stage)）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635659/e6161798-7843-41bd-9b52-02dac7a814a1.png)


>**2. スキーマオブジェクトである外部ステージのOWNERSHIPがスキーマ管理者に自動付与される**

→SC1スキーマ配下のステージのOWNERSHIPがスキーマ管理者のロールに自動付与されるようFUTUREオプションを利用

```
GRANT OWNERSHIP ON FUTURE STAGES IN SCHEMA SC1 TO ROLE SCHEMAADMIN;
--SCHEMAADMINはスキーマ管理者用に作成したロール
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635659/add5738b-8e07-44e3-b9cc-4cf61eb6adaa.png)

（余談）ロール設計の話になりますが、今後の拡張性を考慮しスキーマ管理者用のロールを新たに準備、スキーマオブジェクト（スキーマ配下のオブジェクト）のOWNERSHIP権限はスキーマ管理者に付与されるのが妥当だという考えからこのように設定しました。[参考記事](https://qiita.com/Hikaru_O/items/a2853cfdbaa246bab074#%E7%AE%A1%E7%90%86%E7%94%A8)

# 起きたこと
その後、新規外部ステージを作成しCOPYコマンドによるバルクロードを実行しようとしたのですが、ETLアプリ用のロールで実行できず以下のエラーが発生しました。
`Error:SQL access control error: Insufficient privileges to operate on integration xxxxx`

公式サイトの手順通り統合も作成できており、十分な権限もついているなのになぜ？と原因がわからなかったのですが、偶然[この簡潔な記事](https://qiita.com/bgcanary/items/9165ac274f1e85d95329#%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%8C%E5%87%BA%E3%82%8B%E7%8A%B6%E6%B3%81)を見つけ救われました。
エラーメッセージ、そしてそのエラーが起きている状況が完全に一致🥺


この記事を見てから[公式サイト](https://docs.snowflake.com/ja/user-guide/data-load-s3-config-storage-integration#step-6-create-an-external-stage)も確認すると以下のように書いてありました。
>ステージの所有者（つまり、ステージに対する OWNERSHIP 権限を持つロール）には、ストレージ統合に対する USAGE 権限が必要です

**ストレージ統合のUSAGE権限とステージのOWNERSHIP権限は同じロールにいないとだめですよ**、ということだと理解しました。
図にするとこんな感じ（OK例）↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635659/fcd26895-63d1-427d-babc-e3486cfadf2a.png)
今回起きたのはこちら（NG例）↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635659/3dfe9ce0-2acc-413e-a4e5-94b093f8577c.png)

FUTUREオプションを使用したことでルールに違反してしまい作動しなくなった、ということだったようです。

外部ステージを作成したロールが、外部ステージとして認識するのに必要なストレージ統合を持つべきなのは納得（1つのロールで「外部ステージの作成～統合の作成」の一連の作業をすることが予想されるため）ですが、
実際に運用しようとなったときに今回のようなfutureオプションを使用した例外も考えられるよな～と少し納得しきれない部分もあります。
~~そもそももっとわかりやすく目立つように書いてくれてもいいのではと思ったり~~


・FUTUREオプションを使用した場合に対象のロール同士が継承関係にあればOK
・FUTUREオプションする場合このルールは無効 etc.
ような仕様になったらいいなと思いました。


ということで今回の対応としては、FUTUREオプション*OWNERSHIP権限の制御を優先し、
以下のようにスキーマ管理者にストレージ統合のUSAGEを持たせました。

ただ、SCHEMAADMINは運用の中でバルクロードを実行しないのにストレージ統合のUSAGEを持つことに個人的に違和感があります（ちょっと無理矢理感&この一手間をいつか忘れそう）。。。
```
GRANT USAGE ON INTEGRATION (ストレージ統合の名前) TO ROLE SCHEMAADMIN;
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635659/6ee31d16-5cdc-4392-a717-c9feef1eccdc.png)

この対応の結果、
・ステージのOWNERSHIPとストレージ統合のUSAGEが同じロール(この場合SCHEMAADMIN)に共存している
→ルールに従っているので、APPADMINでバルクロード可能✅
→**やりたかったこと**を実現できる✅



# まとめ
・FUTUREオプションは運用を効率的にする便利な機能。権限付与する際に組み合わせて使うと効果的。
・バルクロードする際はストレージ統合のUSAGE権限とステージのOWNERSHIP権限は同じロールになるように
・公式の仕様はよく読みましょう（ロールの移動による影響範囲を認識すること）

今回は一次対応としてこのようにしました。
やりたいことを実現できることが一番なので形のいびつさは一旦置いておきます。

ストレージ統合のUSAGEとかステージのUSAGE権限とか名前が似ていてややこしい😵

より良い方法があったら教えてください～