使用托管标识将 Function App 连接到 CosmosDB

Connect Function App to CosmosDB with Managed Identity

我正在尝试在 Function App 中编写一个函数来操作 CosmosDB 中的数据。如果我在环境变量中删除读写密钥,我就能正常工作。为了让它更健壮,我希望它作为一个托管身份应用程序工作。该应用在 Cosmos DB 上具有 'DocumentDB Account Contributor' 角色。

但是,CosmosClient 构造函数不接受凭据并且需要读写密钥。我一直在寻找 azure.mgmt.cosmosdb.operations 的兔子洞,那里有 DatabaseAccountsOperations class 和 list_keys() 方法。不过,我找不到访问该功能的巧妙方法。如果我尝试创建该对象(这需要从我的 dbmgmt 对象中窃取配置、序列化程序和反序列化程序),它仍然需要 resourceGroupNameaccountName.

我忍不住认为我在某处走错了路,因为这必须以更直接的方式实现。特别是考虑到 JavaScript SDK 引用了与 SubscriptionClient 一致的更合乎逻辑的 class CosmosDBManagementClient。但是,我在 python 一侧的任何地方都找不到 class。 有什么指点吗?

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。从我的 requirements.txt 中删除 azure-mgmt 并仅加载 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'

真的需要收集这些参数中的每一个吗?比起example that reads a secret from a Vault就显得很绕了

对于其他希望使用托管身份访问 CosmosDB 的不幸用户,截至 2021 年 5 月,这似乎还不可能。

来源:Github

上的讨论

2021 年 5 月 12 日更新 - 我来这里是为了寻找解决方案 Javascript/Typescript。所以把答案留给其他人。我认为类似的方法适用于 Python.

您可以使用 RBAC 通过托管身份进行数据平面操作。很难找到文档。

RBAC for Cosmos DB data plane operations with Managed Identities

重要 - 如果您收到错误 Request blocked by Auth mydb :请求被阻止,因为主体 [xxxxxx-6fad-44e4-98bc-2d423a88b65f] 确实没有执行操作所需的 RBAC 权限 Microsoft.DocumentDB/databaseAccounts/readMetadata 对资源 [/]。 不要使用门户分配角色,使用 Azure CLI for CosmosDB。 How to - creating a role assignment for a user/system MSI/user MSI is done using the Azure CosmosDB CLI

# 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.

的笔记本电脑

Docs for @azure/identity sdk

Examples of authentication with @azure/identity to get the credential object

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
            });