Skip to content
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
202 commits
Select commit Hold shift + click to select a range
1beb554
Feature-25403
Manoj-Kesana Dec 17, 2025
395f293
Fixed Naming Convention
Manoj-Kesana Dec 17, 2025
001c8f2
Moved Variables to assessment logic
Manoj-Kesana Dec 17, 2025
8548042
35001 - Add test for Conditional Access RMS exclusions
alexandair Dec 22, 2025
37680c1
35002 - Add test for Cross-Tenant Access Policy (XTAP) RMS settings
alexandair Dec 22, 2025
9cc8c87
35003 - Add test for sensitivity label configuration and reporting
alexandair Dec 22, 2025
4da6083
Change Pillar from 'Devices' to 'Data'
alexandair Dec 28, 2025
59df975
35004 - Add test for Published Label Policies assessment
alexandair Dec 28, 2025
5fb4b57
35005 - Add test for sensitivity labels in SharePoint Online assessment
alexandair Dec 28, 2025
fa99b68
35006- Add tests for PDF labeling support in SharePoint Online
alexandair Dec 28, 2025
4511f3e
35007 - Add test for Information Rights Management (IRM) in SharePoin…
alexandair Dec 28, 2025
dc05324
35008 - Add test for SPO Default Site Label (Tenant-Wide)
alexandair Dec 28, 2025
f0f20f1
Merge branch 'main' of https://github.com/microsoft/zerotrustassessme…
Manoj-Kesana Dec 30, 2025
1102a8e
Update src/powershell/tests/Test-Assessment.35008.md
alexandair Dec 31, 2025
d0aa4dc
Fix policy link to be consistent to other tests
alexandair Jan 1, 2026
a5605a2
Update MinimumLicense to Microsoft 365 E5 based on new spec
alexandair Jan 1, 2026
a41c2ef
Update Conditional Access policy instructions for RMS exclusion
alexandair Jan 1, 2026
db458f0
Remove unnecessary module imports from Test-Assessment-35001 setup
alexandair Jan 1, 2026
06f6bd9
Refactor Test-Assessment-35001 for improved readability and consisten…
alexandair Jan 1, 2026
e03c9ee
added test file
ashwinikarke Jan 2, 2026
478fddd
Initial commit for 25535
kshitiz-prog Jan 2, 2026
cba56d8
updated code with updated SPEC
Manoj-Kesana Jan 2, 2026
ddba135
Update MinimumLicense to Microsoft 365 E5
alexandair Jan 3, 2026
007c208
Inconsistency in checking which location properties indicate "All Use…
alexandair Jan 3, 2026
59b81b1
Update MinimumLicense from 'MIP_P1' to 'Microsoft 365 E3'
alexandair Jan 3, 2026
28d6cda
Update MinimumLicense from MIP_P1 to Microsoft 365 E3
alexandair Jan 3, 2026
cf693a5
Fix typo in Test-Assessment.35005.md
alexandair Jan 4, 2026
c3d96e8
Add a null check ensuring that $spoTenant cannot be null
alexandair Jan 4, 2026
cb76b24
Fix a typo in Test-Assessment.35006.ps1
alexandair Jan 4, 2026
56b7eb2
Add test case for handling null response from Get-SPOTenant
alexandair Jan 4, 2026
0a35e24
Rename $testResultDetail to $params for consistency with other test f…
alexandair Jan 4, 2026
0a507c5
Refactor assessment logic and result reporting
alexandair Jan 4, 2026
65f0d97
Fix a typo in Test-Assessment.35006.ps1
alexandair Jan 4, 2026
cf4751c
Fix a typo in Test-Assessment.35007.md
alexandair Jan 4, 2026
16672f0
Fix a typo in Test-Assessment.35007.ps1
alexandair Jan 4, 2026
b9e09cb
Fix typo in Test-Assessment.35007.md
alexandair Jan 4, 2026
0b48e04
Update MinimumLicense and simplify assessment logic in Test-Assessmen…
alexandair Jan 4, 2026
63621e6
Fix IRM status check and update result reporting
alexandair Jan 4, 2026
df640e0
Fix terminology in Test-Assessment.35008.ps1 for clarity on document …
alexandair Jan 4, 2026
d86491d
Enhance Test-Assessment.35008.Tests.ps1: Clarify failure message and …
alexandair Jan 4, 2026
3e041d0
requested changes completed
Manoj-Kesana Jan 5, 2026
cc5e48a
initial commit
sandeepjha000 Jan 5, 2026
3ef923b
draft 25411
komalp2025 Dec 15, 2025
59b14c1
Adding network 25411
komalp2025 Dec 16, 2025
6f2d5b7
Adding new property and md formatting
komalp2025 Dec 16, 2025
05affd2
add ZtTest info
komalp2025 Dec 17, 2025
6453606
refactor 25411
komalp2025 Dec 18, 2025
50c3739
refactor 25411
komalp2025 Dec 18, 2025
da5f02a
refine spec
komalp2025 Dec 22, 2025
9ca20d4
draft changes
komalp2025 Dec 30, 2025
ec51c34
draft
komalp2025 Dec 31, 2025
bf28353
refined spec
komalp2025 Jan 2, 2026
cec730e
updated user facing messages
komalp2025 Jan 5, 2026
7c5b06c
Test 25396
praneeth-0000 Jan 5, 2026
58952a5
fix variable case-optimize code for ca policy iteration
komalp2025 Jan 5, 2026
9c9c0c5
fix variable case for baselineprofilefound
komalp2025 Jan 5, 2026
7c34420
added Test file
ashwinikarke Jan 6, 2026
a730098
added Test file
ashwinikarke Jan 6, 2026
94018b5
taken pull
ashwinikarke Jan 6, 2026
9afd381
Changed user facing messages and beautified code
praneeth-0000 Jan 6, 2026
9a81ac6
fixed casing in table headers
praneeth-0000 Jan 6, 2026
3ffa3e8
fixed user facing messages
praneeth-0000 Jan 6, 2026
c0cf2e5
Update- Remediation action
kshitiz-prog Jan 6, 2026
5a88c1c
fix CA policy seperator
komalp2025 Jan 6, 2026
a1bb11d
Merge pull request #720 from alexandair/alex-35002
SagarSathe Jan 6, 2026
b3f3b3c
Merge pull request #719 from alexandair/alex-35001
SagarSathe Jan 6, 2026
d7678e3
Merge pull request #721 from alexandair/alex-35003
SagarSathe Jan 6, 2026
0887b00
Merge pull request #728 from alexandair/alex-35004
SagarSathe Jan 6, 2026
286f839
Merge pull request #729 from alexandair/alex-35005
SagarSathe Jan 6, 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
eb41d25
Merge pull request #730 from alexandair/alex-35006
SagarSathe Jan 6, 2026
fbf8aec
Merge pull request #731 from alexandair/alex-35007
SagarSathe Jan 6, 2026
1bfa643
Merge pull request #732 from alexandair/alex-35008
SagarSathe Jan 6, 2026
a9869ae
Merge pull request #711 from microsoft/Feature-25403
SagarSathe 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
7cee52b
Merge pull request #709 from microsoft/Network-25411-TLS-inspection-i…
SagarSathe 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
21fd120
moved portal links to bottom
praneeth-0000 Jan 7, 2026
e81de1c
Code fixed as per Co-Pilot assessment
kshitiz-prog Jan 7, 2026
f6bee36
added test file
ashwinikarke Jan 7, 2026
b4eb371
taken pull
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
cdc6a56
updated test
ashwinikarke Jan 7, 2026
eca61d6
taken pull
ashwinikarke 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
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
b340228
updated output table
ashwinikarke 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
a7740b8
updated endpoint
ashwinikarke Jan 9, 2026
943356d
combined q1 and q4 queries and simplified logic
praneeth-0000 Jan 9, 2026
21d7674
Merge pull request #756 from microsoft/Data-35030-DLP-Policies-Cloud-…
SagarSathe Jan 9, 2026
56e4c8d
added test
ashwinikarke 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
c7ada61
added logic to handle empty csa
praneeth-0000 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
296e3cd
updated code
ashwinikarke Jan 12, 2026
5775669
updated portal link
ashwinikarke Jan 12, 2026
6fdd459
Add Find-ZtProfilesLinkedToPolicy function to evaluate linked filteri…
alexandair Jan 12, 2026
ffa05a4
Feature-35019
Manoj-Kesana 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
77caabb
taken pull
ashwinikarke Jan 12, 2026
7f1279d
Fix missing sentence
alexandair Jan 12, 2026
0f3ba7a
updated to use helper function
ashwinikarke Jan 12, 2026
05fbd80
updated code
ashwinikarke Jan 12, 2026
8b3eeaa
taken pull
ashwinikarke Jan 12, 2026
5c3ee1b
updated code to use helper function
sandeepjha000 Jan 12, 2026
774f001
initial commit Test 35038
praneeth-0000 Jan 13, 2026
439d78f
Fix missing sentence
Manoj-Kesana Jan 13, 2026
e10e564
Merge branch 'Feature-35019' of https://github.com/microsoft/zerotrus…
Manoj-Kesana Jan 13, 2026
99284da
Updated wrong error message
praneeth-0000 Jan 13, 2026
67bf009
removed redundant variable assignment
praneeth-0000 Jan 13, 2026
e4c9469
Merge pull request #745 from microsoft/Feature-25409
SagarSathe Jan 13, 2026
7a18237
Merge pull request #769 from microsoft/feature-25408
SagarSathe Jan 13, 2026
a8662d6
Feature-35021
Manoj-Kesana Jan 13, 2026
9a6532f
Update src/powershell/tests/Test-Assessment.35021.md
Manoj-Kesana Jan 13, 2026
e787e73
Add missing line break before "Remediation action" in Test-Assessment…
alexandair Jan 13, 2026
434e5be
Merge pull request #773 from microsoft/Feature-35021
SagarSathe Jan 13, 2026
f404c25
Remove Markdown to bold one list entry
alexandair Jan 13, 2026
6b2c5d9
adjusted portal link to header
praneeth-0000 Jan 14, 2026
9de9b61
Merge pull request #771 from microsoft/Feature-35033
SagarSathe Jan 14, 2026
9ae3fe2
Changed logic for checking auth strength
praneeth-0000 Jan 14, 2026
d0c9deb
moved portal link to table header
praneeth-0000 Jan 14, 2026
1a4dc87
Updated phishing resistant logic according to docx
praneeth-0000 Jan 15, 2026
4136445
added and removed spaces as suggested by copilot
praneeth-0000 Jan 15, 2026
bf8edc2
Merge pull request #770 from microsoft/Feature-35038
SagarSathe Jan 16, 2026
6aef9c4
Merge pull request #768 from microsoft/Feature-35019
SagarSathe Jan 16, 2026
5ef46ea
updated sfipillar and q1 with count
praneeth-0000 Jan 16, 2026
a3d65b2
fixed SFI Pillar
praneeth-0000 Jan 16, 2026
77adaa5
25535-code update
Jan 16, 2026
77e8358
25535 update
Jan 16, 2026
64f500d
Merge pull request #744 from microsoft/Feature-25396
SagarSathe Jan 19, 2026
c3d1bbf
add support for aipservice module
komalp2025 Jan 13, 2026
a5fc605
add 35011
komalp2025 Jan 13, 2026
332f905
reworked 35011
komalp2025 Jan 19, 2026
d853e47
Update src/powershell/tests/Test-Assessment.25535.ps1
kshitiz-prog Jan 19, 2026
afe6dc8
Feature-25535 : Update as per alex review
Jan 19, 2026
e22a436
Merge branch 'Feature-25535' of https://github.com/microsoft/zerotrus…
Jan 19, 2026
eea3ea3
Code fix - Query Parameter change
Jan 19, 2026
c082b34
Refactor documentation for clarity and readability in Test-Assessment…
alexandair Jan 20, 2026
cb8ad0c
Update video link in Advanced Analytics documentation
jfdyer13 Jan 20, 2026
ad51f6b
Merge pull request #783 from jfdyer13/patch-2
Clay-Microsoft Jan 21, 2026
e9bf151
Replace old YouTube video with a new one
Clay-Microsoft Jan 21, 2026
b0091e1
Add video walkthrough to RMD_117.md
Clay-Microsoft Jan 21, 2026
31b3a72
Modify iframe for video walkthrough
Clay-Microsoft Jan 21, 2026
832ba3c
Merge pull request #784 from microsoft/Clay-Microsoft-patch-73
Clay-Microsoft Jan 21, 2026
bfafd1f
code fix 25535
Jan 21, 2026
876f30d
Merge branch 'Feature-25535' of https://github.com/microsoft/zerotrus…
Jan 21, 2026
d98dd13
Merge pull request #772 from microsoft/Data-35011-Super-User-Membersh…
SagarSathe Jan 21, 2026
e519b56
code fix
Jan 22, 2026
27ddf31
Initial commit for 25535
kshitiz-prog Jan 2, 2026
c554a91
Update- Remediation action
kshitiz-prog Jan 6, 2026
d131ca7
Code fixed as per Co-Pilot assessment
kshitiz-prog Jan 7, 2026
02c8c9f
25535-code update
Jan 16, 2026
31dfb45
Feature-25535 : Update as per alex review
Jan 19, 2026
d4eb297
Code fix - Query Parameter change
Jan 19, 2026
5608fb7
code fix 25535
Jan 21, 2026
ca95fe8
Refactor documentation for clarity and readability in Test-Assessment…
alexandair Jan 20, 2026
d44ce7e
code fix
Jan 22, 2026
0916711
Merge branch 'Feature-25535' of https://github.com/microsoft/zerotrus…
Jan 22, 2026
593274b
mg context to az context switch
Jan 22, 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
9 changes: 9 additions & 0 deletions src/powershell/tests/Test-Assessment.25535.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Azure Firewall is a cloud-native network security service that provides centralized inspection, logging, and enforcement for network traffic flowing between application workloads and external destinations. Routing outbound traffic through Azure Firewall enables organizations to apply consistent security controls such as threat intelligence filtering, intrusion detection and prevention, TLS inspection, and egress policy enforcement. In a secure network architecture, outbound traffic from workloads hosted in Azure virtual networks should be explicitly routed through Azure Firewall before reaching the internet or external services. VNET integrated workloads include VMs, AKS Node Pools, AKS Pods, App Service (VNet Integration Route All), Functions in VNet. This is typically achieved by configuring routing so that outbound traffic from workload subnets uses Azure Firewall as the next hop. Without this routing in place, outbound traffic may bypass the firewall entirely, reducing visibility and allowing traffic to leave the environment without inspection or policy enforcement.This check verifies that outbound traffic from in-scope workloads is routed through Azure Firewall by validating that the effective network routes direct outbound traffic to the firewall’s private IP address. If outbound traffic is not routed through Azure Firewall, the check fails because traffic may bypass centralized security controls, increasing the risk of data exfiltration, command-and-control communication, and undetected malicious activity.

**Remediation action**

- [Deploy and configure Azure Firewall using the Azure portal](https://learn.microsoft.com/en-us/azure/firewall/tutorial-firewall-deploy-portal#configure-routing)
- [Control outbound traffic with Azure Firewall ](https://learn.microsoft.com/en-us/azure/app-service/network-secure-outbound-traffic-azure-firewall)

<!--- Results --->
%TestResult%
317 changes: 317 additions & 0 deletions src/powershell/tests/Test-Assessment.25535.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
<#
.SYNOPSIS
Test to check if outbound traffic from VNET integrated workloads is routed through Azure Firewall

.NOTES
Some Azure Firewall documentation links may return 404 errors.
This test uses the Azure REST API version 2025-03-01.
#>

function Test-Assessment-25535 {
[ZtTest(
Category = 'Azure Network Security',
ImplementationCost = 'Medium',
MinimumLicense = ('Azure_Firewall_Basic', 'Azure_Firewall_Standard', 'Azure_Firewall_Premium'),
Pillar = 'Network',
RiskLevel = 'High',
SfiPillar = 'Protect networks',
TenantType = ('Workforce', 'External'),
TestId = 25535,
Title = 'Outbound traffic from VNET integrated workloads is routed through Azure Firewall',
UserImpact = 'Low'
)]
[CmdletBinding()]
param()

Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose

if ((Get-MgContext).Environment -ne 'Global') {
Write-PSFMessage "This test is only applicable to the Global environment." -Tag Test -Level VeryVerbose
return
}

#region Data Collection
try {
$accessToken = Get-AzAccessToken -AsSecureString -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
}
catch {
Write-PSFMessage $_.Exception.Message -Tag Test -Level Error
}

if (-not $accessToken) {
Write-PSFMessage "Azure authentication token not found." -Tag Test -Level Warning
Add-ZtTestResultDetail -SkippedBecause NoAzureAccess
return
}

# Query 1: List all subscriptions
$subscriptions = Get-AzSubscription

$firewalls = @()
$nicFindings = @()

foreach ($sub in $subscriptions) {

Set-AzContext -SubscriptionId $sub.Id | Out-Null
$subId = $sub.Id

# Query 2: List Azure Firewalls
$fwListUri = "/subscriptions/$subId/providers/Microsoft.Network/azureFirewalls?api-version=2025-03-01"

try {
$fwResp = Invoke-AzRestMethod -Path $fwListUri -Method GET
}
catch {
Write-PSFMessage "Unable to list Azure Firewalls in subscription $($sub.Name)." -Tag Test -Level Warning
continue
}

$fwItems = ($fwResp.Content | ConvertFrom-Json).value
if (-not $fwItems) { continue }

# Query 3: Get Firewall Details (resolve private IPs)
foreach ($fw in $fwItems) {

$fwDetailUri = "$($fw.id)?api-version=2025-03-01"

try {
$fwDetailResp = Invoke-AzRestMethod -Path $fwDetailUri -Method GET
$fwDetail = $fwDetailResp.Content | ConvertFrom-Json
}
catch { continue }

foreach ($ipconfig in $fwDetail.properties.ipConfigurations) {
if ($ipconfig.properties.privateIPAddress) {
$firewalls += [PSCustomObject]@{
FirewallName = $fwDetail.name
FirewallId = $fwDetail.id
PrivateIP = $ipconfig.properties.privateIPAddress
SubscriptionId = $subId
}
}
}
}

if ($firewalls.Count -eq 0) { continue }

# Query 4: List NICs
$nicListUri = "/subscriptions/$subId/providers/Microsoft.Network/networkInterfaces?api-version=2025-03-01"

try {
$nicResp = Invoke-AzRestMethod -Path $nicListUri -Method GET
}
catch {
Write-PSFMessage "Unable to list network interfaces in subscription $($sub.Name)." -Tag Test -Level Warning
continue
}

$nics = ($nicResp.Content | ConvertFrom-Json).value

# Query 5: Stage 1 - Launch all async effectiveRouteTable requests
$asyncOperations = @()

foreach ($nic in $nics) {
foreach ($ipconfig in $nic.properties.ipConfigurations) {

if (-not $ipconfig.properties.subnet?.id) { continue }

$subnetId = $ipconfig.properties.subnet.id
if ($subnetId -match 'AzureFirewallSubnet|GatewaySubnet|AzureBastionSubnet') {
continue
}

$rg = ($nic.id -split '/')[4]
$nicName = $nic.name

$ertUri = "/subscriptions/$subId/resourceGroups/$rg/providers/Microsoft.Network/networkInterfaces/$nicName/effectiveRouteTable?api-version=2025-03-01"

try {
$ertStart = Invoke-AzRestMethod -Path $ertUri -Method POST
$operationUri = $ertStart.Headers.Location[0]

$retryAfter = if ($ertStart.Headers.'Retry-After') {
[int]$ertStart.Headers.'Retry-After'[0]
} else { 5 }

$asyncOperations += @{
OperationUri = $operationUri
Nic = $nic
RetryAfter = $retryAfter
Timestamp = Get-Date
SubnetId = $subnetId
SubscriptionId = $sub.Id
SubscriptionName = $sub.Name
}
}
catch {
Write-PSFMessage "Failed to initiate effectiveRouteTable request for NIC $nicName : $($_.Exception.Message)" -Tag Test -Level Warning
}
}
}

if ($asyncOperations.Count -eq 0) { continue }

Write-PSFMessage "Launched $($asyncOperations.Count) async effectiveRouteTable requests for subscription $($sub.Name)" -Tag Test -Level Verbose

# Query 5: Stage 2 - Poll all operations in parallel
$completedOperations = @()
$maxRetries = 120 # ~10 minutes with 5 second intervals

do {
$stillPending = @()

foreach ($op in $asyncOperations) {
if ($op.Completed) {
$completedOperations += $op
continue
}

try {
$ertPoll = Invoke-AzRestMethod -Uri $op.OperationUri -Method GET

if ($ertPoll.StatusCode -ne 202) {
$op.Routes = ($ertPoll.Content | ConvertFrom-Json).value
$op.Completed = $true
$completedOperations += $op
} else {
$stillPending += $op
}
}
catch {
Write-PSFMessage "Error polling operation for NIC $($op.Nic.name) : $($_.Exception.Message)" -Tag Test -Level Warning
$op.Completed = $true
$op.Error = $true
$completedOperations += $op
}
}

$asyncOperations = $stillPending

if ($asyncOperations.Count -gt 0) {
$maxRetries--
if ($maxRetries -le 0) {
Write-PSFMessage "Timeout polling effectiveRouteTable operations. Processing $($asyncOperations.Count) incomplete operations." -Tag Test -Level Warning
$completedOperations += $asyncOperations
break
}

$retryAfter = ($asyncOperations | Select-Object -First 1).RetryAfter
Write-PSFMessage "Polling $($asyncOperations.Count) pending operations..." -Tag Test -Level Verbose
Start-Sleep -Seconds $retryAfter
}

} while ($asyncOperations.Count -gt 0)

# Query 5: Stage 3 - Process completed operations
foreach ($op in $completedOperations) {
if ($op.Error -or -not $op.Routes) {
$nicFindings += [PSCustomObject]@{
NicName = $op.Nic.name
NicId = $op.Nic.id
NextHopType = 'Unknown'
NextHopIp = ''
IsCompliant = $false
SubscriptionId = $op.SubscriptionId
SubscriptionName = $op.SubscriptionName
SubnetId = $op.SubnetId
FirewallPrivateIp = 'N/A'
NextHopIpAddress = ''
}
continue
}

$defaultRoute = $op.Routes | Where-Object {
$_.state -eq 'Active' -and
$_.source -eq 'User' -and
($_.addressPrefix -contains '0.0.0.0/0')
} | Select-Object -First 1

if (-not $defaultRoute) {
$nicFindings += [PSCustomObject]@{
NicName = $op.Nic.name
NicId = $op.Nic.id
NextHopType = 'Internet'
NextHopIp = ''
IsCompliant = $false
SubscriptionId = $op.SubscriptionId
SubscriptionName = $op.SubscriptionName
SubnetId = $op.SubnetId
FirewallPrivateIp = 'N/A'
NextHopIpAddress = ''
}
continue
}

$fwMatch = $firewalls | Where-Object {
# Handle nextHopIpAddress being either a string or an array
$nextHop = $defaultRoute.nextHopIpAddress
( ($nextHop -eq $_.PrivateIP) -or ($nextHop -contains $_.PrivateIP) )
} | Select-Object -First 1

$nicFindings += [PSCustomObject]@{
FirewallName = if ($fwMatch) { $fwMatch.FirewallName } else { 'N/A' }
FirewallId = if ($fwMatch) { $fwMatch.FirewallId } else { 'N/A' }
FirewallPrivateIp = if ($fwMatch) { $fwMatch.PrivateIP } else { 'N/A' }
NicName = $op.Nic.name
NicId = $op.Nic.id
RouteSource = $defaultRoute.source
RouteState = $defaultRoute.state
AddressPrefix = ($defaultRoute.addressPrefix -join ',')
NextHopType = $defaultRoute.nextHopType
NextHopIpAddress = ($defaultRoute.nextHopIpAddress -join ',')
IsCompliant = ($fwMatch -ne $null)
SubscriptionId = $op.SubscriptionId
SubscriptionName = $op.SubscriptionName
SubnetId = $op.SubnetId
}
}
}
#endregion Data Collection

#region Assessment Logic
if ($nicFindings.Count -eq 0) {
Add-ZtTestResultDetail -SkippedBecause NoResults
return
}

$passed = ($nicFindings | Where-Object { -not $_.IsCompliant }).Count -eq 0

$testResultMarkdown = if ($passed) {
"Outbound traffic is routed through Azure Firewall.`n`n%TestResult%"
} else {
"Outbound traffic is not routed through Azure Firewall.`n`n%TestResult%"
}
#endregion Assessment Logic

#region Report Generation
$mdInfo = "## Outbound traffic routing evidence`n`n"
$mdInfo += "| Subscription | Network interface | Subnet | Azure firewall private IP | Default route next hop type | Next hop IP address | Result |`n"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The specification is asking for WorkloadType column.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

I only found these column to be included.

$mdInfo += "| :--- | :--- | :--- | :--- | :--- | :--- | :--- |`n"

foreach ($item in $nicFindings | Sort-Object SubscriptionName, NicName) {
$icon = if ($item.IsCompliant) { '✅' } else { '❌' }

$subName = if ($item.SubscriptionName) { $item.SubscriptionName } else { 'N/A' }
$subLink = "https://portal.azure.com/#resource/subscriptions/$($item.SubscriptionId)"
$subMd = "[$(Get-SafeMarkdown -Text $subName)]($subLink)"

$nicName = if ($item.NicName) { $item.NicName } else { 'N/A' }
$nicLink = "https://portal.azure.com/#resource$($item.NicId)"
$nicMd = "[$(Get-SafeMarkdown -Text $nicName)]($nicLink)"

$subnetName = if ($item.SubnetId) { ($item.SubnetId -split '/')[-1] } else { 'N/A' }
$subnetLink = "https://portal.azure.com/#resource$($item.SubnetId)"
$subnetMd = "[$(Get-SafeMarkdown -Text $subnetName)]($subnetLink)"

$fwIp = if ($item.FirewallPrivateIp) { $item.FirewallPrivateIp } else { 'N/A' }
$nextHopType = if ($item.NextHopType) { $item.NextHopType } else { 'None' }
$nextHopIp = if ($item.NextHopIpAddress) { $item.NextHopIpAddress } else { '' }

$mdInfo += "| $subMd | $nicMd | $subnetMd | $fwIp | $nextHopType | $nextHopIp | $icon |`n"
}

$testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $mdInfo
#endregion Report Generation

Add-ZtTestResultDetail -TestId '25535' -Status $passed -Result $testResultMarkdown
}