Private/Get-GSAApplicationSegmentMatch.ps1

#
# Function: Get-GSAApplicationSegmentMatch
# Evaluates a Private Access application's Graph application segments against a target address/port/protocol.
# Example: Get-GSAApplicationSegmentMatch -Segments $segments -FQDNorIP 'fileserver.contoso.com' -InputType FQDN -Port 445 -Protocol TCP
#
function Get-GSAApplicationSegmentMatch
{
    param (
        [object[]]$Segments,
        [string]$FQDNorIP,
        [ValidateSet("IP", "FQDN")]
        [string]$InputType,
        [int]$Port,
        [string]$Protocol
    )

    $result = [PSCustomObject]@{
        AddressMatched  = $false
        PortMatched     = $false
        ProtocolMatched = $false
    }

    # Filter the application segments based on the input type (FQDN or IP).
    if ($InputType -eq 'FQDN')
    {
        $candidates = @($Segments) | Where-Object { $_.destinationType -eq 'fqdn' }
    }
    else
    {
        $candidates = @($Segments) | Where-Object { $_.destinationType -match '^ip' }
    }

    # Iterate through the filtered application segments to check for address, port, and protocol matches.
    foreach ($segment in $candidates)
    {
        $addressMatch = $false

        if ($InputType -eq 'FQDN')
        {
            $destinationHost = $segment.destinationHost
            if ($destinationHost -and ($FQDNorIP -eq $destinationHost -or $destinationHost -match '^\*\.'))
            {
                $addressMatch = $true
            }
        }
        else
        {
            switch ($segment.destinationType)
            {
                'ip' {
                    if ($FQDNorIP -eq $segment.destinationHost)
                    { $addressMatch = $true }
                }
                'ipRange' {
                    $bounds = $segment.destinationHost -split '\.\.'
                    if ($bounds.Count -eq 2)
                    {
                        $startBytes = [System.Net.IPAddress]::Parse($bounds[0]).GetAddressBytes()
                        [Array]::Reverse($startBytes)
                        $endBytes = [System.Net.IPAddress]::Parse($bounds[1]).GetAddressBytes()
                        [Array]::Reverse($endBytes)
                        $addressMatch = Test-GSAIPInRange -Start ([BitConverter]::ToUInt32($startBytes, 0)) -End ([BitConverter]::ToUInt32($endBytes, 0)) -IPAddress $FQDNorIP
                    }
                }
                'ipRangeCidr' {
                    $cidrParts = $segment.destinationHost -split '/'
                    if ($cidrParts.Count -eq 2)
                    {
                        $baseBytes = [System.Net.IPAddress]::Parse($cidrParts[0]).GetAddressBytes()
                        [Array]::Reverse($baseBytes)
                        $baseIP = [BitConverter]::ToUInt32($baseBytes, 0)
                        $mask = -bnot ([uint32]([math]::Pow(2, (32 - [int]$cidrParts[1])) - 1))
                        $startIP = $baseIP -band $mask
                        $endIP = $startIP -bor (-bnot $mask)
                        $addressMatch = Test-GSAIPInRange -Start $startIP -End $endIP -IPAddress $FQDNorIP
                    }
                }
            }
        }

        if (-not $addressMatch)
        { continue }
        $result.AddressMatched = $true

        if (-not (Test-GSAPortInRange -Ports (ConvertTo-GSAPortRange -Ranges $segment.ports) -Port $Port))
        { continue }
        $result.PortMatched = $true

        if ($segment.protocol -match "\b$Protocol\b")
        {
            $result.ProtocolMatched = $true
            break
        }
    }

    return $result
}