Data Factory から CosmosDB へデータをロード with Managed ID

# やりたいこと
ADF (Azure Data Factory) から CosmosDB へ Managed ID を使ってデータをロードする際にちょっと詰まったので、記事にしようと思います。

# 背景
ある案件で、とあるデータソースからデータを CosmosDB へロードするパイプラインを作成した。
当初、CosmosDB への Linked Service では Account Key を使って接続していたが、ADF の Managed ID を使う方針になり、その変更を行った際になかなか接続できなかった。

# どういう事象だったか？
Linked Service の設定で、もろもろ設定した後に「Test connection」を試すと以下のメッセージ (一部伏せている)。

```
Unauthorized to cosmos db endpoint. Please provide a valid credential.
Response status code does not indicate success: Forbidden (403); Substatus: 5301; ActivityId: ****-****-****-****-****; Reason: (Request blocked by Auth ******** : Request is blocked because principal [****-****-****-****-****] does not have required RBAC permissions to perform action [Microsoft.DocumentDB/databaseAccounts/readMetadata] on resource [dbs/****]. Learn more: https://aka.ms/cosmos-native-rbac.
ActivityId: ****-****-****-****-****, Microsoft.Azure.Documents.Common/2.14.0, Microsoft.Azure.Cosmos.Tracing.TraceData.ClientSideRequestStatisticsTraceDatum, Windows/10.0.17763 cosmos-netstandard-sdk/3.19.3);
Activity ID: ****-****-****-****-****-.
```

要は、`Microsoft.DocumentDB/databaseAccounts/readMetadata` のアクションを実行するのに必要な権限が無いとのことらしい。

ひとまずエラーメッセージ内のリンク先を確認してみる。

https://aka.ms/cosmos-native-rbac

以下の CosmosDB 用の組み込みロールを使って、権限の設定を行う必要があるらしい (おそらく一般的な SQL サーバと同様にユーザ毎に権限を設定するイメージ)。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1743460/74bf7ef0-1c00-59c4-a12b-7abb25dacdaa.png)


# 組み込みロールのアサイン
エラーメッセージ的に、メタデータへのアクセスが必要なようなので `Cosmos DB Built-in Data Reader` の権限を ADF の Managed ID に付与する。
上記リンクでは、Azure CLI でロールのアサインを行っているが、Azure リソースは Terraform ですでに管理しているので、Terraformで行う。

https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cosmosdb_sql_role_assignment

上記のドキュメントを参考に以下のように設定した。

```
resource "azurerm_cosmosdb_sql_role_assignment" "adf_write_cosmos" {
  resource_group_name = azurerm_resource_group.examle.name
  account_name        = azurerm_cosmosdb_account.example.name
  principal_id        = azurerm_data_factory.example.identity[0].principal_id
  role_definition_id  = "/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${azurerm_resource_group.examle.name}/providers/Microsoft.DocumentDB/databaseAccounts/${azurerm_cosmosdb_account.sample.name}/sqlRoleDefinitions/00000000-0000-0000-0000-000000000001"
  scope               = azurerm_cosmosdb_account.sample.id
}
```

`terraform apply` して設定を反映させると、CosmosDB への Linked Service が Managed ID を使って接続できるようになった！

# ところが‥？
いざパイプラインを実行してみると、CosmosDB への書き込みでエラーが起きる。

```
Failure happened on 'Sink' side. ErrorCode=CosmosDbSqlApiInvalidCredential,'Type=Microsoft.DataTransfer.Common.Shared.HybridDeliveryException,Message=Unauthorized to cosmos db endpoint. Please provide a valid credential.,Source=Microsoft.DataTransfer.ClientLibrary.CosmosDbSqlApiV3,''Type=Microsoft.Azure.Cosmos.CosmosException,Message=Response status code does not indicate success: Forbidden (403); Substatus: 5301; ActivityId: ****-****-****-****-****; Reason: (Request blocked by Auth cosmos-****-**** : Request is blocked because principal [****-****-****-****-****] does not have required RBAC permissions to perform action [Microsoft.DocumentDB/databaseAccounts/readMetadata] on resource [/]. Learn more: https://aka.ms/cosmos-native-rbac.
```

さっきと同じようなメッセージ。`Microsoft.DocumentDB/databaseAccounts/readMetadata` のアクションを実行するのに必要な権限が無いと言っている…

もう1つの組み込みロール `Cosmos DB Built-in Data Contributor` の方で、許可されているアクションに `containers/*` や `items/*` があるので、そちらならうまくいくのでは？と思い設定し直してみる。Terraform のコードの `role_definition_id` の末尾を 1 → 2 にして再度 apply する。

```
resource "azurerm_cosmosdb_sql_role_assignment" "adf_write_cosmos" {
  resource_group_name = azurerm_resource_group.examle.name
  account_name        = azurerm_cosmosdb_account.example.name
  principal_id        = azurerm_data_factory.example.identity[0].principal_id
  role_definition_id  = "/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${azurerm_resource_group.examle.name}/providers/Microsoft.DocumentDB/databaseAccounts/${azurerm_cosmosdb_account.sample.name}/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002"
  scope               = azurerm_cosmosdb_account.sample.id
}
```

再度パイプラインを実行してみると、、成功！
想定どおりにデータが CosmosDB へ書き込まれている！:tada:

ドキュメントを読み直すと、以下とのことなので書き込みが必要な場合は `Cosmos DB Built-in Data Contributor` のロールが必要ということでした。また、この組み込みロールは CosmosDB 内でのみのロールなようで、通常の RBAC とは異なるとのことなので注意です (IAM から設定はできない)。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1743460/9af7a3db-11f1-943d-72e8-14e86411df98.png)

# ということで
CosmosDB へ Managed ID で接続する際に CosmosDB の組み込みロールをアサインする手順をまとめました。
通常の RBAC とは異なり専用の組み込みロールという点がやや分かりづらく Portal からの設定もできなさそうなので気づきにくい点でした。皆さまご注意ください！


以上です！