チュートリアル: Microsoft Entra ID のデータを使用して Azure Data Explorer (ADX) でレポートをカスタマイズする
このチュートリアルでは、Microsoft Entra ID のデータを使用して、Azure Data Explorer (ADX) でカスタマイズされたレポートを作成する方法について説明します。 このチュートリアルは、より長い保持期間と分析のための監査ログ データのエクスポートに重点を置いた「Azure Monitor を使用したアーカイブとレポート - エンタイトルメント管理」などの他のレポート オプションを補完するものです。 Microsoft Entra ID データを Azure Data Explorer にエクスポートする方法は、他の方法と比較すると、大規模なスケーラビリティを備えた複数のソースからのデータ集計と、柔軟なスキーマと保持ポリシーによって、カスタム レポートを作成する柔軟性が向上します。
このレポートでは、Microsoft Entra からエクスポートされた構成、ユーザー、アクセス権を、SQL データベースを使用するアプリケーションなどの他のソースからエクスポートされたデータと共に表示する方法を示します。 その後、Kusto クエリ言語 (KQL) を使用して、組織の要件に基づいてカスタム レポートを作成できます。 Azure Data Explorer 内でこれらの種類のレポートを生成することは、アクセス データを長期間保持する必要がある場合、アドホック調査を実行する必要がある場合、またはユーザー アクセス データに対してカスタム クエリを実行する必要がある場合に特に役立ちます。
これらのレポートを作成するには、次の手順を実行します。
- Azure サブスクリプションで Azure Data Explorer を設定します。
- PowerShell スクリプトと MS Graph を使用して、データ抽出を Microsoft Entra およびサードパーティのデータベースまたはアプリケーションから行います。
- 高速でスケーラブルなデータ分析サービスである Azure Data Explorer にデータをインポートします。
- Kusto クエリ言語を使用してカスタム クエリを作成します。
このチュートリアルの終わりまでに、Microsoft でサポートされているツールを使用して、さまざまなアプリケーション間でユーザーのアクセス権とアクセス許可のカスタマイズされたビューを開発するスキルが得られます。
前提条件
必要なアクセス許可を持っていることを確認します。 使用する Entra データの種類をエクスポートするための適切なアクセス許可と、エクスポートされた JSON ファイルを保存するための適切なアクセス許可が必要です。
- ユーザー データ: グローバル管理者、特権ロール管理者、ユーザー管理者
- グループ データ: グローバル管理者、特権ロール管理者、グループ管理者
- アプリケーション/アプリ ロールの割り当て: グローバル管理者、特権ロール管理者、アプリケーション管理者、クラウド アプリケーション管理者
User.Read.All、Group.Read.All、Application.Read.All、Directory.Read.All を許可するように、PowerShell を設定する必要があります。 詳細については、「Microsoft Graph のアクセス許可のリファレンス」を参照してください。
必要な MS Graph PowerShell モジュールをインストールし、エクスポートした Entra データを保存するディレクトリへの書き込みアクセス権があることを確認します。
レポートに含めるデータを決定します。 この記事のスクリプトでは、Entra のユーザー、グループ、アプリケーションからの特定のデータを持つサンプルで提供します。 これらのサンプルは、この方法で生成できるレポートの種類を示すことを目的としたものです。レポート作成のニーズはさまざまであり、それに応じて異なるデータや追加のデータが必要になる場合があります。
手順 1: Azure Data Explorer をセットアップする
Azure Data Explorer をまだ使用していない場合は、まずこれを設定する必要があります。 Azure サブスクリプション、クレジット カード、または Azure サブスクリプションを必要とする完全なクラスターを使用せずに、無料のクラスターを作成できます。 開始するには、「クイック スタート: Azure Data Explorer クラスターとデータベースを作成する」を参照してください。
手順 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 資格情報を使用してサインインするように求められます。 必要なアクセス許可の選択: サインイン後、初めて接続する場合、または新しいアクセス許可が必要な場合は、必要なアクセス許可への同意を求められることがあります。
ADX でカスタム レポートを作成するために必要なデータを抽出するための PowerShell クエリ
次のクエリでは、PowerShell を使用して MS Graph から Entra データを抽出し、手順 3 で Azure Data Explorer にインポートされる JSON ファイルにデータをエクスポートします。 この種類のデータを使用してレポートを生成するシナリオは複数あります。
- ある監査担当者は、10 個のグループのグループ メンバーを、メンバーの部署別に一覧表示するレポートを表示したいと考えます。
- ある監査担当者は、2 つの日付の間にアプリケーションにアクセスしたすべてのユーザーのレポートを表示したいと考えます。
- ある管理者は、Microsoft Entra ID と SQL データベースからアプリケーションに追加されたすべてのユーザーを表示したいと考えます。
これらの種類のレポートは Microsoft Entra ID には組み込まれていませんが、Entra からデータを抽出し、Azure Data Explorer でカスタム クエリを使用してそれらを組み合わせることで、これらのレポートを自分で作成できます。
このチュートリアルでは、いくつかの領域から Entra データを抽出します。
- 表示名、UPN、ジョブの詳細などのユーザー情報
- グループ情報
- アプリケーションとロールの割り当て
このデータ セットを使用すると、アプリケーション、ロール情報、および関連する期間へのアクセス権が付与されたユーザーに関する広範なクエリのセットを実行できます。 これらはサンプル クエリであり、データと要件は、ユーザーのニーズに応じてここに示すものと異なることに注意してください。
Note
大規模なテナントでは、MS Graph モジュールによって処理される調整 / 429 エラーが発生する可能性があります。
これらの PowerShell スクリプトでは、選択したプロパティを Entra オブジェクトから JSON ファイルにエクスポートします。 これらのエクスポートされたプロパティのデータは、Azure Data Explorer でカスタム レポートを生成するために使用されます。 次の特定のプロパティは、これらの例に含まれるものであり、Azure Data Explorer で作成できるレポートの種類を示す目的で、このデータを使用しています。 レポート作成のニーズはさまざまであり、次に示す内容とは異なる可能性があるため、作成するレポートに表示する特定のプロパティを選んでこれらのスクリプトに含める必要があります。ただし、スクリプトの構築を練習する目的で、次と同じパターンに従うことができます。
また、特定の日付で JSON ファイル内のデータを識別するハードコーディングされた以下のスナップショット日付も含まれています。これにより、Azure Data Explorer で同様のデータ セットを時系列で追跡できます。 スナップショット日付は、2 つのスナップショット日付間のデータの変更を比較する場合にも役立ちます。
Entra ユーザー データを取得する
このスクリプトは、選択したプロパティを Entra ユーザー オブジェクトから JSON ファイルにエクスポートします。 このデータは、手順 3 で Azure Data Explorer にインポートします。
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
グループ データを取得する
ADX でカスタム ビューを作成するために使用されるグループ名と ID を含む JSON ファイルを生成します。 サンプルにはすべてのグループが含まれますが、必要に応じて追加のフィルター処理を含められます。 特定のグループのみを含めるようにフィルター処理する場合は、入れ子になったグループを確認するロジックをスクリプトに含められます。
# 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"
グループ メンバーシップ データを取得する
ADX でカスタム ビューを作成するために使用されるグループ メンバーシップを持つ JSON ファイルを生成します。
# 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 でエンタープライズ アプリのすべての AppRole の 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 Data Explorer にインポートする
手順 3 では、さらに分析するために、新しく作成した JSON ファイルをインポートします。 Azure Data Explorer をまだセットアップしていない場合は、上記の手順 1 を参照してください。
Azure Data Explorer は、高度にスケーラブルで柔軟な強力なデータ分析ツールであり、カスタマイズされたユーザー アクセス レポートを生成するための理想的な環境を提供します。 ADX は、Kusto 照会言語 (KQL) を使用します。
データベースをセットアップしたら、次の手順に従って、エクスポートしたデータを ADX に取り込みます。
- データベース名を右クリックし、[データを取得] を選択します
- [新しいテーブル] を選択し、インポートする JSON ファイルの名前を入力します。たとえば、EntraUsers.json をインポートする場合は、テーブルに EntraUsers という名前を付けます。 最初のインポート後、テーブルは既に存在し、インポートのターゲット テーブルとして選択できます。
- JSON ファイルを選択します。
- ADX はスキーマを自動的に検出し、プレビューを提供します。 [完了] をクリックしてテーブルを作成し、データをインポートします。
- 手順 1 で生成した JSON ファイルごとに、手順 1 から 4 を繰り返します。
手順 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: これら 2 つの日付の間にアプリへのアクセス許可を持ったユーザーを示す Entra データを含む基本的な監査担当者レポートを作成する
このレポートでは、2 つの日付の間にターゲット アプリのアクセス許可を得たユーザーと許可の種類のビューを提供します。これはセキュリティ監査、コンプライアンス検証、組織内のアクセス パターンの理解に使用できます。
このクエリは、Microsoft Entra ID 内の特定のアプリケーションを対象とし、2 つの日付間のロールの割り当てを分析します。 このクエリでは、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: 2 つのデータ スナップショットの日付の間にアプリに追加されたユーザーを取得する
これらのレポートには、2 つの日付の間にターゲット アプリケーションにアプリ ロールの割り当てが与えられたユーザーのビューが表示されます。 これらのレポートは、時間の経過に伴うアプリ アクセスの変化を追跡するために使用できます。
このクエリは、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 からのアプリの割り当てと 2 つ目のソース (SQL エクスポートなど) を組み合わせて、2 つの日付の間に Salesforce へのアクセス許可を持ったすべてのユーザー (Entra の割り当てとローカルの割り当て) のレポートを作成する
このレポートは、2 つの異なるシステムのデータを組み合わせて、ADX でカスタム レポートを作成する方法を示しています。 2 つのシステムのユーザー、ロール、その他の属性に関するデータを、分析またはレポート用に統合された形式に集計します。
// 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()
)