やりたいこと
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
のアクションを実行するのに必要な権限が無いとのことらしい。
ひとまずエラーメッセージ内のリンク先を確認してみる。
以下の CosmosDB 用の組み込みロールを使って、権限の設定を行う必要があるらしい (おそらく一般的な SQL サーバと同様にユーザ毎に権限を設定するイメージ)。
組み込みロールのアサイン
エラーメッセージ的に、メタデータへのアクセスが必要なようなので Cosmos DB Built-in Data Reader
の権限を ADF の Managed ID に付与する。
上記リンクでは、Azure CLI でロールのアサインを行っているが、Azure リソースは Terraform ですでに管理しているので、Terraformで行う。
上記のドキュメントを参考に以下のように設定した。
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 へ書き込まれている!
ドキュメントを読み直すと、以下とのことなので書き込みが必要な場合は Cosmos DB Built-in Data Contributor
のロールが必要ということでした。また、この組み込みロールは CosmosDB 内でのみのロールなようで、通常の RBAC とは異なるとのことなので注意です (IAM から設定はできない)。
ということで
CosmosDB へ Managed ID で接続する際に CosmosDB の組み込みロールをアサインする手順をまとめました。
通常の RBAC とは異なり専用の組み込みロールという点がやや分かりづらく Portal からの設定もできなさそうなので気づきにくい点でした。皆さまご注意ください!
以上です!