教程:Azure 数据资源管理器中使用 Microsoft Entra 中的数据的自定义报告

本教程介绍如何使用 Microsoft Entra ID 中和 Microsoft Entra ID 治理服务中的数据在 Azure 数据资源管理器 (ADX) 中创建自定义报告。 本教程补充了其他报告选项,例如使用 Azure Monitor 和权利管理进行存档和报告,其侧重于将审核日志导出到 Azure Monitor 进行保留和分析。 相比之下,将 Microsoft Entra ID 数据导出到 Azure 数据资源管理器可以灵活地针对 Microsoft Entra 对象(包括历史对象和已删除的对象)创建自定义报表。 此外,使用 Azure 数据资源管理器可实现更多源的数据的聚合,并提供极高的可伸缩性、灵活的架构和保留策略。 如果需要在较长时间内保留访问数据、执行临时调查或需要对用户访问数据运行自定义查询,Azure 数据资源管理器可能会特别有用。

本文介绍如何显示从 Microsoft Entra 导出的配置、用户和访问权限,以及从其他来源导出的数据,例如有权访问自有 SQL 数据库的应用程序。 然后,可以在 Azure 数据资源管理器中使用 Kusto 查询语言 (KQL) 根据组织的要求生成自定义报告。

请执行以下步骤来创建这些报表:

  1. 在 Azure 订阅中设置 Azure 数据资源管理器,或创建免费群集。
  2. 使用 PowerShell 脚本和 Microsoft Graph 从 Microsoft Entra ID 中提取数据
  3. 创建表并将其数据从 Microsoft Entra ID 导入 Azure 数据资源管理器
  4. 从 Microsoft Entra ID 治理中提取数据
  5. 创建表并将其数据从 Microsoft Entra ID Governance 导入 Azure 数据资源管理器
  6. 使用 Kusto 查询语言生成自定义查询

在本教程结束时,你将能够开发用户访问权限的自定义视图。 这些视图可以通过Microsoft支持的工具在不同的应用程序中使用。 还可以从第三方数据库或应用程序引入数据,并对此数据进行报告。

先决条件

如果您是 Azure 数据资源管理器的新用户并想要了解本文中显示的方案,可以获取 免费的 Azure 数据资源管理器群集。 为了在生产环境中使用具有服务级别协议的 Azure 数据探测器,您需要一个 Azure 订阅来托管完整的 Azure 数据探测器集群。

确定要在报告中包含的数据。 本文中的脚本提供了一些示例,其中包含来自 Microsoft Entra 的用户、组和应用程序的特定数据。 这些示例旨在说明可以使用此方法生成的报告类型,但具体报告需求可能会有所不同,需要不同或额外的数据。 可以从这些对象开始,并随时间推移引入更多类型的 Microsoft Entra 对象。

  • 本文介绍如何以已登录用户的身份从 Microsoft Entra 检索数据。 为此,确保具有从 Microsoft Entra 检索数据所需的角色分配。 需要有具有适当权限的角色才能导出要使用的 Microsoft Entra 数据类型。
    • 用户数据:全局管理员、特权角色管理员、用户管理员
    • 组数据:全局管理员、特权角色管理员、组管理员
    • 应用程序/应用角色分配:全局管理员、特权角色管理员、应用程序管理员、云应用程序管理员
  • 必须对 Microsoft Graph PowerShell 授予同意,以便通过 Microsoft Graph 检索 Microsoft Entra 对象。 本教程中的示例需要委派的 User.Read.All、Group.Read.All、Application.Read.All 和 Directory.Read.All 权限。 如果计划在不使用登录用户的情况下通过自动化操作检索数据,请改为同意授权相应的应用程序权限。 有关其他信息,请参阅 Microsoft Graph 权限参考。 如果尚未向 Microsoft Graph PowerShell 授予这些权限的同意,则您需要是全局管理员才能执行此同意操作。
  • 本教程不说明自定义安全属性。 默认情况下,全局管理员和其他管理员角色不包括从 Microsoft Entra 用户读取自定义安全属性的权限。 如果计划检索自定义安全属性,可能需要更多角色和权限。
  • 在安装了 Microsoft Graph PowerShell 的计算机上,确保具有对文件系统目录的写入权限。 这是安装所需的 Microsoft Graph PowerShell 模块以及保存导出 Microsoft Entra 数据的位置。
  • 如果还希望将该数据合并到 Azure 数据资源管理器中,请确保有权从 Microsoft Entra 以外的其他数据源检索数据。

1:设置 Azure 数据资源管理器

如果以前尚未使用过 Azure 数据资源管理器,则需要先进行设置。 无需 Azure 订阅或信用卡或者需要 Azure 订阅的完整群集即可创建免费群集。 请参阅快速入门:创建 Azure 数据资源管理器群集和数据库以开始使用。

2:使用 PowerShell 提取 Microsoft Entra ID 数据

在本部分中,安装 Microsoft Graph PowerShell 模块,在 PowerShell 中,连接到 Microsoft Graph 以提取 Microsoft Entra ID 数据。

组织首次将这些模块用于此方案时,需要具有全局管理员角色才能允许 Microsoft Graph PowerShell 为租户中的用户授予同意。 后续交互可以使用较低特权角色。

  1. 打开 PowerShell。
  2. 如果未安装全部 Microsoft Graph PowerShell 模块,请安装所需的 Microsoft Graph 模块。 本教程的本部分需要以下模块:Microsoft.Graph.AuthenticationMicrosoft.Graph.UsersMicrosoft.Graph.GroupsMicrosoft.Graph.ApplicationsMicrosoft.Graph.DirectoryObjects。 如果已安装所需模块,请继续执行下一步。
   $modules = @('Microsoft.Graph.Users', 'Microsoft.Graph.Groups', 'Microsoft.Graph.Applications', 'Microsoft.Graph.DirectoryObjects') 
   foreach ($module in $modules) { 
   Install-Module -Name $module -Scope CurrentUser -AllowClobber -Force
   } 
  1. 将模块导入到当前的 PowerShell 会话中。
  $modules = @('Microsoft.Graph.Users', 'Microsoft.Graph.Groups', 'Microsoft.Graph.Applications', 'Microsoft.Graph.DirectoryObjects') 
  foreach ($module in $modules) { 
  Import-Module -Name $module 
  } 
  1. 连接到 Microsoft Graph。 本教程的本部分演示如何读取用户、组和应用程序,因此需要 User.Read.AllGroup.Read.AllApplication.Read.AllDirectory.Read.All 权限范围。 有关权限的详细信息,请参阅 Microsoft Graph 权限参考
  Connect-MgGraph -Scopes "User.Read.All", "Group.Read.All", "Application.Read.All", "Directory.Read.All" -ContextScope Process -NoWelcome

此命令提示你使用 Microsoft Entra 凭据登录。 登录后,如果这是您第一次连接或者需要新的权限,您可能需要同意相关权限。

PowerShell 查询,用于提取 Microsoft Entra ID 数据,以在 Azure 数据资源管理器中生成自定义报表。

以下查询使用 PowerShell 从 Microsoft Graph 中提取 Microsoft Entra ID 数据,将这些数据导出为 JSON 文件,然后在后续的第 3 节中被导入 Azure 数据资源管理器。 使用这种类型的数据生成报表可能有多种情况,包括:

  • 审核员希望看到一份报告,其中列出了 10 个组的组成员,按成员的部门组织。
  • 审核员希望看到在两个日期之间有权访问应用程序的所有用户的报告。

还可以将数据从 Microsoft Entra 以外的其他源引入 Azure 数据资源管理器。 这样可实现以下方案:

  • 管理员希望查看从 Microsoft Entra ID 添加到应用程序的所有用户及其在该应用程序自己的存储库(例如 SQL 数据库)中的访问权限。

这些类型的报表没有内置到 Microsoft Entra ID 中。 但可以通过从 Entra 中提取数据并在 Azure 数据资源管理器中使用自定义查询进行组合来自行创建这些报表。 此内容将在本教程后面的引入其他源的数据部分中进行介绍。

在本教程中,我们将从多个区域提取 Microsoft Entra ID 数据:

  • 用户信息,例如显示名称、UPN 和作业详细信息
  • 组信息(包括其成员身份)
  • 应用程序和应用程序角色分配

通过此数据集,我们能够对有权访问应用程序的人员、其应用程序角色信息和关联的时间范围进行广泛的查询。 请注意,这些是示例查询,你的数据和特定要求可能与此处显示的内容有所不同。

注意

较大的租户可能会遇到限流或由 Microsoft Graph 模块负责处理的 429 错误。 Azure 数据资源管理器还可能限制文件上传大小。

在这些 PowerShell 脚本中,我们将所选属性从 Microsoft Entra 对象导出到 JSON 文件。 这些导出属性中的数据用于在 Azure 数据资源管理器中生成自定义报告。 这些示例中包含了以下特定属性,因为我们使用此数据来说明可以在 Azure 数据资源管理器中创建的报告类型。 由于你的具体报告需求可能与所示内容不同,因此应在这些脚本中包含想要在报告中查看的特定属性。 但可以遵循所示的相同模式来帮助生成脚本。

选择快照日期

我们包含了一个硬编码的 快照日期,用于以特定日期标识 JSON 文件中的数据,并使我们能够在 Azure 数据资源管理器中跟踪类似的数据集。 快照日期还可用于比较两个快照日期之间的数据更改。

$SnapshotDate = Get-Date -AsUTC -Format "yyyy-MM-dd"

获取 Entra 用户数据

此脚本将所选属性从 Entra 用户对象导出到 JSON 文件。 我们将在本教程的后续部分中将此数据和其他 JSON 文件中的数据导入 Azure 数据资源管理器。

  function Export-EntraUsersToJson { 

  # Define a hash table for property mappings 
   $propertyMappings = @{ 
    "Id" = "ObjectID" 
    "DisplayName" = "DisplayName" 
    "UserPrincipalName" = "UserPrincipalName" 
    "EmployeeId" = "EmployeeId" 
    "UserType" = "UserType" 
    "CreatedDateTime" = "CreatedDateTime" 
    "JobTitle" = "JobTitle" 
    "Department" = "Department" 
    "AccountEnabled" = "AccountEnabled" 

   # Add custom properties as needed 
    "custom_extension" = "CustomExtension" 
   } 
  # Retrieve users with specified properties and create custom objects directly 
   $users = Get-MgUser -Select ($propertyMappings.Keys) -All | ForEach-Object { 
      $userObject = @{} 
      foreach ($key in $propertyMappings.Keys) { 
        if ($key -eq "CreatedDateTime") { 
          # Convert date string directly to DateTime and format it 
          $date = [datetime]::Parse($_.$key) 
          $userObject[$propertyMappings[$key]] = $date.ToString("yyyy-MM-dd") 
        } else { 
          $userObject[$propertyMappings[$key]] = $_.$key 
        } 
      } 
      # Additional properties or transformations 
      $userObject["SnapshotDate"] = $SnapshotDate
      [pscustomobject]$userObject 
    } 
    # Convert the user data to JSON and save it to a file 
    $users | ConvertTo-Json -Depth 2 | Set-Content ".\EntraUsers.json" 
  } 
  # Execute the function 
  Export-EntraUsersToJson 

获取组数据

生成包含组名称和 ID 的 JSON 文件,该文件用于在 Azure 数据资源管理器中创建自定义视图。 此示例包含所有组,但如果需要,可以包含其他筛选。 如果要筛选以仅包含某些组,则可能需要在脚本中包含逻辑来检查嵌套组。

    # Get all groups and select Id and DisplayName 
    $groups = Get-MgGroup -All | Select-Object Id,DisplayName 
    # Export the groups to a JSON file 
    $groups | ConvertTo-Json | Set-Content ".\EntraGroups.json" 

获取组成员身份数据

生成包含组成员身份的 JSON 文件,该文件用于在 Azure 数据资源管理器中创建自定义视图。 此示例包含所有组,但如果需要,可以包含其他筛选。

    # Retrieve all groups from Microsoft Entra (Azure AD) 
    $groups = Get-MgGroup -All 
    # Initialize an array to store results 
    $results = @() 
    # Iterate over each group 
    foreach ($group in $groups) { 
      # Extract the group ID 
      $groupId = $group.Id 
      # Get members of the current group and select their IDs 
      $members = Get-MgGroupMember -GroupId $groupId | Select-Object -ExpandProperty Id 
      # Add a custom object with group ID and member IDs to the results array 
      $results += [PSCustomObject]@{ 
        GroupId = $groupId 
        Members = $members 
        SnapshotDate = $SnapshotDate
      } 
      # Pause for a short time to avoid rate limits 
      Start-Sleep -Milliseconds 200 
    } 
    # Convert the results array to JSON format and save it to a file 
    $results | ConvertTo-Json | Set-Content "EntraGroupMembership.json" 

获取应用程序和服务主体数据

生成包含租户中的所有应用程序和相应服务主体的 JSON 文件。 我们将在本教程的后续部分中将此数据导入 Azure 数据资源管理器,以便基于此数据生成与应用程序相关的自定义报表。

    # Fetch applications and their corresponding service principals, then export to JSON 
    Get-MgApplication -All | ForEach-Object { 
      $app = $_ 
      $sp = Get-MgServicePrincipal -Filter "appId eq '$($app.AppId)'" 
      [pscustomobject]@{ 
        Name        = $app.DisplayName 
        ApplicationId   = $app.AppId 
        ServicePrincipalId = $sp.Id 
        SnapshotDate = $SnapshotDate
      } 
    } | ConvertTo-Json -Depth 10 | Set-Content "Applications.json" 

获取 AppRole 数据

为 Microsoft Entra 中的企业应用生成所有 appRoles 的 JSON 文件。 导入 Azure 数据资源管理器后,我们将利用此数据为用户生成涉及应用角色分配的报表。

    # Get a list of all applications, handle pagination manually if necessary 
    $apps = Get-MgApplication -All 
    # Loop through each application to gather the desired information 
    $results = foreach ($app in $apps) { 
      # Get the service principal for the application using its appId 
      $spFilter = "appId eq '$($app.AppId)'" 
      $sp = Get-MgServicePrincipal -Filter $spFilter | Select-Object -First 1 
      # Process AppRoles, if any, for the application 
      $appRoles = if ($app.AppRoles) { 
        $app.AppRoles | Where-Object { $_.AllowedMemberTypes -contains "User" } | 
        Select-Object Id, Value, DisplayName 
      } 
      # Construct a custom object with application and service principal details 
      [PSCustomObject]@{ 
        ApplicationId    = $app.AppId 
        DisplayName     = $app.DisplayName 
        ServicePrincipalId = $sp.Id 
        AppRoles      = $appRoles 
        SnapshotDate = $SnapshotDate
      } 
    } 
    # Export the results to a JSON file 
    $results | ConvertTo-Json -Depth 4 | Out-File 'AppRoles.json' 

获取 AppRole 分配数据

生成租户中用户的所有应用角色分配的 JSON 文件。

    $users = Get-MgUser -All 
    $result = @() 
    foreach ($user in $users) { 
      Get-MgUserAppRoleAssignment -UserId $user.Id | ForEach-Object { 
        # Use the same date formatting approach 
        $createdDateTime = $_.CreatedDateTime -replace "\\/Date\((\d+)\)\\/", '$1' 
        # Convert the milliseconds timestamp to a readable date format if needed 
        $result += [PSCustomObject]@{ 
          AppRoleId      = $_.AppRoleId 
          CreatedDateTime   = $createdDateTime 
          PrincipalDisplayName = $_.PrincipalDisplayName 
          PrincipalId     = $_.PrincipalId 
          ResourceDisplayName = $_.ResourceDisplayName 
          ResourceId      = $_.ResourceId 
          SnapshotDate     = $SnapshotDate
        } 
      } 
    } 
    $result | ConvertTo-Json -Depth 10 | Out-File "AppRoleAssignments.json" 

3:使用 Microsoft Entra ID 中的数据创建表并将 JSON 文件导入 Azure 数据资源管理器

在本部分中,我们将为 Microsoft Entra ID 服务导入新创建的 JSON 文件作为 Azure 数据资源管理器中的表进行进一步分析。 在第一次使用 Azure 数据资源管理器 Web UI 导入时,将基于 Web UI 从每个 JSON 文件建议的架构创建表。

在 Azure 数据资源管理器群集或免费群集中设置数据库后(如本文第一部分所述),导航到该数据库。

  1. 登录到 Azure 数据资源管理器 Web UI
  2. 从左侧菜单中选择“查询”

然后对每个导出的 JSON 文件执行以下步骤,以便将导出的数据导入该 Azure 数据资源管理器数据库作为新表。

  1. 右键单击要引入数据的数据库的名称。 选择“获取数据”

    “查询”选项卡的屏幕截图,并显示右键单击数据库操作和打开的“获取选项”对话框。

  2. 从可用列表中选择数据源。 在本教程中,你将从 本地文件导入数据。

  3. 选择“+ 新建表”并基于导入的 JSON 文件的名称输入表名称,例如,如果要导入 EntraUsers.json,则将表命名为 EntraUsers。 第一次导入后,该表已存在,可以选择其作为后续导入的目标表。

  4. 选择 浏览文件,选择 JSON 文件,然后选择 下一步

  5. Azure 数据资源管理器会自动检测架构,并在“检查”选项卡中提供预览。选择“完成”以创建表并从该文件导入数据。 引入数据后,单击“关闭”。

  6. 针对上一部分中生成的每个 JSON 文件重复前面每个步骤。

在这些步骤结束时,你将在数据库中具有表 EntraUsersEntraGroupsEntraGroupMembershipApplicationsAppRolesAppRoleAssignments

4:使用 PowerShell 提取 Microsoft Entra ID Governance 数据

在本部分中,将使用 PowerShell 从 Microsoft Entra ID Governance 服务中提取数据。 如果您没有 Microsoft Entra ID Governance、Microsoft Entra ID P2 或 Microsoft Entra Suite,请在第 节继续使用 Azure 数据探查器来构建自定义报表

为此,可能需要安装 Microsoft Graph PowerShell 模块以提取 Microsoft Entra ID 治理数据。 组织首次将这些模块用于此方案时,需要具有全局管理员角色才能允许 Microsoft Graph PowerShell 为租户中的用户授予同意。 后续交互可以使用较低特权角色。

  1. 打开 PowerShell。
  2. 如果未安装全部 Microsoft Graph PowerShell 模块,请安装所需的 Microsoft Graph 模块。 本教程的本部分需要以下模块:Microsoft.Graph.Identity.Governance。 如果已安装所需模块,请继续执行下一步。
   $modules = @('Microsoft.Graph.Identity.Governance')
   foreach ($module in $modules) {
   Install-Module -Name $module -Scope CurrentUser -AllowClobber -Force
   }
  1. 将模块导入到当前的 PowerShell 会话中。
  $modules = @('Microsoft.Graph.Identity.Governance')
  foreach ($module in $modules) {
  Import-Module -Name $module
  } 
  1. 连接到 Microsoft Graph。 本教程的本部分说明如何从权利管理和访问评审中检索数据,因此需要 AccessReview.Read.AllEntitlementManagement.Read.All 权限范围。 对于其他报表用例(例如 PIM 或生命周期工作流),请使用必要的权限更新 Scopes 参数。 有关权限的详细信息,请参阅 Microsoft Graph 权限参考
  Connect-MgGraph -Scopes "AccessReview.Read.All, EntitlementManagement.Read.All" -ContextScope Process -NoWelcome

此命令提示你使用 Microsoft Entra 凭据登录。 登录后,如果这是您第一次连接或者需要新的权限,您可能需要同意相关权限。

用于在 Azure 数据资源管理器中生成自定义报表的 PowerShell 查询,以提取所需的 Microsoft Entra ID 治理数据

可以使用查询通过 PowerShell 从 Microsoft Graph 中提取 Microsoft Entra ID Governance 数据,并将数据导出到 JSON 文件,这些文件将导入到后续部分中的 Azure 数据资源管理器中。 使用这种类型的数据生成报表可能有多种情况,包括:

  • 有关历史访问评审的报告
  • 有关通过权限管理进行分配的报告

获取访问评审计划定义数据

生成一个 JSON 文件,其中包含用于在 Azure 数据资源管理器中创建自定义视图的访问评审定义名称和 ID。 样本包括所有访问审核,但如果需要,可以加入其他筛选条件。 有关详细信息,请参阅使用筛选器查询参数

   $allsched = Get-MgIdentityGovernanceAccessReviewDefinition -All
   $definitions = @()
   # Iterate over each definition
   foreach ($definition in $allsched) {
      $definitions += [PSCustomObject]@{
         Id = $definition.Id
         DisplayName = $definition.DisplayName
         SnapshotDate = $SnapshotDate
      }
   }
   $definitions | ConvertTo-Json -Depth 10 | Set-Content "EntraAccessReviewDefinitions.json"

获取 Access 评审实例数据

若要使用 PowerShell 将所有 Access Review 定义、实例和决策导出为结构化文件夹格式,可以使用 Microsoft 图形 API。 此方法可确保数据按层次结构进行组织,使其与指定的文件夹结构保持一致。

在开始之前,请注意以下事项:

  • 确保你具有访问 Microsoft Graph 中的 Access Reviews 数据所需的权限。
  • 根据数据量,脚本的执行时间可能会有所不同。 监视进程并根据需要调整参数。
  1. 下载 Export_Access_Reviews.ps1 脚本并将其保存在本地。
  2. 在文件资源管理器中,取消阻止脚本,以便可以在 PowerShell 中运行。
  3. 运行以下命令,将所有数据输出到三个子文件夹 ReviewInstancesReviewInstanceDecisionItemsReviewInstanceContactedReviewers
 .\ExportAccessReviews.ps1 -InstanceStartDate "11/15/2024" -InstanceEndDate "12/15/2024" -ExportFolder "C:\AccessReviewsExport\11_15_to_12_15" 

获取授权管理访问包数据

生成一个 JSON 文件,其中包含用于在 Azure 数据资源管理器中创建自定义视图的访问包名称和 ID。 该示例包括所有访问包,如有需要还可以包含更多筛选。

   $accesspackages1 = Get-MgEntitlementManagementAccessPackage -All
   $accesspackages2 = @()
   # Iterate over each access package
   foreach ($accesspackage in $accesspackages1) {
      $accesspackages2 += [PSCustomObject]@{
         Id = $accesspackage.Id
         DisplayName = $accesspackage.DisplayName
         SnapshotDate = $SnapshotDate
      }
   }
   $accesspackages2 | ConvertTo-Json -Depth 10 | Set-Content "EntraAccessPackages.json"

获取权限管理访问包分配数据

生成一个 JSON 文件,其中包含用于访问包的分配,这些包用于在 Azure 数据资源管理器中创建自定义视图。 此样本包括提交的所有作业,但如果需要,可以增加其他筛选条件。

   $apassignments1 = Get-MgEntitlementManagementAssignment -ExpandProperty target,accessPackage -filter "state eq 'Delivered'" -all
   $apassignments2 = @()
   # Iterate over each access package assignment
   foreach ($assignment in $apassignments1) {
      $apassignments2 += [PSCustomObject]@{
         Id = $assignment.Id
         ScheduleStartDateTime = $assignment.Schedule.StartDateTime -replace "\\/Date\((\d+)\)\\/", '$1' 
         AccessPackageId = $assignment.AccessPackage.Id
         AccessPackageDisplayName = $assignment.AccessPackage.DisplayName
         TargetId = $assignment.Target.Id
         TargetDisplayName = $assignment.Target.DisplayName
         TargetEmail = $assignment.Target.Email
         TargetObjectId = $assignment.Target.ObjectId
         TargetPrincipalName = $assignment.Target.PrincipalName
         TargetSubjectType = $assignment.Target.SubjectType
         SnapshotDate = $SnapshotDate
      }
   }
   $apassignments2 | ConvertTo-Json -Depth 10 | Set-Content "EntraAccessPackageAssignments.json"

5:创建表并将带有 Microsoft Entra ID Governance 数据的 JSON 文件导入到 Azure 数据浏览器

在本部分中,我们将为 Microsoft Entra ID Governance 服务新建的 JSON 文件导入 Azure 数据资源管理器,这些数据将与已为 Microsoft Entra ID 服务导入的数据一并进行进一步分析。 首次通过 Azure 数据资源管理器 Web UI 导入时,您将根据 Web UI 为每个 JSON 文件建议的架构创建表。

在 Azure 数据资源管理器群集或免费群集中,导航到保存 Microsoft Entra ID 数据的数据库。

  1. 登录到 Azure 数据资源管理器 Web UI
  2. 从左侧菜单中选择“查询”

接下来,针对上一部分导出的每个 JSON 文件执行以下步骤,将导出的数据作为新表导入该 Azure 数据资源管理器数据库。

  1. 右键单击要引入数据的数据库的名称。 选择“获取数据”

    “查询”选项卡的屏幕截图,并显示右键单击数据库操作和打开的“获取选项”对话框。

  2. 从可用列表中选择数据源。 在本教程中,你将从 本地文件导入数据。

  3. 选择“+ 新建表”并基于导入的 JSON 文件的名称输入表名称,第一次导入后,该表已存在,可以选择该表作为后续导入的目标表。

  4. 选择 浏览文件,选择 JSON 文件,然后选择 下一步

  5. Azure 数据资源管理器会自动检测架构,并在“检查”选项卡中提供预览。选择“完成”以创建表并从该文件导入数据。 引入数据后,单击“关闭”。

  6. 针对在上一部分生成的每个 JSON 文件,针对每个文件夹重复上述每个步骤。

  7. 如果文件夹中有许多文件,则可以使用 lightingest 在创建表后导入其余文件。

在这些步骤结束时,除了第 3 节中创建的表之外,您还将在数据库中获得表 EntraAccessReviewDefinitionsEntraAccessPackagesEntraAccessPackageAssignmentsReviewInstancesReviewInstanceDecisionItemsReviewInstanceContactedReviewers

6:使用 Azure 数据资源管理器生成自定义报表

借助 Azure 数据资源管理器中现有的数据,可以根据业务需求开始创建自定义报表。

Azure 数据资源管理器是一种功能强大的数据分析工具,高度可缩放且灵活,为生成自定义的用户访问报告提供了理想的环境。 Azure 数据资源管理器使用 Kusto 查询语言 (KQL)。

  1. 登录到 Azure 数据资源管理器 Web UI
  2. 从左侧菜单中选择“查询”

以下查询提供了常见报告的示例,但你可以自定义这些报告来满足需求并创建其他报告。

还可以在 Excel 中查看报表,方法是选择“导出”选项卡,然后选择“在 Excel 中打开”。

示例 1:为特定快照日期的直接分配和组分配生成应用角色分配

此报告提供了一个视图,显示谁有权访问以及何时访问目标应用,可用于安全审核、合规性验证以及了解组织中的访问模式。

此查询面向 Microsoft Entra AD 中的特定应用程序,并分析截至某个日期的角色分配。 查询将直接角色分配和基于组的角色分配检索出来,然后将此数据与来自 EntraUsers 表的用户详细信息和来自 AppRoles 表的角色信息合并。 在下面的查询中,将 targetSnapshotDate 设置为加载数据时使用的 snapshotDate 值。

/// Define constants 
let targetServicePrincipalId = "<your service principal-id>"; // Target Service Principal ID 
let targetSnapshotDate = datetime("2024-01-13"); // Target Snapshot Date for the data 

// Extract role assignments for the target Service Principal and Snapshot Date 
let roleAssignments = AppRoleAssignments 
    | where ResourceId == targetServicePrincipalId and startofday(SnapshotDate) == targetSnapshotDate 
    | extend AppRoleIdStr = tostring(AppRoleId); // Convert AppRoleId to string for easier comparison 

// Prepare user data from EntraUsers table 
let users = EntraUsers 
    | project ObjectID, UserPrincipalName, DisplayName, ObjectIDStr = tostring(ObjectID); // Include ObjectID as string for joining 

// Prepare role data from AppRoles table 
let roles = AppRoles 
    | mvexpand AppRoles // Expand AppRoles to handle multiple roles 
    | extend RoleName = AppRoles.DisplayName, RoleId = tostring(AppRoles.Id) // Extract Role Name and ID 
    | project RoleId, RoleName; 
// Process direct assignments 
let directAssignments = roleAssignments 
    | join kind=inner users on $left.PrincipalId == $right.ObjectID // Join with EntraUsers on PrincipalId 
    | join kind=inner roles on $left.AppRoleIdStr == $right.RoleId // Join with roles to get Role Names 
    | project UserPrincipalName, DisplayName, CreatedDateTime, RoleName, AssignmentType = "Direct", SnapshotDate; 

// Process group-based assignments 

let groupAssignments = roleAssignments 
    | join kind=inner EntraGroupMembership on $left.PrincipalId == $right.GroupId // Join with Group Membership 
    | mvexpand Members // Expand group members 
    | extend MembersStr = tostring(Members) // Convert member ID to string 
    | distinct MembersStr, CreatedDateTime, AppRoleIdStr, SnapshotDate // Get distinct values 
    | join kind=inner users on $left.MembersStr == $right.ObjectIDStr // Join with EntraUsers for user details 
    | join kind=inner roles on $left.AppRoleIdStr == $right.RoleId // Join with roles for role names 
    | project UserPrincipalName, DisplayName, CreatedDateTime, RoleName, AssignmentType = "Group", SnapshotDate; 

// Combine results from direct and group-based assignments 
directAssignments 
| union groupAssignments 

示例 2:使用 Entra 数据生成基本审核者报告,显示在这两个日期之间谁有权访问应用

此报告提供了一个视图,显示在两个日期之间谁有权访问目标应用,可用于安全审核、合规性验证以及了解组织中的访问模式。

此查询面向 Microsoft Entra ID 中的特定应用程序,并分析两个日期之间的角色分配。 该查询从 AppRoleAssignments 表中检索直接角色分配,并将此数据与 EntraUsers 表的用户详细信息和 AppRoles 表中的角色信息合并。

// Set the date range and service principal ID for the query 
let startDate = datetime('2024-01-01'); 
let endDate = datetime('2024-03-14'); 
let servicePrincipalId = "<your service principal-id>"; 

// Query AppRoleAssignments for the specified service principal within the date range 
AppRoleAssignments 
| where ResourceId == servicePrincipalId and 
    todatetime(CreatedDateTime) between (startDate .. endDate) 

// Extend AppRoleId to a string for joining 
| extend AppRoleIdStr = tostring(AppRoleId) 

// Project the necessary fields for the join with EntraUsers and AppRoles 
| project PrincipalId, AppRoleIdStr, CreatedDateTime 

// Join with EntraUsers to get user details 
| join kind=inner (EntraUsers | project UserPrincipalName, DisplayName, ObjectID) on $left.PrincipalId == $right.ObjectID 

// Join with AppRoles to get the role display names 
| join kind=inner ( 
  AppRoles | mvexpand AppRoles | project RoleIdStr = tostring(AppRoles.Id), RoleDisplayName = tostring(AppRoles.DisplayName) 
) on $left.AppRoleIdStr == $right.RoleIdStr 

// Final projection of the report with the current date and time 
| project UserPrincipalName, DisplayName, RoleDisplayName, CreatedDateTime, ReportDate = now() 

示例 3:在两个数据快照日期之间将用户添加到应用

这些报告提供了一个视图,显示在两个日期之间向哪些用户提供目标应用程序的应用角色分配。 这些报告可用于跟踪应用访问随时间的变化。

此查询面向 Microsoft Entra ID 中的特定应用程序,并更改为开始日期和结束日期之间的角色分配。

// Define the date range and service principal ID for the query 

let startDate = datetime("2024-03-01"); 
let endDate = datetime("2024-03-14"); 
let servicePrincipalId = "<your service principal-id>"; 
let earlierDate = startDate; // Update this to your specific earlier date 

AppRoleAssignments 
| where SnapshotDate < endDate and ResourceId == servicePrincipalId
| project PrincipalId, AppRoleId2 = tostring(AppRoleId), CreatedDateTime 
| join kind=anti ( 
    AppRoleAssignments 
    | where SnapshotDate < earlierDate and ResourceId == servicePrincipalId 
    | project PrincipalId, AppRoleId1 = tostring(AppRoleId) 
) on PrincipalId 
| join kind=inner (EntraUsers) on $left.PrincipalId == $right.ObjectID 
| join kind=inner (AppRoles 
                   | mvexpand AppRoles 
                   | project AppRoleId=tostring(AppRoles.Id), RoleDisplayName=tostring(AppRoles.DisplayName) 
                  ) on $left.AppRoleId2 == $right.AppRoleId 
| project UserPrincipalName, DisplayName, RoleDisplayName, CreatedDateTime, PrincipalId, Change = "Added" 

示例 4:访问审查

查看完成与时间线信息

上传数据后,使用以下 Kusto 查询进行查看。

  • 上次访问评审周期何时完成? 需要多长时间?
ReviewInstances 
| summarize LastCompletedDate = max(ReviewInstanceEndDateTime),  
            ReviewDuration = datetime_diff('minute', max(ReviewInstanceEndDateTime), min(ReviewInstanceStartDateTime))  
  • 访问评审过程是否按时间(例如季度、每年)进行?
ReviewInstances 
| extend ExpectedFrequency = "Quarterly" // Replace with organization's frequency 
| summarize ReviewsCompleted = count(), LastReviewEndDate = max(ReviewInstanceEndDateTime) 
| extend CurrentDate = now(),  
         TimeSinceLastReview = datetime_diff('day', now(), LastReviewEndDate) 
| extend IsOnSchedule = iff(TimeSinceLastReview <= 90, "Yes", "No") // Assuming quarterly = 90 days  

查看参加情况与参与度

  • 被分配的审阅者是谁?
ReviewInstanceContactedReviewers 
| project AccessReviewDefinitionId, AccessReviewInstanceId, ReviewerName = DisplayName, ReviewerUserPrincipalName = UserPrincipalName, CreatedDateTime  
  • 哪些审阅者积极参与并提供响应?
ReviewInstanceDecisionItems 
| where ReviewedBy_DisplayName != "AAD Access Reviews" 
| where Decision in ("Approve", "Deny") 
| project AccessReviewDefinitionId, AccessReviewInstanceId, ReviewerName = ReviewedBy_DisplayName, 
ReviewerUserPrincipalName = ReviewedBy_UserPrincipalName, Decision, ReviewedDateTime 
| distinct AccessReviewDefinitionId, AccessReviewInstanceId, ReviewerName, ReviewerUserPrincipalName, Decision   
  • 响应访问评审请求的审阅者百分比。
let TotalReviewers = ReviewInstanceContactedReviewers 
    | summarize Total = dcount(Id) by AccessReviewDefinitionId, AccessReviewInstanceId;  

let RespondedReviewers = ReviewInstanceDecisionItems 
    | where ReviewedBy_DisplayName != "AAD Access Reviews" 
    | where ReviewedBy_Id != "00000000-0000-0000-0000-000000000000" 
    | where Decision in ("Approve", "Deny") 
    | summarize Responded = dcount(ReviewedBy_Id) by AccessReviewDefinitionId, AccessReviewInstanceId;  

TotalReviewers 
| join kind=leftouter RespondedReviewers on AccessReviewDefinitionId, AccessReviewInstanceId 
| extend Responded = coalesce(Responded, 0)  // Replace null with 0 for Responded 
| extend NotResponded = Total - Responded   // Calculate the number of non-responders 
| extend ResponsePercentage = (Responded * 100.0) / Total  // Percentage of those who responded 
| extend NonResponsePercentage = (NotResponded * 100.0) / Total  // Percentage of those who didn’t respond 
| project AccessReviewDefinitionId, AccessReviewInstanceId, Total, Responded, ResponsePercentage, NotResponded, NonResponsePercentage  
  • 每个审阅者何时完成其任务?
ReviewInstanceDecisionItems 
| where Decision in ("Approve", "Deny") 
| project AccessReviewDefinitionId, AccessReviewInstanceId, ReviewerName = ReviewedBy_DisplayName, ReviewerUserPrincipalName = ReviewedBy_UserPrincipalName, ReviewedDateTime  
  • 哪些审阅者没有做出任何决定?
let AllReviewers = ReviewInstanceContactedReviewers 
    | project AccessReviewDefinitionId, AccessReviewInstanceId, ReviewerId = Id, ReviewerUserPrincipalName = UserPrincipalName, ReviewerName = DisplayName; 
  
let ActiveReviewers = ReviewInstanceDecisionItems 
    | where Decision in ("Approve", "Deny") 
    | where ReviewedBy_DisplayName != "AAD Access Reviews" 
    | where ReviewedBy_Id != "00000000-0000-0000-0000-000000000000" 
    | summarize ActiveReviewers = make_set(ReviewedBy_Id) by AccessReviewDefinitionId, AccessReviewInstanceId; 

AllReviewers 
| extend ReviewerId = tostring(ReviewerId)  // Ensure ReviewerId is a string 
| join kind=leftanti ( 
    ActiveReviewers 
    | mv-expand ActiveReviewers 
    | extend ActiveReviewers = tostring(ActiveReviewers)  // Cast ActiveReviewers to a string 
) on $left.ReviewerId == $right.ActiveReviewers 
| project AccessReviewDefinitionId, AccessReviewInstanceId, ReviewerUserPrincipalName, ReviewerName 
  • 未交互的审阅者的百分比。
let TotalReviewers = ReviewInstanceContactedReviewers 
    | summarize Total = dcount(Id) by AccessReviewDefinitionId, AccessReviewInstanceId; 

let RespondedReviewers = ReviewInstanceDecisionItems 
    | where ReviewedBy_DisplayName != "AAD Access Reviews" 
    | where ReviewedBy_Id != "00000000-0000-0000-0000-000000000000" 
    | where Decision in ("Approve", "Deny") 
    | summarize Responded = dcount(ReviewedBy_Id) by AccessReviewDefinitionId, AccessReviewInstanceId; 
  
TotalReviewers 
| join kind=leftouter RespondedReviewers on AccessReviewDefinitionId, AccessReviewInstanceId 
| extend Responded = coalesce(Responded, 0)  // Replace null with 0 for Responded 
| extend NotResponded = Total - Responded   // Calculate the number of non-responders 
| extend ResponsePercentage = (Responded * 100.0) / Total  // Percentage of those who responded 
| extend NonResponsePercentage = (NotResponded * 100.0) / Total  // Percentage of those who didn’t respond 
| project AccessReviewDefinitionId, AccessReviewInstanceId, Total, Responded, ResponsePercentage, NotResponded, NonResponsePercentage  
  • 是否为未响应的审阅者触发了提醒? 尚未决定的事项?
// Step 1: Get the list of all reviewers 
let TotalReviewers = ReviewInstanceContactedReviewers 
    | project AccessReviewDefinitionId, AccessReviewInstanceId, ReviewerId = Id, ReviewerUserPrincipalName = UserPrincipalName, ReviewerName = DisplayName; 
 
// Step 2: Get the list of reviewers who have responded 
let RespondedReviewers = ReviewInstanceDecisionItems 
    | where ReviewedBy_DisplayName != "AAD Access Reviews" 
    | where ReviewedBy_Id != "00000000-0000-0000-0000-000000000000" 
    | where Decision in ("Approve", "Deny") 
    | project AccessReviewDefinitionId, AccessReviewInstanceId, RespondedReviewerId = ReviewedBy_Id; 

// Step 3: Get the list of review instances 
let ReviewInstancesWithDetails = ReviewInstances 
    | project AccessReviewDefinitionId = ReviewDefinitionId,  
              AccessReviewInstanceId = ReviewInstanceId,  
              RemindersSent = ReviewDefinitionSettings_ReminderNotificationsEnabled,  
              StartDate = todatetime(ReviewInstanceStartDateTime),  
              EndDate = todatetime(ReviewInstanceEndDateTime) 
    | extend 
              ReminderSentDate = iif(RemindersSent, StartDate + (EndDate - StartDate) / 2, datetime(null)); 

// Step 4: Identify non-responsive reviewers and join with review instance details 
TotalReviewers 
| join kind=leftouter (ReviewInstancesWithDetails) on AccessReviewDefinitionId, AccessReviewInstanceId 
| join kind=leftanti RespondedReviewers on $left.ReviewerId == $right.RespondedReviewerId 
| project AccessReviewDefinitionId, AccessReviewInstanceId, ReviewerUserPrincipalName, ReviewerName, RemindersSent, ReminderSentDate 

用户与访问权限更改

  • 访问评审期间谁失去了对特定资源的访问权限?
ReviewInstanceDecisionItems 
| where Decision == "Deny" 
| project User = Principal_DisplayName, Resource = Resource_DisplayName, Decision, Justification 
  • 用户是否已因不活动而被标记?
ReviewInstanceDecisionItems 
| where Insights contains "inactive" 
| project User = Principal_DisplayName, Resource = Resource_DisplayName, Insights, Decision 
  • 访问删除日期和失去访问权限的原因。
ReviewInstanceDecisionItems 
| where Decision == "Deny" 
| project User = Principal_DisplayName, Resource=Resource_DisplayName, AccessRemovalDate = AppliedDateTime, Reason = Justification  
  • 没有做出决策的用户。
ReviewInstanceDecisionItems 
| where Decision == "NotReviewed" 
| project User = Principal_DisplayName, Resource=Resource_DisplayName 
  • 无评审者的评审。
ReviewInstances 
| join kind=leftanti ( 
    ReviewInstanceContactedReviewers 
    | summarize by AccessReviewInstanceId 
) on $left.ReviewInstanceId == $right.AccessReviewInstanceId  
  • 无用户的评审。
ReviewInstances 
| join kind=leftanti ( 
    ReviewInstanceDecisionItems 
    | summarize by AccessReviewInstanceId 
) on $left.ReviewInstanceId == $right.AccessReviewInstanceId 

查看决策数据

  • 做出的决定:已批准、拒绝或未更改。
ReviewInstanceDecisionItems 
| summarize count() by Decision 
  • 已批准或拒绝访问的用户数。
ReviewInstanceDecisionItems 
| summarize ApprovedCount = countif(Decision == "Approve"), DeniedCount = countif(Decision == "Deny") 
  • 是否已记录审批原因?
ReviewInstanceDecisionItems 
| where Decision == "Approve" and isnotempty(Justification) 
| summarize count() by ReviewedBy_DisplayName 

访问评审质量和合规性检查

  • 对于休眠用户而言,访问吊销是考虑的吗?
ReviewInstanceDecisionItems 
| where Insights contains "inactive" and Decision == "Deny" 
| project User = Principal_DisplayName, Decision 
  • 是否存在未正确删除的访问权限?
ReviewInstanceDecisionItems 
| where ApplyResult != "New" and ApplyResult != "AppliedSuccessfully" 
  • 审阅者是否记录了他们的决定?
ReviewInstanceDecisionItems 
| where isnotempty(Justification) 
| summarize count() by ReviewedBy_DisplayName 
  • 是否已为每个用户捕获评论?
ReviewInstanceDecisionItems 
| where isnotempty(Justification) 
| project User = Principal_DisplayName, Resource = Resource_DisplayName, Comments = Justification 

     

设置正在进行的导入

本教程介绍一次性数据提取、转换和加载 (ETL) 过程,以便使用单个快照填充 Azure 数据资源管理器,用于报告目的。 若要持续报告或比较一段时间内的更改,可以自动执行从 Microsoft Entra 向 Azure 数据资源管理器填充数据的过程,以便数据库持续获取最新数据。

可以使用 Azure 自动化(Azure 云服务)托管从 Microsoft Entra ID 和 Microsoft Entra ID 治理中提取数据所需的 PowerShell 脚本。 有关详细信息,请参阅使用 Azure 自动化来自动完成 Microsoft Entra ID 治理任务

还可以使用 Azure 功能或命令行工具(例如 lightingest)引入数据并填充现有的表。 有关详细信息,请参阅使用 LightIngest 将数据引入 Azure 数据资源管理器

例如,作为当前登录用户,要将当前目录中的文件 EntraAccessPackages.json 加载到 EntraAccessPackages 表中:

az login
LightIngest.exe "https://ingest-CLUSTERHOSTNAME;Fed=True" -database:"DATABASE" -table:EntraAccessPackages -sourcepath:"." -pattern:"EntraAccessPackages.json" -format:multijson -azcli:true

在 Azure Monitor 中查询数据

如果您将审核、登录或其他 Microsoft Entra 日志发送至 Azure Monitor,那么您可以将这些日志从 Azure Monitor Log Analytics 工作区整合到查询中。 有关 Azure Monitor 与 Azure 数据资源管理器之间关系的详细信息,请参阅使用 Azure 数据资源管理器查询 Azure Monitor 中的数据

  1. 登录到 Microsoft Entra 管理中心。

  2. 选择“诊断设置”

  3. 选择要发送日志的 Log Analytics 工作区。

  4. 在 Log Analytics 工作区概述中,记录工作区的订阅 ID、资源组名称和工作区名称。

  5. 登录到 Azure 门户。

  6. 导航到 Azure 数据资源管理器 Web UI

  7. 确保您的 Azure Data Explorer 群集被列出。

  8. 选择 + 添加,然后 连接

  9. 在“添加连接”窗口中,输入 Log Analytics 工作区的网址,该网址由特定于云的主机名、订阅 ID、资源组名称以及 Azure Monitor Log Analytics 工作区的工作区名称组成,如 添加 Log Analytics 工作区中所述。

  10. 建立连接后,Log Analytics 工作区将随同 Azure 数据资源管理器本机群集显示在左侧窗格。

  11. 从左侧菜单中选择“查询”,然后选择 Azure 数据资源管理器群集。

  12. 在查询窗格中,可以在 Azure 数据资源管理器查询中引用包含 Microsoft Entra 日志的 Azure Monitor 表。 例如:

    let CL1 = 'https://ade.loganalytics.io/subscriptions/*subscriptionid*/resourcegroups/*resourcegroupname*/providers/microsoft.operationalinsights/workspaces/*workspacename*';
    cluster(CL1).database('*workspacename*').AuditLogs | where Category == "EntitlementManagement"  and OperationName == "Fulfill access package assignment request"
    | mv-expand TargetResources | where TargetResources.type == 'AccessPackage' | project ActivityDateTime,APID = toguid(TargetResources.id)
    | join EntraAccessPackage on $left.APID == $right.Id
    | limit 100
    

从其他源引入数据

还可以在 Azure 数据资源管理器中创建更多表以从其他源引入数据。 如果数据位于 JSON 文件中(类似于上述示例)或 CSV 文件中,可以在首次从文件获取数据时创建表。 一旦表创建完成,还可以 使用 LightIngest 将数据从 JSON 或 CSV 文件引入 Azure 数据探索器

有关数据引入的详细信息,请参阅 Azure 数据资源管理器数据引入概述

示例 5:将 Entra 和另一个源中的应用分配进行组合,创建有权在两个日期之间访问某个应用程序的所有用户的报告

此报告说明了如何合并两个单独系统中的数据来在 Azure 数据资源管理器中创建自定义报告。 它将有关用户、其角色和其他属性的数据从两个系统聚合为统一格式进行分析或报告。

此示例假定有一个名为 salesforceAssignments 的表,其中包含列 UserNameNameEmployeeIdDepartmentJobTitleAppNameRole,以及通过从另一个应用程序引入数据填充的 CreatedDateTime

// Define the date range and service principal ID for the query 

let startDate = datetime("2023-06-01"); 
let endDate = datetime("2024-03-13"); 
let servicePrincipalId = "<your service principal-id>"; 

// Pre-process AppRoleAssignments with specific filters and projections 
let processedAppRoleAssignments = AppRoleAssignments 
    | where ResourceId == servicePrincipalId and todatetime(CreatedDateTime) between (startDate .. endDate) 
    | extend AppRoleId = tostring(AppRoleId) 
    | project PrincipalId, AppRoleId, CreatedDateTime, ResourceDisplayName; // Exclude DeletedDateTime and keep ResourceDisplayName 

// Pre-process AppRoles to get RoleDisplayName for each role 
let processedAppRoles = AppRoles 
    | mvexpand AppRoles 
    | project AppRoleId = tostring(AppRoles.Id), RoleDisplayName = tostring(AppRoles.DisplayName); 

// Main query: Process EntraUsers by joining with processed role assignments and roles 
EntraUsers 
    | join kind=inner processedAppRoleAssignments on $left.ObjectID == $right.PrincipalId // Join with role assignments 
    | join kind=inner processedAppRoles on $left.AppRoleId == $right.AppRoleId // Join with roles to get display names 

    // Summarize to get the latest record for each unique combination of user and role attributes 
    | summarize arg_max(AccountEnabled, *) by UserPrincipalName, DisplayName, tostring(EmployeeId), Department, JobTitle, ResourceDisplayName, RoleDisplayName, CreatedDateTime 

    // Final projection of relevant fields including source indicator and report date 
    | project UserPrincipalName, DisplayName, EmployeeId=tostring(EmployeeId), Department, JobTitle, AccountEnabled=tostring(AccountEnabled), ResourceDisplayName, RoleDisplayName, CreatedDateTime, Source="EntraUsers", ReportDate = now() 

// Union with processed salesforceAssignments to create a combined report 
| union ( 
    salesforceAssignments 

    // Project fields from salesforceAssignments to align with the EntraUsers data structure 
    | project UserPrincipalName = UserName, DisplayName = Name, EmployeeId = tostring(EmployeeId), Department, JobTitle, AccountEnabled = "N/A", ResourceDisplayName = AppName, RoleDisplayName = Role, CreatedDateTime, Source = "salesforceAssignments", ReportDate = now() 
) 

后续步骤