functions/Get-PSReleaseAsset.ps1

function Get-PSReleaseAsset {
    [CmdletBinding()]
    [OutputType('PSReleaseAsset')]
    param(
        [Parameter(HelpMessage = 'Limit results to a given platform. The default is all platforms.')]
        [ValidateSet('Rhel', 'Raspbian', 'Ubuntu', 'Debian', 'Windows', 'Arm', 'MacOS', 'Alpine', 'FXDependent', 'CBL-Mariner', 'CentOS', 'Linux')]
        [string[]]$Family,
        [ValidateSet('deb', 'gz', 'msi', 'pkg', 'rpm', 'zip', 'msix')]
        [Parameter(HelpMessage = 'Limit results to a given format. The default is all formats.')]
        [string[]]$Format,
        [Alias('x64')]
        [switch]$Only64Bit,
        [Parameter(HelpMessage = 'Get the latest preview release.')]
        [switch]$Preview,
        [Parameter(HelpMessage = 'Only get LTS release-related assets.')]
        [switch]$LTS
    )

    begin {
        Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Starting: $($MyInvocation.MyCommand)"
        Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Running PowerShell $($PSVersionTable.PSVersion) in $($host.name)"
        Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN ] Using module version $moduleVersion"
    } #begin

    process {
        try {
            if ($Preview) {
                Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Getting preview assets"
                $data = GetData -Preview -ErrorAction Stop
            }
            else {
                Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Getting normal assets"
                $data = GetData -ErrorAction Stop
            }
            #parse out file names and hashes
            #updated pattern 10 March 2020 to capture LTS assets
            [regex]$rx = '(?<file>[p|P]ower[s|S]hell((-preview)|(-lts))?[-|_]\d.*)\s+-\s+(?<hash>\w+)'
            # pre GA pattern
            #"(?<file>[p|P]ower[s|S]hell(-preview)?[-|_]\d.*)\s+-\s+(?<hash>\w+)"
            # original regex pattern
            #"(?<file>[p|P]ower[s|S]hell[-|_]\d.*)\s+-\s+(?<hash>\w+)"
            $r = $rx.Matches($data.body)
            $r | ForEach-Object -Begin {
                $h = @{ }
            } -Process {
                #if there is a duplicate entry, assume it is part of a Note
                $f = $_.Groups['file'].Value.Trim()
                $v = $_.Groups['hash'].Value.Trim()
                if (-not ($h.ContainsKey($f))) {
                    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Adding $f [$v]"
                    $h.Add($f, $v )
                }
                else {
                    Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Ignoring duplicate asset: $f [$v]"
                }
            }

            Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Found $($data.assets.count) downloads"

            # https://github.com/PowerShell/PowerShell/blob/781c9a545377092439bf881d9d9954517809465b/tools/packages.microsoft.com/mapping.json#L33
            $assetData = $data.assets | where name -NotMatch 'hashes'
            # 22 August 2025 - Create a typed custom object
            $assets = foreach ($asset in $assetData) {
                Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Processing $($asset.Name)"
                $familyName = switch -regex ($asset.Name) {
                    'Win-x\d{2}' { 'Windows' }
                    'arm\d{2}|aarch64' { 'Arm' }
                    'Ubuntu|deb' { 'Ubuntu' }
                    'osx' { 'MacOS' }
                    'deb' { 'Debian' }
                    'appimage' { 'AppImage' }
                    'rhel|rh' { 'Rhel' }
                    'linux-arm' { 'Raspbian' }
                    'alpine|musl' { 'Alpine' }
                    'fxdepend' { 'FXDependent' }
                    'centos|rh' { 'CentOS' }
                    'linux' { 'Linux' }
                    'cm' { 'CBL-Mariner' }
                }
                [PSCustomObject]@{
                    PSTypeName    = 'PSReleaseAsset'
                    FileName      = $asset.Name
                    Family        = $familyName
                    Format        = $asset.name.Split('.')[-1]
                    Size          = $asset.size
                    Hash          = $h.item($asset.name)
                    Created       = $asset.created_at -as [datetime]
                    Updated       = $asset.updated_at -as [datetime]
                    URL           = $asset.browser_download_Url
                    DownloadCount = $asset.download_count
                }
            } #foreach

            if ($Family) {
                Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Filtering by family"
                $assets = $assets.where( { $_.family -match $($family -join '|') })
            }
            if ($Only64Bit) {
                Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Filtering for 64bit"
                $assets = ($assets).where( { $_.filename -match '(x|amd)64' })
            }

            if ($Format) {
                Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Filtering for format"
                $assets = $assets.where( { $_.format -match $("^$format$" -join '|') })
            }

            if ($LTS) {
                Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Filtering for LTS assets"
                $assets = $assets.where( { $_.filename -match 'LTS' })
            }
            #write the results to the pipeline
            if ($assets.filename) {
                $assets
            }
            else {
                Write-Warning 'Get-PSReleaseAsset Failed to find any release assets using the specified criteria.'
            }
        } #Try
        catch {
            throw $_
        }
    } #process

    end {
        Write-Verbose "[$((Get-Date).TimeOfDay) END ] Ending: $($MyInvocation.MyCommand)"
    } #end
}