跨不同 Microsoft 租户搜索用户信息

Search User Information Across different Microsoft Tenants

我希望能够跨多个租户搜索用户,因此我的想法是创建一个在 HTTP 触发的 Azure 函数上运行的 python 脚本。此 python 脚本可以通过服务主体为不同的租户向 Microsoft Graph API 进行身份验证,然后搜索用户和 return 数据。这是个好主意还是有更好的方法?

让我们讨论成就。

我发现一个multi-tenant azure ad 应用程序足以通过图表api 查询不同租户中的用户。例如,有 2 个租户,我在 azure ad app 注册中创建了一个多租户应用程序,之后我生成了客户端密码并添加了 api User.Read.All.

权限

现在我在 'tenant_a' 中有一个应用程序,其客户端 ID 和密码。接下来,在浏览器中访问https://login.microsoftonline.com/{tenant_b}/adminconsent?client_id={client-id},在tenant_b中使用admin账号登录后,会出现'permission'window表示同意应用程序在[=]中有权限36=],同意后,您在tenant_a中创建的应用将出现在tenant_b中的Enterprise applications列表中。

现在我们需要为不同的租户生成访问令牌来调用graph api。需要为每个租户生成访问令牌,因为我尝试使用common替换请求中的域(https://login.microsoftonline.com/common/oauth2/v2.0/token),它可以成功生成访问令牌,但令牌不能用于api查询用户信息。查询用户 api 需要用户主体名称作为输入参数。比如我有一个账号是'bob@tenant_b.onmicrosoft.com'的用户,用账号作为参数是可以得到响应的,但是如果我用'bob'作为参数,它就会return 'Resource xxx does not exist...'.

我不是python方面的专家,我只是找到了一个样本并用它测试成功。这是我的代码,它将执行循环查询直到找到用户。如果你想要一个函数,你可以基于它创建一个 http 触发器。

import sys
import json
import logging
import requests
import msal

config = json.load(open(sys.argv[1]))
authorityName = ["<tenant_a>.onmicrosoft.com","<tenant_b>.onmicrosoft.com"]
username = "userone@<tenant_a>.onmicrosoft.com"

for domainName in authorityName:
    # Create a preferably long-lived app instance which maintains a token cache.
    print("==============:"+config["authority"]+domainName)
    app = msal.ConfidentialClientApplication(
        "<client_id>", authority="https://login.microsoftonline.com/"+domainName,
        client_credential="<client_secret>",
        )
        # The pattern to acquire a token looks like this.
    result = None

    # Firstly, looks up a token from cache
    # Since we are looking for token for the current app, NOT for an end user,
    # notice we give account parameter as None.
    result = app.acquire_token_silent(["https://graph.microsoft.com/.default"], account=None)

    if not result:
        result = app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"])

    if "access_token" in result:
        print("access token===:"+result['access_token'])
        # Calling graph using the access token
        graph_data = requests.get(  # Use token to call downstream service
            "https://graph.microsoft.com/v1.0/users/"+username,
            headers={'Authorization': 'Bearer ' + result['access_token']}, ).json()
        if "error" in graph_data:
            print("error===="+json.dumps(graph_data, indent=2))
        else:
            print(json.dumps(graph_data, indent=2))
            break

    else:
        print(result.get("error"))
        print(result.get("error_description"))
        print(result.get("correlation_id"))