public/Get-KbUpdate.ps1
function Get-KbUpdate { <# .SYNOPSIS Gets download links and detailed information for KB files (SPs/hotfixes/CUs, etc) from local db, catalog.update.microsoft.com or WSUS. .DESCRIPTION Gets detailed information including download links for KB files (SPs/hotfixes/CUs, etc) from local db, catalog.update.microsoft.com or WSUS. By default, the local sqlite database (updated regularly) is searched first and if no result is found, the catalog will be searched as a failback. Because Microsoft's RSS feed does not work, this can result in slowness. Use the Simple parameter for simplified output and faster results when using the web option. If you'd prefer searching and downloading from a local WSUS source, this is an option as well. See the examples for more information. .PARAMETER Pattern Any pattern. Can be the KB name, number or even MSRC numbrer. For example, KB4057119, 4057119, or MS15-101. .PARAMETER Architecture Can be x64, x86, ia64, or ARM. .PARAMETER Language Cumulative Updates come in one file for all languages, but Service Packs have a file for every language. If you want to get only a specific language, use this parameter. You you can press tab for auto-complete or use the two letter code that is used for Accept-Language HTTP header, e. g. "en" for English or "de" for German. .PARAMETER OperatingSystem Specify one or more operating systems. Tab complete to see what's available. If anything is missing, please file an issue. .PARAMETER ComputerName Used to connect to a remote host - gets the Operating System and architecture information automatically .PARAMETER Credential The optional alternative credential to be used when connecting to ComputerName .PARAMETER Product Specify one or more products (SharePoint, SQL Server, etc). Tab complete to see what's available. If anything is missing, please file an issue. .PARAMETER Latest Filters out any patches that have been superseded by other patches in the batch .PARAMETER Force When using Latest, the Web is required to get the freshest data unless Force is used. .PARAMETER Simple A lil faster. Returns, at the very least: Title, Architecture, Language, UpdateId and Link .PARAMETER Source Search source. By default, Database is searched first, then if no matches are found, it tries finding it on the web. .PARAMETER Multithread Multithread when three or more matches are returned. This is a lot faster than the default singlethread but also a lot less reliable. .PARAMETER NoMultithreading Obsolete as multithreading is no longer enabled by default. It's too unreliable. .PARAMETER EnableException By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message. This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting. Using this switch turns this "nice by default" feature off and enables you to catch exceptions with your own try/catch. .NOTES Author: Chrissy LeMaire (@cl), netnerds.net Copyright: (c) licensed under MIT License: MIT https://opensource.org/licenses/MIT .EXAMPLE PS C:\> Get-KbUpdate KB4057119 Gets detailed information about KB4057119. .EXAMPLE PS C:\> Get-KbUpdate -Pattern KB4057119, 4057114 -Source Database Gets detailed information about KB4057119 and KB4057114. Only searches the database (useful for offline enviornments). .EXAMPLE PS C:\> Get-KbUpdate -Pattern MS15-101 -Source Web Downloads KBs related to MSRC MS15-101 to the current directory. Only searches the web and not the local db or WSUS. .EXAMPLE PS C:\> Connect-KbWsusServer -ComputerName server1 -SecureConnection PS C:\> Get-KbUpdate -Pattern KB2764916 This command will make a secure connection (Default: 443) to a WSUS server. Then use Wsus as a source for Get-KbUpdate. .EXAMPLE PS C:\> Connect-KbWsusServer -ComputerName server1 -SecureConnection PS C:\> Get-KbUpdate -Pattern KB2764916 -Source Database Search the database even if you've connected to WSUS in the same session. .EXAMPLE PS C:\> Get-KbUpdate -Pattern KB4057119, 4057114 -Simple A lil faster when using web as a source. Returns, at the very least: Title, Architecture, Language, UpdateId and Link .EXAMPLE PS C:\> Get-KbUpdate -Pattern "KB2764916 Nederlands" -Simple An alternative way to search for language specific packages #> [CmdletBinding()] param( [Parameter(ValueFromPipeline)] [Alias("UpdateId", "Id", "KBUpdate", "HotfixId", "Name")] [string[]]$Pattern, [string[]]$Architecture, [string[]]$OperatingSystem, [PSFComputer[]]$ComputerName, [pscredential]$Credential, [string[]]$Product, [string]$Language, [switch]$Simple, [switch]$Latest, [switch]$Force, [switch]$Multithread, [switch]$NoMultithreading, [ValidateSet("Wsus", "Web", "Database")] [string[]]$Source = @("Web", "Database"), [switch]$EnableException ) begin { if ($NoMultithreading) { Write-PSFMessage -Level Warning -Message "Multithreading now disabled by default. This parameter will likely be removed in future versions." } if ($PSBoundParameters.Language) { Write-PSFMessage -Level Verbose -Message "Language specified, switching to web source only" $Source = "Web" } if ($script:ConnectedWsus -and -not $PSBoundParameters.Source) { Write-PSFMessage -Level Verbose -Message "Source not specified and WSUS connection detected. Setting source to Wsus." $Source = "Wsus" } Write-PSFMessage -Level Verbose -Message "Source set to $Source" $script:allresults = @() function Get-KbItemFromDb { [CmdletBinding()] param($kb) process { # Join to dupe and check dupe $kb = $kb.ToLower() $allitems = Invoke-SqliteQuery -DataSource $script:basedb -Query "select *, NULL AS SupersededBy, NULL AS Supersedes, NULL AS Link from kb where UpdateId in (select UpdateId from kb where UpdateId = '$kb' or Title like '%$kb%' or Id like '%$kb%' or Description like '%$kb%' or MSRCNumber like '%$kb%')" | Where-Object UpdateId -notin $script:allresults | Sort-Object UpdateId -Unique if ($allitems.UpdateId) { Write-PSFMessage -Level Verbose -Message "Found $([array]($allitems.UpdateId).count) in the database for $kb" } foreach ($item in $allitems) { $script:allresults += $item.UpdateId # I do wish my import didn't return empties but sometimes it does so check for length of 3 $item.SupersededBy = Invoke-SqliteQuery -DataSource $script:basedb -Query "select KB, Description from SupersededBy where UpdateId = '$($item.UpdateId)' and LENGTH(kb) > 3" # I do wish my import didn't return empties but sometimes it does so check for length of 3 $item.Supersedes = Invoke-SqliteQuery -DataSource $script:basedb -Query "select KB, Description from Supersedes where UpdateId = '$($item.UpdateId)' and LENGTH(kb) > 3" $item.Link = (Invoke-SqliteQuery -DataSource $script:basedb -Query "select DISTINCT Link from Link where UpdateId = '$($item.UpdateId)'").Link $item if ($item.SupportedProducts -match "\|") { $item.SupportedProducts = $item.SupportedProducts -split "\|" } if ($item.Architecture -eq "n/a") { $item.Architecture = $null } if ($item.title -match "ia32") { $item.Architecture = "IA32" } if ($item.title -match "ia64") { $item.Architecture = "IA64" } if ($item.title -match "64-Bit" -and $item.title -notmatch "32-Bit" -and -not $item.Architecture) { $item.Architecture = "x64" } if ($item.title -notmatch "64-Bit" -and $item.title -match "32-Bit" -and -not $item.Architecture) { $item.Architecture = "x86" } if ($item.title -match "x64" -or $item.title -match "AMD64") { $item.Architecture = "x64" } if ($item.title -match "x86") { $item.Architecture = "x86" } if ($item.title -match "ARM64") { $item.Architecture = "ARM64" } if ($item.title -match "ARM-based") { $item.Architecture = "ARM32" } if ($item.link -match "x64" -or $item.link -match "AMD64" -and -not $item.Architecture) { $item.Architecture = "x64" } if ($item.link -match "x86" -and -not $item.Architecture) { $item.Architecture = "x86" } if ($item.link -match "ARM64" -and -not $item.Architecture) { $item.Architecture = "ARM64" } if ($item.link -match "ARM-based" -and -not $item.Architecture) { $item.Architecture = "ARM32" } } if (-not $item -and $Source -eq "Database") { Write-PSFMessage -Level Verbose -Message "No results found for $kb in the local database" } } } function Get-KbItemFromWsusApi ($kb) { $results = Get-PSWSUSUpdate -Update $kb foreach ($wsuskb in $results) { # cacher $guid = $wsuskb.UpdateID $script:allresults += $guid $hashkey = "$guid-$Simple" if ($script:kbcollection.ContainsKey($hashkey)) { $script:kbcollection[$hashkey] continue } $severity = $wsuskb.MsrcSeverity | Select-Object -First 1 $alert = $wsuskb.SecurityBulletins | Select-Object -First 1 if ($severity -eq "MsrcSeverity") { $severity = $null } if ($alert -eq "") { $alert = $null } $file = $wsuskb | Get-PSWSUSInstallableItem | Get-PSWSUSUpdateFile $link = $file.FileURI if ($null -ne $link -and "" -ne $link) { $link = $file.OriginUri } if ($link -eq "") { $link = $null } if ($title -match "ia32") { $arch = "IA32" } if ($title -match "ia64") { $arch = "IA64" } if ($title -match "64-Bit" -and $title -notmatch "32-Bit" -and -not $arch) { $arch = "x64" } if ($title -notmatch "64-Bit" -and $title -match "32-Bit" -and -not $arch) { $arch = "x86" } if ($title -match "x64" -or $title -match "AMD64") { $arch = "x64" } if ($title -match "x86") { $arch = "x86" } if ($title -match "ARM64") { $arch = "ARM64" } if ($title -match "ARM-based") { $arch = "ARM32" } if ($link -match "x64" -or $link -match "AMD64" -and -not $arch) { $arch = "x64" } if ($link -match "x86" -and -not $arch) { $arch = "x86" } if ($link -match "ARM64" -and -not $arch) { $arch = "ARM64" } if ($link -match "ARM-based" -and -not $arch) { $arch = "ARM32" } $null = $script:kbcollection.Add($hashkey, ( [pscustomobject]@{ Title = $wsuskb.Title Id = ($wsuskb.KnowledgebaseArticles | Select-Object -First 1) Architecture = $null Language = $null Hotfix = $null Description = $wsuskb.Description LastModified = $wsuskb.ArrivalDate Size = $wsuskb.Size Classification = $wsuskb.UpdateClassificationTitle SupportedProducts = $wsuskb.ProductTitles MSRCNumber = $alert MSRCSeverity = $severity RebootBehavior = $wsuskb.InstallationBehavior.RebootBehavior RequestsUserInput = $wsuskb.InstallationBehavior.CanRequestUserInput ExclusiveInstall = $null NetworkRequired = $wsuskb.InstallationBehavior.RequiresNetworkConnectivity UninstallNotes = $null # $wsuskb.uninstallnotes UninstallSteps = $null # $wsuskb.uninstallsteps UpdateId = $guid Supersedes = $null #TODO SupersededBy = $null #TODO Link = $link InputObject = $kb })) $script:kbcollection[$hashkey] } } function Get-GuidsFromWeb ($kb) { Write-PSFMessage -Level Verbose -Message "$kb" if ($kb -match '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$') { Write-Verbose -Message "Guid passed in, skipping initial web search" $guids = @() $guids += [PSCustomObject]@{ Guid = $kb Title = $kb } } else { Write-Progress -Activity "Searching catalog for $kb" -Id 1 -Status "Contacting catalog.update.microsoft.com" if ($OperatingSystem) { $url = "https://www.catalog.update.microsoft.com/Search.aspx?q=$kb+$OperatingSystem" Write-PSFMessage -Level Verbose -Message "Accessing $url" $results = Invoke-TlsWebRequest -Uri $url $kbids = $results.InputFields | Where-Object { $_.type -eq 'Button' -and ($_.Value -eq 'Download' -or $_.class -eq 'flatBlueButtonDownload focus-only') } | Select-Object -ExpandProperty ID } if (-not $kbids) { $url = "https://www.catalog.update.microsoft.com/Search.aspx?q=$kb" $boundparams.OperatingSystem = $OperatingSystem Write-PSFMessage -Level Verbose -Message "Failing back to $url" $results = Invoke-TlsWebRequest -Uri $url $kbids = $results.InputFields | Where-Object { $_.type -eq 'Button' -and ($_.Value -eq 'Download' -or $_.class -eq 'flatBlueButtonDownload focus-only') } | Select-Object -ExpandProperty ID } Write-Progress -Activity "Searching catalog for $kb" -Id 1 -Completed if (-not $kbids) { try { $null = Invoke-TlsWebRequest -Uri "https://support.microsoft.com/en-us/topic/$kb" Stop-PSFFunction -EnableException:$EnableException -Message "Matches were found for $kb, but the results no longer exist in the catalog" return } catch { Write-PSFMessage -Level Verbose -Message "No results found for $kb at microsoft.com" return } } Write-PSFMessage -Level Verbose -Message "$kbids" # Thanks! https://keithga.wordpress.com/2017/05/21/new-tool-get-the-latest-windows-10-cumulative-updates/ $resultlinks = $results.Links | Where-Object ID -match '_link' | Where-Object { $_.OuterHTML -match ( "(?=.*" + ( $Filter -join ")(?=.*" ) + ")" ) } # get the title too $guids = @() foreach ($resultlink in $resultlinks) { $itemguid = $resultlink.id.replace('_link', '') $itemtitle = ($resultlink.outerHTML -replace '<[^>]+>', '').Trim() if ($itemguid -in $kbids) { $guids += [pscustomobject]@{ Guid = $itemguid Title = $itemtitle } } } } $guids | Where-Object Guid -notin $script:allresults } function Get-KbItemFromWeb ($kb) { # Wishing Microsoft offered an RSS feed. Since they don't, we are forced to parse webpages. function Get-Info ($Text, $Pattern) { if ($Pattern -match "labelTitle") { if ($Pattern -match "SupportedProducts") { # no idea what the regex below does but it's not working for SupportedProducts # do it the manual way instead $block = [regex]::Match($Text, $Pattern + '[\s\S]*?\s*(.*?)\s*<\/div>').Groups[0].Value $supported = $block -split "</span>" | Select-Object -Last 1 $supported.Trim().Replace("</div>","").Split(",").Trim() } else { # this should work... not accounting for multiple divs however? [regex]::Match($Text, $Pattern + '[\s\S]*?\s*(.*?)\s*<\/div>').Groups[1].Value } } elseif ($Pattern -match "span ") { [regex]::Match($Text, $Pattern + '(.*?)<\/span>').Groups[1].Value } else { [regex]::Match($Text, $Pattern + "\s?'?(.*?)'?;").Groups[1].Value } } function Get-SuperInfo ($Text, $Pattern) { # this works, but may also summon cthulhu $span = [regex]::match($Text, $pattern + '[\s\S]*?<div id') switch -Wildcard ($span.Value) { "*div style*" { $regex = '">\s*(.*?)\s*<\/div>' } "*a href*" { $regex = "<div[\s\S]*?'>(.*?)<\/a" } default { $regex = '"\s?>\s*(\S+?)\s*<\/div>' } } $spanMatches = [regex]::Matches($span, $regex).ForEach( { $_.Groups[1].Value }) if ($spanMatches -eq 'n/a') { $spanMatches = $null } if ($spanMatches) { foreach ($superMatch in $spanMatches) { $detailedMatches = [regex]::Matches($superMatch, '\b[kK][bB]([0-9]{6,})\b') # $null -ne $detailedMatches can throw cant index null errors, get more detailed if ($null -ne $detailedMatches.Groups) { [PSCustomObject] @{ 'KB' = $detailedMatches.Groups[1].Value 'Description' = $superMatch } | Add-Member -MemberType ScriptMethod -Name ToString -Value { $this.Description } -PassThru -Force } } } } try { $guids = Get-GuidsFromWeb -kb $kb foreach ($item in $guids) { $guid = $item.Guid $itemtitle = $item.Title $hashkey = "$guid-$Simple" if ($script:kbcollection.ContainsKey($hashkey)) { $guids = $guids | Where-Object Guid -notin $guid $script:kbcollection[$hashkey] continue } } $scriptblock = { $guid = $psitem.Guid $itemtitle = $psitem.Title Write-Verbose -Message "Downloading information for $itemtitle" $post = @{ size = 0; updateID = $guid; uidInfo = $guid } | ConvertTo-Json -Compress $body = @{ updateIDs = "[$post]" } Invoke-TlsWebRequest -Uri 'https://www.catalog.update.microsoft.com/DownloadDialog.aspx' -Method Post -Body $body | Select-Object -ExpandProperty Content } if ($guids.Count -gt 2 -and $Multithread) { $downloaddialogs = $guids | Invoke-Parallel -ImportVariables -ImportFunctions -ScriptBlock $scriptblock -ErrorAction Stop -RunspaceTimeout 60 } else { $downloaddialogs = $guids | ForEach-Object -Process $scriptblock } foreach ($downloaddialog in $downloaddialogs) { $title = Get-Info -Text $downloaddialog -Pattern 'enTitle =' $arch = $null $longlang = Get-Info -Text $downloaddialog -Pattern 'longLanguages =' if ($Pattern -match '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$') { $updateid = "$Pattern" } else { $updateid = Get-Info -Text $downloaddialog -Pattern 'updateID =' } $ishotfix = Get-Info -Text $downloaddialog -Pattern 'isHotFix =' $hashkey = "$updateid-$Simple" if ($ishotfix) { $ishotfix = "True" } else { $ishotfix = "False" } if ($longlang -eq "all") { $longlang = "All" } if ($title -match "ia32") { $arch = "IA32" } if ($title -match "ia64") { $arch = "IA64" } if ($title -match "64-Bit" -and $title -notmatch "32-Bit" -and -not $arch) { $arch = "x64" } if ($title -notmatch "64-Bit" -and $title -match "32-Bit" -and -not $arch) { $arch = "x86" } if ($title -match "x64" -or $title -match "AMD64") { $arch = "x64" } if ($title -match "x86") { $arch = "x86" } if ($title -match "ARM64") { $arch = "ARM64" } if ($title -match "ARM-based") { $arch = "ARM32" } if (-not $Simple) { # Multi-byte character is corrupted if passing BasicHtmlWebResponseObject to Get-Info -Text. $detaildialog = Invoke-TlsWebRequest -Uri "https://www.catalog.update.microsoft.com/ScopedViewInline.aspx?updateid=$updateid" | Select-Object -ExpandProperty Content $description = Get-Info -Text $detaildialog -Pattern '<span id="ScopedViewHandler_desc">' $lastmodified = Get-Info -Text $detaildialog -Pattern '<span id="ScopedViewHandler_date">' $size = Get-Info -Text $detaildialog -Pattern '<span id="ScopedViewHandler_size">' $classification = Get-Info -Text $detaildialog -Pattern '<span id="ScopedViewHandler_labelClassification_Separator" class="labelTitle">' if (-not $arch) { $arch = Get-Info -Text $detaildialog -Pattern '<span id="ScopedViewHandler_labelArchitecture_Separator" class="labelTitle">' } $supportedproducts = Get-Info -Text $detaildialog -Pattern '<span id="ScopedViewHandler_labelSupportedProducts_Separator" class="labelTitle">' $msrcnumber = Get-Info -Text $detaildialog -Pattern '<span id="ScopedViewHandler_labelSecurityBulliten_Separator" class="labelTitle">' $msrcseverity = Get-Info -Text $detaildialog -Pattern '<span id="ScopedViewHandler_msrcSeverity">' $kbnumbers = Get-Info -Text $detaildialog -Pattern '<span id="ScopedViewHandler_labelKBArticle_Separator" class="labelTitle">' $rebootbehavior = Get-Info -Text $detaildialog -Pattern '<span id="ScopedViewHandler_rebootBehavior">' $requestuserinput = Get-Info -Text $detaildialog -Pattern '<span id="ScopedViewHandler_userInput">' $exclusiveinstall = Get-Info -Text $detaildialog -Pattern '<span id="ScopedViewHandler_installationImpact">' $networkrequired = Get-Info -Text $detaildialog -Pattern '<span id="ScopedViewHandler_connectivity">' $uninstallnotes = Get-Info -Text $detaildialog -Pattern '<span id="ScopedViewHandler_labelUninstallNotes_Separator" class="labelTitle">' $uninstallsteps = Get-Info -Text $detaildialog -Pattern '<span id="ScopedViewHandler_labelUninstallSteps_Separator" class="labelTitle">' # Thanks @klorgas! https://github.com/potatoqualitee/kbupdate/issues/131 $supersededby = Get-SuperInfo -Text $detaildialog -Pattern '<div id="supersededbyInfo".*>' $supersedes = Get-SuperInfo -Text $detaildialog -Pattern '<div id="supersedesInfo".*>' if ($uninstallsteps -eq "n/a") { $uninstallsteps = $null } if ($msrcnumber -eq "n/a" -or $msrcnumber -eq "Unspecified") { $msrcnumber = $null } } $downloaddialog = $downloaddialog.Replace('www.download.windowsupdate', 'download.windowsupdate') $links = $downloaddialog | Select-String -AllMatches -Pattern "(http[s]?\://.*download\.windowsupdate\.com\/[^\'\""]*)" | Select-Object -Unique foreach ($link in $links) { if ($arch -eq "n/a") { $arch = $null } if ($link -match "x64" -or $link -match "AMD64") { $arch = "x64" } if ($link -match "x86") { $arch = "x86" } if ($link -match "ARM64") { $arch = "ARM64" } if ($link -match "ARM-based") { $arch = "ARM32" } if ($kbnumbers -eq "n/a") { $kbnumbers = $null } $properties = $baseproperties if ($Simple) { $properties = $properties | Where-Object { $PSItem -notin "LastModified", "Description", "Size", "Classification", "SupportedProducts", "MSRCNumber", "MSRCSeverity", "RebootBehavior", "RequestsUserInput", "ExclusiveInstall", "NetworkRequired", "UninstallNotes", "UninstallSteps", "SupersededBy", "Supersedes" } } $ishotfix = switch ($ishotfix) { 'Yes' { $true } 'No' { $false } default { $ishotfix } } $requestuserinput = switch ($requestuserinput) { 'Yes' { $true } 'No' { $false } default { $requestuserinput } } $exclusiveinstall = switch ($exclusiveinstall) { 'Yes' { $true } 'No' { $false } default { $exclusiveinstall } } $networkrequired = switch ($networkrequired) { 'Yes' { $true } 'No' { $false } default { $networkrequired } } if ('n/a' -eq $uninstallnotes) { $uninstallnotes = $null } if ('n/a' -eq $uninstallsteps) { $uninstallsteps = $null } # may fix later $ishotfix = $null $null = $script:kbcollection.Add($hashkey, ( [pscustomobject]@{ Title = $title Id = $kbnumbers Architecture = $arch Language = $Language Hotfix = $ishotfix Description = $description LastModified = $lastmodified Size = $size Classification = $classification SupportedProducts = $supportedproducts MSRCNumber = $msrcnumber MSRCSeverity = $msrcseverity RebootBehavior = $rebootbehavior RequestsUserInput = $requestuserinput ExclusiveInstall = $exclusiveinstall NetworkRequired = $networkrequired UninstallNotes = $uninstallnotes UninstallSteps = $uninstallsteps UpdateId = $updateid Supersedes = $supersedes SupersededBy = $supersededby Link = $link.matches.value InputObject = $kb })) $script:kbcollection[$hashkey] } } } catch { Stop-PSFFunction -EnableException:$EnableException -Message "Failure" -ErrorRecord $_ -Continue } } $properties = "Title", "Id", "Description", "Architecture", "Language", "Classification", "SupportedProducts", "MSRCNumber", "MSRCSeverity", "Size", "UpdateId", "RebootBehavior", "RequestsUserInput", "ExclusiveInstall", "NetworkRequired", "UninstallNotes", "UninstallSteps", "SupersededBy", "Supersedes", "LastModified", "Link" if ($Simple) { $properties = $properties | Where-Object { $PSItem -notin "ID", "LastModified", "Description", "Size", "Classification", "SupportedProducts", "MSRCNumber", "MSRCSeverity", "RebootBehavior", "RequestsUserInput", "ExclusiveInstall", "NetworkRequired", "UninstallNotes", "UninstallSteps", "SupersededBy", "Supersedes" } } if ($Source -eq "WSUS") { $properties = $properties | Where-Object { $PSItem -notin "Architecture", "Language", "Size", "ExclusiveInstall", "UninstallNotes", "UninstallSteps" } } # if latest is used, needs a collection $allkbs = @() } process { if ($Source -contains "Wsus" -and -not $script:ConnectedWsus) { Stop-PSFFunction -Message "Please use Connect-KbWsusServer before selecting WSUS as a Source" -EnableException:$EnableException return } if ($Latest -and $Simple) { Write-PSFMessage -Level Warning -Message "Simple is ignored when Latest is specified, as latest requires detailed data" $Simple = $false } if ($Latest -and $PSBoundParameters.Source -and $Source -eq "Database" -and -not $Force) { Write-PSFMessage -Level Verbose -Message "Source is ignored when Latest is specified, as latest requires the freshest data from the web. Use -Force to override this." $PSBoundParameters.Source = $null $Source = "Web" } foreach ($computer in $Computername) { # tempting to add language but for now I won't $results = $script:compcollection[$computer] if (-not $results) { try { $results = Invoke-PSFCommand -ComputerName $computer -Credential $Credential -ScriptBlock { $proc = $env:PROCESSOR_ARCHITECTURE if ($proc -eq "AMD64") { $proc = "x64" } $os = Get-CimInstance Win32_OperatingSystem | Select-Object -ExpandProperty Caption $os = $os.Replace("Standard", "").Replace("Microsoft ", "").Replace(" Pro", "").Replace("Professional", "").Replace("Home", "").Replace("Enterprise", "").Replace("Datacenter", "").Trim() [pscustomobject]@{ Architecture = $proc OperatingSystem = $os } } -ErrorAction Stop } catch { Stop-PSFFunction -Message "Failure" -ErrorRecord $_ -EnableException:$EnableException return } $null = $script:compcollection.Add($computer, $results) } if ($results.Architecture) { if ($results.Architecture -notin $Architecture) { Write-PSFMessage -Level Verbose -Message "Adding $($results.Architecture)" $Architecture += $results.Architecture } } if ($results.OperatingSystem) { if ($results.OperatingSystem -notin $OperatingSystem) { Write-PSFMessage -Level Verbose -Message "Adding $($results.OperatingSystem)" $OperatingSystem += $results.OperatingSystem } } } $boundparams = @{ OperatingSystem = $OperatingSystem Architecture = $Architecture Product = $PSBoundParameters.Product Language = $PSBoundParameters.Language Source = $Source } foreach ($kb in $Pattern) { $results = @() if ($Source -contains "Wsus") { $results += Get-KbItemFromWsusApi $kb } if ($Source -contains "Database") { $results += Get-KbItemFromDb $kb } if ($Source -contains "Web") { $results += Get-KbItemFromWeb $kb } $allkbs += $results } } end { # I'm not super awesome with the pipeline, and am open to suggestions if this is not the best way if ($Latest -and $allkbs) { $allkbs | Search-Kb @boundparams | Select-KbLatest | Select-DefaultView -Property $properties } else { $allkbs | Search-Kb @boundparams | Select-DefaultView -Property $properties } } } # SIG # Begin signature block # MIIjZQYJKoZIhvcNAQcCoIIjVjCCI1ICAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUkVu61Nu6y4I+Jqz/T3w7fBnQ # dmCggh2DMIIFGjCCBAKgAwIBAgIQAwW7hiGwoWNfv96uEgTnbTANBgkqhkiG9w0B # AQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD # VQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFz # c3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4XDTIwMDUxMjAwMDAwMFoXDTIzMDYw # ODEyMDAwMFowVzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCFZpcmdpbmlhMQ8wDQYD # VQQHEwZWaWVubmExETAPBgNVBAoTCGRiYXRvb2xzMREwDwYDVQQDEwhkYmF0b29s # czCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALy/Y3ur47++CAG2mOa1 # 6h8WjXjSTvcldDmw4PpAvOOCKNr6xyhg/FOYVIiaeq2N9kVaa5wBawOIxVWuj/rI # aOxeYklQDugPkGUx0Ap+6KrjnnxgE6ONzQGnc1tjlka6N0KazD2WodEBWKXo/Vmk # C/cP9PJVWroCMOwlj7GtEv2IxzxikPm2ICP5KxFK5PmrA+5bzcHJEeqRonlgMn9H # zZkqHr0AU1egnfEIlH4/v6lry1t1KBF/bnDhl9g/L0icS+ychFVkx4OOO4a+qvT8 # xqvvdQjv3PQ1hbzTI3/tXOWu9XxGeeIdZjaJv16FmWKCnloSp1Xb9cVU9XhIpomz # xH0CAwEAAaOCAcUwggHBMB8GA1UdIwQYMBaAFFrEuXsqCqOl6nEDwGD5LfZldQ5Y # MB0GA1UdDgQWBBTwwKD7tgOAQ077Cdfd33qxy+OeIjAOBgNVHQ8BAf8EBAMCB4Aw # EwYDVR0lBAwwCgYIKwYBBQUHAwMwdwYDVR0fBHAwbjA1oDOgMYYvaHR0cDovL2Ny # bDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwNaAzoDGGL2h0 # dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtY3MtZzEuY3JsMEwG # A1UdIARFMEMwNwYJYIZIAYb9bAMBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3 # LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQBMIGEBggrBgEFBQcBAQR4MHYwJAYI # KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBOBggrBgEFBQcwAoZC # aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJ # RENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQAD # ggEBAI/N+XCVDB/WNqQSrKY85zScHGJjsXgXByYvsitMuG5vo+ODhlh+ILv0CTPl # o2Wo75MnSSqCWR+c6xyN8pDPMPBxm2EtVmXzeKDMIudYyjxmT8PZ3hktj16wXCo8 # 2+65UOse+CHsfoMn/M9WbkQ4rSyWNPRRDodATC2i4flLyeuoIZnyMoz/4N4mWb6s # IAYZ/tNXzm6qwCfkmoMSf9tcTUCXIbVDliJcUZLlJ/SpLg2KzDu9GtnpBzg3AG3L # hwBiPMM8OLGitYjz4VU5RYox0vu1XyLf3f9fKTCxxwKy0EKntWdJk37i+DOMQlCq # Xm5B/KyNxb2utv+qLGlyw9MphEcwggUwMIIEGKADAgECAhAECRgbX9W7ZnVTQ7Vv # lVAIMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdp # Q2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0Rp # Z2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xMzEwMjIxMjAwMDBaFw0yODEw # MjIxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx # GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNI # QTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUA # A4IBDwAwggEKAoIBAQD407Mcfw4Rr2d3B9MLMUkZz9D7RZmxOttE9X/lqJ3bMtdx # 6nadBS63j/qSQ8Cl+YnUNxnXtqrwnIal2CWsDnkoOn7p0WfTxvspJ8fTeyOU5JEj # lpB3gvmhhCNmElQzUHSxKCa7JGnCwlLyFGeKiUXULaGj6YgsIJWuHEqHCN8M9eJN # YBi+qsSyrnAxZjNxPqxwoqvOf+l8y5Kh5TsxHM/q8grkV7tKtel05iv+bMt+dDk2 # DZDv5LVOpKnqagqrhPOsZ061xPeM0SAlI+sIZD5SlsHyDxL0xY4PwaLoLFH3c7y9 # hbFig3NBggfkOItqcyDQD2RzPJ6fpjOp/RnfJZPRAgMBAAGjggHNMIIByTASBgNV # HRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEF # BQcDAzB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp # Z2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQu # Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0fBHoweDA6oDig # NoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v # dENBLmNybDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0 # QXNzdXJlZElEUm9vdENBLmNybDBPBgNVHSAESDBGMDgGCmCGSAGG/WwAAgQwKjAo # BggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAKBghghkgB # hv1sAzAdBgNVHQ4EFgQUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHwYDVR0jBBgwFoAU # Reuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZIhvcNAQELBQADggEBAD7sDVoks/Mi # 0RXILHwlKXaoHV0cLToaxO8wYdd+C2D9wz0PxK+L/e8q3yBVN7Dh9tGSdQ9RtG6l # jlriXiSBThCk7j9xjmMOE0ut119EefM2FAaK95xGTlz/kLEbBw6RFfu6r7VRwo0k # riTGxycqoSkoGjpxKAI8LpGjwCUR4pwUR6F6aGivm6dcIFzZcbEMj7uo+MUSaJ/P # QMtARKUT8OZkDCUIQjKyNookAv4vcn4c10lFluhZHen6dGRrsutmQ9qzsIzV6Q3d # 9gEgzpkxYz0IGhizgZtPxpMQBvwHgfqL2vmCSfdibqFT+hKUGIUukpHqaGxEMrJm # oecYpJpkUe8wggWxMIIEmaADAgECAhABJAr7HjgLihbxS3Gd9NPAMA0GCSqGSIb3 # DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAX # BgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3Vy # ZWQgSUQgUm9vdCBDQTAeFw0yMjA2MDkwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIx # CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 # dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBH # NDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIw # aTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLK # EdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4Tm # dDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembu # d8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnD # eMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1 # XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVld # QnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTS # YW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSm # M9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzT # QRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kx # fgommfXkaS+YHS312amyHeUbAgMBAAGjggFeMIIBWjAPBgNVHRMBAf8EBTADAQH/ # MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv # 9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYB # BQUHAwgweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5k # aWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0 # LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDig # NoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v # dENBLmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZI # hvcNAQEMBQADggEBAJoWAqUB74H7DbRYsnitqCMZ2XM32mCeUdfL+C9AuaMffEBO # Mz6QPOeJAXWF6GJ7HVbgcbreXsY3vHlcYgBN+El6UU0GMvPF0gAqJyDqiS4VOeAs # Pvh1fCyCQWE1DyPQ7TWV0oiVKUPL4KZYEHxTjp9FySA3FMDtGbp+dznSVJbHphHf # NDP2dVJCSxydjZbVlWxHEhQkXyZB+hpGvd6w5ZFHA6wYCMvL22aJfyucZb++N06+ # LfOdSsPMzEdeyJWVrdHLuyoGIPk/cuo260VyknopexQDPPtN1khxehARigh0zWwb # BFzSipUDdlFQU9Yu90pGw64QLHFMsIe2JzdEYEQwggauMIIElqADAgECAhAHNje3 # JFR82Ees/ShmKl5bMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYD # VQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAf # BgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMjAzMjMwMDAwMDBa # Fw0zNzAzMjIyMzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2Vy # dCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNI # QTI1NiBUaW1lU3RhbXBpbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK # AoICAQDGhjUGSbPBPXJJUVXHJQPE8pE3qZdRodbSg9GeTKJtoLDMg/la9hGhRBVC # X6SI82j6ffOciQt/nR+eDzMfUBMLJnOWbfhXqAJ9/UO0hNoR8XOxs+4rgISKIhjf # 69o9xBd/qxkrPkLcZ47qUT3w1lbU5ygt69OxtXXnHwZljZQp09nsad/ZkIdGAHvb # REGJ3HxqV3rwN3mfXazL6IRktFLydkf3YYMZ3V+0VAshaG43IbtArF+y3kp9zvU5 # EmfvDqVjbOSmxR3NNg1c1eYbqMFkdECnwHLFuk4fsbVYTXn+149zk6wsOeKlSNbw # sDETqVcplicu9Yemj052FVUmcJgmf6AaRyBD40NjgHt1biclkJg6OBGz9vae5jtb # 7IHeIhTZgirHkr+g3uM+onP65x9abJTyUpURK1h0QCirc0PO30qhHGs4xSnzyqqW # c0Jon7ZGs506o9UD4L/wojzKQtwYSH8UNM/STKvvmz3+DrhkKvp1KCRB7UK/BZxm # SVJQ9FHzNklNiyDSLFc1eSuo80VgvCONWPfcYd6T/jnA+bIwpUzX6ZhKWD7TA4j+ # s4/TXkt2ElGTyYwMO1uKIqjBJgj5FBASA31fI7tk42PgpuE+9sJ0sj8eCXbsq11G # deJgo1gJASgADoRU7s7pXcheMBK9Rp6103a50g5rmQzSM7TNsQIDAQABo4IBXTCC # AVkwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUuhbZbU2FL3MpdpovdYxq # II+eyG8wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/ # BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggr # BgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVo # dHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0 # LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20v # RGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjAL # BglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBAH1ZjsCTtm+YqUQiAX5m1tgh # QuGwGC4QTRPPMFPOvxj7x1Bd4ksp+3CKDaopafxpwc8dB+k+YMjYC+VcW9dth/qE # ICU0MWfNthKWb8RQTGIdDAiCqBa9qVbPFXONASIlzpVpP0d3+3J0FNf/q0+KLHqr # hc1DX+1gtqpPkWaeLJ7giqzl/Yy8ZCaHbJK9nXzQcAp876i8dU+6WvepELJd6f8o # VInw1YpxdmXazPByoyP6wCeCRK6ZJxurJB4mwbfeKuv2nrF5mYGjVoarCkXJ38SN # oOeY+/umnXKvxMfBwWpx2cYTgAnEtp/Nh4cku0+jSbl3ZpHxcpzpSwJSpzd+k1Os # Ox0ISQ+UzTl63f8lY5knLD0/a6fxZsNBzU+2QJshIUDQtxMkzdwdeDrknq3lNHGS # 1yZr5Dhzq6YBT70/O3itTK37xJV77QpfMzmHQXh6OOmc4d0j/R0o08f56PGYX/sr # 2H7yRp11LB4nLCbbbxV7HhmLNriT1ObyF5lZynDwN7+YAN8gFk8n+2BnFqFmut1V # wDophrCYoCvtlUG3OtUVmDG0YgkPCr2B2RP+v6TR81fZvAT6gt4y3wSJ8ADNXcL5 # 0CN/AAvkdgIm2fBldkKmKYcJRyvmfxqkhQ/8mJb2VVQrH4D6wPIOK+XW+6kvRBVK # 5xMOHds3OBqhK/bt1nz8MIIGxjCCBK6gAwIBAgIQCnpKiJ7JmUKQBmM4TYaXnTAN # BgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs # IEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEy # NTYgVGltZVN0YW1waW5nIENBMB4XDTIyMDMyOTAwMDAwMFoXDTMzMDMxNDIzNTk1 # OVowTDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMSQwIgYD # VQQDExtEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMiAtIDIwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQC5KpYjply8X9ZJ8BWCGPQz7sxcbOPgJS7SMeQ8QK77 # q8TjeF1+XDbq9SWNQ6OB6zhj+TyIad480jBRDTEHukZu6aNLSOiJQX8Nstb5hPGY # Pgu/CoQScWyhYiYB087DbP2sO37cKhypvTDGFtjavOuy8YPRn80JxblBakVCI0Fa # +GDTZSw+fl69lqfw/LH09CjPQnkfO8eTB2ho5UQ0Ul8PUN7UWSxEdMAyRxlb4pgu # j9DKP//GZ888k5VOhOl2GJiZERTFKwygM9tNJIXogpThLwPuf4UCyYbh1RgUtwRF # 8+A4vaK9enGY7BXn/S7s0psAiqwdjTuAaP7QWZgmzuDtrn8oLsKe4AtLyAjRMruD # +iM82f/SjLv3QyPf58NaBWJ+cCzlK7I9Y+rIroEga0OJyH5fsBrdGb2fdEEKr7mO # CdN0oS+wVHbBkE+U7IZh/9sRL5IDMM4wt4sPXUSzQx0jUM2R1y+d+/zNscGnxA7E # 70A+GToC1DGpaaBJ+XXhm+ho5GoMj+vksSF7hmdYfn8f6CvkFLIW1oGhytowkGvu # b3XAsDYmsgg7/72+f2wTGN/GbaR5Sa2Lf2GHBWj31HDjQpXonrubS7LitkE956+n # GijJrWGwoEEYGU7tR5thle0+C2Fa6j56mJJRzT/JROeAiylCcvd5st2E6ifu/n16 # awIDAQABo4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYD # VR0lAQH/BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZI # AYb9bAcBMB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQW # BBSNZLeJIf5WWESEYafqbxw2j92vDTBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8v # Y3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2 # VGltZVN0YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcw # AYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8v # Y2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hB # MjU2VGltZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQANLSN0ptH1 # +OpLmT8B5PYM5K8WndmzjJeCKZxDbwEtqzi1cBG/hBmLP13lhk++kzreKjlaOU7Y # hFmlvBuYquhs79FIaRk4W8+JOR1wcNlO3yMibNXf9lnLocLqTHbKodyhK5a4m1Wp # Gmt90fUCCU+C1qVziMSYgN/uSZW3s8zFp+4O4e8eOIqf7xHJMUpYtt84fMv6XPfk # U79uCnx+196Y1SlliQ+inMBl9AEiZcfqXnSmWzWSUHz0F6aHZE8+RokWYyBry/J7 # 0DXjSnBIqbbnHWC9BCIVJXAGcqlEO2lHEdPu6cegPk8QuTA25POqaQmoi35komWU # EftuMvH1uzitzcCTEdUyeEpLNypM81zctoXAu3AwVXjWmP5UbX9xqUgaeN1Gdy4b # esAzivhKKIwSqHPPLfnTI/KeGeANlCig69saUaCVgo4oa6TOnXbeqXOqSGpZQ65f # 6vgPBkKd3wZolv4qoHRbY2beayy4eKpNcG3wLPEHFX41tOa1DKKZpdcVazUOhdbg # LMzgDCS4fFILHpl878jIxYxYaa+rPeHPzH0VrhS/inHfypex2EfqHIXgRU4SHBQp # WMxv03/LvsEOSm8gnK7ZczJZCOctkqEaEf4ymKZdK5fgi9OczG21Da5HYzhHF1tv # E9pqEG4fSbdEW7QICodaWQR2EaGndwITHDGCBUwwggVIAgEBMIGGMHIxCzAJBgNV # BAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdp # Y2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2Rl # IFNpZ25pbmcgQ0ECEAMFu4YhsKFjX7/erhIE520wCQYFKw4DAhoFAKB4MBgGCisG # AQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFL7N # KwDlmvyjalPMEZCNLAANJbvbMA0GCSqGSIb3DQEBAQUABIIBAHesaku1CapsDSRr # IWimwaLlNVS9uhlAf1fObvg34R9GSEyE7K2z5Gbb/Qgu8fwot7671LW4+zzezMqY # 9i9WZwMvwWtR/hH4tdelycrWWlapwrYFevpfzqtebbpn7nYTRs1ivmlIyyNore46 # SxnRAMosPDjvT3xtGd9fQ/mJDYQtpBhN/JEur2+eEcLNBDpLeATz8c1OlakYNFeE # R+RHf8ZqlH7d3plfS7j8xIzmW2+TrC4GNJRg4jARLmBSxYn/XANerezLyMFMozT4 # fL6kjAmf1SuSSkm+39k2ll3MU1vo65NSpldsv34c+i7hQuFvfAJC10rSrzYFkjr8 # d4CTga6hggMgMIIDHAYJKoZIhvcNAQkGMYIDDTCCAwkCAQEwdzBjMQswCQYDVQQG # EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0 # IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBAhAKekqI # nsmZQpAGYzhNhpedMA0GCWCGSAFlAwQCAQUAoGkwGAYJKoZIhvcNAQkDMQsGCSqG # SIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjIwODAxMjAzMTUzWjAvBgkqhkiG9w0B # CQQxIgQgNFaQjOhtdvHb/dT8cOnTNIV66GH0Mgj18Dv60Auc4UUwDQYJKoZIhvcN # AQEBBQAEggIAsELoczUR6AYgwiwCBwiyH6B2S2NBRwLKC3dgDc6lGIVyg/HBO1Ez # 9adT/m5o2dNNbmxWOj7j5wI1Nij0rmgBO08lD75VOjZBQkVS1b2Y0S332JbFphbn # fgcPE0bcYBb0gJhDAmyx2cj8MsNstUpdd9qhGnF8uNqfiVWiyya5JgQFCDuaUllq # sYYKEnEZneZ/pxwe0A911csuPa0vb7iv8APhvSm65crtQ2vvFwizimhcNC4iVxmI # MuLnwZZe4PB5V0AyliU5N+pHg/Qfvl69GjUpHj+zD+MgiF4Kutnpgqlv9hK5PGyR # EFmwSjgO5Se2FLFza012gldig4ZfWBpJFJW1kGmh34GJRC5Fp9RDqknvFObwxcNA # cJDQhVeoA2O3eCfXIlUi/0LnYhyhksRMtfvvTUSeCrCYSmV7DuG7s+i7q9Yf52dS # EkOVraNn2hCf76gYK5coxVu4WoQdFUaqvN056uNSFNwCycEj8ib6RIvMKYt8brVJ # 1P4HriivkNPhHtPgh0Ki5LyiCDdAhJORrSSzusKwb5foEhri3zVgkE/CB53EtqQ1 # 0u4Y+JiQLOnBKCSlirVhq3NE1kG/ZyjogkMLZyffVbGuvIAdL6rCiC3VzMU+8/oS # emJssV3B55snyOqaOEqxxjHEFPpqiZL90AHWl44gly/8vMjO+EAHHOM= # SIG # End signature block |