Public/Get-MSCatalogUpdate.ps1

<#
    .SYNOPSIS
        This command is used to retrieve updates from the https://www.catalog.update.microsoft.com website.

    .EXAMPLE
        $update = Get-MSCatalogUpdate -AllPages -Search "Cumulative Update for Windows 10 Version 21H2" -GetFramework
#>

function Get-MSCatalogUpdate {  
    [CmdLetBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string] $Search,

        [Parameter(Mandatory = $false)]
        [ValidateSet("Title", "Products", "Classification", "LastUpdated", "Size")]
        [string] $SortBy,

        [Parameter(Mandatory = $false)]
        [switch] $Descending,

        [Parameter(Mandatory = $false)]
        [switch] $Strict,

        [Parameter(Mandatory = $false)]
        [switch] $IncludeFileNames,

        [Parameter(Mandatory = $false)]
        [switch] $AllPages,

        [Parameter(Mandatory = $false)]
        [switch] $ExcludeFramework,

        [Parameter(Mandatory = $false)]
        [switch] $GetFramework
              
    )

# Default settings for the search
    
    $Bit64 = $true  # Include only x64 updates
    $ExcludePreview = $true # Exclude Preview updates
    $ExcludeDynamic = $true # Exclude Dynamic updates

    try {
        $ProgPref = $ProgressPreference
        $ProgressPreference = "SilentlyContinue"

        $Uri = "https://www.catalog.update.microsoft.com/Search.aspx?q=$([uri]::EscapeDataString($Search))"
        $Res = Invoke-CatalogRequest -Uri $Uri

        if ($PSBoundParameters.ContainsKey("SortBy")) {
            $SortParams = @{
                Uri = $Uri
                SortBy = $SortBy
                Descending = $Descending
                EventArgument = $Res.EventArgument
                EventValidation = $Res.EventValidation
                ViewState = $Res.ViewState
                ViewStateGenerator = $Res.ViewStateGenerator
            }
            $Res = Sort-CatalogResults @SortParams
        } else {
            # Default sort is by LastUpdated and in descending order.
            $SortParams = @{
                Uri = $Uri
                SortBy = "LastUpdated"
                Descending = $true
                EventArgument = $Res.EventArgument
                EventValidation = $Res.EventValidation
                ViewState = $Res.ViewState
                ViewStateGenerator = $Res.ViewStateGenerator
            }
            $Res = Sort-CatalogResults @SortParams
        }

        $Rows = $Res.Rows

        if ($Strict -and -not $AllPages) {
            $StrictRows = $Rows.Where({
                $_.SelectNodes("td")[1].InnerText.Trim() -like "*$Search*"
            })
            while (($StrictRows.Count -lt 25) -and ($Res.NextPage -eq "")) {
                $NextParams = @{
                    Uri = $Uri
                    EventArgument = $Res.EventArgument
                    EventTarget = 'ctl00$catalogBody$nextPageLinkText'
                    EventValidation = $Res.EventValidation
                    ViewState = $Res.ViewState
                    ViewStateGenerator = $Res.ViewStateGenerator
                    Method = "Post"
                }
                $Res = Invoke-CatalogRequest @NextParams
                $StrictRows += $Res.Rows.Where({
                    $_.SelectNodes("td")[1].InnerText.Trim() -like "*$Search*"
                })
            }
            $Rows = $StrictRows[0..24]
        } elseif ($AllPages) {
            while ($Res.NextPage -eq "") {
                $NextParams = @{
                    Uri = $Uri
                    EventArgument = $Res.EventArgument
                    EventTarget = 'ctl00$catalogBody$nextPageLinkText'
                    EventValidation = $Res.EventValidation
                    ViewState = $Res.ViewState
                    ViewStateGenerator = $Res.ViewStateGenerator
                    Method = "Post"
                }
                $Res = Invoke-CatalogRequest @NextParams
                $Rows += $Res.Rows
            }
            if ($Strict) {
                $Rows = $Rows.Where({
                    $_.SelectNodes("td")[1].InnerText.Trim() -like "*$Search*"
                })
            }
        }

        if ($ExcludePreview) {
            $Rows = $Rows | Where-Object {
                $nodes = $_.SelectNodes("td")
                if ($nodes -and $nodes.Count -gt 1) {
                    $nodes[1].InnerText.Trim() -notlike "*Preview*"
                } else {
                    $false
                }
            }
        }

        if ($ExcludeDynamic) {
            $Rows = $Rows | Where-Object {
                $nodes = $_.SelectNodes("td")
                if ($nodes -and $nodes.Count -gt 1) {
                    $nodes[1].InnerText.Trim() -notlike "*Dynamic*"
                } else {
                    $false
                }
            }
        }

        if ($Bit64) {
            $Rows = $Rows | Where-Object {
                $nodes = $_.SelectNodes("td")
                if ($nodes -and $nodes.Count -gt 1) {
                    $nodes[1].InnerText.Trim() -like "*x64*" -or $nodes[1].InnerText.Trim() -like "*64-Bit*"
                } else {
                    $false
                }
            }
        }

        if ($GetFramework) {
            $Rows = $Rows | Where-Object {
                $nodes = $_.SelectNodes("td")
                if ($nodes -and $nodes.Count -gt 1) {
                    $nodes[1].InnerText.Trim() -like "*4.8*" -and $nodes[1].InnerText.Trim() -match "Framework"
                } else {
                    $false
                }
            }
        }
        
        if ($ExcludeFramework) {
            $Rows = $Rows | Where-Object {
                $nodes = $_.SelectNodes("td")
                if ($nodes -and $nodes.Count -gt 1) {
                    $nodes[1].InnerText.Trim() -notmatch "Framework"
                } else {
                    $false
                }
            }
        } 

        if ($Rows.Count -gt 0) {
            foreach ($Row in $Rows) {
                if ($Row.Id -ne "headerRow") {
                    [MSCatalogUpdate]::new($Row, $IncludeFileNames)
                }
            }
        } else {
            Write-Warning "No updates found matching the search term."
        }
        $ProgressPreference = $ProgPref
    } catch {
        $ProgressPreference = $ProgPref
        if ($_.Exception.Message -like "No updates found matching*") {
            Write-Warning "No updates found matching the search term."
        } else {
            throw $_
        }
    }
}