教程:Azure 数据资源管理器中使用 Microsoft Entra ID 中的数据创建的自定义报告
本教程介绍如何使用 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) 根据组织的要求生成自定义报告。
请执行以下步骤来创建这些报告:
- 在 Azure 订阅中设置 Azure 数据资源管理器,或创建免费的群集。
- 使用 PowerShell 脚本和 Microsoft Graph 从 Microsoft Entra 中提取数据。
- 将数据导入 Azure 数据资源管理器,这是一项快速且可缩放的数据分析服务。
- 使用 Kusto 查询语言生成自定义查询。
在本教程结束时,你将掌握使用 Microsoft 支持的工具跨不同应用程序开发用户访问权限的自定义视图的技能。 还可以从第三方数据库或应用程序引入数据,以便对其进行报告。
先决条件
对于 Azure 数据资源管理器的服务级别协议支持的生产使用,您需要 Azure 订阅来托管完整的 Azure 数据资源管理器群集。
确定要在报告中包含的数据。 本文中的脚本提供了一些示例,其中包含来自 Entra 的用户、组和应用程序的特定数据。 这些示例旨在说明可以使用此方法生成的报告类型,但具体报告需求可能会有所不同,需要不同或额外的数据。 可以从这些对象开始,并随着时间的推移引入更多种类的 Microsoft Entra 对象。
- 本文演示如何以已登录用户的身份从 Microsoft Entra 中检索数据。 为此,请确保具有从 Microsoft Entra 中检索数据所需的角色分配。 需要具有适当权限的角色才能导出要使用的 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 模块的文件系统目录以及导出的 Entra 数据的保存位置具有写入访问权限。
- 确保具有从除 Microsoft Entra 以外的其他数据源检索数据的权限。
1:设置 Azure 数据资源管理器
如果以前尚未使用过 Azure 数据资源管理器,则需要先进行设置。 可以创建需要 Azure 订阅的完整群集。 请参阅快速入门:创建 Azure 数据资源管理器群集和数据库以开始使用。
2:使用 PowerShell 连接到 Microsoft Graph 并提取 Entra 数据
在本部分中,你将安装 Microsoft Graph PowerShell 模块并连接到 Microsoft Graph。
组织首次将这些模块用于此应用场景时,需要具有全局管理员角色才能允许同意将 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。
Connect-MgGraph -Environment China -ClientId 'YOUR_CLIENT_ID' -TenantId 'YOUR_TENANT_ID' -Scopes "User.Read.All", "Group.Read.All", "Application.Read.All", "Directory.Read.All" -ContextScope Process
此命令提示你使用 Microsoft Entra 凭证登录。 登录后,如果是第一次连接或需要新权限,则可能需要同意所需权限。
用于提取在 Azure 数据资源管理器中生成自定义报告所需的数据的 PowerShell 查询
以下查询使用 PowerShell 从 Microsoft Graph 中提取 Entra 数据,并将数据导出到 JSON 文件,这些文件将在随后的第 3 部分导入到 Azure 数据资源管理器中。 使用这种类型的数据生成报告可能有多种情况:
- 审核员希望看到一份报告,其中列出了 10 个组的组成员,按成员的部门组织。
- 审核员希望看到在两个日期之间有权访问应用程序的所有用户的报告。
还可以将数据从除 Microsoft Entra 以外的其他源引入 Azure 数据资源管理器。 这可实现以下应用场景:
- 管理员希望查看从 Microsoft Entra ID 添加到某个应用程序的所有用户及其在应用程序自身存储库(如 SQL 数据库)中的访问权限。
这些类型的报告没有内置到 Microsoft Entra ID 中,但可以通过从 Entra 中提取数据并在Azure 数据资源管理器中使用自定义查询进行组合来自行创建这些报告。
在本教程中,我们将从几个区域提取 Entra 数据:
- 用户信息,例如显示名称、UPN 和作业详细信息
- 组信息
- 应用程序和角色分配
通过此数据集,我们能够对有权访问应用程序、角色信息和关联时间范围的人员执行一组广泛的查询。 请注意,这些是示例查询,你的数据和特定要求可能与此处显示的内容有所不同。
注意
较大的租户可能会遇到 Microsoft Graph 模块将处理的限制/429 错误。 Azure 数据资源管理器也可能会限制文件上传大小。
在这些 PowerShell 脚本中,我们将所选属性从 Entra 对象导出到 JSON 文件。 然后,这些导出属性中的数据将用于在 Azure 数据资源管理器中生成自定义报告。 这些示例中包含了以下特定属性,因为我们使用此数据来说明可以在 Azure 数据资源管理器中创建的报告类型。 由于你的具体报告需求可能与下面所示的内容不同,因此应在这些脚本中包含想要在报告中查看的特定属性,但可以遵循下面所示的相同模式来帮助生成脚本。
选择快照日期
我们还在下面包含了一个硬编码的快照日期,该日期将 JSON 文件中的数据标识为特定日期,这样我们便能在 Azure 数据资源管理器中跟踪类似数据集。 快照日期还可用于比较两个快照日期之间的数据更改。
$SnapshotDate = "2024-01-11"
获取 Entra 用户数据
此脚本会将所选属性从 Entra 用户对象导出到 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 数据
为 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:将 JSON 文件数据导入 Azure 数据资源管理器
在此部分中,我们将导入新创建的 JSON 文件以供进一步分析。
在 Azure 数据资源管理器群集或免费群集中设置数据库后,如本文档的第一部分所述,导航到该数据库。
- 登录到 Azure 数据资源管理器 Web UI。
- 从左侧菜单中选择“查询”。
接下来,针对每个导出的 JSON 文件执行以下步骤,以将导出的数据放入该 Azure 数据资源管理器数据库。
右键单击要在其中引入数据的数据库的数据库名称。 选择“获取数据” 。
从可用列表中选择数据源。 此教程将从本地文件引入数据。
选择“+ 新表”并基于要导入的 JSON 文件的名称输入表名。例如,如果要导入 EntraUsers.json,请将表命名为 EntraUsers。 第一次导入后,该表将已存在,可以将其选择为后续导入的目标表。
选择“浏览文件”,选择 JSON 文件,然后选择“下一步”。
Azure 数据资源管理器将自动检测架构并在“检查”选项卡中提供预览。单击“完成”以创建表并从该文件导入数据。
对在第一部分中生成的每个 JSON 文件重复上述每个步骤。
4:使用 Azure 数据资源管理器生成自定义报表
借助 Azure 数据资源管理器中现有的数据,可以根据业务需求开始创建自定义报告。
Azure 数据资源管理器是一种功能强大的数据分析工具,高度可缩放且灵活,为生成自定义的用户访问报告提供了理想的环境。 Azure 数据资源管理器使用 Kusto 查询语言 (KQL)。
以下查询提供了常见报告的示例,但你可以自定义这些报告来满足需求并创建其他报告。
示例 1:为特定快照日期的直接分配和组分配生成应用角色分配
此报告提供了一个视图,显示谁有权访问以及何时访问目标应用,可用于安全审核、合规性验证以及了解组织中的访问模式。
此查询面向 Entra AD 中的特定应用程序,并分析截至某个日期的角色分配。 该查询检索直接和基于组的角色分配,将此数据与 EntraUsers 表中的用户详细信息和 AppRoles 表中的角色信息合并。
/// 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"
从其他源引入数据
还可以在 Azure 数据资源管理器中创建其他表,以便从其他源引入数据。 如果数据位于 JSON 文件中(类似于上述示例)或 CSV 文件中,则可以在首次从文件获取数据时创建表。
有关数据引入的更多信息,请参阅 Azure 数据资源管理器数据引入概述。
示例 4:合并来自 Entra 的应用分配和第二个源,为在两个日期之间有权访问应用程序的所有用户创建报告
此报告说明了如何合并两个单独系统中的数据来在 Azure 数据资源管理器中创建自定义报告。 它将有关用户、其角色和其他属性的数据从两个系统聚合为统一格式进行分析或报告。
此示例假定有一个名为 salesforceAssignments
的表,该表通过从另一个应用程序引入数据来填充。
// 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()
)