Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
cc5e48a
initial commit
sandeepjha000 Jan 5, 2026
c94eab2
Adding test for assessment 25395
aahmed-spec Jan 6, 2026
c7c7398
Feature-35009
Manoj-Kesana Jan 6, 2026
56b7637
Updated region markers and app conditional check
aahmed-spec Jan 6, 2026
4e0014e
added test file
ashwinikarke Jan 6, 2026
4cf5067
taken pull
ashwinikarke Jan 6, 2026
b1f861f
removed extra lines
aahmed-spec Jan 6, 2026
408cd8e
Update src/powershell/tests/Test-Assessment.35009.ps1
Manoj-Kesana Jan 6, 2026
608ceef
Update src/powershell/tests/Test-Assessment.35009.ps1
Manoj-Kesana Jan 6, 2026
c7f977e
Fixed the evaluation logic
Manoj-Kesana Jan 6, 2026
fd0029e
refactored report table
sandeepjha000 Jan 6, 2026
6708695
adding blade links to profiles and policies
sandeepjha000 Jan 7, 2026
ae8e049
updated comments and word capitalization
sandeepjha000 Jan 7, 2026
bc856ef
add support for AIPService
komalp2025 Jan 7, 2026
9d6b6dc
add md file
komalp2025 Jan 7, 2026
f6bee36
added test file
ashwinikarke Jan 7, 2026
9684145
Merge pull request #749 from microsoft/Feature-35009
SagarSathe Jan 7, 2026
0a648d9
Initial plan
Copilot Jan 7, 2026
6a0065d
Extract AD ports to constant $AD_WELL_KNOWN_PORTS
Copilot Jan 7, 2026
4bdf259
Merge pull request #752 from microsoft/copilot/sub-pr-747
aahmed-spec Jan 7, 2026
1d168ed
Update src/powershell/tests/Test-Assessment.25395.ps1
aahmed-spec Jan 7, 2026
003cabc
Update src/powershell/tests/Test-Assessment.25395.ps1
aahmed-spec Jan 7, 2026
b0e888d
Update src/powershell/tests/Test-Assessment.25395.ps1
aahmed-spec Jan 7, 2026
a621f44
"url" capitalized as "URL"
sandeepjha000 Jan 7, 2026
802dfa6
corrected grammar
sandeepjha000 Jan 7, 2026
1ea5d16
simplified priority check
sandeepjha000 Jan 7, 2026
e8203c6
Merge branch 'feature-25408' of https://github.com/microsoft/zerotrus…
sandeepjha000 Jan 7, 2026
da4d1fd
Added comments for Test-IsBroadCidr function
aahmed-spec Jan 8, 2026
cc80027
Added BroadPortRangeThreshold variable for src/powershell/tests/Test-…
aahmed-spec Jan 8, 2026
6244d6b
Update line no: 69 src/powershell/tests/Test-Assessment.25395.ps1
aahmed-spec Jan 8, 2026
0349d5a
Initial plan
Copilot Jan 8, 2026
6b15741
Fix off-by-one errors in IP and port range calculations
Copilot Jan 8, 2026
f4bb78f
Merge pull request #754 from microsoft/copilot/sub-pr-747-another-one
aahmed-spec Jan 8, 2026
c160132
fix invetigate status and md file
komalp2025 Jan 8, 2026
3e80636
Merge pull request #750 from microsoft/feature-25408
SagarSathe Jan 8, 2026
184a79c
Update test logic for assessment 25395
aahmed-spec Jan 8, 2026
c1fdf84
Refine segment evaluation logic for assessment 25395
aahmed-spec Jan 8, 2026
0cfa271
updated test
ashwinikarke Jan 8, 2026
3535ca1
taken pull
ashwinikarke Jan 8, 2026
8c9c78d
add test 35030
komalp2025 Jan 8, 2026
20d428a
Refine segment scope column logic for assessment 25395
aahmed-spec Jan 8, 2026
5349e01
remove duplicate link
komalp2025 Jan 8, 2026
3164c97
Fix formatting of DLP policy links in Test-Assessment.35030.md
alexandair Jan 8, 2026
e65ba67
Update DLP policy title and optimize enabled policies count query
alexandair Jan 8, 2026
8f8a6f6
added test
ashwinikarke Jan 9, 2026
21d7674
Merge pull request #756 from microsoft/Data-35030-DLP-Policies-Cloud-…
SagarSathe Jan 9, 2026
1685849
updated test
ashwinikarke Jan 9, 2026
5c8ab65
taken pull
ashwinikarke Jan 9, 2026
a4a0ce7
Merge pull request #747 from microsoft/test-25395
SagarSathe Jan 9, 2026
92428bc
added portal link
ashwinikarke Jan 9, 2026
a020439
taken pull
ashwinikarke Jan 9, 2026
961251f
fixed Copilot PR comments
ashwinikarke Jan 9, 2026
9a536c2
draft 35031
komalp2025 Jan 12, 2026
4fcf99f
integrate Find-ZtProfileLinkedToPolicy
komalp2025 Jan 12, 2026
b841097
Revert draft 35031
komalp2025 Jan 12, 2026
5775669
updated portal link
ashwinikarke Jan 12, 2026
6fdd459
Add Find-ZtProfilesLinkedToPolicy function to evaluate linked filteri…
alexandair Jan 12, 2026
aababe0
Merge pull request #765 from microsoft/add-ZTProfilesLinkedToPolicy-2…
SagarSathe Jan 12, 2026
190ffc4
Merge pull request #763 from microsoft/Feature-35025
SagarSathe Jan 12, 2026
7258fa5
Merge pull request #767 from alexandair/alex-Find-ZtProfilesLinkedToP…
alexandair Jan 12, 2026
6f0b0f8
add support for AIPService
komalp2025 Jan 7, 2026
d313f25
add md file
komalp2025 Jan 7, 2026
929c481
fix invetigate status and md file
komalp2025 Jan 8, 2026
838ac69
Merge branch 'Data-35011-Super-User-Membership-Configuration' of http…
komalp2025 Jan 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 195 additions & 0 deletions src/powershell/private/graph/Find-ZtProfilesLinkedToPolicy.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
function Find-ZtProfilesLinkedToPolicy {
<#
.SYNOPSIS
Finds filtering profiles that are linked to a specific policy and evaluates if they meet pass criteria.

.DESCRIPTION
This function searches through Global Secure Access filtering profiles to find those linked to a specific policy.
It evaluates whether each linked profile meets the pass criteria based on profile type:
- Baseline Profile (priority = 65000): Passes automatically regardless of link state
- Security Profile (priority < 65000): Passes only if linked to an enabled Conditional Access policy

.PARAMETER PolicyId
The ID of the filtering policy to search for.

.PARAMETER FilteringProfiles
Collection of all filtering profiles to search through.

.PARAMETER CAPolicies
Collection of Conditional Access policies for Security Profile validation.

.PARAMETER BaselinePriority
The priority value that identifies the Baseline Profile (typically 65000).

.PARAMETER PolicyLinkType
The type of policy link to search for. Valid values:
- filteringPolicyLink (Web Content Filtering)
- tlsInspectionPolicyLink (TLS Inspection)
- filePolicyLink (File Policy)
- promptPolicyLink (Prompt Policy)

.PARAMETER PolicyRules
Collection of policy rules associated with the policy (e.g., webCategory rules, TLS inspection rules).

.EXAMPLE
$findParams = @{
PolicyId = $policyId
FilteringProfiles = $filteringProfiles
CAPolicies = $caPolicies
BaselinePriority = 65000
PolicyLinkType = 'filteringPolicyLink'
PolicyRules = $webCategoryRules
}
$linkedProfiles = Find-ZtProfilesLinkedToPolicy @findParams

.OUTPUTS
Array of PSCustomObject with the following properties:
- ProfileId: The profile ID
- ProfileName: The profile name
- ProfileType: 'Baseline Profile' or 'Security Profile'
- ProfileState: The profile state
- ProfilePriority: The profile priority value
- PolicyLinkState: The state of the policy link (enabled/disabled/unknown)
- PassesCriteria: Boolean indicating if the profile meets pass criteria
- CAPolicy: Linked Conditional Access policies (for Security Profiles only)
- PolicyRules: The policy rules passed in

.NOTES
This function is used by Global Secure Access assessment tests to evaluate policy enforcement.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$PolicyId,

[Parameter(Mandatory)]
[AllowEmptyCollection()]
[object[]]$FilteringProfiles,

[Parameter(Mandatory)]
[AllowEmptyCollection()]
[object[]]$CAPolicies,

[Parameter(Mandatory)]
[int]$BaselinePriority,

[Parameter(Mandatory)]
[ValidateSet('filteringPolicyLink', 'tlsInspectionPolicyLink', 'filePolicyLink', 'promptPolicyLink')]
[string]$PolicyLinkType,

[Parameter(Mandatory)]
[AllowEmptyCollection()]
[object[]]$PolicyRules
)

# OData type lookup for type safety
$odataTypeMap = @{
'filteringPolicyLink' = '#microsoft.graph.networkaccess.filteringPolicyLink'
'tlsInspectionPolicyLink' = '#microsoft.graph.networkaccess.tlsInspectionPolicyLink'
'filePolicyLink' = '#microsoft.graph.networkaccess.filePolicyLink'
'promptPolicyLink' = '#microsoft.graph.networkaccess.promptPolicyLink'
}

$odataType = $odataTypeMap[$PolicyLinkType]
if (-not $odataType) {
Write-PSFMessage "Unknown PolicyLinkType: $PolicyLinkType" -Tag Test -Level Warning
return @()
}

$linkedProfiles = [System.Collections.Generic.List[PSCustomObject]]::new()

foreach ($filteringProfile in $FilteringProfiles) {
# Get profile policies safely
$profilePolicies = @()
if ($null -ne $filteringProfile.policies) {
# Force array to handle both scalar and array returns from Graph API
$profilePolicies = @($filteringProfile.policies)
}

foreach ($policyLink in $profilePolicies) {
$plinkType = $policyLink.'@odata.type'
$linkedPolicyId = $null

# Only process the specified policy link type
if ($plinkType -eq $odataType -and $null -ne $policyLink.policy) {
$linkedPolicyId = $policyLink.policy.id
}

if ($null -ne $linkedPolicyId -and $linkedPolicyId -eq $PolicyId) {
# Determine profile type based on priority
$priority = if ($null -ne $filteringProfile.priority) {
[int]$filteringProfile.priority
}
else {
$null
}

# Per spec: Only process Baseline Profile (priority = 65000) or Security Profile (priority < 65000)
if ($null -eq $priority) {
Write-PSFMessage "Skipping profile '$($filteringProfile.name)' (ID: $($filteringProfile.id)) - missing priority property" -Tag Test -Level Debug
continue
}

$linkState = if ($null -ne $policyLink.state) {
$policyLink.state
}
else {
'unknown'
}

if ($priority -eq $BaselinePriority) {
# Baseline Profile: passes regardless of enabled state per spec
$profileInfo = [PSCustomObject]@{
ProfileId = $filteringProfile.id
ProfileName = $filteringProfile.name
ProfileType = 'Baseline Profile'
ProfileState = $filteringProfile.state
ProfilePriority = $priority
PolicyLinkState = $linkState
PassesCriteria = $true
CAPolicy = $null
PolicyRules = $PolicyRules
}
$linkedProfiles.Add($profileInfo) | Out-Null
}
elseif ($priority -lt $BaselinePriority) {
# Security Profile: check if linked to enabled CA policy
$linkedCAPolicies = $CAPolicies | Where-Object {
# Use null-conditional operator for safe navigation
$_.sessionControls?.globalSecureAccessFilteringProfile?.profileId -eq $filteringProfile.id -and
$_.sessionControls?.globalSecureAccessFilteringProfile?.isEnabled -eq $true
}

$profileInfo = [PSCustomObject]@{
ProfileId = $filteringProfile.id
ProfileName = $filteringProfile.name
ProfileType = 'Security Profile'
ProfileState = $filteringProfile.state
ProfilePriority = $priority
PolicyLinkState = $linkState
PassesCriteria = $false
CAPolicy = $null
PolicyRules = $PolicyRules
}

if ($linkedCAPolicies) {
# Check if at least one CA policy is enabled
$enabledCAPolicies = $linkedCAPolicies | Where-Object { $_.state -eq 'enabled' }
if ($enabledCAPolicies) {
$profileInfo.PassesCriteria = $true
}
$profileInfo.CAPolicy = $linkedCAPolicies
}

$linkedProfiles.Add($profileInfo) | Out-Null
}
else {
# Priority > BaselinePriority
Write-PSFMessage "Skipping profile '$($filteringProfile.name)' (ID: $($filteringProfile.id)) - unexpected priority value: $priority (expected <= $BaselinePriority)" -Tag Test -Level Debug
}
}
}
}

return $linkedProfiles
}
44 changes: 42 additions & 2 deletions src/powershell/public/Connect-ZtAssessment.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
$SkipAzureConnection,

# The services to connect to such as Azure and ExchangeOnline. Default is Graph.
[ValidateSet('All', 'Azure', 'ExchangeOnline', 'Graph', 'SecurityCompliance', 'SharePointOnline')]
[ValidateSet('All', 'Azure', 'AipService', 'ExchangeOnline', 'Graph', 'SecurityCompliance', 'SharePointOnline')]
[string[]]$Service = 'Graph',

# The Exchange environment to connect to. Default is O365Default. Supported values include O365China, O365Default, O365GermanyCloud, O365USGovDoD, O365USGovGCCHigh.
Expand Down Expand Up @@ -117,7 +117,7 @@
}


$OrderedImport = Get-ModuleImportOrder -Name @('Az.Accounts', 'ExchangeOnlineManagement', 'Microsoft.Graph.Authentication', 'Microsoft.Online.SharePoint.PowerShell')
$OrderedImport = Get-ModuleImportOrder -Name @('Az.Accounts', 'ExchangeOnlineManagement', 'Microsoft.Graph.Authentication', 'Microsoft.Online.SharePoint.PowerShell', 'AipService')

Write-Verbose "Import Order: $($OrderedImport.Name -join ', ')"

Expand Down Expand Up @@ -348,6 +348,32 @@
}
}
}

'AipService' {
if ($Service -contains 'AipService' -or $Service -contains 'All') {
try {
# Import module with compatibility if needed
if ($PSVersionTable.PSEdition -ne 'Desktop') {
# Assume module is installed in Windows PowerShell as per instructions
Import-Module AipService -UseWindowsPowerShell -WarningAction SilentlyContinue -ErrorAction Stop -Global
}
else {
Import-Module AipService -ErrorAction Stop -Global
}
}
catch {
# Provide clearer guidance when import fails, especially under PowerShell Core
if ($PSVersionTable.PSEdition -ne 'Desktop') {
$message = "Failed to import AipService module. When running in PowerShell Core, 'AipService' must be installed in Windows PowerShell 5.1 (Desktop) for -UseWindowsPowerShell to work. Underlying error: $_"
}
else {
$message = "Failed to import AipService module: $_"
}
Write-Host "`n$message" -ForegroundColor Red
Write-PSFMessage $message -Level Error
}
}
}
}

if ($Service -contains 'SharePointOnline' -or $Service -contains 'All') {
Expand Down Expand Up @@ -388,4 +414,18 @@
}
}
}

if ($Service -contains 'AipService' -or $Service -contains 'All') {
Write-Host "`nConnecting to Azure Information Protection" -ForegroundColor Yellow
Write-PSFMessage 'Connecting to Azure Information Protection'

try {
Connect-AipService -ErrorAction Stop
Write-Verbose "Successfully connected to Azure Information Protection."
}
catch {
Write-Host "`nFailed to connect to Azure Information Protection: $_" -ForegroundColor Red
Write-PSFMessage "Failed to connect to Azure Information Protection: $_" -Level Error
}
}
}
20 changes: 20 additions & 0 deletions src/powershell/tests/Test-Assessment.25395.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
When organizations configure Microsoft Entra Private Access with broad application segments—such as wide IP ranges, multiple protocols, or Quick Access configurations—they effectively replicate the over-permissive access model of traditional VPNs. This approach contradicts the Zero Trust principle of least-privilege access, where users should only reach the specific resources required for their role. Threat actors who compromise a user's credentials or device can leverage these broad network permissions to perform reconnaissance, identifying additional systems and services within the permitted range.

With visibility into the network topology, they can escalate privileges by targeting vulnerable systems, move laterally to access sensitive data stores or administrative interfaces, and establish persistence by deploying backdoors across multiple accessible systems. The lack of granular segmentation also complicates incident response, as security teams cannot quickly determine which specific resources a compromised identity could access. By contrast, per-application segmentation with tightly scoped destination hosts, specific ports, and Custom Security Attributes enables dynamic, attribute-driven Conditional Access enforcement—requiring stronger authentication or device compliance for high-risk applications while streamlining access to lower-risk resources.

This approach aligns with the Zero Trust "verify explicitly" principle by ensuring each access request is evaluated against the specific security requirements of the target application rather than applying uniform policies to broad network segments.

**Remediation action**
- [Transition from Quick Access](https://learn.microsoft.com/en-us/entra/global-secure-access/how-to-configure-per-app-access) to per-app Private Access by creating individual Global Secure Access enterprise applications with specific FQDNs, IP addresses, and ports for each private resource.
- [Use Application Discovery](https://learn.microsoft.com/en-us/entra/global-secure-access/how-to-application-discovery) to identify which resources users access through Quick Access, then create targeted Private Access apps for those resources.
- [Create Custom Security Attribute sets](https://learn.microsoft.com/en-us/entra/fundamentals/custom-security-attributes-add) and definitions to categorize Private Access applications by risk level, department, or compliance requirements.
- [Assign Custom Security Attributes](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/custom-security-attributes-apps) to Private Access application service principals to enable attribute-based access control.
- [Create Conditional Access policies using application filters](https://learn.microsoft.com/en-us/entra/identity/conditional-access/concept-filter-for-applications) to target Private Access apps based on their Custom Security Attributes, enforcing granular controls like MFA or device compliance.
- [Apply Conditional Access policies to Private Access](https://learn.microsoft.com/en-us/entra/global-secure-access/how-to-target-resource-private-access-apps) apps from within Global Secure Access for streamlined configuration.

Review
- [Zero Trust network segmentation guidance for software-defined perimeters](https://learn.microsoft.com/en-us/security/zero-trust/deploy/networks#1-network-segmentation-and-software-defined-perimeters).


<!--- Results --->
%TestResult%
Loading