Databricksからサービスプリンシパル認証でAzure Synapse Analytics(旧 SQL Data Warehouse) Polybaseロードを実装する

#はじめに
マルチプロトコルアクセスによりPolybaseがAzure Data Lake Storage Gen2で実施可能になったのですが、ドキュメントのサンプルコードだとストレージキー認証での例しかなかったので、サービスプリンシパルによる認証の方法を残します。
以下、Azure Synapse AnalyticsはDWと記載します。（Azure Synapse Analyticsってなんて略せばいいんだ。。）
#仕組み
[ドキュメント](https://docs.microsoft.com/ja-jp/azure/databricks/data/data-sources/azure/sql-data-warehouse)から転載

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281819/d6af1f84-e84b-51c9-8495-a84e6e9da644.png)
※めっちゃAAずれるのでスクショ
Managed Service IDとOAuth 2.0を使います。
上述通り、ドキュメントのサンプルコードだとStorage Account アクセスキーの例しかありません。

#手順
##サービスプリンシパルを準備～Storage AccountのBLOBデータ共同作成者ロールに割り当てる
マウントと同じですね。ドキュメントにもまとまっているので、割愛します。
##Create Master Keyを実行する
DW上で実行してください

```sql:sql
CREATE MASTER KEY
```

##SQL ServerをAADに登録して、Managed IDを発行する
コード

```powershell:pss
Connect-AzAccount

Select-AzSubscription -SubscriptionId <subscriptionId>

Set-AzSqlServer -ResourceGroupName <your-database-server-resourceGroup> -ServerName <your-SQL-servername> -AssignIdentity

```


##SQL ServerのManaged IDをStorage AccountのBLOBデータ共同作成者ロールに割り当てる
上記のコード発行で SQL Serverのリソース名でManaged IDが選択できるようになります。することはサービスプリンシパルと同様です。割愛。

##Databricksのコード
pythonで書きます。Scalaもほぼ一緒です。

まず、PolybaseにはURIでアクセスできるStorageが必要です。spark.confで認証設定を作成しましょう
ドキュメントだと、fs.azure.account.auth.type.acctname.とか書いてあってはまりました。
accctnameは変更する箇所なんです。

```python:Python

filesystem = <FileSystem名>
storageaccount = <ストレージアカウント名
tenantID = <テナントID>

app_id = <サービスプリンシパルのアプリケーションID>
app_secretkey = <サービスプリンシパルのキー>

spark.conf.set("fs.azure.account.auth.type."+storageaccount+".dfs.core.windows.net", "OAuth")
spark.conf.set("fs.azure.account.oauth.provider.type."+storageaccount+".dfs.core.windows.net", "org.apache.hadoop.fs.azurebfs.oauth2.ClientCredsTokenProvider")
spark.conf.set("fs.azure.account.oauth2.client.id."+storageaccount+".dfs.core.windows.net", app_id)
spark.conf.set("fs.azure.account.oauth2.client.secret."+storageaccount+".dfs.core.windows.net",app_secretkey )
spark.conf.set("fs.azure.account.oauth2.client.endpoint."+storageaccount+".dfs.core.windows.net", "https://login.microsoftonline.com/"+ tenantID +"/oauth2/token")
```

次にSQL Serverのjdbc接続文字列を生成します。
ここもほんとはサービスプリンシパルの認証でいきたいですが、わかりませんでした。scalaなら探せばありそうかも？
(※サポートの回答では現時点ではサポートされていませんでした 2020/01時点)

```python:Python
servername = <SQL Serverのリソース名>
databasename = <SQL DWのリソース名>
password = <SQL Server認証のパスワード>
username = <SQL Server認証のユーザID>

sql_dw_connection_string = "jdbc:sqlserver://{}.database.windows.net:1433;database={};user={}@{};password={};encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30;".format(servername, databasename, username,servername, password)

```

dataframeを作成後、DWにPolybaseロードします。（dataframe作成は割愛。）

```python:Python
target_table_name = <テーブル名>
temp_dir_url = "abfss://"+filesystem+"@"+storageaccount+".dfs.core.windows.net/" #Polybase用領域のURI

df.write \
  .format("com.databricks.spark.sqldw") \
  .option("url", sql_dw_connection_string) \
  .option("useAzureMSI", "true") \
  .option("dbtable", target_table_name) \
  .option("tempdir", temp_dir_url) \
  .mode("overwrite") \
  .save()
```

ポイントは
> .option("useAzureMSI", "true")

ここです。これによりSQL DW -> ADLSの部分がManaged IDで認証されます。
ドキュメントでは

>.option("forwardSparkAzureStorageCredentials", "true") 

となっており、これは資格情報を自動的に検出するらしく、DW側で作ったスコープをURIから検出して実行されるように感じます。このオプションが有効だとManaged IDに権限あててもURIにアクセスできません。

#おわりに

Azure Synapse Analyticsの登場でDatabricksがどうなるかわかりませんが、SparkクラスタによるDWへのロードはなかなか威力があり、およそ1億レコードのロードも30分ほどで完了しました。

サービスプリンシパルによるjdbcの認証はわかり次第アップします～

#備考
SQLDW の　MSI認証によるpolybase