Databricks の Delta Lake ディレクトリを Snowflake にて Secure Data Sharing する方法

## 概要

Databricks の Delata Lake テーブルのディレクトリ、Snowflake の Apache Iceberg テーブル（外部カタログを使用するテーブル）として登録し、別の Snowflake アカウントに共有する手順を紹介します。

本記事は下記記事の一部です。

- [Databricks のデータを Snowflake の Secure Data Sharing 機能でデータ共有する際のベストプラクティス #deltalake - Qiita](https://qiita.com/manabian/items/7469be5112319c4c0080)

## 手順

### Databricks 上でテーブルを作成

まず、データベースとテーブルを作成し、データを挿入します。

```python
%sql
CREATE CATALOG IF NOT EXISTS sf_delta;
```

```python
%sql
CREATE TABLE sf_delta.default.nation_delte (
  n_nationkey  integer
  ,n_name       string
  ,n_regionkey  integer 
  ,n_comment    string
)
TBLPROPERTIES(
  'delta.enableDeletionVectors' = 'false'
);
```

```python
%sql
INSERT OVERWRITE sf_delta.default.nation_delte
SELECT
  n_nationkey
  ,n_name
  ,n_regionkey 
  ,n_comment
FROM
  samples.tpch.nation
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/d1a35aa2-9554-44d5-8801-0539cabb3134.png)

テーブルのデータを取得できることを確認します。

```python
%sql
SELECT * FROM sf_iceberg.default.nation_iceberg;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/cca032ba-41b2-468f-9398-11dcc39c1bdc.png)

### 共有元 Snowflake にテーブル作成

まず、カタログ統合を作成します。

```sql
CREATE OR REPLACE CATALOG INTEGRATION databricks_delta_catalog_integration
  CATALOG_SOURCE = OBJECT_STORE
  TABLE_FORMAT = DELTA
  ENABLED = TRUE;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/b933b2e0-2a97-4a72-97f2-325dfafaded9.png)

次に、Snowflake で外部 Volume を作成します。

```sql
CREATE OR REPLACE EXTERNAL VOLUME {external_volume_name}
STORAGE_LOCATIONS = (
    (
        NAME = '{name}'
        STORAGE_PROVIDER = 'AZURE'
        STORAGE_BASE_URL = 'azure://{azure_storage_account_name}.blob.core.windows.net/{azure_storage_container_name}'
        AZURE_TENANT_ID = '{tenant_id}'
    )
);
```

| プレースホルダー                 | 説明                                                                                             |
| -------------------------------- | ------------------------------------------------------------------------------------------------ |
| `{external_volume_name}`         | Snowflake の外部 Volume 名を指定します。                                                         |
| `{name}`                         | Snowflake の外部 Volume の名前を指定します。                                                     |
| `{azure_storage_account_name}`   | Azure ストレージアカウントの名前を指定します。                                                   |
| `{azure_storage_container_name}` | Azure ストレージアカウントのコンテナ名を指定します。                                             |
| `{tenant_id}`                    | Azure のテナント ID を指定します。                                                               |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/3d0414b3-af34-4da3-8b1f-1ea4538bf1cc.png)


続いて、下記クエリで `AZURE_CONSENT_URL` の値を取得します。

```sql
DESC EXTERNAL VOLUME azure_sf_share_01_exvol;
SELECT 
    PARSE_JSON($4):AZURE_MULTI_TENANT_APP_NAME::string AS AZURE_MULTI_TENANT_APP_NAME,
    PARSE_JSON($4):AZURE_CONSENT_URL::string AS AZURE_CONSENT_URL
FROM TABLE(RESULT_SCAN(LAST_QUERY_ID()))
WHERE $2 = 'STORAGE_LOCATION_1';
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/9323f54f-1850-4b6a-b8fd-40a81455ffcb.png)

Azure にログインしているブラウザで `AZURE_CONSENT_URL` にアクセスし、表示された画面で `承諾` を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/c2e25baf-8add-43b4-932a-c6ad3a4dd8e8.png)

すると、このサービスプリンシパルが Azure テナントに追加されます。そのため、必要に応じてストレージに対する権限（例：`ストレージ BLOB データ共同作成者`）を付与します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/78fe9e82-3de3-4fde-b75c-64449a6ce96d.png)

外部 Volume へのアクセスが問題なく行えるか、下記クエリで確認します。

```sql
SELECT SYSTEM$VERIFY_EXTERNAL_VOLUME('azure_sf_share_01_exvol');
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/7a07d240-bd38-489e-836d-52363dc262b5.png)


次にデータベースを作成します。

```sql
CREATE DATABASE IF NOT EXISTS databricks_delta_share_db;
USE databricks_delta_share_db;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/9bf1307e-d069-422a-9a13-2415d9d3df38.png)

Databricks にてテーブルのディレクトリを取得します。

```sql
%sql
DESC DETAIL sf_delta.default.nation_delte;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/eaaa2e6a-b074-45d5-9099-d914259c4691.png)

Databricks の Delta Lake のディレクトリを、Snowflake 上で Iceberg テーブルとして作成します。`{table_realative_dir}`には、上記結果の`location`列の値から外部 Volumeで指定した値以降のパス（例：`__unitystorage/catalogs/2f7d152e-a1a6-47d2-844e-396d4032f1a5/tables/2eebb9d2-105c-4148-8b38-a563f341adf1`）を取得します。

```sql
CREATE OR REPLACE ICEBERG TABLE nation_delte_from_db_by_delte
  CATALOG = databricks_delta_catalog_integration
  EXTERNAL_VOLUME = azure_sf_share_01_exvol
  BASE_LOCATION = '{table_realative_dir}'
  AUTO_REFRESH = TRUE;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/aff0d639-bdb4-42d8-bc8f-2b846f01fbaf.png)

テーブルにクエリを発行できることを確認します。

```sql
SELECT * FROM nation_delte_from_db_by_delte;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/6f238321-8c55-4d6d-8453-a2507e47f60e.png)


### データ共有先アカウントを作成

はじめに、`ORGADMIN` ロールに切り替えます。

```sql
-- ORGADMIN にカレントロールを変更
USE ROLE ORGADMIN;
SELECT CURRENT_ROLE();
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/4375b4e5-b01c-4bd8-ac02-57987160fa47.png)

データ共有先の Snowflake アカウントを新規で作成します。

```sql
-- データ共有先の Snowflake アカウントを作成
-- ToDo EMAIL と REEGION をかえる必要あり
CREATE ACCOUNT manabian_comsumer_01_account
   ADMIN_NAME = 'comsumer_admin'
   ADMIN_PASSWORD = 'MySecurePassword2025!'
   EMAIL = 'admin@example.com'
   MUST_CHANGE_PASSWORD = FALSE
   EDITION = ENTERPRISE
   REGION = 'AZURE_EASTUS2';
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/883211f2-e689-4faf-87ba-898cb52d0e2b.png)

次に、データ共有先の新アカウントの URL を取得します。

```sql
-- アカウントの URL を取得
SHOW ACCOUNTS LIKE 'manabian_comsumer_01_account';
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/d639b85c-da0f-4d1e-9ba5-c57690b783ec.png)

上記で作成したアカウントにログインします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/9c5191bb-b019-4f80-ac51-7d1e84fe3145.png)

ワークスペースで下記クエリを実行し、`Data Sharing Account Identifier` の値を取得します。必要に応じてクラスタを作成してください。

```sql
SELECT
    CURRENT_ORGANIZATION_NAME() || '.' || CURRENT_ACCOUNT_NAME() AS "Data Sharing Account Identifier";
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/4707d96e-d2cf-4531-a963-ac1705d3147f.png)

### 共有元 Snowflake アカウントにて共有の作成と権限付与

共有を作成します。

```sql
CREATE OR REPLACE SHARE db_delta_uf_share;
```

続いて、共有に対する権限を付与します。

```sql
GRANT USAGE ON DATABASE databricks_delta_share_db TO SHARE db_delta_uf_share;
GRANT USAGE ON SCHEMA databricks_delta_share_db.public TO SHARE db_delta_uf_share;
```

```sql
GRANT SELECT ON ICEBERG TABLE nation_delte_from_db_by_delte TO SHARE db_delta_uf_share;
```


先ほど取得した Data Sharing Account Identifier を使用して、共有先アカウントがこの共有を利用できるよう設定します。

```sql
-- 共有先アカウントで取得した Data Sharing Account Identifier の値をセット
SET data_sharing_account_identifier = 'LAMPVRN.MANABIAN_COMSUMER_01_ACCOUNT';
ALTER SHARE db_delta_uf_share
    ADD ACCOUNTS = $data_sharing_account_identifier;
```

### 共有先 Snowflake アカウントにて共有に基づきデータベースを作成

共有先の Snowflake アカウントで、`ACCOUNTADMIN` ロールに切り替えます。

```sql
USE ROLE ACCOUNTADMIN;
SELECT CURRENT_ROLE();
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/f3455229-a153-493d-8132-972a4f133c20.png)

次に、`NAME` が `DB_DELTA_UF_SHARE` の `owner_account_and_name` を確認します。

```sql
SHOW SHARES;
SELECT
    CONCAT($3, '.', $4) AS owner_account_and_name
    ,$3 AS owner_account
    ,$4 AS name
FROM TABLE(RESULT_SCAN(LAST_QUERY_ID()));
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/37bf3114-5c9c-4258-9eb5-9cec0957ae71.png)


取得した `owner_account_and_name` をもとに、共有されたテーブルを含むデータベースを作成します。

```sql
CREATE OR REPLACE DATABASE SHARE_BY_DB_DELTA_DB
    FROM SHARE LAMPVRN.OFA41652.DB_DELTA_UF_SHARE;
USE DATABASE SHARE_BY_DB_DELTA_DB;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/da56c188-86cb-4a60-8293-117be3ed0e65.png)

テーブルにアクセスできるか確認します。

```sql
SHOW TABLES IN DATABASE SHARE_BY_DB_DELTA_DB;
```

```sql
SELECT * FROM NATION_DELTE_FROM_DB_BY_DELTE;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/7d4d5464-8c43-4f06-b507-fc98c00e0735.png)

### Databricks にて DELETE 実行後の 共有先 Snowflake の反映確認

Databricks 側でテーブルのデータを削除してみます。

```sql
%sql
DELETE FROM sf_delta.default.nation_delte WHERE n_nationkey > 3;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/342e46f4-6bff-4a34-902a-bea547974c04.png)

共有先の Snowflake でデータが反映されているか確認します。

```sql
SELECT count(*) FROM NATION_DELTE_FROM_DB_BY_DELTE;
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/fe6497dd-a4a7-4ab1-ba44-d8eea77d4a6d.png)