tests/Test-Assessment.25392.ps1

<#
.SYNOPSIS
    Validates that all Private Network Connectors are running the latest version.
 
.DESCRIPTION
    This test checks if all Microsoft Entra Private Network Connectors in the tenant
    are running the latest version by comparing installed versions against the
    latest release from Microsoft documentation.
 
.NOTES
    Test ID: 25392
    Category: Private Access
    Required API: onPremisesPublishingProfiles/applicationProxy/connectors (beta)
#>


function Test-Assessment-25392 {
    [ZtTest(
        Category = 'Private Access',
        ImplementationCost = 'Low',
        MinimumLicense = ('Entra_Premium_Private_Access'),
        Pillar = 'Network',
        RiskLevel = 'Medium',
        SfiPillar = 'Protect networks',
        TenantType = ('Workforce'),
        TestId = 25392,
        Title = 'Private Access Connectors are running the latest version',
        UserImpact = 'Low'
    )]
    [CmdletBinding()]
    param()

    #region Helper Functions
    function Get-LatestConnectorVersion {
        <#
        .SYNOPSIS
            Gets the latest Microsoft Entra Private Network Connector version from documentation.
        .OUTPUTS
            System.String - The latest version number (e.g., "1.5.4522.0") or $null if retrieval fails.
        #>

        $url = "https://raw.githubusercontent.com/MicrosoftDocs/entra-docs/refs/heads/main/docs/global-secure-access/reference-version-history.md"

        try {
            $content = Invoke-RestMethod -Uri $url -ErrorAction Stop

            if ($content -match '## Version (\d+\.\d+\.\d+\.\d+)') {
                return $matches[1]
            }
            else {
                Write-PSFMessage "Could not parse version from documentation" -Level Warning
                return $null
            }
        }
        catch {
            Write-PSFMessage "Failed to fetch connector version: $_" -Level Warning
            return $null
        }
    }
    #endregion Helper Functions

    #region Data Collection
    Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose

    $activity = 'Checking Private Network Connector versions'
    Write-ZtProgress -Activity $activity -Status 'Getting connectors'

    # Query Q1: Get all private network connectors
    $connectors = Invoke-ZtGraphRequest -RelativeUri 'onPremisesPublishingProfiles/applicationProxy/connectors' -ApiVersion beta

    # Initialize test variables
    $testResultMarkdown = ''
    $passed = $false
    $outdatedConnectors = @()
    $upToDateConnectors = @()
    $latestVersion = $null

    # Step 1: Check if any connectors exist
    if ($connectors -and $connectors.Count -gt 0) {
        # Step 2: Get the latest version from documentation
        Write-ZtProgress -Activity $activity -Status 'Getting latest version from documentation'
        $latestVersion = Get-LatestConnectorVersion
    }
    #endregion Data Collection

    #region Assessment Logic
    if (-not $connectors -or $connectors.Count -eq 0) {
        $testResultMarkdown = "ℹ️ No private network connectors found in this tenant.`n`n%TestResult%"
        $passed = $true  # No connectors means nothing to check - pass by default
    }
    elseif (-not $latestVersion) {
        $testResultMarkdown = "⚠️ Could not retrieve the latest connector version from documentation. Manual verification required.`n`n%TestResult%"
        $passed = $false # Fail if we can't verify
    }
    else {
        # Step 3: Compare versions
        Write-ZtProgress -Activity $activity -Status 'Comparing connector versions'

        foreach ($connector in $connectors) {
            $connectorVersion = $connector.version
            # Use -ge to account for preview versions or documentation lag
            $isUpToDate = [version]$connectorVersion -ge [version]$latestVersion

            $connectorInfo = [PSCustomObject]@{
                Id          = $connector.id
                MachineName = $connector.machineName
                Version     = $connectorVersion
                IsUpToDate  = $isUpToDate
            }

            if ($isUpToDate) {
                $upToDateConnectors += $connectorInfo
            }
            else {
                $outdatedConnectors += $connectorInfo
            }
        }

        # Step 4: Determine pass/fail status
        if ($outdatedConnectors.Count -eq 0) {
            $passed = $true
            $testResultMarkdown = "✅ All private network connectors are running the latest version ($latestVersion).`n`n%TestResult%"
        }
        else {
            $passed = $false
            $testResultMarkdown = "❌ At least one private network connector is not running the latest version ($latestVersion).`n`n%TestResult%"
        }
    }
    #endregion Assessment Logic

    #region Report Generation
    # Build detailed markdown information
    $mdInfo = ''

    if ($connectors -and $connectors.Count -gt 0 -and $latestVersion) {
        $reportTitle = "Connector version assessment"
        $tableRows = ""

        $mdInfo += "`n## $reportTitle`n`n"
        $mdInfo += "**Latest available version**: $latestVersion`n`n"
        $mdInfo += "**Total connectors**: $($connectors.Count)`n"
        $mdInfo += "**Up to date**: $($upToDateConnectors.Count)`n"
        $mdInfo += "**Outdated**: $($outdatedConnectors.Count)`n`n"

        # Show outdated connectors first (if any)
        if ($outdatedConnectors.Count -gt 0) {
            $formatTemplate = @'
### ❌ Outdated connectors
 
| ID | Machine Name | Current Version |
| :-- | :----------- | :-------------- |
{0}
 
'@

            foreach ($connector in ($outdatedConnectors | Sort-Object -Property MachineName)) {
                $tableRows += "| $($connector.Id) | $(Get-SafeMarkdown $connector.MachineName) | $($connector.Version) |`n"
            }
            $mdInfo += $formatTemplate -f $tableRows
        }

        # Show up-to-date connectors
        if ($upToDateConnectors.Count -gt 0) {
            $tableRows = ""
            $formatTemplate = @'
### ✅ Up-to-date connectors
 
| ID | Machine Name | Current Version |
| :-- | :----------- | :-------------- |
{0}
 
'@

            foreach ($connector in ($upToDateConnectors | Sort-Object -Property MachineName)) {
                $tableRows += "| $($connector.Id) | $(Get-SafeMarkdown $connector.MachineName) | $($connector.Version) |`n"
            }
            $mdInfo += $formatTemplate -f $tableRows
        }
    }
    # Replace the placeholder with detailed information
    $testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $mdInfo
    #endregion Report Generation

    $params = @{
        TestId = '25392'
        Title  = 'Private Access Connectors are running the latest version'
        Status = $passed
        Result = $testResultMarkdown
    }

    # Add test result details
    Add-ZtTestResultDetail @params

}