-
Notifications
You must be signed in to change notification settings - Fork 120
Network - 25411 tls inspection is enabled and correctly configured for outbound traffic #709
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
SagarSathe
merged 14 commits into
main
from
Network-25411-TLS-inspection-is-enabled-and-correctly-configured-for-outbound-traffic
Jan 6, 2026
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
3ef923b
draft 25411
komalp2025 59b14c1
Adding network 25411
komalp2025 6f2d5b7
Adding new property and md formatting
komalp2025 05affd2
add ZtTest info
komalp2025 6453606
refactor 25411
komalp2025 50c3739
refactor 25411
komalp2025 da5f02a
refine spec
komalp2025 9ca20d4
draft changes
komalp2025 ec51c34
draft
komalp2025 bf28353
refined spec
komalp2025 cec730e
updated user facing messages
komalp2025 58952a5
fix variable case-optimize code for ca policy iteration
komalp2025 9c9c0c5
fix variable case for baselineprofilefound
komalp2025 5a88c1c
fix CA policy seperator
komalp2025 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| TLS Inspection empowers Microsoft Entra Global Secure Access to securely interpret encrypted traffic, enabling advanced protection. While basic Web Content Filtering works without it, TLS Inspection is required for many Internet Access features like URL Filtering. By decrypting and inspecting TLS sessions, organizations gain deeper visibility and control, ensuring policies are applied effectively to safeguard users and data. | ||
|
|
||
| **Remediation action** | ||
|
|
||
| Please check the article below for guidance on configuring the TLS Inspection Policy. | ||
| - [Configure Transport Layer Security Inspection Policies - Global Secure Access | Microsoft Learn](https://learn.microsoft.com/en-us/entra/global-secure-access/how-to-transport-layer-security) | ||
|
|
||
| <!--- Results ---> | ||
| %TestResult% |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,250 @@ | ||
| <# | ||
| .SYNOPSIS | ||
| TLS inspection is enabled and correctly configured for outbound traffic in Global Secure Access. | ||
| .DESCRIPTION | ||
| Verifies that a TLS Inspection policy is properly configured. It will fail if no TLS Inspection policy exists, if the policy is not linked to a Security Profile, or if no Conditional Access policy assigning that Security Profile can be identified. | ||
| #> | ||
|
|
||
| function Test-Assessment-25411 { | ||
| [ZtTest( | ||
| Category = 'Global Secure Access', | ||
| ImplementationCost = 'High', | ||
| MinimumLicense = ('Entra_Premium_Internet_Access'), | ||
| Pillar = 'Network', | ||
| RiskLevel = 'High', | ||
| SfiPillar = 'Protect networks', | ||
| TenantType = ('Workforce'), | ||
| TestId = 25411, | ||
| Title = 'TLS inspection is enabled and correctly configured for outbound traffic in Global Secure Access', | ||
| UserImpact = 'Medium' | ||
| )] | ||
| [CmdletBinding()] | ||
| param() | ||
|
|
||
| # Define constants | ||
| [int]$BASELINE_PROFILE_PRIORITY = 65000 | ||
|
|
||
| #region Data Collection | ||
| Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose | ||
|
|
||
| $activity = 'TLS inspection is enabled and correctly configured for outbound traffic in Global Secure Access.' | ||
| Write-ZtProgress -Activity $activity -Status 'Querying TLS inspection policies' | ||
|
|
||
| # Step 1: Get TLS Inspection policies | ||
| $tlsInspectionPolicies = Invoke-ZtGraphRequest -RelativeUri 'networkAccess/tlsInspectionPolicies' -ApiVersion beta | ||
|
|
||
| # Step 2: List all policies in the Baseline Profile and in each Security Profile | ||
| Write-ZtProgress -Activity $activity -Status 'Querying filtering profiles and policies' | ||
| $filteringProfiles = Invoke-ZtGraphRequest -RelativeUri 'networkAccess/filteringProfiles' -QueryParameters @{ | ||
| '$select' = 'id,name,description,state,version,priority' | ||
komalp2025 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| '$expand' = 'policies($select=id,state;$expand=policy($select=id,name,version)),conditionalAccessPolicies($select=id,displayName)' | ||
| } -ApiVersion beta | ||
|
|
||
| # Query all Conditional Access policies with details | ||
| Write-ZtProgress -Activity $activity -Status 'Querying Conditional Access policies' | ||
| $allCAPolicies = Get-ZtConditionalAccessPolicy | ||
|
|
||
| # Build CA profile lookup for O(1) access instead of O(N) search per profile | ||
| Write-ZtProgress -Activity $activity -Status 'Building Conditional Access policy lookup' | ||
| $caProfileLookup = @{} | ||
| foreach ($cap in $allCAPolicies) { | ||
| $session = $cap.sessionControls | ||
| if ($null -ne $session -and $null -ne $session.globalSecureAccessFilteringProfile) { | ||
| $sessionProfileId = $session.globalSecureAccessFilteringProfile.profileId | ||
| $sessionEnabled = $session.globalSecureAccessFilteringProfile.isEnabled | ||
|
|
||
| if ($sessionEnabled -eq $true -and $cap.state -eq 'enabled') { | ||
| if (-not $caProfileLookup.ContainsKey($sessionProfileId)) { | ||
| $caProfileLookup[$sessionProfileId] = @() | ||
| } | ||
| $caProfileLookup[$sessionProfileId] += [PSCustomObject]@{ | ||
| Id = $cap.id | ||
| DisplayName = $cap.displayName | ||
| State = $cap.state | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #endregion Data Collection | ||
|
|
||
| #region Data Processing | ||
| # Graph responses are automatically unwrapped by Invoke-ZtGraphRequest | ||
| $enabledSecurityProfiles = @() | ||
| $enabledBaseLineProfiles = @() | ||
|
|
||
| # Iterate each TLS inspection policy and find linked profiles | ||
| foreach ($tlsPolicy in $tlsInspectionPolicies) { | ||
| $tlsId = $tlsPolicy.id | ||
| $baseLineProfileFound = $false | ||
| foreach ($profileItem in $filteringProfiles) { | ||
| $profilePolicies = @() | ||
| if ($null -ne $profileItem.policies) { | ||
| $profilePolicies = $profileItem.policies | ||
| } | ||
|
|
||
| foreach ($plink in $profilePolicies) { | ||
| $plinkType = $plink.'@odata.type' | ||
| $linkedPolicyId = $null | ||
| # Only process tlsInspectionPolicyLink entries | ||
| if ($plinkType -eq '#microsoft.graph.networkaccess.tlsInspectionPolicyLink' -and $null -ne $plink.policy) { | ||
| $linkedPolicyId = $plink.policy.id | ||
| } | ||
|
|
||
| if ($null -ne $linkedPolicyId -and $linkedPolicyId -eq $tlsId) { | ||
| $linkState = if ($null -ne $plink.state) { | ||
| $plink.state | ||
| } | ||
| else { | ||
| 'unknown' | ||
| } | ||
| $profileState = if ($null -ne $profileItem.state) { | ||
| $profileItem.state | ||
| } | ||
| else { | ||
| 'unknown' | ||
| } | ||
| $priority = if ($null -ne $profileItem.priority) { | ||
| [int]$profileItem.priority | ||
| } | ||
| else { | ||
| $null | ||
| } | ||
|
|
||
| if ($priority -eq $BASELINE_PROFILE_PRIORITY) { | ||
| # Baseline Profile: apply without CA | ||
|
|
||
| if ($linkState -eq 'enabled' -and $profileState -eq 'enabled') { | ||
| $baseLineProfileFound = $true | ||
| $enabledBaseLineProfiles += [PSCustomObject]@{ | ||
| ProfileId = $profileItem.id | ||
| ProfileName = $profileItem.name | ||
| ProfileState = $profileState | ||
| ProfilePriority = $priority | ||
| TLSPolicyId = $tlsId | ||
| TLSPolicyName = $plink.policy.name | ||
| TLSPolicyLinkState = $linkState | ||
| } | ||
| break | ||
| } | ||
| } elseif ($null -ne $priority -and $priority -lt $BASELINE_PROFILE_PRIORITY) { | ||
| # Security Profile: must be applied via Conditional Access | ||
| # Validate CA policies reference this profile via sessionControls | ||
| $matchedCAPolicies = @() | ||
| if ($caProfileLookup.ContainsKey($profileItem.id)) { | ||
| $matchedCAPolicies = $caProfileLookup[$profileItem.id] | ||
| } | ||
|
|
||
| if ($matchedCAPolicies.Count -gt 0 -and $profileState -eq 'enabled' -and $linkState -eq 'enabled') { | ||
| $enabledSecurityProfiles += [PSCustomObject]@{ | ||
| ProfileId = $profileItem.id | ||
| ProfileName = $profileItem.name | ||
| ProfileState = $profileState | ||
| ProfilePriority = $priority | ||
| TLSPolicyId = $tlsId | ||
| TLSPolicyName = $plink.policy.name | ||
| TLSPolicyLinkState = $linkState | ||
| MatchedCAPolicies = $matchedCAPolicies | ||
| CAPolicyCount = $matchedCAPolicies.Count | ||
| DefaultAction = if ($null -ne $tlsPolicy.settings) { | ||
| $tlsPolicy.settings.defaultAction | ||
| } | ||
| else { | ||
| 'unknown' | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| if ($baseLineProfileFound) { | ||
| break | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #endregion Data Processing | ||
| #region Assessment logic | ||
|
|
||
| $testResultMarkdown = '' | ||
| $passed = $false | ||
| $mdInfo = '' | ||
|
|
||
| if ($null -eq $tlsInspectionPolicies -or $tlsInspectionPolicies.Count -eq 0) { | ||
| $testResultMarkdown = "❌ TLS Inspection Policy has not been properly configured. `n`n%TestResult%" | ||
komalp2025 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| $passed = $false | ||
| } | ||
| elseif ($enabledBaseLineProfiles.Count -gt 0) { | ||
| $testResultMarkdown = "✅ TLS Inspection Policy is enabled and properly configured to inspect encrypted outbound traffic.`n`n%TestResult%" | ||
| $passed = $true | ||
| } | ||
| elseif ($enabledSecurityProfiles.Count -gt 0) { | ||
| $testResultMarkdown = "✅ TLS Inspection Policy is enabled and properly configured to inspect encrypted outbound traffic.`n`n%TestResult%" | ||
| $passed = $true | ||
| } | ||
| else { | ||
| $testResultMarkdown = "❌ TLS Inspection Policy has not been properly configured.`n`n%TestResult%" | ||
komalp2025 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| $passed = $false | ||
| } | ||
|
|
||
| #endregion Assessment logic | ||
|
|
||
| #region Report Generation | ||
|
|
||
| if ($enabledBaseLineProfiles.Count -gt 0) { | ||
|
|
||
| $mdInfo += "`n## TLS Inspection Policies Linked to Baseline Profiles`n`n" | ||
| $mdInfo += "| Linked Profile Name | Linked Profile Priority | Linked Policy Name | Policy Link State | Profile State |`n" | ||
| $mdInfo += "| :--- | :--- | :--- | :--- | :--- |`n" | ||
| foreach ($policy in $enabledBaseLineProfiles) { | ||
| $baseLineProfilePortalLink = "https://entra.microsoft.com/#view/Microsoft_Azure_Network_Access/EditProfileMenuBlade.MenuView/~/basics/profileId/$(($policy.ProfileId))" | ||
| $tlsPolicyPortalLink = "https://entra.microsoft.com/#view/Microsoft_Azure_Network_Access/EditTlsInspectionPolicyMenuBlade.MenuView/~/basics/policyId/$(($policy.TLSPolicyId))" | ||
| $profileName = Get-SafeMarkdown -Text $policy.ProfileName | ||
| $profilePriority = $policy.ProfilePriority | ||
| $tlsPolicyName = Get-SafeMarkdown -Text $policy.TLSPolicyName | ||
| $tlsPolicyLinkState = $policy.TLSPolicyLinkState | ||
| $profileState = $policy.ProfileState | ||
| $mdInfo += "| [$profileName]($baseLineProfilePortalLink) | $profilePriority | [$tlsPolicyName]($tlsPolicyPortalLink) | $tlsPolicyLinkState | $profileState |`n" | ||
| } | ||
| } | ||
|
|
||
| if ($enabledSecurityProfiles.Count -gt 0) { | ||
| $mdInfo += "`n## Security Profiles Linked to Conditional Access Policies`n`n" | ||
| $mdInfo += "| Linked Profile Name | Linked Profile Priority | CA Policy Names | CA Policy State | Profile State | TLS Inspection Policy Name | Default Action |`n" | ||
| $mdInfo += "| :--- | :--- | :--- | :--- | :--- | :--- | :--- |`n" | ||
| foreach ($enabledProfile in $enabledSecurityProfiles) { | ||
| $securityProfilePortalLink = "https://entra.microsoft.com/#view/Microsoft_Azure_Network_Access/EditProfileMenuBlade.MenuView/~/basics/profileId/$(($enabledProfile.ProfileId))" | ||
| $tlsPolicyPortalLink = "https://entra.microsoft.com/#view/Microsoft_Azure_Network_Access/EditTlsInspectionPolicyMenuBlade.MenuView/~/basics/policyId/$(($enabledProfile.TLSPolicyId))" | ||
| $profileName = Get-SafeMarkdown -Text $enabledProfile.ProfileName | ||
| $profilePriority = $enabledProfile.ProfilePriority | ||
| # Build CA policy links | ||
| $caPolicyLinksMarkdown = @() | ||
| $caPolicyStatesList = @() | ||
| foreach ($caPolicy in $enabledProfile.MatchedCAPolicies) { | ||
| $caPolicyPortalLink = "https://entra.microsoft.com/#view/Microsoft_AAD_ConditionalAccess/PolicyBlade/policyId/$($caPolicy.Id)" | ||
| $safeName = Get-SafeMarkdown -Text $caPolicy.DisplayName | ||
| $caPolicyLinksMarkdown += "[$safeName]($caPolicyPortalLink)" | ||
| $caPolicyStatesList += $caPolicy.State | ||
| } | ||
| $caPolicyNamesLinked = $caPolicyLinksMarkdown -join ', ' | ||
| $caPolicyStates = $caPolicyStatesList -join ', ' | ||
| $profileState = $enabledProfile.ProfileState | ||
| $tlsPolicyName = Get-SafeMarkdown -Text $enabledProfile.TLSPolicyName | ||
| $defaultAction = $enabledProfile.DefaultAction | ||
| $mdInfo += "| [$profileName]($securityProfilePortalLink) | $profilePriority | $caPolicyNamesLinked | $caPolicyStates | $profileState | [$tlsPolicyName]($tlsPolicyPortalLink) | $defaultAction |`n" | ||
| } | ||
| } | ||
|
|
||
| $testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $mdInfo | ||
| #endregion Report Generation | ||
|
|
||
|
|
||
| $params = @{ | ||
| TestId = '25411' | ||
| Status = $passed | ||
| Result = $testResultMarkdown | ||
| } | ||
|
|
||
| # Add test result details | ||
| Add-ZtTestResultDetail @params | ||
| } | ||
komalp2025 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.