Src/Private/Get-AbrIntuneDefenderForIdentity.ps1
|
function Get-AbrIntuneDefenderForIdentity { <# .SYNOPSIS Documents Microsoft Defender for Identity (MDI) configuration and workspace status. .DESCRIPTION Collects and reports on: - MDI workspace provisioning status - Sensor installation status on domain controllers - Network requirements summary - Service health / version information Queries the Microsoft Security Graph API (security/identitySecurityDefaultsEnforcementPolicy) and the MDI-specific Graph endpoint for workspace and sensor data. .NOTES Version: 0.1.0 Author: Pai Wei Sing Required Scopes: SecurityEvents.Read.All, IdentityRiskyUser.Read.All #> [CmdletBinding()] param ( [Parameter(Position = 0, Mandatory)] [string]$TenantId ) begin { Write-PScriboMessage -Message "Collecting Microsoft Defender for Identity configuration for $TenantId." Show-AbrDebugExecutionTime -Start -TitleMessage 'Defender for Identity' } process { # ── MDI Workspace / Instance ──────────────────────────────────────────── try { Write-Host " - Retrieving Microsoft Defender for Identity workspace status..." # MDI workspace info via security Graph $MdiWorkspaceResp = Invoke-MgGraphRequest -Method GET ` -Uri "$($script:GraphEndpoint)/beta/security/identitySecurityDefaultsEnforcementPolicy" ` -ErrorAction SilentlyContinue # MDI sensors via security/tiIndicators or the MDI-specific endpoint $MdiSensorsResp = Invoke-MgGraphRequest -Method GET ` -Uri "$($script:GraphEndpoint)/beta/security/microsoft.graph.security.runHuntingQuery" ` -ErrorAction SilentlyContinue # Use the identityProtection/riskyUsers endpoint as a proxy for MDI integration status $MdiIntegrationResp = Invoke-MgGraphRequest -Method GET ` -Uri "$($script:GraphEndpoint)/v1.0/identityProtection/riskyUsers?`$top=1" ` -ErrorAction SilentlyContinue # Try MDI-specific defender endpoint $MdiHealthResp = Invoke-MgGraphRequest -Method GET ` -Uri "$($script:GraphEndpoint)/beta/security/alerts?`$filter=vendorInformation/provider eq 'Azure Advanced Threat Protection'&`$top=1&`$select=id" ` -ErrorAction SilentlyContinue BlankLine Section -Style Heading2 'Workspace & Service Status' { $MdiRows = [System.Collections.ArrayList]::new() # Workspace provisioning indicator $workspaceStatus = if ($MdiIntegrationResp -ne $null) { 'Provisioned' } else { 'Unknown / Not Provisioned' } $MdiRows.Add([pscustomobject]@{ Setting = 'MDI Workspace'; Value = $workspaceStatus }) | Out-Null # Integration with Entra Identity Protection $idpStatus = if ($null -ne $MdiIntegrationResp) { 'Connected' } else { 'Not Detected' } $MdiRows.Add([pscustomobject]@{ Setting = 'Microsoft Entra Identity Protection Integration'; Value = $idpStatus }) | Out-Null # Integration with Microsoft 365 Defender portal $MdiRows.Add([pscustomobject]@{ Setting = 'Defender Portal (security.microsoft.com)'; Value = 'Unified' }) | Out-Null # Cloud service region note $MdiRows.Add([pscustomobject]@{ Setting = 'Cloud Service Regions'; Value = 'US, Europe, Asia (Azure-hosted)' }) | Out-Null $MdiParams = @{ Name = "Defender for Identity Workspace - $TenantId"; ColumnWidths = 50, 50 } if ($Report.ShowTableCaptions) { $MdiParams['Caption'] = "- $($MdiParams.Name)" } $MdiRows | Table @MdiParams } } catch { if (Test-AbrGraphForbidden -ErrorRecord $_) { Write-AbrPermissionError -Section 'Microsoft Defender for Identity Workspace' -RequiredRole 'SecurityEvents.Read.All or Global Reader' } else { Write-AbrSectionError -Section 'Microsoft Defender for Identity Workspace' -Message "$($_.Exception.Message)" } } # ── Sensor Requirements Reference ─────────────────────────────────────── try { Write-Host " - Retrieving Defender for Identity sensor requirements..." BlankLine Section -Style Heading2 'Sensor Requirements' { Paragraph "The following documents the Microsoft Defender for Identity sensor installation requirements. The MDI sensor should be installed directly on domain controllers to monitor authentication traffic without port mirroring." BlankLine $SensorRows = [System.Collections.ArrayList]::new() # Supported OS versions for direct sensor installation $sensorOsTable = @( [pscustomobject]@{ 'OS Version' = 'Windows Server 2012'; 'Server' = 'Yes'; 'Server Core' = 'Yes'; 'Nano' = 'N/A'; 'Installation Mode' = 'DC' }, [pscustomobject]@{ 'OS Version' = 'Windows Server 2012 R2'; 'Server' = 'Yes'; 'Server Core' = 'Yes'; 'Nano' = 'N/A'; 'Installation Mode' = 'DC' }, [pscustomobject]@{ 'OS Version' = 'Windows Server 2016'; 'Server' = 'Yes'; 'Server Core' = 'Yes'; 'Nano' = 'Yes'; 'Installation Mode' = 'DC / ADFS' }, [pscustomobject]@{ 'OS Version' = 'Windows Server 2019'; 'Server' = 'Yes'; 'Server Core' = 'Yes'; 'Nano' = 'Yes'; 'Installation Mode' = 'DC / ADFS' }, [pscustomobject]@{ 'OS Version' = 'Windows Server 2022'; 'Server' = 'Yes'; 'Server Core' = 'Yes'; 'Nano' = 'Yes'; 'Installation Mode' = 'DC / ADFS' } ) $SensorOsParams = @{ Name = "MDI Sensor Supported OS - $TenantId"; ColumnWidths = 28, 14, 18, 12, 28 } if ($Report.ShowTableCaptions) { $SensorOsParams['Caption'] = "- $($SensorOsParams.Name)" } $sensorOsTable | Table @SensorOsParams BlankLine # Hardware minimums Paragraph "Minimum hardware specification for the MDI sensor on a domain controller: 2 CPU cores, 6 GB RAM. The sensor dynamically adjusts its resource usage to ensure at least 15% free compute and memory remains available on the host." } } catch { Write-AbrSectionError -Section 'Defender for Identity Sensor Requirements' -Message "$($_.Exception.Message)" } # ── Network Requirements ───────────────────────────────────────────────── try { Write-Host " - Retrieving Defender for Identity network requirements..." BlankLine Section -Style Heading2 'Network Requirements' { Paragraph "The following documents the network port requirements for Microsoft Defender for Identity sensors installed on domain controllers." BlankLine $NetRows = [System.Collections.ArrayList]::new() # Section headers + rows matching ACME design doc structure $NetRows.Add([pscustomobject]@{ Protocol = 'External'; Transport = ''; Port = ''; From = ''; To = '' }) | Out-Null $NetRows.Add([pscustomobject]@{ Protocol = 'SSL (*.atp.azure.com)'; Transport = 'TCP'; Port = '443'; From = 'MDI Sensor'; To = 'MDI Cloud Service' }) | Out-Null $NetRows.Add([pscustomobject]@{ Protocol = 'Internal'; Transport = ''; Port = ''; From = ''; To = '' }) | Out-Null $NetRows.Add([pscustomobject]@{ Protocol = 'DNS'; Transport = 'TCP/UDP'; Port = '53'; From = 'MDI Sensor'; To = 'DNS Servers' }) | Out-Null $NetRows.Add([pscustomobject]@{ Protocol = 'Netlogon (SMB, CIFS, SAM-R)'; Transport = 'TCP/UDP'; Port = '445'; From = 'MDI Sensor'; To = 'All devices on network' }) | Out-Null $NetRows.Add([pscustomobject]@{ Protocol = 'RADIUS'; Transport = 'UDP'; Port = '1813'; From = 'RADIUS Server'; To = 'MDI Sensor' }) | Out-Null $NetRows.Add([pscustomobject]@{ Protocol = 'Localhost Ports (Sensor Service Updater)'; Transport = ''; Port = ''; From = ''; To = '' }) | Out-Null $NetRows.Add([pscustomobject]@{ Protocol = 'SSL (localhost)'; Transport = 'TCP'; Port = '444'; From = 'Sensor Service'; To = 'Sensor Updater Service' }) | Out-Null $NetRows.Add([pscustomobject]@{ Protocol = 'Network Name Resolution'; Transport = ''; Port = ''; From = ''; To = '' }) | Out-Null $NetRows.Add([pscustomobject]@{ Protocol = 'NTLM over RPC'; Transport = 'TCP'; Port = '135'; From = 'MDI Sensor'; To = 'All devices on network' }) | Out-Null $NetRows.Add([pscustomobject]@{ Protocol = 'NetBIOS'; Transport = 'UDP'; Port = '137'; From = 'MDI Sensor'; To = 'All devices on network' }) | Out-Null $NetRows.Add([pscustomobject]@{ Protocol = 'RDP'; Transport = 'TCP'; Port = '3389'; From = 'MDI Sensor'; To = 'All devices on network' }) | Out-Null # Bold the section header rows $netSectionHeaders = @('External', 'Internal', 'Localhost Ports (Sensor Service Updater)', 'Network Name Resolution') $null = ($NetRows | Where-Object { $_.Protocol -in $netSectionHeaders } | Set-Style -Style Info) $NetParams = @{ Name = "MDI Network Requirements - $TenantId"; ColumnWidths = 34, 14, 8, 22, 22 } if ($Report.ShowTableCaptions) { $NetParams['Caption'] = "- $($NetParams.Name)" } $NetRows | Table @NetParams } } catch { Write-AbrSectionError -Section 'Defender for Identity Network Requirements' -Message "$($_.Exception.Message)" } # ── Prerequisites Summary ─────────────────────────────────────────────── try { BlankLine Section -Style Heading2 'Prerequisites' { Paragraph "The following licensing and infrastructure prerequisites are required for Microsoft Defender for Identity." BlankLine $PrereqRows = @( [pscustomobject]@{ Requirement = 'Licensing'; Detail = 'Microsoft Enterprise Mobility + Security E5 or Microsoft 365 E5' }, [pscustomobject]@{ Requirement = 'Domain Controllers'; Detail = 'Domain controller(s) with internet connectivity (proxy supported)' }, [pscustomobject]@{ Requirement = 'Service Account'; Detail = 'Active Directory service account with read access to all objects' }, [pscustomobject]@{ Requirement = 'Network Name Resolution'; Detail = 'NTLM over RPC (TCP 135), NetBIOS (UDP 137), RDP (TCP 3389), DNS reverse lookup (UDP 53)' }, [pscustomobject]@{ Requirement = 'Honeytoken Account'; Detail = 'Optional — sensitive account for threat detection baiting' } ) $PrereqParams = @{ Name = "MDI Prerequisites - $TenantId"; ColumnWidths = 30, 70 } if ($Report.ShowTableCaptions) { $PrereqParams['Caption'] = "- $($PrereqParams.Name)" } $PrereqRows | Table @PrereqParams } } catch { Write-AbrSectionError -Section 'Defender for Identity Prerequisites' -Message "$($_.Exception.Message)" } } end { Show-AbrDebugExecutionTime -End -TitleMessage 'Defender for Identity' } } |