我正在尝试在函数应用程序中编写一个函数来操作 CosmosDB 中的数据。如果我将读写键放入环境变量中,我就能正常工作。为了使其更加强大,我希望它能够作为托管身份应用程序运行。该应用程序在 Cosmos DB 上具有“DocumentDB 帐户贡献者”角色。
但是,CosmosClient 构造函数不接受 Credential,并且需要读写密钥。我一直在寻找 azure.mgmt.cosmosdb.operations 的兔子洞,其中有一个带有
DatabaseAccountsOperations
方法的 list_keys()
类。但我找不到访问该功能的简洁方法。如果我尝试创建该对象(这需要从我的 dbmgmt 对象中窃取配置、序列化器和反序列化器),它仍然需要 resourceGroupName
和 accountName
。
我情不自禁地认为我在某个地方走错了方向,因为这必须以更直接的方式实现。特别是考虑到 JavaScript SDK 引用了一个与 SubscriptionClient 一致的更符合逻辑的类 CosmosDBManagementClient。但是,我在 python 端的任何地方都找不到该类。 有什么指点吗?
from azure.identity import DefaultAzureCredential
from azure.cosmos import CosmosClient
from azure.mgmt.resource import SubscriptionClient
from azure.mgmt.cosmosdb import CosmosDB
from .cred_wrapper import CredentialWrapper
def main(req: func.HttpRequest) -> func.HttpResponse:
request_body = req.get_body()
# credential = DefaultAzureCredential()
# https://gist.github.com/lmazuel/cc683d82ea1d7b40208de7c9fc8de59d
credential = CredentialWrapper()
uri = os.environ.get('cosmos-db-uri')
# db = CosmosClient(url=uri, credential=credential) # Doesn't work, wants a credential that is a RW/R key
# Does work if I replace it with my primary / secondary key but the goal is to remove dependence on that.
subscription_client = SubscriptionClient(credential)
subscription = next(subscription_client.subscriptions.list())
dbmgmt = CosmosDB(credential, subscription.subscription_id) # This doesn't accept the DB URI??
operations = list(dbmgmt.operations.list()) # I see the list_keys() Operation there...
一个乐于助人的灵魂在这里提供了回应,但在我做出反应或接受它作为答案之前将其删除。他们指出有一个等效的 python SDK 并且
from azure.mgmt.cosmosdb import CosmosDBManagementClient
可以解决问题。
从那时起,我就只能靠自己了,因为这导致了
ImportError: cannot import name 'CosmosDBManagementClient' from 'azure.mgmt.cosmosdb'
我相信问题的根源在于软件包不兼容
azure-mgmt
。从我的azure-mgmt
中删除requirements.txt
并仅加载cosmos和identiy相关包后,导入错误已解决。
这解决了90%的问题。
dbmgmt = CosmosDBManagementClient(credential, subscription.subscription_id, c_uri)
print(dbmgmt.database_accounts.list_keys())
TypeError: list_keys() missing 2 required positional arguments: 'resource_group_name' and 'account_name'
真的需要收集这些参数吗?与从 Vault 读取秘密的示例相比,它看起来非常复杂。
更新 12/05/2021 - 我来这里寻找 Javascript/Typescript 的解决方案。所以把答案留给其他人。我认为类似的方法也适用于 Python。
您可以使用 RBAC 通过托管身份进行数据平面操作。查找文档很困难。
使用托管身份进行 Cosmos DB 数据平面操作的 RBAC
重要 - 如果您收到错误请求被 Auth mydb 阻止:请求被阻止,因为主体 [xxxxxx-6fad-44e4-98bc-2d423a88b65f] 不具备在 Microsoft.DocumentDB/databaseAccounts/readMetadata 上执行操作所需的 RBAC 权限资源 [/]。 不要使用门户分配角色,请使用 Azure CLI for CosmosDB。 如何 - 使用 Azure CosmosDB CLI 为用户/系统 MSI/用户 MSI 创建角色分配
# Find the role ID:
resourceGroupName='<myResourceGroup>'
accountName='<myCosmosAccount>'
az cosmosdb sql role definition list --account-name $accountName --resource-group $resourceGroupName
# Assign to the MSI or user managed MSI:
readOnlyRoleDefinitionId = '<roleDefinitionId>' # as fetched above
principalId = '<aadPrincipalId>'
az cosmosdb sql role assignment create --account-name $accountName --resource-group $resourceGroupName --scope "/" --principal-id $principalId --role-definition-id $readOnlyRoleDefinitionId
完成这一步后,连接的代码就非常简单了。使用 @azure/identity 包的默认凭据。 这适用于具有托管身份的 Azure Function App 以及具有 VS 代码或 az login
使用 @azure/identity 进行身份验证以获取凭据对象的示例
import { CosmosClient } from "@azure/cosmos";
import { DefaultAzureCredential, ManagedIdentityCredential, ChainedTokenCredential } from "@azure/identity";
const defaultCredentials = new DefaultAzureCredential();
const managedCredentials = new ManagedIdentityCredential();
const aadCredentials = new ChainedTokenCredential(managedCredentials, defaultCredentials);
client = new CosmosClient({
endpoint: "https://mydb.documents.azure.com:443/",
aadCredentials
});