本教程介绍如何使用来自 Microsoft Entra ID 和 Microsoft Entra ID 治理服务的数据在 Azure 数据资源管理器 中创建自定义报表。
本教程补充了报告选项,例如 使用 Azure Monitor 进行存档和报告以及权利管理,重点介绍如何将审核日志导出到 Azure Monitor 进行保留和分析。 相比之下,将 Microsoft Entra ID 和 Microsoft Entra ID Governance 数据导出到 Azure 数据资源管理器可以灵活地针对 Microsoft Entra 对象(包括历史对象和已删除对象)创建自定义报表。
以下视频提供了有关使用 Azure 数据资源管理器报告 Microsoft Entra ID Governance 数据的快速概述。
使用 Azure 数据资源管理器还可以实现来自其他源的数据聚合,具有巨大的可伸缩性、灵活的架构和保留策略。 当需要多年来保留用户访问数据、执行即席调查或对访问数据运行自定义查询时,Azure 数据资源管理器特别有用。
本教程演示如何显示从 Microsoft Entra 导出的配置、用户和访问权限,以及从其他源导出的数据,例如在其自己的 SQL 数据库中具有访问权限的应用程序。 然后,可以在 Azure 数据资源管理器中使用 Kusto 查询语言 (KQL) 根据组织的要求生成自定义报告。
在本教程中,你将:
- 在 Azure 订阅中设置 Azure 数据资源管理器,或创建免费群集。
- 使用 PowerShell 脚本和 Microsoft Graph 从 Microsoft Entra ID 中提取数据。
- 创建表并将 Microsoft Entra ID 中的数据导入 Azure 数据资源管理器。
- 从 Microsoft Entra ID Governance 中提取数据。
- 创建表并将 Microsoft Entra ID Governance 中的数据导入 Azure 数据资源管理器。
- 使用 KQL 生成自定义查询。
- 在 Azure Monitor 中查询数据。
在本教程结束时,你将能够开发用户访问权限的自定义视图。 通过微软支持的工具,这些视图可以跨多个应用程序。 还可以引入来自非Microsoft数据库或应用程序的数据,以报告这些访问权限和权限。
先决条件
如果你不熟悉 Azure 数据资源管理器,并且想要了解本教程显示的方案,则可以获取 免费的 Azure 数据资源管理器群集。 若用于 Azure 数据浏览器的生产支持,且需要服务级协议,您需要一个 Azure 订阅来托管完整的 Azure 数据浏览器群集。
确定要在报告中包含的数据。 本教程中的脚本提供了来自 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 以外的其他数据源检索数据。
设置 Azure 数据资源管理器
如果以前未使用过 Azure 数据资源管理器,则需要先对其进行设置。 无需 Azure 订阅或信用卡即可创建免费群集。 或者,可以创建需要 Azure 订阅的完整群集。 若要开始,请参阅 快速入门:创建 Azure 数据资源管理器群集和数据库。
使用 PowerShell 提取Microsoft Entra ID 数据
在本部分中,将 安装 Microsoft Graph PowerShell 模块。 在 PowerShell 中,连接到 Microsoft Graph 以提取Microsoft Entra ID 数据。
组织首次将这些模块用于此方案时,需要具有全局管理员角色,以允许 Microsoft Graph PowerShell 授予在租户中使用的许可。 后续交互可以使用较低特权角色。
打开 PowerShell。
如果未安装全部 Microsoft Graph PowerShell 模块,请安装所需的 Microsoft Graph 模块。 本教程的此部分需要以下模块:
Microsoft.Graph.Authentication
、、Microsoft.Graph.Users
Microsoft.Graph.Groups
、Microsoft.Graph.Applications
和Microsoft.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 }
将模块导入到当前的 PowerShell 会话中:
$modules = @('Microsoft.Graph.Users', 'Microsoft.Graph.Groups', 'Microsoft.Graph.Applications', 'Microsoft.Graph.DirectoryObjects') foreach ($module in $modules) { Import-Module -Name $module }
连接到 Microsoft Graph。 本教程的此部分演示了读取用户、组和应用程序,因此它需要
User.Read.All
权限Group.Read.All
Application.Read.All
Directory.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 数据用于自定义报表
以下查询使用 PowerShell 从 Microsoft Graph 中提取Microsoft Entra ID 数据,并将数据导出到 JSON 文件。 在本教程的后的部分,您将这些文件导入 Azure 数据资源管理器。
使用此类数据生成报表的方案包括:
- 审核员希望看到一份报告,其中列出了由成员部门组织的 10 个组的组成员。
- 审核员希望查看在两个日期之间有权访问应用程序的所有用户的报告。
还可以将数据从 Microsoft Entra 以外的源引入 Azure 数据资源管理器。 此功能的方案可能是:
- 管理员希望查看从Microsoft Entra ID 添加到应用程序的所有用户及其在应用程序自己的存储库(例如 SQL 数据库)中的访问权限。
这些类型的报表不会内置到 Microsoft Entra ID 中。 您可以通过从 Microsoft Entra ID 提取数据,并在 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"
获取Microsoft Entra 用户数据
此脚本将所选属性从 Microsoft 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 | Foreach-Object {
$groupObject = @{}
$groupObject["Id"] = $_.Id
$groupObject["DisplayName"] = $_.DisplayName
$groupObject["SecurityEnabled"] = $_.SecurityEnabled
$groupObject["MailEnabled"] = $_.MailEnabled
$groupObject["MailNickname"] = $_.MailNickname
$groupObject["SecurityIdentifier"] = $_.SecurityIdentifier
$date = [datetime]::Parse($_.CreatedDateTime)
$groupObject["CreatedDateTime"] = $date.ToString("yyyy-MM-dd")
$groupObject["SnapshotDate"] = $SnapshotDate
[pscustomobject]$groupObject
}
# Export the groups to a JSON file
$groups | ConvertTo-Json | Set-Content ".\EntraGroups.json"
获取组成员身份数据
生成具有组成员身份的 JSON 文件,该文件用于在 Azure 数据资源管理器中创建自定义视图。 该示例包括所有组,但如有必要,可以包含其他筛选。
# Retrieve all groups from Microsoft Entra ID
$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 "EntraGroupMemberships.json"
获取应用程序和服务主体数据
生成包含租户中所有应用程序和相应服务主体的 JSON 文件。 本教程 的后面部分将此数据导入 Azure 数据资源管理器,以便基于此数据生成与应用程序相关的自定义报表。
# Fetch applications and their corresponding service principals, and then export to JSON
Get-MgApplication -All | ForEach-Object {
$app = $_
$sp = Get-MgServicePrincipal -Filter "appId eq '$($app.AppId)'"
$date = [datetime]::Parse($app.CreatedDateTime)
[pscustomobject]@{
DisplayName = $app.DisplayName
ApplicationId = $app.AppId
ServicePrincipalId = $sp.Id
SnapshotDate = $SnapshotDate
CreatedDateTime = $date.ToString("yyyy-MM-dd")
}
} | ConvertTo-Json -Depth 10 | Set-Content "Applications.json"
获取应用角色数据
在 Microsoft Entra 中为企业应用生成所有应用角色的 JSON 文件。 将此数据导入 Azure 数据资源管理器后,你将使用它来生成涉及用户应用角色分配的报告。
# Get a list of all applications, and 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 by using its app ID
$spFilter = "appId eq '$($app.AppId)'"
$sp = Get-MgServicePrincipal -Filter $spFilter | Select-Object -First 1
# Process app roles, 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'
获取应用角色分配数据
生成租户中用户的所有应用角色分配的 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 necessary
$result += [PSCustomObject]@{
Id = $_.Id
AppRoleId = $_.AppRoleId
CreatedDateTime = $createdDateTime
PrincipalDisplayName = $user.DisplayName
PrincipalId = $user.Id
AssignmentPrincipalType = $_.PrincipalType
AssignmentPrincipalDisplayName = $_.PrincipalDisplayName
AssignmentPrincipalId = $_.PrincipalId
ResourceDisplayName = $_.ResourceDisplayName
ResourceId = $_.ResourceId
SnapshotDate = $SnapshotDate
}
}
}
$result | ConvertTo-Json -Depth 10 | Out-File "AppRoleAssignments.json"
创建表格并将 Microsoft Entra ID 数据的 JSON 文件导入 Azure 数据资源管理器
在本部分中,将新创建的 JSON 文件作为 Azure 数据资源管理器中的表导入Microsoft Entra ID 服务,以便进一步分析。 在第一次通过 Azure 数据资源管理器 Web UI 导入时,将基于 Web UI 从每个 JSON 文件建议的架构创建表。
登录到 Azure 数据资源管理器 Web UI。
转到在 Azure 数据资源管理器群集或本教程前面的免费群集中设置的数据库。
在左侧菜单中,选择 “查询”。
针对每个导出的 JSON 文件执行以下步骤,以新表的形式将数据导出到该 Azure 数据资源管理器数据库中:
右键单击要在其中引入数据的数据库的名称。 然后选择“ 获取数据”。
从可用列表中选择数据源。 在本教程中,需要从本地文件引入数据,因此请选择 “本地文件”。
根据要导入的 JSON 文件的名称,选择 “+ 新建”表 并输入表名称。 例如,如果要导入 EntraUsers.json,请命名表 EntraUsers。 第一次导入后,该表已存在,可以选择其作为后续导入的目标表。
选择 “浏览文件”,选择 JSON 文件,然后选择“ 下一步”。
Azure 数据资源管理器会自动检测架构,并在 “检查 ”选项卡上提供预览。选择 “完成 ”以创建表并从该文件导入数据。 引入数据后,选择“ 关闭”。
对上一部分生成的每个 JSON 文件重复上述步骤。
在这些步骤结束时,你将在数据库中拥有表EntraUsers
、EntraGroups
、EntraGroupMemberships
、Applications
、AppRoles
和AppRoleAssignments
。
使用 PowerShell 提取Microsoft Entra ID 治理数据
在本部分中,将使用 PowerShell 从 Microsoft Entra ID Governance 服务中提取数据。 如果没有 Microsoft Entra ID Governance、Microsoft Entra ID P2 或 Microsoft Entra Suite,请继续在 使用 Azure 数据资源管理器生成自定义报表 部分。
对于以下步骤,可能需要 安装 Microsoft Graph PowerShell 模块 来提取Microsoft Entra ID Governance 数据。 组织首次将这些模块用于此方案时,需要具有全局管理员角色,以允许 Microsoft Graph PowerShell 授予在租户中使用的许可。 后续交互可以使用较低特权角色。
打开 PowerShell。
如果未安装全部 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 }
将模块导入到当前的 PowerShell 会话中:
$modules = @('Microsoft.Graph.Identity.Governance') foreach ($module in $modules) { Import-Module -Name $module }
连接到 Microsoft Graph。 本教程的这一部分演示了从权利管理和访问评审中检索数据,因此需要
AccessReview.Read.All
和EntitlementManagement.Read.All
权限范围。 对于其他报告用例,例如特权身份管理 (PIM) 或生命周期工作流,请使用Scopes
参数更新必要的权限。 有关权限的详细信息,请参阅 Microsoft Graph 权限参考。Connect-MgGraph -Scopes "AccessReview.Read.All, EntitlementManagement.Read.All" -ContextScope Process -NoWelcome
此命令提示你使用 Microsoft Entra 凭据登录。 登录后,如果你是第一次连接,或者需要新权限,则可能需要同意所需的权限。
用于为自定义报表提取Microsoft Entra ID Governance 数据的 PowerShell 查询
以下查询使用 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"
获取访问评审的实例数据
若要使用 PowerShell 将所有访问评审定义、实例和决策导出为结构化文件夹格式,可以使用Microsoft图形 API。 此方法可确保数据按层次结构进行组织,并与指定的文件夹结构保持一致。
在开始之前,请注意以下几点:
- 确保你有权访问 Microsoft Graph 中的评审数据。
- 根据数据量,脚本的执行时间可能会有所不同。 监视进程并根据需要调整参数。
获取实例数据:
下载 Export_Access_Reviews.ps1 脚本并将其保存在本地。
在文件资源管理器中,取消阻止脚本,以便可以在 PowerShell 中运行它。
运行以下命令。 输出将所有数据放入三个子文件夹:
ReviewInstances
、ReviewInstanceDecisionItems
和ReviewInstanceContactedReviewers
。.\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"
使用 Microsoft Entra ID Governance 中的数据在 Azure 数据探查器中创建表并导入 JSON 文件
在本部分中,将Microsoft Entra ID Governance Services 的新创建的 JSON 文件导入 Azure 数据资源管理器,以便进一步分析。 这些文件加入您已为 Microsoft Entra ID 服务导入的数据。 在第一次通过 Azure 数据资源管理器 Web UI 导入时,将基于 Web UI 从每个 JSON 文件建议的架构创建表。
登录到 Azure 数据资源管理器 Web UI。
在 Azure 数据资源管理器群集或免费群集中,转到保存Microsoft Entra ID 数据的数据库。
在左侧菜单中,选择 “查询”。
针对每个导出的 JSON 文件执行以下步骤,以新表的形式将数据导出到该 Azure 数据资源管理器数据库中:
右键单击要在其中引入数据的数据库的数据库名称。 然后选择“ 获取数据”。
从可用列表中选择数据源。 在本教程中,需要从本地文件引入数据,因此请选择 “本地文件”。
根据要导入的 JSON 文件的名称,选择 “+ 新建”表 并输入表名称。 第一次导入后,该表已存在,可以选择其作为后续导入的目标表。
选择 “浏览文件”,选择 JSON 文件,然后选择“ 下一步”。
Azure 数据资源管理器会自动检测架构,并在 “检查 ”选项卡上提供预览。选择 “完成 ”以创建表并从该文件导入数据。 引入数据后,选择“ 关闭”。
针对在上一部分生成的每个 JSON 文件,针对每个文件夹重复上述步骤。
如果文件夹中有许多文件,则可以在
lightingest
创建表后导入其余文件。
在这些步骤结束时,除了前面创建的表之外,还具有表EntraAccessReviewDefinitions
、EntraAccessPackages
、EntraAccessPackageAssignments
ReviewInstances
、ReviewInstanceDecisionItems
和ReviewInstanceContactedReviewers
数据库中的表。
使用 Azure 数据资源管理器生成自定义报表
在 Azure 数据资源管理器中现在提供的数据后,可以根据业务需求开始创建自定义报表:
登录到 Azure 数据资源管理器 Web UI。
在左侧菜单中,选择 “查询”。
以下查询提供了常见报告的示例,但你可以自定义这些报告来满足需求并创建其他报告。
还可以在 Excel 中查看报表,方法是选择“导出”选项卡,然后选择“在 Excel 中打开”。
示例:为特定快照日期的直接分配和组分配生成应用角色分配
此报表提供谁有权访问目标应用以及何时访问的目标应用。 可以使用它进行安全审核、合规性验证以及了解组织中的访问模式。
以下查询针对Microsoft Entra ID 中的特定应用程序,并分析特定日期的角色分配。 查询检索直接和基于组的角色分配。 它将此数据与表中的用户详细信息 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 a string for easier comparison
// Prepare user data from the EntraUsers table
let users = EntraUsers
| project ObjectID, UserPrincipalName, DisplayName, ObjectIDStr = tostring(ObjectID); // Include ObjectID as string for joining
// Prepare role data from the 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 EntraGroupMemberships on $left.PrincipalId == $right.GroupId // Join with group membership
| mvexpand Members // Expand group members
| extend MembersStr = tostring(Members) // Convert the member ID to a 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
示例:生成包含 Microsoft 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()
示例:查看在两个数据快照日期之间添加到应用的用户
这些报告提供两个日期之间哪些用户收到了目标应用程序的应用角色分配的视图。 可以使用这些报表跟踪应用访问随时间推移的更改。
此查询面向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"
各种使用访问评审的查询示例
查看访问权限评审的完成情况和时间线信息
数据上传后,请使用以下 Kusto 查询查看数据。
上次访问评审周期何时完成? 需要多长时间?
ReviewInstances | summarize LastCompletedDate = max(ReviewInstanceEndDateTime), ReviewDuration = datetime_diff('minute', max(ReviewInstanceEndDateTime), min(ReviewInstanceStartDateTime))
访问评审过程是否按时间(例如季度)进行?
ReviewInstances | extend ExpectedFrequency = "Quarterly" // Replace with the 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 nonresponders | 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 nonresponders | 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 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 nonresponsive 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 Graph 自动执行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
报告来自更多源的数据
还可以将数据从 Microsoft Entra 以外的源引入 Azure 数据资源管理器。 此功能的方案包括:
- 管理员希望查看审核日志中的事件,其中包含有关用户、访问包或其他不属于审核记录本身的对象的其他详细信息。
- 管理员希望查看从Microsoft Entra ID 添加到应用程序的所有用户及其在应用程序自己的存储库(例如 SQL 数据库)中的访问权限。
有关详细信息,请参阅 使用来自其他源的数据在 Azure 数据资源管理器中创建自定义报表。