public/Get-AppVentiXContentSharePackages.ps1
|
<#
.SYNOPSIS Retrieves packages from the AppVentiX content shares. .DESCRIPTION The Get-AppVentiXContentSharePackages function scans the content shares configured for machine groups and retrieves information about App-V (.appv), MSIX (.msix), and connection group (.appg) packages. It returns package details including name, version, publisher, and connection group membership. .PARAMETER MachineGroupFriendlyName (Optional) Specifies the friendly name of the machine group to retrieve packages from. If not specified, packages from all machine groups are retrieved. .PARAMETER IncludeDependencies (Optional) When specified, includes packages located in Dependencies folders. By default, dependency packages are excluded from the results. .PARAMETER CachePackages (Optional) When specified, package manifest identity fields are cached in memory for the duration of the PowerShell session and reused on subsequent calls, avoiding a re-read of packages that have not changed. The cache is keyed on the file path together with its last write time and size, so a repackaged file (new content) is always re-read. The cache is module-scoped (not accessible outside the module) and is cleared when the module is reimported. Off by default. .PARAMETER ThrottleLimit (Optional) Specifies the maximum number of package manifests to read concurrently from the content share. Reading a manifest opens the package archive over the (usually remote) share, so scanning is network-latency bound and benefits from parallelism. Defaults to 16. Increase on high-latency shares; decrease to reduce load on the file server. .PARAMETER ConfigShare Specifies the path to the AppVentiX configuration share. You can omit this parameter if Set-AppVentiXConfigShare has been called. .EXAMPLE Get-AppVentiXContentSharePackages Retrieves all packages from all machine group content shares. .EXAMPLE Get-AppVentiXContentSharePackages -MachineGroupFriendlyName 'Production' Retrieves packages from the content shares of the 'Production' machine group. .EXAMPLE Get-AppVentiXContentSharePackages -IncludeDependencies Retrieves all packages including those in Dependencies folders. .NOTES Function : Get-AppVentiXContentSharePackages Author : John Billekens Copyright: (c) John Billekens Consultancy & AppVentiX Version : 2026.707.1400 Requires : Valid AppVentiX license #> function Get-AppVentiXContentSharePackages { [CmdletBinding(DefaultParameterSetName = 'All')] param( [Alias('FriendlyName')] [Parameter(ParameterSetName = 'Named')] [ValidateNotNullOrEmpty()] [String]$MachineGroupFriendlyName = "All Machine Groups", [switch]$IncludeDependencies, [switch]$CachePackages, [ValidateRange(1, 128)] [Int]$ThrottleLimit = 16, [Parameter(DontShow, ParameterSetName = 'All')] [Parameter(DontShow, ParameterSetName = 'Named')] [ValidateNotNullOrEmpty()] [String]$ConfigShare = $Script:AppVentiX.ConfigShare ) Write-Verbose "Starting function 'Get-AppVentiXContentSharePackages'" if (Test-AppVentiXIsLicensed -ConfigShare $ConfigShare) { if ($PSCmdlet.ParameterSetName -eq 'Named') { $machineGroups = Get-AppVentiXMachineGroup -ConfigShare $ConfigShare -Name $MachineGroupFriendlyName if ($machineGroup.FriendlyName -like "$MachineGroupFriendlyName") { $MachineGroupFriendlyName = $machineGroup.FriendlyName } else { Write-Error "Machine group `"$MachineGroupFriendlyName`" not found!" -ErrorAction Stop } } else { $machineGroups = Get-AppVentiXMachineGroup -ConfigShare $ConfigShare } $Packages = [System.Collections.Generic.List[object]]::new() $PackagesByFullName = [System.Collections.Generic.Dictionary[string, object]]::new([System.StringComparer]::OrdinalIgnoreCase) foreach ($machineGroup in $machineGroups) { foreach ($GroupContentShare in $machineGroup.GroupContentshares.ContentShare) { $AppvConnectionGroups = [System.Collections.Generic.List[object]]::new() Write-Verbose "[$($machineGroup.FriendlyName)] Processing Content Share $($GroupContentShare.Path)" $ContentShareFiles = Get-ChildItem -Path $GroupContentShare.Path -Include *.appv, *.msix, *.appg -Recurse Write-AppVentiXDebugLog -Message 'Enumerated Content Share files' -Level Timing -Data @{ MachineGroup = "$($machineGroup.FriendlyName)" ContentSharePath = "$($GroupContentShare.Path)" FileCount = @($ContentShareFiles).Count } # First pass: create the package objects and classify by type. The (network-bound) manifest reads are gathered here and performed in parallel afterwards. $ManifestFilesToScan = [System.Collections.Generic.List[string]]::new() # Cache key per file (FullName|LastWriteTimeUtc ticks|Length); only tracked when caching. $ManifestCacheKeys = [System.Collections.Generic.Dictionary[string, string]]::new([System.StringComparer]::OrdinalIgnoreCase) foreach ($File in $ContentShareFiles) { if ($IncludeDependencies.IsPresent -eq $false -and $File.FullName -like "*\Dependencies\*$($File.Name)") { Write-Verbose "Skipping dependency file: $($File.FullName)" continue } $package = [PSCustomObject]@{ Type = "ERROR" MachineGroup = $machineGroup.FriendlyName Name = $null Version = $null Publisher = $null LastUpdated = $File.LastWriteTime PackageId = $null VersionId = $null Filename = $File.Name Fullname = $File.FullName ContentSharePath = $GroupContentShare.Path MemberOf = @() } if ($File.Name -like "*.appv") { $package.Type = "APPV" $ManifestFilesToScan.Add($File.FullName) if ($CachePackages) { $ManifestCacheKeys[$File.FullName] = '{0}|{1}|{2}' -f $File.FullName, $File.LastWriteTimeUtc.Ticks, $File.Length } } elseif ($File.Name -like "*.msix") { $package.Type = "MSIX" $ManifestFilesToScan.Add($File.FullName) if ($CachePackages) { $ManifestCacheKeys[$File.FullName] = '{0}|{1}|{2}' -f $File.FullName, $File.LastWriteTimeUtc.Ticks, $File.Length } } elseif ($File.Name -like "*.appg") { $package.Type = "Group" Write-Verbose "Processing AppG package: $($File.FullName)" $AppvConnectionGroups.Add($File) } elseif ($File.Name -like "*.cim") { Write-Warning "CIM files are currently not supported within this function: $($File.FullName)" } elseif ($File.Name -like "*.vhd" -or $File.Name -like "*.vhdx") { Write-Warning "VHD/VHDX files are currently not supported within this function: $($File.FullName)" } else { Write-Warning "Unknown file type: $($File.FullName)" } $Packages.Add($package) $PackagesByFullName[$File.FullName] = $package } # Second pass: read all App-V/MSIX manifests and merge the results back onto the package objects. # When caching is enabled, files whose cache key (path|LastWriteTimeUtc|Length) still matches are served from the in-memory cache and only the remaining (changed or never-seen) files are read in parallel over the share. if ($ManifestFilesToScan.Count -gt 0) { $manifestFailures = 0 $cacheHitCount = 0 # Partition into cache hits (applied immediately from the in-memory cache) and misses (read in parallel over the share below). ManifestResultsToApply collects both so a single merge loop applies them to the package objects. $ManifestResultsToApply = [System.Collections.Generic.List[object]]::new() $FilesToScan = [System.Collections.Generic.List[string]]::new() foreach ($file in $ManifestFilesToScan) { $cacheKey = $null if ($CachePackages -and $ManifestCacheKeys.TryGetValue($file, [ref]$cacheKey) -and $Script:AppVentiX.ManifestCache.ContainsKey($cacheKey)) { $ManifestResultsToApply.Add($Script:AppVentiX.ManifestCache[$cacheKey]) $cacheHitCount++ } else { $FilesToScan.Add($file) } } if ($FilesToScan.Count -gt 0) { Write-Verbose "[$($machineGroup.FriendlyName)] Reading $($FilesToScan.Count) manifest(s) from $($GroupContentShare.Path) ($cacheHitCount served from cache)" foreach ($manifest in (Invoke-AppVentiXManifestScan -Path $FilesToScan.ToArray() -ThrottleLimit $ThrottleLimit)) { $ManifestResultsToApply.Add($manifest) # Store successful reads in the cache for reuse later in the session. if ($CachePackages -and $null -ne $manifest -and $manifest.Success -and -not [String]::IsNullOrEmpty($manifest.FullName) -and $ManifestCacheKeys.ContainsKey($manifest.FullName)) { $Script:AppVentiX.ManifestCache[$ManifestCacheKeys[$manifest.FullName]] = $manifest } } } # Merge every manifest result (cached + freshly read) onto its package object. foreach ($manifest in $ManifestResultsToApply) { if ($null -eq $manifest -or [String]::IsNullOrEmpty($manifest.FullName)) { Write-Warning "Failed to read a package manifest. Error: $($manifest.Error)" $manifestFailures++ continue } $package = $null if (-not $PackagesByFullName.TryGetValue($manifest.FullName, [ref]$package)) { continue } if (-not $manifest.Success) { Write-Warning "Failed to process $($package.Type) package: $($manifest.FullName). Error: $($manifest.Error)" $manifestFailures++ continue } $package.Name = $manifest.Name $package.Version = $manifest.Version if ($package.Type -eq "APPV") { $package.PackageId = $manifest.PackageId $package.VersionId = $manifest.VersionId } elseif ($package.Type -eq "MSIX") { $package.Publisher = $manifest.Publisher } } Write-AppVentiXDebugLog -Message 'Read package manifests' -Level Timing -Data @{ MachineGroup = "$($machineGroup.FriendlyName)" ContentSharePath = "$($GroupContentShare.Path)" ManifestCount = $ManifestFilesToScan.Count ScannedCount = $FilesToScan.Count CacheHitCount = $cacheHitCount FailureCount = $manifestFailures ThrottleLimit = $ThrottleLimit } } foreach ($ConnectionGroup in $AppvConnectionGroups) { try { $connectionGroupXML = New-Object System.Xml.XmlDocument $connectionGroupXML.Load($ConnectionGroup.FullName) } catch { # A single missing or malformed .appg must not abort the entire (multi-hour) content share scan. Log it and move on. Write-Warning "Failed to load connection group: $($ConnectionGroup.FullName). Error: $($_.Exception.Message)" continue } $package = $null if (-not $PackagesByFullName.TryGetValue($ConnectionGroup.FullName, [ref]$package)) { Write-Warning "Connection group package object not found for: $($ConnectionGroup.FullName)" $connectionGroupXML = $null continue } $package.Name = $connectionGroupXML.AppConnectionGroup.DisplayName $package.PackageId = $connectionGroupXML.AppConnectionGroup.AppConnectionGroupId $package.VersionId = $connectionGroupXML.AppConnectionGroup.VersionGuid foreach ($item in $connectionGroupXML.AppConnectionGroup.Packages.Package) { foreach ($pkg in $Packages) { if ($pkg.PackageId -ieq $item.PackageId -and $pkg.VersionId -like $item.VersionId) { $pkg.MemberOf += $package.Name } } } $connectionGroupXML = $null } if ($AppvConnectionGroups.Count -gt 0) { Write-AppVentiXDebugLog -Message 'Resolved connection groups' -Level Timing -Data @{ MachineGroup = "$($machineGroup.FriendlyName)" ContentSharePath = "$($GroupContentShare.Path)" ConnectionGroupCount = $AppvConnectionGroups.Count } } } } Write-AppVentiXDebugLog -Message 'Finished retrieving Content Share packages' -Level Timing -Data @{ PackageCount = $Packages.Count MachineGroupCount = @($machineGroups).Count } return $Packages } else { Write-Warning 'AppVentiX is not licensed!' } Write-Verbose "Ending function 'Get-AppVentiXContentSharePackages'" } # SIG # Begin signature block # MIInigYJKoZIhvcNAQcCoIInezCCJ3cCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDSP81cd4bA0uTX # LZRWVQHgzz21m+WtRxoat2NSwJARN6CCIR0wggZFMIIELaADAgECAhAIMk+dt9qR # b2Pk8qM8Xl1RMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQK # ExhBc3NlY28gRGF0YSBTeXN0ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBDb2Rl # IFNpZ25pbmcgMjAyMSBDQTAeFw0yNDA0MDQxNDA0MjRaFw0yNzA0MDQxNDA0MjNa # MGsxCzAJBgNVBAYTAk5MMRIwEAYDVQQHDAlTY2hpam5kZWwxIzAhBgNVBAoMGkpv # aG4gQmlsbGVrZW5zIENvbnN1bHRhbmN5MSMwIQYDVQQDDBpKb2huIEJpbGxla2Vu # cyBDb25zdWx0YW5jeTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAMsl # ntDbSQwHZXwFhmibivbnd0Qfn6sqe/6fos3pKzKxEsR907RkDMet2x6RRg3eJkiI # r3TFPwqBooyXXgK3zxxpyhGOcuIqyM9J28DVf4kUyZHsjGO/8HFjrr3K1hABNUsz # P0o7H3o6J31eqV1UmCXYhQlNoW9FOmRC1amlquBmh7w4EKYEytqdmdOBavAD5Xq4 # vLPxNP6kyA+B2YTtk/xM27TghtbwFGKnu9Vwnm7dFcpLxans4ONt2OxDQOMA5Nwg # cUv/YTpjhq9qoz6ivG55NRJGNvUXsM3w2o7dR6Xh4MuEGrTSrOWGg2A5EcLH1XqQ # tkF5cZnAPM8W/9HUp8ggornWnFVQ9/6Mga+ermy5wy5XrmQpN+x3u6tit7xlHk1H # c+4XY4a4ie3BPXG2PhJhmZAn4ebNSBwNHh8z7WTT9X9OFERepGSytZVeEP7hgypt # SLcuhpwWeR4QdBb7dV++4p3PsAUQVHFpwkSbrRTv4EiJ0Lcz9P1HPGFoHiFAQQID # AQABo4IBeDCCAXQwDAYDVR0TAQH/BAIwADA9BgNVHR8ENjA0MDKgMKAuhixodHRw # Oi8vY2NzY2EyMDIxLmNybC5jZXJ0dW0ucGwvY2NzY2EyMDIxLmNybDBzBggrBgEF # BQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0dHA6Ly9jY3NjYTIwMjEub2NzcC1jZXJ0 # dW0uY29tMDUGCCsGAQUFBzAChilodHRwOi8vcmVwb3NpdG9yeS5jZXJ0dW0ucGwv # Y2NzY2EyMDIxLmNlcjAfBgNVHSMEGDAWgBTddF1MANt7n6B0yrFu9zzAMsBwzTAd # BgNVHQ4EFgQUO6KtBpOBgmrlANVAnyiQC6W6lJwwSwYDVR0gBEQwQjAIBgZngQwB # BAEwNgYLKoRoAYb2dwIFAQQwJzAlBggrBgEFBQcCARYZaHR0cHM6Ly93d3cuY2Vy # dHVtLnBsL0NQUzATBgNVHSUEDDAKBggrBgEFBQcDAzAOBgNVHQ8BAf8EBAMCB4Aw # DQYJKoZIhvcNAQELBQADggIBAEQsN8wgPMdWVkwHPPTN+jKpdns5AKVFjcn00psf # 2NGVVgWWNQBIQc9lEuTBWb54IK6Ga3hxQRZfnPNo5HGl73YLmFgdFQrFzZ1lnaMd # Icyh8LTWv6+XNWfoyCM9wCp4zMIDPOs8LKSMQqA/wRgqiACWnOS4a6fyd5GUIAm4 # CuaptpFYr90l4Dn/wAdXOdY32UhgzmSuxpUbhD8gVJUaBNVmQaRqeU8y49MxiVrU # KJXde1BCrtR9awXbqembc7Nqvmi60tYKlD27hlpKtj6eGPjkht0hHEsgzU0Fxw7Z # JghYG2wXfpF2ziN893ak9Mi/1dmCNmorGOnybKYfT6ff6YTCDDNkod4egcMZdOSv # +/Qv+HAeIgEvrxE9QsGlzTwbRtbm6gwYYcVBs/SsVUdBn/TSB35MMxRhHE5iC3aU # TkDbceo/XP3uFhVL4g2JZHpFfCSu2TQrrzRn2sn07jfMvzeHArCOJgBW1gPqR3Wr # J4hUxL06Rbg1gs9tU5HGGz9KNQMfQFQ70Wz7UIhezGcFcRfkIfSkMmQYYpsc7rfz # j+z0ThfDVzzJr2dMOFsMlfj1T6l22GBq9XQx0A4lcc5Fl9pRxbOuHHWFqIBD/BCE # hwniOCySzqENd2N+oz8znKooSISStnkNaYXt6xblJF2dx9Dn89FK7d1IquNxOwt0 # tI5dMIIGgjCCBGqgAwIBAgIQNsKwvXwbOuejs902y8l1aDANBgkqhkiG9w0BAQwF # ADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcT # C0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAs # BgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcN # MjEwMzIyMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjBXMQswCQYDVQQGEwJHQjEYMBYG # A1UEChMPU2VjdGlnbyBMaW1pdGVkMS4wLAYDVQQDEyVTZWN0aWdvIFB1YmxpYyBU # aW1lIFN0YW1waW5nIFJvb3QgUjQ2MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC # CgKCAgEAiJ3YuUVnnR3d6LkmgZpUVMB8SQWbzFoVD9mUEES0QUCBdxSZqdTkdizI # CFNeINCSJS+lV1ipnW5ihkQyC0cRLWXUJzodqpnMRs46npiJPHrfLBOifjfhpdXJ # 2aHHsPHggGsCi7uE0awqKggE/LkYw3sqaBia67h/3awoqNvGqiFRJ+OTWYmUCO2G # AXsePHi+/JUNAax3kpqstbl3vcTdOGhtKShvZIvjwulRH87rbukNyHGWX5tNK/WA # BKf+Gnoi4cmisS7oSimgHUI0Wn/4elNd40BFdSZ1EwpuddZ+Wr7+Dfo0lcHflm/F # DDrOJ3rWqauUP8hsokDoI7D/yUVI9DAE/WK3Jl3C4LKwIpn1mNzMyptRwsXKrop0 # 6m7NUNHdlTDEMovXAIDGAvYynPt5lutv8lZeI5w3MOlCybAZDpK3Dy1MKo+6aEtE # 9vtiTMzz/o2dYfdP0KWZwZIXbYsTIlg1YIetCpi5s14qiXOpRsKqFKqav9R1R5vj # 3NgevsAsvxsAnI8Oa5s2oy25qhsoBIGo/zi6GpxFj+mOdh35Xn91y72J4RGOJEoq # zEIbW3q0b2iPuWLA911cRxgY5SJYubvjay3nSMbBPPFsyl6mY4/WYucmyS9lo3l7 # jk27MAe145GWxK4O3m3gEFEIkv7kRmefDR7Oe2T1HxAnICQvr9sCAwEAAaOCARYw # ggESMB8GA1UdIwQYMBaAFFN5v1qqK0rPVIDh2JvAnfKyA2bLMB0GA1UdDgQWBBT2 # d2rdP/0BE/8WoWyCAi/QCj0UJTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUw # AwEB/zATBgNVHSUEDDAKBggrBgEFBQcDCDARBgNVHSAECjAIMAYGBFUdIAAwUAYD # VR0fBEkwRzBFoEOgQYY/aHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VTRVJUcnVz # dFJTQUNlcnRpZmljYXRpb25BdXRob3JpdHkuY3JsMDUGCCsGAQUFBwEBBCkwJzAl # BggrBgEFBQcwAYYZaHR0cDovL29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0B # AQwFAAOCAgEADr5lQe1oRLjlocXUEYfktzsljOt+2sgXke3Y8UPEooU5y39rAARa # AdAxUeiX1ktLJ3+lgxtoLQhn5cFb3GF2SSZRX8ptQ6IvuD3wz/LNHKpQ5nX8hjsD # LRhsyeIiJsms9yAWnvdYOdEMq1W61KE9JlBkB20XBee6JaXx4UBErc+YuoSb1SxV # f7nkNtUjPfcxuFtrQdRMRi/fInV/AobE8Gw/8yBMQKKaHt5eia8ybT8Y/Ffa6HAJ # yz9gvEOcF1VWXG8OMeM7Vy7Bs6mSIkYeYtddU1ux1dQLbEGur18ut97wgGwDiGin # CwKPyFO7ApcmVJOtlw9FVJxw/mL1TbyBns4zOgkaXFnnfzg4qbSvnrwyj1NiurMp # 4pmAWjR+Pb/SIduPnmFzbSN/G8reZCL4fvGlvPFk4Uab/JVCSmj59+/mB2Gn6G/U # YOy8k60mKcmaAZsEVkhOFuoj4we8CYyaR9vd9PGZKSinaZIkvVjbH/3nlLb0a7SB # IkiRzfPfS9T+JesylbHa1LtRV9U/7m0q7Ma2CQ/t392ioOssXW7oKLdOmMBl14su # VFBmbzrt5V5cQPnwtd3UOTpS9oCG+ZZheiIvPgkDmA8FzPsnfXW5qHELB43ET7HH # FHeRPRYrMBKjkb8/IN7Po0d0hQoF4TeMM+zYAJzoKQnVKOLg8pZVPT8wgganMIIE # j6ADAgECAhEAkKwIciD9xafEa1zHDfc9BjANBgkqhkiG9w0BAQwFADBXMQswCQYD # VQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMS4wLAYDVQQDEyVTZWN0 # aWdvIFB1YmxpYyBUaW1lIFN0YW1waW5nIFJvb3QgUjQ2MB4XDTI2MDMyNTAwMDAw # MFoXDTQxMDMyNDIzNTk1OVowVTELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1NlY3Rp # Z28gTGltaXRlZDEsMCoGA1UEAxMjU2VjdGlnbyBQdWJsaWMgVGltZSBTdGFtcGlu # ZyBDQSBSNDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCu5EqiAa2C # HGL5Zi1bmgPM8NUXwYZJ+BtQqHps43GLTC+sjVLypsBh+8uv+TLkgtVGD//vSmA0 # qrzELf9YRCh2MTAA/aGaQZKGg0BRCmziR3pbCnvgWjtGXBDUyn3j3K2lZAO8KxgF # tlxwOYEAkL+CCqK4v9zzTl8ZwzDpPMiDIFa5THk8an1ieF5I09cXNrPQw+1ER1li # ThaG0z6FrOpqwxZWmPRZQBw2E32878UB1bL0Zp91vuWZgsMpNNiPCoBj0/1F+LE8 # +NRokfqacFI0F2tftrRB2W7HQClLR9zjxFbWb5be2rceIfNyHUUfKGIvMI2NzoxS # lxXnFqUG887D8W1Cj8DFok688JKxWvHR/9aQykSbd+9Vutj36ij2sgq/125wTpUZ # /AgC0ph50bRs7gFrUyaXE9wSsOqMvCCC+sEm7vd/BemSG0TSHNXSmyCba+FCzeke # WX03TRIcF3Laqd0Rw24OH7jpei4zaGhcI7nfdhBA4c8RScxNY6jeHLHHmSMMTk9W # qn7H4dLhUBP5YEwbgbN4uv1i9ltTnHli8t1xHV0StX9BFgrnmunTX19kUXY1H5OR # JbRZyZDdvm1oZyteDj0SnMozr+YSmdIleDUTXdfoY7b2taz8s2+QbOxLxcahEIYG # Wzqu6h955tKwcANHcZ4gTmAhT3btuOiQsQIDAQABo4IBbjCCAWowHwYDVR0jBBgw # FoAU9ndq3T/9ARP/FqFsggIv0Ao9FCUwHQYDVR0OBBYEFDp0pQxnxkJQwv21/Me7 # KTSC9Hq5MA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMBMGA1Ud # JQQMMAoGCCsGAQUFBwMIMCMGA1UdIAQcMBowCAYGZ4EMAQQCMA4GDCsGAQQBsjEB # AgEDCDBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLnNlY3RpZ28uY29tL1Nl # Y3RpZ29QdWJsaWNUaW1lU3RhbXBpbmdSb290UjQ2LmNybDB8BggrBgEFBQcBAQRw # MG4wRwYIKwYBBQUHMAKGO2h0dHA6Ly9jcnQuc2VjdGlnby5jb20vU2VjdGlnb1B1 # YmxpY1RpbWVTdGFtcGluZ1Jvb3RSNDYucDdjMCMGCCsGAQUFBzABhhdodHRwOi8v # b2NzcC5zZWN0aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAgEAMt5SR2bxngNm+N8o # c6Gq76Gx1c235fkX7jw8Ho9MAkJGADerHE7dhsBXttqmzgr/7ZZahZSykGRPhPY1 # crj028kB8KzO0dKC2qQBAwtfgqMLKkkX/6bYq2uT33eD6ByAp2/XKD0LcmZh0kKe # cvSBr6ln9ajX6u1dnx2fA7xEKy1M3qBhfQSUWLtjs2nFt0ELVLptzTlX9ID0cL+i # OPfdboZ3CelT+JXKVKR2Sge0d4YiFAtPZkfSo8z1Z1x7y/Z9mwMIlBAnyuWXs4Ys # NuxdrYIt/QxE31PDOJ9DesS4Bc7H9OTORlEV/AvfiF/VepKZpira1MzLYuCw+uoL # Zn/pkpvd+CvNTS+mEHjBJNa6WK1j8qXFu+jIq+sG9QILHiyB6p/xpHrkJu8zkw39 # 3+VqF9eKlTY2VjRxdycZLrVemZ4Yp3wi33b+W58CllH3HqjmowlZ7SOrgmx8YwYO # kgrHsXOQHyBp6O4FRb8In0+FzjT7ElGie9V7CfhL3IlVFZ4zjuKsZtH1iU3fGu4z # /JnOGT6sCb0BbTqe/uhvpFCQBdH5xPGIA/LrbQUXjU2tWJgHhTIqnN/HvHyOHi5t # M4zP3nhgh2rJ6Kqq2xsHBeNYs/R18xQ8DeIg+c90Eoaeh0YlN1KU8AyYol3K9M+q # Y5ez8syd/7ZlrRnoVewgH3P1pcswgga5MIIEoaADAgECAhEAmaOACiZVO2Wr3G6E # prPqOTANBgkqhkiG9w0BAQwFADCBgDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVu # aXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZp # Y2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29y # ayBDQSAyMB4XDTIxMDUxOTA1MzIxOFoXDTM2MDUxODA1MzIxOFowVjELMAkGA1UE # BhMCUEwxITAfBgNVBAoTGEFzc2VjbyBEYXRhIFN5c3RlbXMgUy5BLjEkMCIGA1UE # AxMbQ2VydHVtIENvZGUgU2lnbmluZyAyMDIxIENBMIICIjANBgkqhkiG9w0BAQEF # AAOCAg8AMIICCgKCAgEAnSPPBDAjO8FGLOczcz5jXXp1ur5cTbq96y34vuTmflN4 # mSAfgLKTvggv24/rWiVGzGxT9YEASVMw1Aj8ewTS4IndU8s7VS5+djSoMcbvIKck # 6+hI1shsylP4JyLvmxwLHtSworV9wmjhNd627h27a8RdrT1PH9ud0IF+njvMk2xq # bNTIPsnWtw3E7DmDoUmDQiYi/ucJ42fcHqBkbbxYDB7SYOouu9Tj1yHIohzuC8KN # qfcYf7Z4/iZgkBJ+UFNDcc6zokZ2uJIxWgPWXMEmhu1gMXgv8aGUsRdaCtVD2bSl # bfsq7BiqljjaCun+RJgTgFRCtsuAEw0pG9+FA+yQN9n/kZtMLK+Wo837Q4QOZgYq # VWQ4x6cM7/G0yswg1ElLlJj6NYKLw9EcBXE7TF3HybZtYvj9lDV2nT8mFSkcSkAE # xzd4prHwYjUXTeZIlVXqj+eaYqoMTpMrfh5MCAOIG5knN4Q/JHuurfTI5XDYO962 # WZayx7ACFf5ydJpoEowSP07YaBiQ8nXpDkNrUA9g7qf/rCkKbWpQ5boufUnq1UiY # PIAHlezf4muJqxqIns/kqld6JVX8cixbd6PzkDpwZo4SlADaCi2JSplKShBSND36 # E/ENVv8urPS0yOnpG4tIoBGxVCARPCg1BnyMJ4rBJAcOSnAWd18Jx5n858JSqPEC # AwEAAaOCAVUwggFRMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFN10XUwA23uf # oHTKsW73PMAywHDNMB8GA1UdIwQYMBaAFLahVDkCw6A/joq8+tT4HKbROg79MA4G # A1UdDwEB/wQEAwIBBjATBgNVHSUEDDAKBggrBgEFBQcDAzAwBgNVHR8EKTAnMCWg # I6Ahhh9odHRwOi8vY3JsLmNlcnR1bS5wbC9jdG5jYTIuY3JsMGwGCCsGAQUFBwEB # BGAwXjAoBggrBgEFBQcwAYYcaHR0cDovL3N1YmNhLm9jc3AtY2VydHVtLmNvbTAy # BggrBgEFBQcwAoYmaHR0cDovL3JlcG9zaXRvcnkuY2VydHVtLnBsL2N0bmNhMi5j # ZXIwOQYDVR0gBDIwMDAuBgRVHSAAMCYwJAYIKwYBBQUHAgEWGGh0dHA6Ly93d3cu # Y2VydHVtLnBsL0NQUzANBgkqhkiG9w0BAQwFAAOCAgEAdYhYD+WPUCiaU58Q7EP8 # 9DttyZqGYn2XRDhJkL6P+/T0IPZyxfxiXumYlARMgwRzLRUStJl490L94C9LGF3v # jzzH8Jq3iR74BRlkO18J3zIdmCKQa5LyZ48IfICJTZVJeChDUyuQy6rGDxLUUAsO # 0eqeLNhLVsgw6/zOfImNlARKn1FP7o0fTbj8ipNGxHBIutiRsWrhWM2f8pXdd3x2 # mbJCKKtl2s42g9KUJHEIiLni9ByoqIUul4GblLQigO0ugh7bWRLDm0CdY9rNLqyA # 3ahe8WlxVWkxyrQLjH8ItI17RdySaYayX3PhRSC4Am1/7mATwZWwSD+B7eMcZNhp # n8zJ+6MTyE6YoEBSRVrs0zFFIHUR08Wk0ikSf+lIe5Iv6RY3/bFAEloMU+vUBfSo # uCReZwSLo8WdrDlPXtR0gicDnytO7eZ5827NS2x7gCBibESYkOh1/w1tVxTpV2Na # 3PR7nxYVlPu1JPoRZCbH86gc96UTvuWiOruWmyOEMLOGGniR+x+zPF/2DaGgK2W1 # eEJfo2qyrBNPvF7wuAyQfiFXLwvWHamoYtPZo0LHuH8X3n9C+xN4YaNjt2ywzOr+ # tKyEVAotnyU9vyEVOaIYMk3IeBrmFnn0gbKeTTyYeEEUz/Qwt4HOUBCrW602NCmv # O1nm+/80nLy5r0AZvCQxaQ4wggbiMIIEyqADAgECAhEA507yVbBQT/rbpt/3/Iuj # FTANBgkqhkiG9w0BAQwFADBVMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGln # byBMaW1pdGVkMSwwKgYDVQQDEyNTZWN0aWdvIFB1YmxpYyBUaW1lIFN0YW1waW5n # IENBIFI0MTAeFw0yNjAzMjUwMDAwMDBaFw0zNzA2MjQyMzU5NTlaMHIxCzAJBgNV # BAYTAkdCMRcwFQYDVQQIEw5HcmVhdGVyIExvbmRvbjEYMBYGA1UEChMPU2VjdGln # byBMaW1pdGVkMTAwLgYDVQQDEydTZWN0aWdvIFB1YmxpYyBUaW1lIFN0YW1waW5n # IFNpZ25lciBSMzcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCy/8Nt # S9xQ2UUtBRF32bj7VK3n4m50Uqjk/zTciSziYV40H1LKah0/oEklYG42E4VCP3Dv # sBUB6DmpCkDZ0jCnZBPIEevaH15ZJOQwFWP2ZXr5YjlJpb68Nlbs+ElNvKx32/1Y # Hde3qqUSLybjulxPLz6T85+HOIqK7M1Bep8LspyhEP/q6nw5kGxTSrGvufmeH+JF # 8CnVBcVMFA40FlIYh0cDJVFhhfTfdWgLy/vWuLMQoKkf3s/FvByf16r0rtbyHm/i # emwxSioJL9zyZDDKUNAbHXl0dhXo2VxUV2NcPXWXuoKsjL+6cfk6Vm2DHnxAlFdF # saBDIF1JOkSnC6PeLlBznZn2buF3vIIYJcq6N/zeFRCk4/HXDz7zgRsRRMdUB+rh # yk5FoZaBjw0nLq3GZ3fClLUx5es5pUAxzNODMBn7JkFYip2BAGBPER5eV0ROhk6t # GTG+fUiMiV+vgjg1YnP5FvnYWyEtWeQD/B2hp3vz0RvtdkM0p3igyadzrfpOBq5p # pVk/YsuhTQkP99ivneHAGfi5e7lmxJ+meoBPrRLuzMmb81rzzbESjJHMsn5RVtc6 # Ucs7rcMqQC13PUIO7BbGBETV2ufCmV6lPTp3P7XJOvmnUCRTPbVvMTpxP/z+SOHg # 4/OCBhiqs4FA9+4oQvlkk9w32NGASli9GWrm5wIDAQABo4IBjjCCAYowHwYDVR0j # BBgwFoAUOnSlDGfGQlDC/bX8x7spNIL0erkwHQYDVR0OBBYEFGEQ6XoSr1HEhdTy # z6R0D1DNIK/4MA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB # /wQMMAoGCCsGAQUFBwMIMEoGA1UdIARDMEEwCAYGZ4EMAQQCMDUGDCsGAQQBsjEB # AgEDCDAlMCMGCCsGAQUFBwIBFhdodHRwczovL3NlY3RpZ28uY29tL0NQUzBKBgNV # HR8EQzBBMD+gPaA7hjlodHRwOi8vY3JsLnNlY3RpZ28uY29tL1NlY3RpZ29QdWJs # aWNUaW1lU3RhbXBpbmdDQVI0MS5jcmwwegYIKwYBBQUHAQEEbjBsMEUGCCsGAQUF # BzAChjlodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNUaW1lU3Rh # bXBpbmdDQVI0MS5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNlY3RpZ28u # Y29tMA0GCSqGSIb3DQEBDAUAA4ICAQAD6j2N0azN+hl6k6bKB5/U6VuSOs93ZBb3 # Pczy9VtBIKu4947Z5GwL0aFngIxl+GSuLFrJgPruBCRvKJEJsm7kv+LQ1COVCEG9 # tZ+IRtr4ocUoa53lgdFaENlS0N4wgkZkbQEPv+x+1lSjYh+T4JeL9mUznT7Erc6S # p5dWLka5sMP/m3GZi6oJPdPcsCKWagH7m2H2xDGIyHJC5PdH9phvi/KmhkktiSVT # NNqVeV5bWdX2zhRE6UTfz0IcMoCL996lFIydXxOCE4MNDHDM0as4lnTiT/KHMccO # 6l8c9TnUVgmpci9ar1IABZ2U1XUkYjGGSn9MC3EHDP9V39VuBVvZ33/BEV/EWSRr # f07T7jFplKX+gQr/UOqPGMlE7ZJ72UaUkNJy7bVl3bcLKzdpjIHzLkf/4MVa1V7w # 8wqCv5W4gOnRGTlud5UMARbRM8BPxR/CXYXoMmIOD8pmTk2axgRL4LG8XtuchISd # CHRmtacAmLGq5XSYSVTHTXADlO48iDKh3HM2r98LSF6f0sG12d8V9Jn7C3wDUieO # xuKj4MdWrW+hiJU2kF87v6eH00HgCFFc2V0+CvfOCMn7juzS41jLaINcBlKWQ/fK # b/uDLfWOW73z1I2lFY7Xj8tQ1XYtK5eREjWItM8jpl1cbQOc88btR+0XS2TmboE/ # 141+va2PWzGCBcMwggW/AgEBMGowVjELMAkGA1UEBhMCUEwxITAfBgNVBAoTGEFz # c2VjbyBEYXRhIFN5c3RlbXMgUy5BLjEkMCIGA1UEAxMbQ2VydHVtIENvZGUgU2ln # bmluZyAyMDIxIENBAhAIMk+dt9qRb2Pk8qM8Xl1RMA0GCWCGSAFlAwQCAQUAoIGE # MBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQB # gjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkE # MSIEIM4koU60TlwZSC5Rw+Z7G8kjtcVi0nM8FJEcfe45eHtUMA0GCSqGSIb3DQEB # AQUABIIBgAPt8hJht4kELpKuB2KcsIUqguNU+44q3zEgHQmR/rlJnENR0FeNXvUu # CSnh3I6sLRyMJEy7FQgKemje819suyE0I29mHlxY5JtRziA49/rgKixIRR/BCwpL # sGToyjfC75rdC6zBmwf9E7T+LTncoF6lowqt10HJuJv0XbXo9XRIC5tbOtJiqHC1 # GRtU15LIFdqM3tXlC+l5Tv7Un4xLf8brEKG2BAfXxQtbWqhTG0Opv9qmZkqmTCoH # gNgtAdzL/eX2MeWs8DImY4RN8B/5M+d6fk7jgQu4tB4rzj8pXKMU1hxFuqmQhvdS # pZMQOYOWFurvvDDehtmz6wxb1kRxsqDdVdY7sbCbRd2zkvYSaGUkRj6eWfRL8T7B # HBLnV+bDK3nwWWnbT+kSVsDq4zXtqd3NHeHf21Pf9/5VoU9aNFD6fjIXEjSZW8y8 # TLcJKyDqrBRtynCI6/PKOg/yEOF1PKx+gdN77yqad9gyStpZ1+8bdbnPq544vM8I # DlVK3sJ56KGCAyMwggMfBgkqhkiG9w0BCQYxggMQMIIDDAIBATBqMFUxCzAJBgNV # BAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxLDAqBgNVBAMTI1NlY3Rp # Z28gUHVibGljIFRpbWUgU3RhbXBpbmcgQ0EgUjQxAhEA507yVbBQT/rbpt/3/Iuj # FTANBglghkgBZQMEAgIFAKB5MBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJ # KoZIhvcNAQkFMQ8XDTI2MDcwNzEyMTk1MFowPwYJKoZIhvcNAQkEMTIEMCMpnoDL # nfQNoSYQaIGgbrUipOpsHypYU3B2LFBfuFkcK0GE7T9FvyLply2uGNwvATANBgkq # hkiG9w0BAQEFAASCAgAPc1egmQmlQKjB6BIwSgxBhWfOLmLdze494/eGyUObhM13 # Pk8P2dzBiJ5susbeoDyE46vjOM+kqx1Fvh7bjjPMgWEoqokFr4g20YAW29iqqDlC # 5p/UoadIpwUCelKuu3ESaHh4WcnimCwuxR+KFl/F3EWguIJOZVPkFDRG9rzqT+PI # zE9epe5sX0o1IbYLYMFasEdBxrB2Rl2MmsJpQX6Uw/4eaz6uG2Sfn9tCYNaPWttM # jBOR1+sCxiv7gOIGhH85fs8lS6llTSx3RReY4E3xBxrJyjbD0mgGLyG+9V4ZSMr8 # Kh6vLgJe0JB9PuJOfKy3ABbfCam4lAg7FPo1pEl9KzhYJEanFxnaf0oV09Lzwc2J # qislmWyaNVIyYe+Mt+n6GWaXKTD2CgJT9xtkp5MGLUDZ4Zcl1hMLJRMQr3jW29tT # NfE1ZJhUgwwZ+x8ZWOlyIPvf/auHxNaQwbHcqMuyezKSfa3kMt5KSOBXNomzcxGz # fblS7qgl/hC5Bv7FWOrD1Fh6em7BIzCOpbD4LNG4eLhxWcUePJfCxgENRCh/bv4p # eafgnzRa8wqJi4Q2wEGeaGOiKbPndO4Y/J8oh4EEtWO1oeWfjbAGu6xaXiXOou61 # 3PeWwJbNsAbD7GQERVOqjKKOChg6uEDmRvO1KrfnUaB9hzRwlvOYPlRa9kMGNw== # SIG # End signature block |