You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Python获取Azure应用注册所有者遇问题,求技术指导

问题分析与解决方案

核心问题:误解appOwnerOrganizationId字段含义

你的代码中错误将appOwnerOrganizationId当作用户/所有者的Object ID,但这个字段实际代表的是应用注册所属的Azure AD租户ID,并非具体的用户或所有者ID,这是导致无法获取正确所有者信息的根本原因。

正确的所有者获取方式

要获取应用注册(或服务主体)的实际所有者,需要调用Microsoft Graph的owners导航属性接口,以下是修正后的实现:

1. 修正权限配置

确保你的服务主体已被授予以下Microsoft Graph权限(需管理员同意):

  • Application.Read.All:读取应用注册信息
  • Directory.Read.All:读取目录对象(所有者信息)

2. 修正代码逻辑

import logging
import requests
import datetime
from datetime import timezone
from dateutil.parser import parse
from azure.identity import ClientSecretCredential
from msgraph.core import GraphClient

def get_application_owners(graph_client, app_object_id):
    """
    获取应用注册的所有所有者
    """
    try:
        # 调用owners导航属性获取所有者列表
        response = graph_client.get(f"/applications/{app_object_id}/owners")
        response.raise_for_status()
        owners = response.json().get('value', [])
        
        # 提取所有者名称(支持用户、组、服务主体)
        owner_names = []
        for owner in owners:
            if 'userPrincipalName' in owner:
                owner_names.append(owner.get('displayName') or owner.get('userPrincipalName'))
            elif 'displayName' in owner:
                owner_names.append(owner.get('displayName'))
            else:
                owner_names.append('Unknown Owner')
        
        return ', '.join(owner_names) if owner_names else 'N/A'
    except Exception as e:
        logging.error(f"获取所有者失败: {str(e)}")
        return 'N/A'

# 初始化凭证和Graph客户端
credential = ClientSecretCredential(tenant_id, client_id, client_secret)
graph_client = GraphClient(credential=credential)

# 获取所有服务主体并关联应用注册信息
expiring_secrets = []
expiry_threshold = datetime.datetime.now(timezone.utc)  # 替换为你的阈值时间

# 先获取所有应用注册,关联appId和objectId
apps_response = graph_client.get("/applications")
apps = apps_response.json().get('value', [])
app_id_to_object_id = {app['appId']: app['id'] for app in apps}

# 遍历服务主体
sp_response = graph_client.get("/servicePrincipals")
service_principals = sp_response.json().get('value', [])

for sp in service_principals:
    app_id = sp.get('appId')
    app_object_id = app_id_to_object_id.get(app_id)
    owner_name = 'N/A'
    
    if app_object_id:
        owner_name = get_application_owners(graph_client, app_object_id)
    
    # 处理密钥过期逻辑
    secrets = sp.get('passwordCredentials', [])
    for secret in secrets:
        end_date_str = secret.get('endDateTime')
        if end_date_str:
            end_date = parse(end_date_str).replace(tzinfo=timezone.utc)
            if end_date <= expiry_threshold:
                human_friendly_date = end_date.strftime('%Y-%m-%d %H:%M:%S %Z')
                expiring_secrets.append({
                    'displayName': sp.get('displayName'),
                    'secretId': secret.get('keyId'),
                    'expiryDate': human_friendly_date,
                    'owner': owner_name
                })

关键修正点说明

  • 区分应用注册与服务主体:应用注册(applications)是定义应用元数据的对象,服务主体(servicePrincipals)是应用在租户中的实例,两者通过appId关联。
  • 使用owners导航属性:通过/applications/{objectId}/owners或/servicePrincipals/{objectId}/owners获取实际所有者,这些所有者可以是用户、组或其他服务主体。
  • 处理多所有者场景:一个应用注册可能有多个所有者,需遍历返回的列表并提取名称。
  • 时区处理:确保日期比较时使用UTC时区,避免时区偏差导致的判断错误。

内容的提问来源于stack exchange,提问作者user3307437

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 21:15:35