Azure Databricks における Unity Catalog と Spark Config によるAzure Storageへの認証優先度の検証

## 概要

Azure Databricksを使用する際に、Azure Storageへの認証方法として、Unity Catalogの外部ロケーションによる認証とSpark Config（ストレージアカウントキー）による認証のどちらが優先されるかを検証しました。その結果、Unity Catalogの外部ロケーションによる認証が優先されることが確認できました。

具体的な検証方法としては、下記表に示す2つの異なるアカウントを用意し、それぞれで以下の手順を実行しました。Unity Catalogの外部ロケーションを作成したところ、Spark Config（ストレージアカウントキー）による認証が行えず、エラーが発生しました。しかし、Unity Catalogの外部ロケーションを削除すると、再びSpark Config（ストレージアカウントキー）による認証が可能になりました。これらの結果から、Unity Catalogの外部ロケーションによる認証が優先されると結論付けました。

| #    | アカウント         | 概要                                                     |
| ---- | ------------------ | -------------------------------------------------------- |
| 1    | 管理者アカウント   | Unity Catalog 、および、ワークスペースの管理者であるユーザーアカウント |
| 2    | ユーザーアカウント | ワークスペースの管理者であるユーザーアカウント                         |

ドキュメントにて、本記事に関する記載が次のようにあります。下記の記載内容を正確に理解することは困難であり、私も関連するリンクの提示を受けて紐づけて理解することができました。

> Unity Catalog では、ファイルシステム設定のクラスター構成は考慮されません。 つまり、Unity Catalog を使用してデータにアクセスする場合、クラウド オブジェクト ストレージを使用してカスタム動作を構成するための Hadoop ファイルシステム設定は機能しません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/57aaaf69-074e-1305-7b20-a0ae986afeb2.png)

引用元：[DBFS と Unity Catalog のベスト プラクティス - Azure Databricks | Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/databricks/dbfs/unity-catalog#cluster-configurations-are-ignored-by-unity-catalog-filesystem-access)


本仕様による注意すべきケースとしては、Spark Config（ストレージアカウントキー）による認証からUnity Catalogの外部ロケーションによる認証へ切り替えるケースです。すべてのプログラムにて認証方法を切り替える必要があり、Spark Config（ストレージアカウントキー）による認証が残っていた場合にそのプログラムがエラーとなります。

本記事の前提条件として、Azure DatabricksとAzure Storage（階層型名前空間が有効）のリソースが既に構築されており、Azure Storageには`qiita`という名前のコンテナが作成され、その中に`test.txt`というファイルが配置されています。

## 事前準備

### Databricks アクセスコネクターに対してストレージへの `ストレージ BLOB データ共同作成者`権限を付与

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/51c24193-023c-3ccb-80bf-45b2f1d6db71.png)

### 管理者アカウントにて Databricks シークレットに Azure Storege のアクセスキーを登録

Databricks SDK にて WorkspaceClient をインスタンス化します。

```python
%pip install databricks-sdk --upgrade --q
dbutils.library.restartPython()
```

```python
from databricks.sdk import WorkspaceClient

w = WorkspaceClient()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/a787a66f-a154-ec5b-c275-6c74e1911e15.png)

Databricks シークレットのスコープを作成します。

```python
# scope 名をセット
scope_name = 'qiita'

try:
    w.secrets.create_scope(scope=scope_name)
except DatabricksError:
    print(f"Scope `{scope_name}` already exists!")

# Scoep を確認
scopes = w.secrets.list_scopes()
 
for scope in scopes:
    if scope.name == scope_name:
        print(scope.as_dict())
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/3f042e38-2301-5f58-2038-fb3c14bcbe65.png)

Databricks シークレットのシークレットを登録します。


```python
# シークレットの項目名と値をセット
scope_key_and_values = {
    "AccessKey": "",  # Azure Storage のアクセスキーをセット 
}
# シークレットを登録
for key,value in scope_key_and_values.items():
    w.secrets.put_secret(
        scope=scope_name,
        key=key,
        string_value=value,
    )

# シークレットのリストを表示
scrts = w.secrets.list_secrets(scope=scope_name)
print(scrts)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/b7d6d2ae-8636-221f-453b-3ba97a942621.png)

### 管理者アカウントにて Azure Storege に対する接続確認

```python
storage_account_name = "" # Azure Storage アカウント名をセット
access_key = dbutils.secrets.get("qiita", "AccessKey")

spark.conf.set(
    f"fs.azure.account.key.{storage_account_name}.dfs.core.windows.net",
    access_key,
)

dbutils.fs.ls("abfss://qiita@qiitastoragetest001.dfs.core.windows.net/")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/f7c2d114-f90e-dd07-889f-89f4e0e38b00.png)

## 検証コードと実行結果

### 1. ユーザーアカウントにて Azure Storege に対して ストレージアカウントキーにて接続確認

```python
storage_account_name = "qiitastoragetest001" # Azure Storage アカウント名をセット
access_key = dbutils.secrets.get("qiita", "AccessKey")

spark.conf.set(
    f"fs.azure.account.key.{storage_account_name}.dfs.core.windows.net",
    access_key,
)

dbutils.fs.ls("abfss://qiita@qiitastoragetest001.dfs.core.windows.net/")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/2acc39ae-d8ec-1d12-4d85-868378049b7c.png)

### 2. 管理者アカウントにて外部ロケーションを作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/eb04869d-b727-fc19-069f-6140d455e4b3.png)

### 3. ユーザーアカウントにて Azure Storege に対して接続確認

次のようなエラーが発生する。

```python
dbutils.fs.ls("abfss://qiita@qiitastoragetest001.dfs.core.windows.net/")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/730af8a3-68f2-5845-9e1f-56fcb4c5507d.png)

```text
com.databricks.sql.managedcatalog.acl.UnauthorizedAccessException: PERMISSION_DENIED: User does not have READ FILES on External Location 'qiitastoragetest001_qiita'.
```

### 4. 管理者アカウントにてユーザーアカウントに対して外部ロケーションの`READ FILES`権原を付与

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/10690dfb-c2da-289a-2fd7-dfc239df2ec7.png)

### 5. ユーザーアカウントにて Azure Storege に対して接続確認

想定通りの動作となることを確認

```python
dbutils.fs.ls("abfss://qiita@qiitastoragetest001.dfs.core.windows.net/")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/59fa4862-e883-84d6-cba0-856a235eae10.png)

### 6. 管理者アカウントにてユーザーアカウントに対して外部ロケーションを削除

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/5685366c-f668-da99-c246-dfea1f270a2a.png)

### 7. ユーザーアカウントにて Azure Storege に対して接続確認

クラスターをデタッチと再アタッチして、下記のコードを実行すると認証エラーとなることを確認。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/2ead12fe-1ba4-fa01-183d-ffc490d8e90f.png)

```python
dbutils.fs.ls("abfss://qiita@qiitastoragetest001.dfs.core.windows.net/")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/3afa2a2b-cd32-316f-5bf7-0ac59b7e0b3d.png)


```text
Failure to initialize configuration for storage account qiitastoragetest001.dfs.core.windows.net: Invalid configuration value detected for fs.azure.account.keyInvalid configuration value detected for fs.azure.account.key
```

### 8. ユーザーアカウントにて Azure Storege に対して ストレージアカウントキーにて接続確認

Spark Config （ストレージアカウントキー）による認証設定を実施により想定通りの動作となることが確認できました。

```python
storage_account_name = "qiitastoragetest001" # Azure Storage アカウント名をセット
access_key = dbutils.secrets.get("qiita", "AccessKey")

spark.conf.set(
    f"fs.azure.account.key.{storage_account_name}.dfs.core.windows.net",
    access_key,
)

dbutils.fs.ls("abfss://qiita@qiitastoragetest001.dfs.core.windows.net/")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/5226887e-e506-5563-6e80-db1cd8d29e9b.png)


## 結論

本検証を通じて、Azure Databricks 利用時に Azure Storage への認証方法として、Unity Catalog の外部ロケーションによる認証と Spark Config （ストレージアカウントキー）による認証がありますが、Unity Catalog の外部ロケーションによる認証が優先されることが確認できました。Unity Catalog の外部ロケーションを作成すると、Spark Config による認証が実施できず、エラーとなりますが、Unity Catalog の外部ロケーションを削除すると、再度、Spark Config による認証が利用できるようになります。この結果から、Azure Databricks と Azure Storage を連携させる際の認証設定において、Unity Catalog の外部ロケーションによる認証の設定が重要であることが理解できます。