Tutorial: Relatórios personalizados no Azure Data Explorer (ADX) usando dados do Microsoft Entra ID
Neste tutorial, você aprenderá a criar relatórios personalizados no Azure Data Explorer (ADX) usando dados do Microsoft Entra ID. Este tutorial complementa outras opções de relatório, como Arquivar e relatar com o Azure Monitor e o gerenciamento de direitos, que se concentram na exportação de dados de log de auditoria para maior retenção e análise. Em comparação, a exportação de dados do Microsoft Entra ID para o Azure Data Explorer oferece maior flexibilidade para criar relatórios personalizados, permitindo a agregação de dados de várias fontes com escalabilidade maciça e políticas flexíveis de esquema e retenção.
Este relatório ilustra como mostrar a configuração, os usuários e os direitos de acesso exportados do Microsoft Entra juntamente com os dados exportados de outras fontes, como aplicativos com bancos de dados SQL. Em seguida, você pode usar a Linguagem de Consulta Kusto (KQL) para criar relatórios personalizados com base nos requisitos da sua organização. Gerar esses tipos de relatórios no Azure Data Explorer pode ser especialmente útil se você precisar reter dados de acesso por períodos mais longos, executar investigações ad hoc ou precisar executar consultas personalizadas nos dados de acesso do usuário.
Você executará as seguintes etapas para criar estes relatórios:
- Configurar o Azure Data Explorer em uma assinatura do Azure.
- Extrair dados do Microsoft Entra e de bancos de dados ou aplicativos de terceiros usando scripts do PowerShell e o MS Graph.
- Importar os dados para o Azure Data Explorer, um serviço de análise de dados rápido e escalonável.
- Criar uma consulta personalizada usando a Linguagem de Consulta Kusto.
Ao final deste tutorial, você será capaz para desenvolver exibições personalizadas dos direitos de acesso e permissões de usuários em diferentes aplicativos usando ferramentas com suporte da Microsoft.
Pré-requisitos
Certifique-se de ter as permissões necessárias. Você precisará das permissões certas para exportar o tipo de dados do Entra com os quais deseja trabalhar e permissões para salvar arquivos JSON exportados.
- Dados do usuário: Administrador de Segurança, Administrador de Função com Privilégios, Administrador Global
- Dados de grupos: Administrador Global, Administrador de Funções Com Privilégios, Administrador de Grupo
- Aplicativos/Atribuição de Função de Aplicativo: Administrador Global, Administrador de Função com Privilégios, Administrador de Aplicativos, Administrador de Aplicativos de Nuvem
O PowerShell deve ser definido para permitir User.Read.All, Group.Read.All, Application.Read.All e Directory.Read.All. Confira Referência de permissões do Microsoft Graph para saber informações adicionais.
Verifique se você tem acesso de gravação no diretório em que instalará os módulos necessários do PowerShell do MS Graph e onde os dados do Entra exportados serão salvos.
Determine quais dados você deseja incluir em seus relatórios. Os scripts neste artigo fornecem exemplos com dados específicos de usuários, grupos e aplicativos do Entra. Esses exemplos servem para ilustrar os tipos de relatórios que você pode gerar com essa abordagem, mas suas necessidades específicas de relatório podem variar e exigir dados diferentes ou adicionais.
Etapa 1: configurar o Azure Data Explorer
Se você ainda não usou o Azure Data Explorer anteriormente, precisará configurá-lo primeiro. Você pode criar um cluster gratuito sem uma assinatura do Azure ou cartão de crédito ou um cluster completo que requer uma assinatura do Azure. Início Rápido: criar um cluster e um banco de dados no Azure Data Explorer para começar.
Etapa 2: conectar-se ao MS Graph e extrair dados do Entra com o PowerShell
Instale os módulos do PowerShell do MS Graph e conecte-se ao MS Graph.
- Instale os módulos do MS Graph necessários. Os módulos a seguir são necessários para este tutorial: 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
}
- Importe os módulos:
$modules = @('Microsoft.Graph.Users', 'Microsoft.Graph.Groups', 'Microsoft.Graph.Applications', 'Microsoft.Graph.DirectoryObjects')
foreach ($module in $modules) {
Import-Module -Name $module
}
- Conectar o Microsoft Graph
Connect-MgGraph -Scopes "User.Read.All", "Group.Read.All", "Application.Read.All", "Directory.Read.All"
Esse comando solicitará que você entre com suas credenciais do MS Graph. Selecione Permissões Necessárias: depois de entrar, talvez seja necessário consentir com as permissões necessárias se for a primeira vez que se conectar ou se novas permissões forem necessárias.
Consultas do PowerShell para extrair dados necessários para criar relatórios personalizados no ADX
As consultas a seguir extraem dados do Entra do MS Graph usando o PowerShell e exportam os dados para arquivos JSON que serão importados para o Azure Data Explorer na Etapa 3. Pode haver vários cenários para gerar relatórios com esse tipo de dados:
- Um auditor gostaria de ver um relatório que lista os membros do grupo para 10 grupos, organizados pelo departamento dos membros.
- Um auditor gostaria de ver um relatório de todos os usuários que tiveram acesso a um aplicativo entre duas datas.
- Um administrador gostaria de exibir todos os usuários adicionados a um aplicativo a partir de bancos de dados SQL e do Microsoft Entra ID.
Esses tipos de relatórios não são integrados ao Microsoft Entra ID, mas você pode criar esses relatórios por conta própria extraindo dados do Entra e combinando-os usando consultas personalizadas no Azure Data Explorer.
Para este tutorial, extrairemos dados do Entra de várias áreas:
- Informações do usuário, como nome de exibição, UPN e detalhes do trabalho
- Informações do grupo
- Atribuições de aplicativo e função
Esse conjunto de dados nos permitirá executar um amplo conjunto de consultas em torno de quem recebeu acesso a um aplicativo, informações de função e o período de tempo associado. Observe que essas são consultas de exemplo, e seus dados e requisitos específicos podem variar do que é mostrado aqui.
Observação
Locatários maiores podem apresentar limitação / erros 429 que serão tratados pelo módulo do MS Graph.
Nesses scripts do PowerShell, exportaremos propriedades selecionadas dos objetos do Entra para arquivos JSON. Os dados dessas propriedades exportadas serão usados para gerar relatórios personalizados no Azure Data Explorer. As propriedades específicas abaixo foram incluídas nestes exemplos, pois estamos usando esses dados para ilustrar os tipos de relatórios que você pode criar no Azure Data Explorer. Como suas necessidades de relatório específicas provavelmente variam do que é mostrado abaixo, você deve incluir nesses scripts as propriedades específicas que você está interessado em exibir em seus relatórios, no entanto, você pode seguir o mesmo padrão mostrado abaixo para ajudar a criar seus scripts.
Também incluímos uma data de instantâneo embutida no código abaixo, que identifica os dados no arquivo JSON com uma data específica e nos permitirá acompanhar conjuntos de dados semelhantes ao longo do tempo no Azure Data Explorer. A data do instantâneo também é útil para comparar alterações nos dados entre duas datas de instantâneo.
Obter dados do usuário do Entra
Esse script exportará as propriedades selecionadas do objeto de usuário do Entra para um arquivo JSON. Importaremos esses dados para o Azure Data Explorer na Etapa 3.
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
Obter dados de grupos
Gere um arquivo JSON com os nomes de grupos e as IDs que serão usados para criar exibições personalizadas no ADX. O exemplo incluirá todos os grupos, mas uma filtragem adicional poderá ser incluída, se necessário. Se você estiver filtrando para incluir apenas determinados grupos, talvez queira incluir lógica em seu script para verificar se há grupos aninhados.
# 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"
Obter dados de associações de grupo
Gere um arquivo JSON com associação de grupo que será usado para criar exibições personalizadas no 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"
Obter dados de aplicativos e entidades de serviço
Gera o arquivo JSON com todos os aplicativos e entidades de serviço correspondentes no locatário. Importaremos esses dados para o ADX na Etapa 3, o que nos permitirá gerar relatórios personalizados relacionados a aplicativos com base nesses dados.
# 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"
Obter dados do AppRole
Gere um arquivo JSON de todos os appRoles para aplicativos empresariais no Entra. Depois de importados para o ADX, utilizaremos esses dados para gerar relatórios envolvendo atribuições de função de aplicativo para usuários.
# 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'
Obter dados de Atribuição do AppRole
Gere um arquivo JSON de todas as atribuições de função de aplicativo no locatário.
$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"
Etapa 3: importar dados de arquivo JSON para o Azure Data Explorer
Na Etapa 3, importaremos os arquivos JSON recém-criados para análise adicional. Se você ainda não configurou o Azure Data Explorer, consulte a Etapa 1 acima.
O Azure Data Explorer é uma poderosa ferramenta de análise de dados altamente escalonável e flexível, fornecendo um ambiente ideal para gerar relatórios personalizados de acesso do usuário. O ADX usa a Linguagem de Consulta Kusto (KQL).
Depois de configurar um banco de dados, siga estas etapas para colocar seus dados exportados no ADX.
- Clique com o botão direito do mouse no nome do banco de dados e escolha Obter Dados
- Escolha Nova Tabela e insira o nome do arquivo JSON que você está importando. Por exemplo, se você estiver importando EntraUsers.json, nomeie a tabela EntraUsers. Após a primeira importação, a tabela já existirá e você poderá selecioná-la como a tabela de destino para a importação.
- Selecione o arquivo JSON.
- O ADX detectará automaticamente o esquema e fornecerá uma visualização. Clique em Concluir para criar a tabela e importar os dados.
- Siga as etapas 1 a 4 para cada um dos arquivos JSON gerados na Etapa 1.
Etapa 4: usar o ADX para criar relatórios personalizados
Com os dados agora disponíveis no ADX, você está pronto para começar a criar relatórios personalizados com base nos requisitos do seu negócio. As consultas a seguir fornecem exemplos de relatórios comuns, mas você pode personalizar esses relatórios para atender às suas necessidades e criar relatórios adicionais.
Exemplo 1: gerar atribuições de função de aplicativo para atribuições diretas e de grupo para uma data de instantâneo específica
Este relatório fornece uma visão de quem teve acesso ao aplicativo de destino e em que data, e pode ser usado para auditorias de segurança, verificação de conformidade e compreensão de padrões de acesso dentro da organização.
Essa consulta tem como alvo um aplicativo específico no Entra AD e analisa as atribuições de função a partir de uma determinada data. A consulta recupera atribuições de função diretas e baseadas em grupo, mesclando esses dados com detalhes do usuário da tabela EntraUsers e informações de função da tabela 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
Exemplo 2: criar Relatório de Auditor Básico com dados do Entra mostrando quem teve acesso a um aplicativo entre essas duas datas
Este relatório fornece uma visão de quem teve acesso ao aplicativo de destino entre duas datas e pode ser usado para auditorias de segurança, verificação de conformidade e compreensão de padrões de acesso dentro da organização.
Essa consulta tem como alvo um aplicativo específico no Microsoft Entra ID e analisa as atribuições de função entre duas datas. A consulta recupera atribuições de função diretas da tabela AppRoleAssignments e mescla esses dados com detalhes do usuário da tabela EntraUsers e informações de função da tabela 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()
Exemplo 3: obter usuários adicionados a um aplicativo entre duas datas de instantâneo de dados
Esses relatórios fornecem uma exibição de quais usuários receberam uma atribuição de função de aplicativo para o aplicativo de destino entre duas datas. Esses relatórios podem ser usados para acompanhar as alterações no acesso ao aplicativo ao longo do tempo.
Essa consulta tem como alvo um aplicativo específico dentro da Microsoft Entra ID e altera as atribuições de função entre uma data de início e de término.
// 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"
Exemplo 4: combinar atribuições de aplicativo de um Entra e uma segunda origem (por exemplo, exportação de SQL) para criar um relatório de todos os usuários (atribuições do Entra e atribuições locais) que tiveram acesso ao Salesforce entre duas datas
Este relatório ilustra como você pode combinar dados de dois sistemas separados para criar relatórios personalizados no ADX. Ele agrega dados sobre usuários, suas funções e outros atributos de dois sistemas em um formato unificado para análise ou relatório.
// 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()
)