教學課程:Azure 資料總管 (ADX) 中使用 Microsoft Entra ID 資料的自訂報告
在本教學課程中,您將了解如何使用 Microsoft Entra ID 資料以在 Azure 資料總管 (ADX) 中建立自訂報告。 本教學課程可補充其他報告選項,例如使用 Azure 監視器和權利管理來封存和報告,其著重於匯出稽核記錄資料以取得較長的保留和分析。 相較之下,將 Microsoft Entra ID 資料匯出至 Azure 資料總管可提供更大的彈性來建立自訂報告,方法是允許具有大規模可擴縮性以及彈性架構和保留原則的多個來源的資料彙總。
此報告說明如何顯示從 Microsoft Entra 匯出的設定、使用者和存取權限,以及從其他來源匯出的資料,例如具有 SQL 資料庫的應用程式。 接著,您可以使用 Kusto 查詢語言 (KQL) 以根據組織的需求來建置自訂報告。 如果您需要將存取資料保留較長的時間、執行臨機操作調查,或需要對使用者存取資料執行自訂查詢,則在 Azure 資料總管內產生這些類型的報告可能會特別有用。
您將採取下列步驟來建立這些報告:
- 在 Azure 訂用帳戶中設定 Azure 資料總管。
- 使用 PowerShell 指令碼和 MS Graph,以從 Microsoft entra 和第三方資料庫或應用程式擷取資料。
- 將資料匯入至 Azure 資料總管,這是快速且可調整的資料分析服務。
- 使用 Kusto 查詢語言來建置自訂查詢。
本教學課程結束時,您將已建置技能,以使用 Microsoft 所支援的工具跨不同的應用程式來開發使用者存取權限和權限的自訂檢視。
必要條件
請確定您具有必要權限。 您將需要可匯出所想要處理之 Entra 資料類型的正確權限,以及可儲存所匯出 JSON 檔案的權限。
- 使用者資料:全域系統管理員、特殊權限角色系統管理員、使用者系統管理員
- 群組資料:全域系統管理員、特殊權限角色系統管理員、群組系統管理員
- 應用程式角色指派:全域系統管理員、特殊權限角色系統管理員、應用程式系統管理員、雲端應用程式系統管理員
PowerShell 必須設定為允許 User.Read.All、Group.Read.All、Application.Read.All 和 Directory.Read.All。 如需其他資訊,請參閱 Microsoft Graph 權限參考。
請確定您具有可安裝必要 MS Graph PowerShell 模組的目錄寫入權,以及所匯出 Entra 資料的儲存位置。
判斷您想要在報告中包括的資料。 本文中的指令碼提供具有 Entra 使用者、群組和應用程式特定資料的範例。 這些範例旨在說明您可以使用此方式產生的報告類型,但您的特定報告需求可能會不同,而且需要不同或其他資料。
步驟 1:設定 Azure 資料總管
如果您先前尚未使用過 Azure 資料總管,則需要先進行此設定。 您可以建立沒有 Azure 訂用帳戶或信用卡的免費叢集,或需要 Azure 訂用帳戶的完整叢集。 請參閱快速入門:建立 Azure 資料總管叢集和資料庫,以開始使用。
步驟 2:使用 PowerShell 連線至 MS Graph 和擷取 Entra 資料
安裝 MS Graph PowerShell 模組和連線至 MS Graph。
- 安裝必要的 MS Graph 模組。 本教學課程需要下列模組: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
}
- 匯入模組:
$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 -Scopes "User.Read.All", "Group.Read.All", "Application.Read.All", "Directory.Read.All"
此命令將會提示您使用 MS Graph 認證進行登入。 選取 [必要權限:登入之後],如果您是第一次連線,或需要新的權限,則可能需要同意必要權限。
PowerShell 查詢以擷取在 ADX 中建置自訂報告所需的資料
下列查詢會使用 PowerShell 以從 MS Graph 擷取 Entra 資料,並將資料匯出至將在步驟 3 中匯入至 Azure 資料總管的 JSON 檔案。 產生具有此資料類型的報告時,可能會有多個案例:
- 稽核者所想要看到的報告是列出 10 個依成員部門所組織的群組的群組成員。
- 稽核者想要查看已在兩個日期之間存取應用程式的所有使用者的報告。
- 系統管理員想要從 Microsoft Entra ID 和 SQL 資料庫檢視所有新增至應用程式的使用者。
這些類型的報告未內建至 Microsoft Entra ID,但您可以從 Entra 擷取資料,並在 Azure 資料總管中使用自訂查詢將其合併,以自行建立這些報告。
在本教學課程中,我們將從數個區域擷取 Entra 資料:
- 使用者資訊,例如顯示名稱、UPN 和工作詳細資料
- 群組資訊
- 應用程式和角色指派
此資料集可讓我們針對獲授與存取應用程式、角色資訊和相關聯時間範圍的人員執行一組廣泛的查詢。 請注意,這些是範例查詢,而您的資料和特定需求可能會與此處所顯示的內容不同。
注意
較大的租用戶可能會遇到 MS Graph 模組將會處理的節流/429 錯誤。
在這些 PowerShell 指令碼中,我們會將所選取的屬性從 Entra 物件匯出至 JSON 檔案。 接著將會使用這些所匯出屬性中的資料,以在 Azure 資料總管中產生自訂報告。 下面的特定屬性之前包括在這些範例中,因為我們要使用此資料來說明您可以在 Azure 資料總管中建立的報告類型。 因為您的特定報告需求可能會與下面所顯示的內容不同,所以您應該在這些指令碼中包括您想要在報告中檢視的特定屬性,不過,您可以遵循下面所顯示的相同模式來協助建置指令碼。
我們也包括下面的硬式編碼「快照集日期」,其會識別 JSON 檔案中具有特定日期的資料,並將允許我們在 Azure 資料總管中追蹤一段時間的類似資料集。 快照集日期也有助於比較兩個快照日期之間的資料變更。
取得 Entra 使用者資料
此指令碼會將所選取的屬性從 Entra 使用者物件匯出至 JSON 檔案。 我們將在步驟 3 中將此資料匯入至 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"] = "2024-01-11"
[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
取得群組資料
產生具有群組名稱和識別碼的 JSON 檔案,以用來在 ADX 中建立自訂檢視。 此範例將會包括所有群組,但如有需要,也可以包括其他篩選。 若要篩選成只包括特定群組,則可能會想要在指令碼中包括邏輯來檢查巢狀群組。
# 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 檔案,以用來在 ADX 中建立自訂檢視。
# 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
}
# 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 檔案,而此檔案包含租用戶中的所有應用程式和對應的服務主體。 我們將在步驟 3 中將此資料匯入至 ADX,以允許我們根據此資料來產生與應用程式相關的自訂報告。
# 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
}
} | ConvertTo-Json -Depth 10 | Set-Content "Applications.json"
取得 AppRole 資料
針對 Entra 中的企業應用程式產生所有 appRoles 的 JSON 檔案。 匯入至 ADX 之後,我們將使用此資料來產生使用者應用程式角色指派的報告。
# 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
}
}
# 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 = "2024-03-13" # Hard-coded date
}
}
}
$result | ConvertTo-Json -Depth 10 | Out-File "AppRoleAssignments.json"
步驟 3:將 JSON 檔案資料匯入至 Azure 資料總管
在步驟 3 中,我們將匯入新建立的 JSON 檔案,以進行進一步分析。 如果您尚未設定 Azure 資料總管,則請參閱上面的步驟 1。
Azure 資料總管是一種功能強大的資料分析工具,可進行高度調整且具有彈性,以提供產生自訂使用者存取報告的理想環境。 ADX 會使用 Kusto 查詢語言 (KQL)。
設定資料庫之後,請遵循下列步驟,以將所匯出的資料放入 ADX。
- 以滑鼠右鍵按一下資料庫名稱,然後選擇 [取得資料]
- 選擇 [新增資料表],然後輸入您要匯入的 JSON 檔案名稱。例如,若要匯入EntraUsers.json,則請將資料表命名為 EntraUsers。 第一次匯入之後,資料表就已經存在,而且您可以將其選取為匯入的目標資料表。
- 選取 JSON 檔案。
- ADX 將會自動偵測結構描述,並提供預覽。 按一下 [完成] 以建立資料表並匯入資料。
- 針對您已在步驟 1 中產生的每個 JSON 檔案,遵循步驟 1-4 的步驟 1。
步驟 4:使用 ADX 來建置自訂報告
現在,ADX 中有了可用的資料之後,您就可以根據業務需求來開始建立自訂報告。 下列查詢提供一般報告的範例,但您可以自訂這些報告以符合您的需求,並建立其他報告。
範例 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"
範例 4:合併 Entra 與第二個來源 (例如 SQL 匯出) 中的應用程式指派,以建立已在兩個日期之間存取 Salesforce 之所有使用者的報告 (Entra 指派和本機指派)
此報告說明如何合併兩個不同系統的資料,以在 ADX 中建立自訂報告。 其會將使用者、其角色和其他屬性的相關資料從兩個系統彙總成統一格式,以進行分析或報告。
// 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()
)