From 7be7157176f94d677cceb30fd9b7a0bcc51e7c4a Mon Sep 17 00:00:00 2001 From: Manoj K Date: Tue, 23 Dec 2025 12:48:17 +0530 Subject: [PATCH 1/4] Feature-25376 --- src/powershell/tests/Test-Assessment.25376.md | 22 ++ .../tests/Test-Assessment.25376.ps1 | 211 ++++++++++++++++++ 2 files changed, 233 insertions(+) create mode 100644 src/powershell/tests/Test-Assessment.25376.md create mode 100644 src/powershell/tests/Test-Assessment.25376.ps1 diff --git a/src/powershell/tests/Test-Assessment.25376.md b/src/powershell/tests/Test-Assessment.25376.md new file mode 100644 index 000000000..f96b5c28a --- /dev/null +++ b/src/powershell/tests/Test-Assessment.25376.md @@ -0,0 +1,22 @@ +When Microsoft 365 traffic bypasses Global Secure Access, organizations lose visibility and control over their most critical productivity workloads. Threat actors exploiting unmonitored Microsoft 365 connections can exfiltrate sensitive data through SharePoint, OneDrive, or Exchange without triggering security policies or generating actionable telemetry. + +Token theft and replay attacks become more difficult to detect when traffic does not flow through the Security Service Edge, as source IP correlation with sign-in logs and Conditional Access evaluation cannot be applied consistently. + +Organizations with significant bypassed traffic—whether due to incomplete client deployment, misconfigured forwarding profiles, or users on unmanaged devices—create blind spots where adversary-in-the-middle attacks, credential harvesting, and unauthorized data transfers can proceed undetected. Traffic that bypasses Global Secure Access also cannot benefit from compliant network checks in Conditional Access policies, tenant restrictions, or source IP restoration—leaving significant security controls ineffective. + +## Remediation Steps + +1. **Enable Microsoft 365 traffic profile** + - Navigate to Global Secure Access > Connect > Traffic forwarding + - Enable the Microsoft 365 traffic forwarding profile + - [Learn more about managing Microsoft 365 profile](https://learn.microsoft.com/en-us/entra/global-secure-access/how-to-manage-microsoft-profile) + +2. **Deploy Global Secure Access client** + - Install the Global Secure Access client on all managed Windows endpoints + - Ensure client is configured to connect automatically + - [Learn more about deploying the Windows client](https://learn.microsoft.com/en-us/entra/global-secure-access/how-to-install-windows-client) + +3. **Review traffic forwarding rules** + - Verify Microsoft 365 workloads are included in the forwarding profile + - Check for any exclusions that may be bypassing critical traffic + - [Learn more about Microsoft 365 traffic profile](https://learn.microsoft.com/en-us/entra/global-secure-access/concept-microsoft-traffic-profile) diff --git a/src/powershell/tests/Test-Assessment.25376.ps1 b/src/powershell/tests/Test-Assessment.25376.ps1 new file mode 100644 index 000000000..c94cd049c --- /dev/null +++ b/src/powershell/tests/Test-Assessment.25376.ps1 @@ -0,0 +1,211 @@ +<# +.SYNOPSIS + Checks if Microsoft 365 traffic is actively flowing through Global Secure Access. + +.DESCRIPTION + This test validates that the Microsoft 365 traffic forwarding profile is enabled and + that Microsoft 365 traffic is actively being tunneled through Global Secure Access. + +.NOTES + Test ID: 25376 + Category: Traffic Acquisition + Required API: networkAccess/reports (beta) +#> + +function Test-Assessment-25376 { + [ZtTest( + Category = 'Traffic Acquisition', + ImplementationCost = 'Medium', + MinimumLicense = ('P1'), + Pillar = 'Network', + RiskLevel = 'High', + SfiPillar = 'Protect networks', + TenantType = ('Workforce'), + TestId = 25376, + Title = 'Microsoft 365 traffic is actively flowing through Global Secure Access', + UserImpact = 'Low' + )] + [CmdletBinding()] + param() + + #region Data Collection + Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose + + $activity = 'Checking Microsoft 365 traffic through Global Secure Access' + Write-ZtProgress -Activity $activity -Status 'Verifying M365 traffic forwarding profile' + + # Define evaluation time window (last 7 days) + $endDateTime = (Get-Date).ToUniversalTime() + $startDateTime = $endDateTime.AddDays(-7) + $startDateTimeStr = $startDateTime.ToString('yyyy-MM-ddTHH:mm:ssZ') + $endDateTimeStr = $endDateTime.ToString('yyyy-MM-ddTHH:mm:ssZ') + + # Query 3: Verify Microsoft 365 traffic forwarding profile is enabled + $m365Profile = $null + try { + $m365Profile = Invoke-ZtGraphRequest -RelativeUri "networkAccess/forwardingProfiles" -Filter "trafficForwardingType eq 'm365'" -ApiVersion beta + } catch { + Write-PSFMessage "Unable to retrieve M365 forwarding profile: $_" -Level Warning + } + + # Query 1 & 2: Execute in parallel conceptually, but sequentially due to tool constraints + Write-ZtProgress -Activity $activity -Status 'Retrieving traffic statistics' + + # Query 1: Get transaction summaries + $transactionSummary = $null + try { + $transactionSummary = Invoke-ZtGraphRequest -RelativeUri "networkAccess/reports/transactionSummaries(startDateTime=$startDateTimeStr,endDateTime=$endDateTimeStr)" -ApiVersion beta + } catch { + Write-PSFMessage "Unable to retrieve transaction summaries: $_" -Level Warning + } + + # Query 2: Get device usage summary + $deviceUsage = $null + try { + $deviceUsage = Invoke-ZtGraphRequest -RelativeUri "networkAccess/reports/getDeviceUsageSummary(startDateTime=$startDateTimeStr,endDateTime=$endDateTimeStr)" -ApiVersion beta + } catch { + Write-PSFMessage "Unable to retrieve device usage summary: $_" -Level Warning + } + #endregion Data Collection + + #region Assessment Logic + $passed = $true + $warnings = [System.Collections.Generic.List[string]]::new() + + # Extract M365 profile state + $profileEnabled = $false + $profileState = 'Not found' + $profileName = 'N/A' + if ($m365Profile -and $m365Profile.Count -gt 0) { + $profile = $m365Profile | Select-Object -First 1 + $profileName = $profile.name + $profileState = $profile.state + $profileEnabled = ($profile.state -eq 'enabled') + } + + # Extract M365 transaction data + $m365TotalCount = 0 + $m365BlockedCount = 0 + if ($transactionSummary) { + $m365Entry = $transactionSummary | Where-Object { $_.trafficType -eq 'microsoft365' } | Select-Object -First 1 + if ($m365Entry) { + $m365TotalCount = [int]$m365Entry.totalCount + $m365BlockedCount = [int]$m365Entry.blockedCount + } + } + + # Extract device usage data + $totalDeviceCount = 0 + $activeDeviceCount = 0 + $inactiveDeviceCount = 0 + if ($deviceUsage) { + $totalDeviceCount = [int]$deviceUsage.totalDeviceCount + $activeDeviceCount = [int]$deviceUsage.activeDeviceCount + $inactiveDeviceCount = [int]$deviceUsage.inactiveDeviceCount + } + + # Evaluation logic + if (-not $profileEnabled) { + $passed = $false + } + + if ($m365TotalCount -eq 0) { + $passed = $false + } + + # Warning conditions + if ($profileEnabled -and $m365TotalCount -gt 0 -and $m365TotalCount -lt 1000) { + $warnings.Add("Low Microsoft 365 transaction count ($m365TotalCount) may indicate deployment issues") + } + + if ($activeDeviceCount -eq 0 -and $totalDeviceCount -gt 0) { + $warnings.Add("No active devices detected despite $totalDeviceCount total devices registered") + } + + if ($activeDeviceCount -lt 10 -and $profileEnabled) { + $warnings.Add("Low active device count ($activeDeviceCount) - verify client deployment across endpoints") + } + + # Generate result message + if ($passed -and $warnings.Count -eq 0) { + $testResultMarkdown = "✅ Microsoft 365 traffic forwarding is enabled and a healthy volume of Microsoft 365 traffic is flowing through Global Secure Access.`n`n%TestResult%" + } + elseif ($passed -and $warnings.Count -gt 0) { + $testResultMarkdown = "⚠️ Microsoft 365 traffic is flowing through Global Secure Access, but some concerns were detected.`n`n%TestResult%" + } + else { + $testResultMarkdown = "❌ Microsoft 365 traffic forwarding is disabled or no Microsoft 365 traffic is being tunneled through Global Secure Access.`n`n%TestResult%" + } + #endregion Assessment Logic + + #region Report Generation + $mdInfo = [System.Text.StringBuilder]::new() + + # Summary Section + [void]$mdInfo.AppendLine("`n## Summary`n") + [void]$mdInfo.AppendLine("| Metric | Value |") + [void]$mdInfo.AppendLine("| :--- | ---: |") + [void]$mdInfo.AppendLine("| Profile Enabled | $(if ($profileEnabled) { '✅ Yes' } else { '❌ No' }) |") + [void]$mdInfo.AppendLine("| M365 Transactions (7 days) | $($m365TotalCount.ToString('N0')) |") + [void]$mdInfo.AppendLine("| M365 Blocked Transactions | $($m365BlockedCount.ToString('N0')) |") + [void]$mdInfo.AppendLine("| Active Devices | $activeDeviceCount |") + [void]$mdInfo.AppendLine("| Total Devices | $totalDeviceCount |`n") + + # Traffic Forwarding Profile Section + [void]$mdInfo.AppendLine("`n## Traffic Forwarding Profile`n") + [void]$mdInfo.AppendLine("| Property | Value |") + [void]$mdInfo.AppendLine("| :--- | :--- |") + [void]$mdInfo.AppendLine("| Profile Name | $(Get-SafeMarkdown -Text $profileName) |") + [void]$mdInfo.AppendLine("| State | $profileState |") + [void]$mdInfo.AppendLine("| Traffic Type | m365 |`n") + + # Transaction Summary Section + [void]$mdInfo.AppendLine("`n## Transaction Summary`n") + [void]$mdInfo.AppendLine("| Traffic Type | Total Count | Blocked Count |") + [void]$mdInfo.AppendLine("| :--- | ---: | ---: |") + if ($transactionSummary) { + foreach ($entry in $transactionSummary | Sort-Object trafficType) { + $total = [int]$entry.totalCount + $blocked = [int]$entry.blockedCount + [void]$mdInfo.AppendLine("| $($entry.trafficType) | $($total.ToString('N0')) | $($blocked.ToString('N0')) |") + } + } else { + [void]$mdInfo.AppendLine("| - | No data available | - |") + } + [void]$mdInfo.AppendLine() + [void]$mdInfo.AppendLine("*Evaluation Period: $($startDateTime.ToString('yyyy-MM-dd')) to $($endDateTime.ToString('yyyy-MM-dd'))*`n") + + # Device Usage Section + [void]$mdInfo.AppendLine("`n## Device Usage`n") + [void]$mdInfo.AppendLine("| Metric | Count |") + [void]$mdInfo.AppendLine("| :--- | ---: |") + [void]$mdInfo.AppendLine("| Total Devices | $totalDeviceCount |") + [void]$mdInfo.AppendLine("| Active Devices | $activeDeviceCount |") + [void]$mdInfo.AppendLine("| Inactive Devices | $inactiveDeviceCount |`n") + + # Warnings Section + if ($warnings.Count -gt 0) { + [void]$mdInfo.AppendLine("`n## ⚠️ Warnings`n") + foreach ($warning in $warnings) { + [void]$mdInfo.AppendLine("- $warning") + } + [void]$mdInfo.AppendLine() + } + + # Portal Link + $portalLink = "https://entra.microsoft.com/#view/Microsoft_Azure_Network_Access/TrafficForwardingBlade" + [void]$mdInfo.AppendLine("`n[$(Get-SafeMarkdown -Text 'View in Entra Portal: Traffic forwarding')]($portalLink)") + + # Replace placeholder with detailed information + $testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $mdInfo.ToString() + #endregion Report Generation + + $params = @{ + TestId = '25376' + Title = 'Microsoft 365 traffic is actively flowing through Global Secure Access' + Status = $passed + Result = $testResultMarkdown + } + + Add-ZtTestResultDetail @params +} From 8c1d84354e5e87a7ac054abb368380033d32209f Mon Sep 17 00:00:00 2001 From: Manoj K Date: Tue, 30 Dec 2025 08:53:16 +0530 Subject: [PATCH 2/4] Add license detail --- src/powershell/tests/Test-Assessment.25376.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/powershell/tests/Test-Assessment.25376.ps1 b/src/powershell/tests/Test-Assessment.25376.ps1 index c94cd049c..5bb4a0675 100644 --- a/src/powershell/tests/Test-Assessment.25376.ps1 +++ b/src/powershell/tests/Test-Assessment.25376.ps1 @@ -16,7 +16,7 @@ function Test-Assessment-25376 { [ZtTest( Category = 'Traffic Acquisition', ImplementationCost = 'Medium', - MinimumLicense = ('P1'), + MinimumLicense = ('P1','E3'), Pillar = 'Network', RiskLevel = 'High', SfiPillar = 'Protect networks', From 4c21750a182bc3afdb656b39ef5dd30a87694ae3 Mon Sep 17 00:00:00 2001 From: Manoj K Date: Fri, 2 Jan 2026 13:21:24 +0530 Subject: [PATCH 3/4] FIxed requested changes on the feedback --- .../tests/Test-Assessment.25376.ps1 | 102 ++++++++++-------- 1 file changed, 55 insertions(+), 47 deletions(-) diff --git a/src/powershell/tests/Test-Assessment.25376.ps1 b/src/powershell/tests/Test-Assessment.25376.ps1 index 5bb4a0675..dbc615238 100644 --- a/src/powershell/tests/Test-Assessment.25376.ps1 +++ b/src/powershell/tests/Test-Assessment.25376.ps1 @@ -77,10 +77,10 @@ function Test-Assessment-25376 { $profileState = 'Not found' $profileName = 'N/A' if ($m365Profile -and $m365Profile.Count -gt 0) { - $profile = $m365Profile | Select-Object -First 1 - $profileName = $profile.name - $profileState = $profile.state - $profileEnabled = ($profile.state -eq 'enabled') + $m365ProfileData = $m365Profile | Select-Object -First 1 + $profileName = $m365ProfileData.name + $profileState = $m365ProfileData.state + $profileEnabled = ($m365ProfileData.state -eq 'enabled') } # Extract M365 transaction data @@ -139,65 +139,73 @@ function Test-Assessment-25376 { #endregion Assessment Logic #region Report Generation - $mdInfo = [System.Text.StringBuilder]::new() + $mdInfo = '' # Summary Section - [void]$mdInfo.AppendLine("`n## Summary`n") - [void]$mdInfo.AppendLine("| Metric | Value |") - [void]$mdInfo.AppendLine("| :--- | ---: |") - [void]$mdInfo.AppendLine("| Profile Enabled | $(if ($profileEnabled) { '✅ Yes' } else { '❌ No' }) |") - [void]$mdInfo.AppendLine("| M365 Transactions (7 days) | $($m365TotalCount.ToString('N0')) |") - [void]$mdInfo.AppendLine("| M365 Blocked Transactions | $($m365BlockedCount.ToString('N0')) |") - [void]$mdInfo.AppendLine("| Active Devices | $activeDeviceCount |") - [void]$mdInfo.AppendLine("| Total Devices | $totalDeviceCount |`n") + $mdInfo += "`n## Summary`n`n" + $mdInfo += "| Metric | Value |`n" + $mdInfo += "| :--- | ---: |`n" + $mdInfo += "| Profile Enabled | $(if ($profileEnabled) { '✅ Yes' } else { '❌ No' }) |`n" + + # Only show transaction and device counts if profile is enabled + if ($profileEnabled) { + $mdInfo += "| M365 Transactions (7 days) | $($m365TotalCount.ToString('N0')) |`n" + $mdInfo += "| M365 Blocked Transactions | $($m365BlockedCount.ToString('N0')) |`n" + $mdInfo += "| Active Devices | $activeDeviceCount |`n" + $mdInfo += "| Total Devices | $totalDeviceCount |`n" + } + $mdInfo += "`n" # Traffic Forwarding Profile Section - [void]$mdInfo.AppendLine("`n## Traffic Forwarding Profile`n") - [void]$mdInfo.AppendLine("| Property | Value |") - [void]$mdInfo.AppendLine("| :--- | :--- |") - [void]$mdInfo.AppendLine("| Profile Name | $(Get-SafeMarkdown -Text $profileName) |") - [void]$mdInfo.AppendLine("| State | $profileState |") - [void]$mdInfo.AppendLine("| Traffic Type | m365 |`n") - - # Transaction Summary Section - [void]$mdInfo.AppendLine("`n## Transaction Summary`n") - [void]$mdInfo.AppendLine("| Traffic Type | Total Count | Blocked Count |") - [void]$mdInfo.AppendLine("| :--- | ---: | ---: |") - if ($transactionSummary) { - foreach ($entry in $transactionSummary | Sort-Object trafficType) { - $total = [int]$entry.totalCount - $blocked = [int]$entry.blockedCount - [void]$mdInfo.AppendLine("| $($entry.trafficType) | $($total.ToString('N0')) | $($blocked.ToString('N0')) |") + $mdInfo += "`n## Traffic Forwarding Profile`n`n" + $mdInfo += "| Property | Value |`n" + $mdInfo += "| :--- | :--- |`n" + $mdInfo += "| Profile Name | $(Get-SafeMarkdown -Text $profileName) |`n" + $mdInfo += "| State | $profileState |`n" + $mdInfo += "| Traffic Type | m365 |`n`n" + + # Only show transaction and device data if profile is enabled + if ($profileEnabled) { + # Transaction Summary Section + $mdInfo += "`n## Transaction Summary`n`n" + $mdInfo += "| Traffic Type | Total Count | Blocked Count |`n" + $mdInfo += "| :--- | ---: | ---: |`n" + if ($transactionSummary) { + foreach ($entry in $transactionSummary | Sort-Object trafficType) { + $total = [int]$entry.totalCount + $blocked = [int]$entry.blockedCount + $mdInfo += "| $($entry.trafficType) | $($total.ToString('N0')) | $($blocked.ToString('N0')) |`n" + } + } else { + $mdInfo += "| - | No data available | - |`n" } - } else { - [void]$mdInfo.AppendLine("| - | No data available | - |") - } - [void]$mdInfo.AppendLine() - [void]$mdInfo.AppendLine("*Evaluation Period: $($startDateTime.ToString('yyyy-MM-dd')) to $($endDateTime.ToString('yyyy-MM-dd'))*`n") + $mdInfo += "`n" + $mdInfo += "*Evaluation Period: $($startDateTime.ToString('yyyy-MM-dd')) to $($endDateTime.ToString('yyyy-MM-dd'))*`n`n" - # Device Usage Section - [void]$mdInfo.AppendLine("`n## Device Usage`n") - [void]$mdInfo.AppendLine("| Metric | Count |") - [void]$mdInfo.AppendLine("| :--- | ---: |") - [void]$mdInfo.AppendLine("| Total Devices | $totalDeviceCount |") - [void]$mdInfo.AppendLine("| Active Devices | $activeDeviceCount |") - [void]$mdInfo.AppendLine("| Inactive Devices | $inactiveDeviceCount |`n") + # Device Usage Section + $mdInfo += "`n## Device Usage`n`n" + $mdInfo += "| Metric | Count |`n" + $mdInfo += "| :--- | ---: |`n" + $mdInfo += "| Total Devices | $totalDeviceCount |`n" + $mdInfo += "| Active Devices | $activeDeviceCount |`n" + $mdInfo += "| Inactive Devices | $inactiveDeviceCount |`n`n" + } # Warnings Section if ($warnings.Count -gt 0) { - [void]$mdInfo.AppendLine("`n## ⚠️ Warnings`n") + $mdInfo += "`n## ⚠️ Warnings`n`n" foreach ($warning in $warnings) { - [void]$mdInfo.AppendLine("- $warning") + $mdInfo += "- $warning`n" } - [void]$mdInfo.AppendLine() + $mdInfo += "`n" } # Portal Link - $portalLink = "https://entra.microsoft.com/#view/Microsoft_Azure_Network_Access/TrafficForwardingBlade" - [void]$mdInfo.AppendLine("`n[$(Get-SafeMarkdown -Text 'View in Entra Portal: Traffic forwarding')]($portalLink)") + $portalLink = "https://entra.microsoft.com/#view/Microsoft_Azure_Network_Access/ForwardingProfile.ReactView" + $mdInfo += "`n[$(Get-SafeMarkdown -Text 'View in Entra Portal: Traffic forwarding')]($portalLink)" # Replace placeholder with detailed information - $testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $mdInfo.ToString() + $testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $mdInfo #endregion Report Generation $params = @{ From 736eb1b0a75a42019e3de608c59c201ddfeb9dad Mon Sep 17 00:00:00 2001 From: Manoj K Date: Fri, 2 Jan 2026 17:16:35 +0530 Subject: [PATCH 4/4] Modified to work with Client side Filtering --- src/powershell/tests/Test-Assessment.25376.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/powershell/tests/Test-Assessment.25376.ps1 b/src/powershell/tests/Test-Assessment.25376.ps1 index dbc615238..c3c6e66f0 100644 --- a/src/powershell/tests/Test-Assessment.25376.ps1 +++ b/src/powershell/tests/Test-Assessment.25376.ps1 @@ -43,7 +43,8 @@ function Test-Assessment-25376 { # Query 3: Verify Microsoft 365 traffic forwarding profile is enabled $m365Profile = $null try { - $m365Profile = Invoke-ZtGraphRequest -RelativeUri "networkAccess/forwardingProfiles" -Filter "trafficForwardingType eq 'm365'" -ApiVersion beta + $allProfiles = Invoke-ZtGraphRequest -RelativeUri "networkAccess/forwardingProfiles" -ApiVersion beta + $m365Profile = $allProfiles | Where-Object { $_.trafficForwardingType -eq 'm365' } } catch { Write-PSFMessage "Unable to retrieve M365 forwarding profile: $_" -Level Warning }