functions/softwareupdate/Get-AvailableDotNetComponents.ps1

function Get-AvailableDotNetComponents {
    [CmdletBinding()]
    param (
        [ValidateSet('lts', 'sts', 'preview', 'all')]
        [string]$ReleaseType = 'lts',

        [ValidateSet('win-x64', 'win-x86', 'win-arm64', `
                'linux-x64', 'linux-arm64', 'linux-arm', `
                'linux-musl-x64', 'linux-musl-arm64', 'linux-musl-arm', `
                'osx-x64', 'osx-arm64')]
        [string]$Rid = 'win-x64',

        [string]$ChannelVersion = '8.0',

        [string]$FromVersion,

        [string]$FileFilter = 'exe|zip|msi'
    )

    $indexUrl = 'https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/releases-index.json'
    $index = Invoke-RestMethod -Uri $indexUrl -UseBasicParsing
    $channels = $index.'releases-index'

    switch ($ReleaseType) {
        'lts' { $sel = $channels | Where-Object { ($_.'release-type' -eq 'lts') -and ($_.'support-phase' -ne 'preview') } }
        'sts' { $sel = $channels | Where-Object { ($_.'release-type' -eq 'sts') -and ($_.'support-phase' -ne 'preview') } }
        'preview' { $sel = $channels | Where-Object { $_.'release-type' -eq 'preview' } }
        'all' { $sel = $channels }
    }

    if ($ChannelVersion) {
        $sel = $sel | Where-Object { $_.'channel-version' -eq $ChannelVersion }
    }

    $sel = $sel | Sort-Object 'latest-release-date' -Descending

    $result = foreach ($chan in $sel) {
        $channelVersion = $chan.'channel-version'
        $data = Invoke-RestMethod -Uri $chan.'releases.json' -UseBasicParsing

        $releases = $data.releases
        
        if (-not [string]::IsNullOrWhiteSpace($FromVersion)) {
            $releases = $releases | Where-Object {
                $relVerStr = $_.'release-version'
                if ($relVerStr -match '^(\d+\.\d+\.\d+)') {
                    [version]$Matches[1] -gt [version]$FromVersion
                }
                else {
                    $false
                }
            }
        }

        function Get-MatchingFiles {
            param (
                [array]$Files,
                [string]$Rid,
                [string]$FileFilter
            )

            if ($Files) {
                return $Files | Where-Object {
                    $_.rid -eq $Rid -and $_.url -match "\.($FileFilter)$"
                }
            }
            return @()
        }

        foreach ($rel in $releases) {
            $sdkFiles = Get-MatchingFiles -Files $rel.sdk?.files -Rid $Rid -FileFilter $FileFilter
            foreach ($sdk in $rel.sdks) {
                $sdkFiles = Get-MatchingFiles -Files $sdk.files -Rid $Rid -FileFilter $FileFilter
                $sdk.PSObject.Properties.Remove('files')
                $sdk | Add-Member -MemberType NoteProperty -Name 'files' -Value $sdkFiles -Force
            }
            $runtimeFiles = Get-MatchingFiles -Files $rel.runtime?.files -Rid $Rid -FileFilter $FileFilter
            $aspnetFiles = Get-MatchingFiles -Files $rel.'aspnetcore-runtime'?.files -Rid $Rid -FileFilter $FileFilter
            $desktopFiles = Get-MatchingFiles -Files $rel.windowsdesktop?.files -Rid $Rid -FileFilter $FileFilter

            $relVerStr = $rel.'release-version'
            if ($relVerStr -match '^(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?') {
                $major = [int]$Matches[1]
                $minor = [int]$Matches[2]
                $build = [int]$Matches[3]
                $revision = if ($Matches[4]) { [int]$Matches[4] } else { 0 }
                [version]$revisionVersion = [version]::new($major, $minor, $build, $revision)
            }

            [PSCustomObject]@{
                Name           = ".NET $channelVersion"
                Version        = $rel.'release-version'
                SDK            = $sdkFiles
                SDKs           = $rel.sdks
                Runtime        = $runtimeFiles
                AspNetCore     = $aspnetFiles
                WindowsDesktop = $desktopFiles
                Security       = ($rel.security -eq 'True')
                CVEIds         = $rel.'cve-list'.'cve-id'
                CVEUrls        = $rel.'cve-list'.'cve-url'
                ReleaseNotes   = $rel.'release-notes'
                ReleaseDate    = $rel.'release-date'
                Channel        = $channelVersion
                Rid            = $Rid
                Revision       = $revisionVersion
            }
        }
    }

    return $result | Sort-Object Revision -Descending | Select-Object * -ExcludeProperty Revision
}