public/Import-AppVManagementPackage.ps1
|
<#
.SYNOPSIS Imports App-V Management packages into AppVentiX as publishing tasks. .DESCRIPTION Import-AppVManagementPackage retrieves packages from an App-V Management SQL database and converts them into AppVentiX publishing tasks. The function matches package URLs against the configured AppVentiX Content Stores and associates packages with a Machine Group, either matched automatically or specified. Packages without AD Groups in the App-V Management database are skipped (see -UnassignedADGroup). If a Deployment Configuration XML is present in the database, it is saved as an .appd file alongside the package. Packages can optionally be copied to the Content Store of a specific Machine Group (-CopyPackages). .PARAMETER SQLServer The name or IP address of the SQL Server hosting the App-V Management database. Default: 'localhost'. .PARAMETER SQLInstance The SQL Server instance name (optional). Leave empty for the default instance. .PARAMETER SQLDatabase The name of the App-V Management database. Default: 'AppVManagement'. .PARAMETER SQLCredential A PSCredential object for SQL Server authentication. Use [System.Management.Automation.PSCredential]::Empty for Windows authentication (default). .PARAMETER MatchPackageWithMachineGroup When specified, attempts to match each package to the appropriate AppVentiX Machine Group based on its Content Store location. If no match is found, the package is published to all Machine Groups. .PARAMETER GUI When specified, displays a graphical selection window allowing the user to manually choose which packages to import. .PARAMETER CachePackages When specified, package manifest identity fields read from the Content Stores are cached in memory for the duration of the PowerShell session and reused on subsequent runs, so unchanged packages are not re-read. The cache is keyed on file path, last write time and size (a repackaged file is always re-read), is module-scoped, and is cleared when the module is reimported. Off by default. .PARAMETER ThrottleLimit The maximum number of package manifests to read concurrently from the Content Stores when retrieving AppVentiX Content Store packages. Reading manifests over a (usually remote) share is network-latency bound and benefits from parallelism. Default: 16. Increase on high-latency shares; decrease to reduce load on the file server. .PARAMETER MachineGroupFriendlyName The friendly name of the AppVentiX Machine Group the packages are matched to. If not specified, packages are matched to All Machine Groups. .PARAMETER CopyPackages Copies the .appv file of each package from its App-V Management location to the Content Store of the Machine Group specified with -MachineGroupFriendlyName, using robocopy. The folder structure below the source share is kept. The publishing task then points to the copied package. If the package already exists in the Content Store, a warning is shown and the existing file is used. If the Machine Group has multiple Content Stores, the package is copied to the first Content Store of the Machine Group. Only the .appv file is copied. The .appd and user configuration files are created from the App-V Management database next to the copied package. Example: '\\fs01.domain.local\Source\WS12345\MSOffice365.appv' is copied to '<Content Store>\WS12345\MSOffice365.appv'. .PARAMETER RenamePackageFolder Used with -CopyPackages. Copies the package to a folder in the Content Store root named after the .appv file basename, instead of keeping the original folder structure. Example: '\\fs01.domain.local\Source\Apps\WS12345\MSOffice365.appv' is copied to '<Content Store>\MSOffice365\MSOffice365.appv'. NOTE: Packages with the same file name in different source folders get the same target. The first package is copied, the next ones get an "already exists" warning and their publishing task points to the first package. With -Force, each next package overwrites the previous one. A warning is shown and the parameter is ignored when -CopyPackages is not specified. .PARAMETER Force Used with -CopyPackages. Overwrites a package that already exists in the Content Store. A warning is shown and the parameter is ignored when -CopyPackages is not specified. .PARAMETER PublishingMachineGroupFriendlyName The friendly name(s) of the AppVentiX Machine Group(s) to which the packages are published. If not specified, packages are published to the same Machine Group as matched by the Content Store location. .PARAMETER UnassignedADGroup The AD group, in 'domain.local\Group' format, used to entitle a package that has no AD Groups associated with it in the App-V Management database. Without this parameter, such packages are skipped. NOTE: Use the full domain name, not the NETBIOS name. .PARAMETER ConfigShare Internal parameter. Points to the AppVentiX configuration share path. Automatically populated from the module configuration and does not normally need to be specified manually. .EXAMPLE Import-AppVManagementPackage Imports all enabled App-V packages from the local SQL Server ('localhost', database 'AppVManagement') and publishes them to All Machine Groups. .EXAMPLE Import-AppVManagementPackage -SQLServer 'sql01.domain.local' -SQLDatabase 'AppVManagement' -MatchPackageWithMachineGroup Imports all enabled App-V packages from SQL Server 'sql01.domain.local' and associates each package with the corresponding AppVentiX Machine Group based on its Content Store location. .EXAMPLE Import-AppVManagementPackage -SQLServer 'sql01.domain.local' -GUI -MatchPackageWithMachineGroup Displays a selection window listing all available packages so the user can choose which ones to import, then associates them with the appropriate Machine Group. .EXAMPLE Import-AppVManagementPackage -SQLServer 'sql01.domain.local' -MachineGroupFriendlyName 'VDI' -CopyPackages Imports all enabled App-V packages from SQL Server 'sql01.domain.local', copies each .appv file to the Content Store of Machine Group 'VDI' and creates the publishing tasks for the copied packages. Packages that already exist in the Content Store are not overwritten unless -Force is specified. .EXAMPLE Import-AppVManagementPackage -SQLServer 'sql01.domain.local' -MachineGroupFriendlyName 'VDI' -CopyPackages -RenamePackageFolder -Force Imports all enabled App-V packages from SQL Server 'sql01.domain.local' and copies each .appv file to a folder named after the file basename in the Content Store of Machine Group 'VDI', for example '\\fs01.domain.local\Source\WS12345\MSOffice365.appv' to '<Content Store>\MSOffice365\MSOffice365.appv'. Existing packages in the Content Store are overwritten. .LINK Test-AppVManagementSQLConnection .LINK Get-AppVManagementPackage .LINK Get-AppVManagementConnectionGroup .LINK Import-AppVManagementConnectionGroup .LINK New-AppVentiXPublishingTask .LINK New-AppVentiXConnectionGroup .NOTES Function : Import-AppVManagementPackage Author : John Billekens Copyright : (c) John Billekens Consultancy & AppVentiX Version : 2026.1006.1100 Requires : SqlServer PowerShell module #> function Import-AppVManagementPackage { [CmdletBinding(DefaultParameterSetName = 'MatchWithMachineGroup')] [OutputType([PSCustomObject])] param ( [String]$SQLServer = "localhost", [String]$SQLInstance, [String]$SQLDatabase = 'AppVManagement', [System.Management.Automation.PSCredential]$SQLCredential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $true, ParameterSetName = 'MatchWithMachineGroup')] [switch]$MatchPackageWithMachineGroup, [Parameter(Mandatory = $true, ParameterSetName = 'MachineGroupName')] [String]$MachineGroupFriendlyName, [Parameter(Mandatory = $false, ParameterSetName = 'MachineGroupName')] [Switch]$CopyPackages, [Parameter(Mandatory = $false, ParameterSetName = 'MachineGroupName')] [Switch]$RenamePackageFolder, [Parameter(Mandatory = $false, ParameterSetName = 'MachineGroupName')] [Switch]$Force, [String[]]$PublishingMachineGroupFriendlyName, [switch]$GUI, [switch]$CachePackages, [Parameter(Mandatory = $false)] [ValidatePattern('^[^\\]+\\[^\\]+$')] [string]$UnassignedADGroup, [ValidateRange(1, 128)] [Int]$ThrottleLimit = 16, [Parameter(DontShow)] [ValidateNotNullOrEmpty()] [String]$ConfigShare = $Script:AppVentiX.ConfigShare ) $PSDefaultParameterValues['Write-AppVentiXError:Cmdlet'] = $PSCmdlet if (-not $CopyPackages) { if ($RenamePackageFolder) { Write-Warning "-RenamePackageFolder is ignored without -CopyPackages." } if ($Force) { Write-Warning "-Force is ignored without -CopyPackages." } } # Correlation id for this invocation; stamped on every debug entry so one run's log can be grouped. $Script:AppVentiXDebugRunId = '{0:yyyyMMddHHmmss}-{1:x4}' -f [datetime]::Now, (Get-Random -Maximum 0x10000) if ($Script:AppVentiXDebugLogEnabled) { # Only re-detect the layout (a few Test-Path probes) when debug logging is on. Write-AppVentiXDebugLog -Message 'Import-AppVManagementPackage started' -Data @{ SQLServer = $SQLServer SQLDatabase = $SQLDatabase MatchPackageWithMachineGroup = [bool]$MatchPackageWithMachineGroup MachineGroupFriendlyName = "$MachineGroupFriendlyName" CopyPackages = [bool]$CopyPackages RenamePackageFolder = [bool]$RenamePackageFolder Force = [bool]$Force GUI = [bool]$GUI ConfigShare = "$ConfigShare" Layout = "$(Get-AppVentiXConfigStoreLayout -ConfigShare $ConfigShare)" BackendType = "$($Script:AppVentiX.BackendType)" } } if (Test-AppVentiXIsLicensed -ConfigShare $ConfigShare) { try { $SQLParams = @{ SQLServer = $SQLServer SQLInstance = $SQLInstance SQLDatabase = $SQLDatabase SQLCredential = $SQLCredential } $preselectedMachineGroup = $false if (-not [string]::IsNullOrEmpty($MachineGroupFriendlyName)) { $preselectedMachineGroup = $true } try { $connectionResult = Test-AppVManagementSQLConnection @SQLParams -ErrorAction Stop if (-not $connectionResult) { Write-AppVentiXError -Message "Cannot connect to the App-V Management database on server '$serverInstance'." ` -ErrorId 'SQLConnectionFailed' ` -Category ConnectionError ` -Cmdlet $PSCmdlet return } } catch { Write-AppVentiXError -Message "Cannot connect to the App-V Management database on server '$serverInstance'." ` -ErrorId 'SQLConnectionFailed' ` -Category ConnectionError ` -Cmdlet $PSCmdlet return } Write-Host "Connected to App-V Management database '$SQLDatabase' on server '$SQLServer'" Write-AppVentiXDebugLog -Message 'SQL connection verified' -Level Timing Write-Host "Retrieving App-V Management Packages..." # ToDo: cache by package name and version instead of path so packages shared across machine groups are read once. $AppVentiXContentSharePackages = Get-AppVentiXContentStorePackage -ConfigShare $ConfigShare -ThrottleLimit $ThrottleLimit -CachePackages:$CachePackages $MachineGroups = Get-AppVentiXMachineGroup -ConfigShare $ConfigShare Write-AppVentiXDebugLog -Message 'Retrieved Content Store packages and Machine Groups' -Level Timing -Data @{ ContentSharePackageCount = @($AppVentiXContentSharePackages).Count MachineGroupCount = @($MachineGroups).Count } if ($AppVManagementPackageDetails = Get-AppVManagementPackage @SQLParams) { Write-AppVentiXDebugLog -Message 'Retrieved App-V Management packages from database' -Level Timing -Data @{ PackageCount = @($AppVManagementPackageDetails).Count } if ($GUI.IsPresent -eq $true) { Write-AppVentiXDebugLog -Message 'Displaying GUI for package selection' -Level Timing $AppVManagementPackageDetails = @( $AppVManagementPackageDetails | Select-ObjectFromGrid -Title "Select App-V Management Packages to import" ` -ColumnsToShow @('Name', 'Version', 'IsEnabled', 'PackageGuid', 'VersionGuid', 'PackageUrl') ) Write-AppVentiXDebugLog -Message 'GUI selection completed' -Level Timing -Data @{ SelectedPackageCount = @($AppVManagementPackageDetails).Count } } if (-not $AppVManagementPackageDetails -or $AppVManagementPackageDetails.Count -eq 0) { Write-AppVentiXDebugLog -Message 'No packages selected for import' -Level Warning Write-Warning "No App-V Management Packages selected for import." return } Write-Host "Found $($AppVManagementPackageDetails.Count) App-V Management Packages to import." # Collect results and emit after the loop; emitting inside interleaves with Write-Host and breaks table formatting. $ImportResults = [System.Collections.Generic.List[object]]::new() foreach ($appVManagementPackage in $AppVManagementPackageDetails) { # Reset from the parameters each package; the loop reassigns these while matching. $currentMachineGroup = $MachineGroupFriendlyName $currentPublishingGroups = $PublishingMachineGroupFriendlyName Write-Host "`r`n================================" -ForegroundColor Cyan if ($appVManagementPackage.IsEnabled -eq $false) { Write-Host "`r`nSkipping disabled App-V Management Package '$($appVManagementPackage.Name)'" -ForegroundColor Yellow continue } if ([string]::IsNullOrEmpty($UnassignedADGroup) -and @($appVManagementPackage.ADGroups).Count -eq 0) { Write-Warning "No AD Groups associated with package '$($appVManagementPackage.Name)'. Skipping import.`r`nIf you still want to import this package, consider specifying AD Group using the -UnassignedADGroup parameter." continue } elseif (-not [string]::IsNullOrEmpty($UnassignedADGroup) -and @($appVManagementPackage.ADGroups).Count -eq 0) { Write-Host "`r`nNo AD Groups associated with package '$($appVManagementPackage.Name)'. Using unassigned AD Group '$UnassignedADGroup'." -ForegroundColor Yellow $domainName, $groupName = $UnassignedADGroup.Split('\', 2) $appVManagementPackage.ADGroups += [PSCustomObject]@{ Domain = $domainName Name = $groupName } } Write-Host "`r`nProcessing package '$($appVManagementPackage.Name)'..." -ForegroundColor Cyan Write-AppVentiXDebugLog -Message 'Processing package' -Level Timing -Data @{ Package = "$($appVManagementPackage.Name)" PackageGuid = "$($appVManagementPackage.PackageGuid)" ADGroups = @($appVManagementPackage.ADGroups).Count } if (-not [string]::IsNullOrEmpty($appVManagementPackage.PackageUrl) -and $appVManagementPackage.PackageUrl.StartsWith('\\')) { $fileServer, $fileserverPath = $appVManagementPackage.PackageUrl.TrimStart('\\').Split('\', 2) $AppVentiXPackage = @($AppVentiXContentSharePackages | Where-Object { $_.FullName -like "\\$($fileServer)*\$($fileserverPath)" }) if ($currentMachineGroup) { $MachineGroup = $MachineGroups | Where-Object { $_.FriendlyName -ieq $currentMachineGroup } } else { $currentMachineGroup = 'All Machine Groups' $MachineGroup = $MachineGroups } if ([string]::IsNullOrEmpty($currentPublishingGroups)) { $currentPublishingGroups = @($currentMachineGroup) } Write-AppVentiXDebugLog -Message 'Matching package with AppVentiX Content Store Packages' -Level Timing -Data @{ Package = "$($appVManagementPackage.Name)" PackagePath = "$($appVManagementPackage.PackageUrl)" MatchCount = @($AppVentiXPackage).Count MachineGroupFriendlyName = "$currentMachineGroup" PublishingMachineGroupFriendlyName = "$($currentPublishingGroups -join ', ')" } if ($MatchPackageWithMachineGroup.IsPresent -eq $false) { if ($AppVentiXPackage.Count -eq 0) { Write-Warning "Package URL: '$($appVManagementPackage.PackageUrl)'" Write-Warning "Package not found in any of the configured Machine Group Content Stores." Write-AppVentiXDebugLog -Message 'Package not found in any configured Content Stores' -Level Warning -Data @{ Package = "$($appVManagementPackage.Name)" PackagePath = "$($appVManagementPackage.PackageUrl)" MatchCount = @($AppVentiXPackage).Count MachineGroupFriendlyName = "$currentMachineGroup" PublishingMachineGroupFriendlyName = "$($currentPublishingGroups -join ', ')" } } } elseif ($AppVentiXPackage.Count -eq 0) { Write-Warning "Package URL: '$($appVManagementPackage.PackageUrl)'" Write-Warning "Package not found in AppVentiX Content Store Packages. Publishing to All Machine Groups." Write-AppVentiXDebugLog -Message 'Package not found in any configured Content Stores' -Level Warning -Data @{ Package = "$($appVManagementPackage.Name)" PackagePath = "$($appVManagementPackage.PackageUrl)" MatchCount = @($AppVentiXPackage).Count MachineGroupFriendlyName = "$currentMachineGroup" PublishingMachineGroupFriendlyName = "$($currentPublishingGroups -join ', ')" } } elseif ($AppVentiXPackage.Count -gt 1) { Write-Warning "Package URL: '$($appVManagementPackage.PackageUrl)'" Write-Warning "Multiple packages found ($($AppVentiXPackage.Count)) in AppVentiX Content Store Packages matching the specified URL." Write-Warning "Publishing to $($currentPublishingGroups -join ', ')" Write-AppVentiXDebugLog -Message 'Multiple packages found in AppVentiX Content Store Packages' -Level Warning -Data @{ Package = "$($appVManagementPackage.Name)" PackagePath = "$($appVManagementPackage.PackageUrl)" MatchCount = @($AppVentiXPackage).Count MachineGroupFriendlyName = "$currentMachineGroup" PublishingMachineGroupFriendlyName = "$($currentPublishingGroups -join ', ')" } } else { Write-Host "Found package in AppVentiX Content Store Packages." -ForegroundColor Green $currentMachineGroup = $AppVentiXPackage | Select-Object -ExpandProperty MachineGroup $MachineGroup = $MachineGroups | Where-Object { $_.FriendlyName -ieq $currentMachineGroup } Write-AppVentiXDebugLog -Message 'Package matched with AppVentiX Content Store Package' -Level Timing -Data @{ Package = "$($appVManagementPackage.Name)" PackagePath = "$($appVManagementPackage.PackageUrl)" MatchCount = @($AppVentiXPackage).Count MachineGroupFriendlyName = "$currentMachineGroup" PublishingMachineGroupFriendlyName = "$($currentPublishingGroups -join ', ')" } } } else { Write-Warning "Package URL: '$($appVManagementPackage.PackageUrl)'" Write-Warning "Package is not a valid UNC path. Skipping package." Write-AppVentiXDebugLog -Message 'Package URL is not a valid UNC path' -Level Warning -Data @{ Package = "$($appVManagementPackage.Name)" PackagePath = "$($appVManagementPackage.PackageUrl)" MachineGroupFriendlyName = "$currentMachineGroup" PublishingMachineGroupFriendlyName = "$($currentPublishingGroups -join ', ')" } continue } $ContentSharePackageRoots = @($MachineGroup.GroupContentshares | ForEach-Object { $_.ContentShare } | Select-Object -ExpandProperty Path -Unique) Write-AppVentiXDebugLog -Message 'Retrieved Content Store roots for Machine Group' -Level Timing -Data @{ MachineGroupFriendlyName = "$currentMachineGroup" ContentShareRootCount = @($ContentSharePackageRoots).Count } Write-Verbose "Found $($ContentSharePackageRoots.Count) Content Store roots for machine group '$currentMachineGroup'." $ContentSharePackageRoots | ForEach-Object { Write-Verbose "Content Store Root: $_" } if ($ContentSharePackageRoots.Count -eq 0) { Write-Warning "No Content Stores found for machine group '$currentMachineGroup'. Skipping package." Write-AppVentiXDebugLog -Message 'No Content Stores found for machine group' -Level Warning -Data @{ Package = "$($appVManagementPackage.Name)" PackagePath = "$($appVManagementPackage.PackageUrl)" MachineGroupFriendlyName = "$currentMachineGroup" } continue } elseif ($ContentSharePackageRoots.Count -gt 1) { Write-Warning "Multiple Content Stores found for machine group '$currentMachineGroup'. Using the first match." Write-AppVentiXDebugLog -Message 'Multiple Content Stores found for machine group.' -Level Warning -Data @{ Package = "$($appVManagementPackage.Name)" PackagePath = "$($appVManagementPackage.PackageUrl)" MachineGroupFriendlyName = "$currentMachineGroup" PublishingMachineGroupFriendlyName = "$($currentPublishingGroups -join ', ')" ContentShareRootCount = $ContentSharePackageRoots.Count } } else { Write-Host "Found Content Store for machine group '$currentMachineGroup': $($ContentSharePackageRoots[0])" -ForegroundColor Green Write-AppVentiXDebugLog -Message 'Found Content Store for machine group' -Level Timing -Data @{ Package = "$($appVManagementPackage.Name)" PackagePath = "$($appVManagementPackage.PackageUrl)" MachineGroupFriendlyName = "$currentMachineGroup" PublishingMachineGroupFriendlyName = "$($currentPublishingGroups -join ', ')" ContentShareRoot = "$($ContentSharePackageRoots[0])" } } Write-Host "Importing App-V Management Package to Machine Group '$($currentMachineGroup -join ', ')'" -ForegroundColor Cyan if ($preselectedMachineGroup) { Write-Verbose "Package validation skipped, Machine Group was specified." if ($ContentSharePackageRoots.Count -gt 1) { Write-Warning "Multiple Content Stores found for machine group '$currentMachineGroup'. Using the first match." $ContentSharePackageRoot = $ContentSharePackageRoots[0] Write-AppVentiXDebugLog -Message 'Multiple Content Stores found for machine group (preselected)' -Level Warning -Data @{ Package = "$($appVManagementPackage.Name)" PackagePath = "$($appVManagementPackage.PackageUrl)" MachineGroupFriendlyName = "$currentMachineGroup" PublishingMachineGroupFriendlyName = "$($currentPublishingGroups -join ', ')" ContentShareRootCount = $ContentSharePackageRoots.Count SelectedContentShareRoot = "$($ContentSharePackageRoot)" } } else { $ContentSharePackageRoot = $ContentSharePackageRoots[0] Write-AppVentiXDebugLog -Message 'Found Content Store for machine group (preselected)' -Level Timing -Data @{ Package = "$($appVManagementPackage.Name)" PackagePath = "$($appVManagementPackage.PackageUrl)" MachineGroupFriendlyName = "$currentMachineGroup" PublishingMachineGroupFriendlyName = "$($currentPublishingGroups -join ', ')" ContentShareRoot = "$($ContentSharePackageRoot)" } } # Probe progressively shorter sub-paths under the content store root to locate the package (handles DFS/nested share layouts). $fileserver, $sharePath = "$($appVManagementPackage.PackageUrl)".TrimStart("\\").Split('\', 2) $nrTests = ($sharePath.Split('\').Count) $testSharePath = $sharePath for ($i = 0; $i -lt $nrTests; $i++) { $newPath = Join-Path -Path $ContentSharePackageRoot -ChildPath $testSharePath if (Test-Path -Path $newPath) { Write-Host "Package found in Content Store: $newPath" -ForegroundColor Green $PackagePath = $newPath break } else { Write-Verbose "Package not found in Content Store: $newPath" $PackagePath = $appVManagementPackage.PackageUrl } $null, $testSharePath = $testSharePath.Split('\', 2) } } else { $Fileserver, $Share, $SharePath = "$($appVManagementPackage.PackageUrl)".TrimStart("\\").Split('\', 3) $PackageContentShareMatch = "\\{0}*\{1}" -f $Fileserver, $Share Write-Verbose "Looking for Content Store matching pattern '$PackageContentShareMatch' in machine group '$currentMachineGroup'" $ContentSharePackageRoot = $ContentSharePackageRoots | Where-Object { $_ -like $PackageContentShareMatch } | Select-Object -First 1 if ([string]::IsNullOrEmpty($ContentSharePackageRoot)) { Write-Verbose "No Content Store match found for package in machine group '$currentMachineGroup'." } else { Write-Verbose "Content Store match for package in machine group '$currentMachineGroup': $($ContentSharePackageRoot)" } Write-AppVentiXDebugLog -Message 'Matching package URL with Content Store roots' -Level Timing -Data @{ Package = "$($appVManagementPackage.Name)" PackagePath = "$($appVManagementPackage.PackageUrl)" MachineGroupFriendlyName = "$currentMachineGroup" ContentShareRootCount = @($ContentSharePackageRoots).Count Matched = if ([string]::IsNullOrEmpty($ContentSharePackageRoot)) { $false } else { $true } } if ([string]::IsNullOrEmpty($ContentSharePackageRoot)) { # Retry one level deeper; the share may be a subfolder of the Content Store root (DFS). $Fileserver, $dfspath, $folder, $SharePath = "$($appVManagementPackage.PackageUrl)".TrimStart("\\").Split('\', 4) $PackageContentShareMatch = "\\{0}*\{1}\{2}" -f $Fileserver, $dfspath, $folder Write-Verbose "Looking for another attempt Content Store matching pattern '$PackageContentShareMatch' in machine group '$currentMachineGroup'" $ContentSharePackageRoot = $ContentSharePackageRoots | Where-Object { $_ -like $PackageContentShareMatch } | Select-Object -First 1 if ([string]::IsNullOrEmpty($ContentSharePackageRoot)) { Write-Verbose "No Content Store match found for package in machine group '$currentMachineGroup'." } else { Write-Verbose "Content Store match for package in machine group '$currentMachineGroup': $($ContentSharePackageRoot)" } Write-AppVentiXDebugLog -Message 'Second attempt matching package URL with Content Store roots' -Level Timing -Data @{ Package = "$($appVManagementPackage.Name)" PackagePath = "$($appVManagementPackage.PackageUrl)" MachineGroupFriendlyName = "$currentMachineGroup" PublishingMachineGroupFriendlyName = "$($currentPublishingGroups -join ', ')" ContentShareRootCount = @($ContentSharePackageRoots).Count Matched = if ([string]::IsNullOrEmpty($ContentSharePackageRoot)) { $false } else { $true } } } } if ([string]::IsNullOrEmpty($ContentSharePackageRoot)) { Write-Warning "No matching Content Store found for package in machine group '$currentMachineGroup'." } Write-Verbose "Content Store match for package in machine group '$currentMachineGroup': $($ContentSharePackageRoot)" if ([string]::IsNullOrEmpty($ContentSharePackageRoot)) { Write-Warning "No matching Content Store found for package in machine group '$currentMachineGroup'. Using Original Package URL." $PackagePath = $appVManagementPackage.PackageUrl Write-AppVentiXDebugLog -Message 'Resolved package path (no Content Store match, using original URL)' -Level Warning -Data @{ Package = "$($appVManagementPackage.Name)" PackagePath = "$PackagePath" MachineGroupFriendlyName = "$currentMachineGroup" PublishingMachineGroupFriendlyName = "$($currentPublishingGroups -join ', ')" Matched = $false } } elseif ($preselectedMachineGroup) { Write-Host "Using preselected Machine Group '$currentMachineGroup' and Content Store '$ContentSharePackageRoot' for package." -ForegroundColor Green Write-AppVentiXDebugLog -Message 'Resolved package path from preselected Machine Group and Content Store' -Level Timing -Data @{ Package = "$($appVManagementPackage.Name)" PackagePath = "$PackagePath" ContentShareRoot = "$ContentSharePackageRoot" MachineGroupFriendlyName = "$currentMachineGroup" PublishingMachineGroupFriendlyName = "$($currentPublishingGroups -join ', ')" Matched = $true } } else { Write-Host "Found matching Content Store for package." -ForegroundColor Green $ContentShareFileserver, $ContentShareShare = $ContentSharePackageRoot.TrimStart("\\").Split('\', 2) $PackagePath = '\\{0}\{1}\{2}' -f $ContentShareFileserver, $ContentShareShare, $SharePath Write-AppVentiXDebugLog -Message 'Resolved package path from Content Store match' -Data @{ Package = "$($appVManagementPackage.Name)" PackagePath = "$PackagePath" ContentShareRoot = "$ContentSharePackageRoot" MachineGroupFriendlyName = "$currentMachineGroup" PublishingMachineGroupFriendlyName = "$($currentPublishingGroups -join ', ')" Matched = $true } } if ($CopyPackages) { $sourcePath = "$($appVManagementPackage.PackageUrl)" $contentStoreRoot = $ContentSharePackageRoot.TrimEnd('\') $foundInContentStore = $PackagePath.StartsWith("$contentStoreRoot\", [System.StringComparison]::OrdinalIgnoreCase) if ($foundInContentStore) { $relativePath = $PackagePath.Substring($contentStoreRoot.Length + 1) } else { # Keep the folder structure below the source share. $null, $null, $relativePath = $sourcePath.TrimStart('\').Split('\', 3) } if ($RenamePackageFolder) { # <Store>\<basename>\<file>.appv $packageFileName = Split-Path -Path $relativePath -Leaf $relativePath = '{0}\{1}' -f [System.IO.Path]::GetFileNameWithoutExtension($packageFileName), $packageFileName } $destinationPath = Join-Path -Path $contentStoreRoot -ChildPath $relativePath Write-AppVentiXDebugLog -Message 'Resolved package copy destination' -Data @{ Package = "$($appVManagementPackage.Name)" SourcePath = "$sourcePath" DestinationPath = "$destinationPath" ContentShareRoot = "$contentStoreRoot" FoundInContentStore = $foundInContentStore RenamePackageFolder = [bool]$RenamePackageFolder Force = [bool]$Force } if ($destinationPath -ieq $sourcePath) { Write-Verbose "Package source is already in the Content Store, nothing to copy." Write-AppVentiXDebugLog -Message 'Package copy skipped, source equals destination' -Data @{ Package = "$($appVManagementPackage.Name)" DestinationPath = "$destinationPath" } } elseif ((Test-Path -Path $destinationPath -PathType Leaf) -and -not $Force) { Write-Warning "Package already exists in Content Store: '$destinationPath'. Use -Force to overwrite." Write-AppVentiXDebugLog -Message 'Package copy skipped, package already exists in Content Store' -Level Warning -Data @{ Package = "$($appVManagementPackage.Name)" SourcePath = "$sourcePath" DestinationPath = "$destinationPath" } } elseif (-not (Test-Path -Path $sourcePath -PathType Leaf)) { Write-Warning "Source package not found: '$sourcePath'. Package not copied." Write-AppVentiXDebugLog -Message 'Package copy skipped, source package not found' -Level Warning -Data @{ Package = "$($appVManagementPackage.Name)" SourcePath = "$sourcePath" DestinationPath = "$destinationPath" } } else { Write-Host "Copying package to Content Store: $destinationPath" -ForegroundColor Cyan # /J: unbuffered I/O for large files. /IS /IT: overwrite even when identical. $robocopyArgs = @( (Split-Path -Path $sourcePath -Parent) (Split-Path -Path $destinationPath -Parent) (Split-Path -Path $sourcePath -Leaf) '/J', '/R:2', '/W:5', '/NP', '/NJH', '/NJS', '/NDL' ) if ($Force) { $robocopyArgs += '/IS', '/IT' } $robocopyOutput = & robocopy.exe @robocopyArgs $robocopyExitCode = $LASTEXITCODE Write-Verbose "Robocopy exit code $($robocopyExitCode): $($robocopyOutput | Out-String)" # Robocopy exit codes 0-7 are success, 8 and up are failures. if ($robocopyExitCode -ge 8) { Write-Error -Message "Failed to copy package '$($appVManagementPackage.Name)' to '$destinationPath', robocopy exit code $robocopyExitCode." -ErrorId 'ImportCopyPackageFailed' -Category WriteError -TargetObject $sourcePath Write-AppVentiXDebugLog -Message 'Failed to copy package to Content Store' -Level Warning -Data @{ Package = "$($appVManagementPackage.Name)" SourcePath = "$sourcePath" DestinationPath = "$destinationPath" Arguments = "$($robocopyArgs -join ' ')" ExitCode = $robocopyExitCode Output = "$($robocopyOutput | Out-String)" } continue } Write-AppVentiXDebugLog -Message 'Copied package to Content Store' -Level Timing -Data @{ Package = "$($appVManagementPackage.Name)" SourcePath = "$sourcePath" DestinationPath = "$destinationPath" Arguments = "$($robocopyArgs -join ' ')" ExitCode = $robocopyExitCode Output = "$($robocopyOutput | Out-String)" } } $PackagePath = $destinationPath } if (Test-Path -Path $PackagePath -PathType Leaf) { Write-Host "Package found in Content Store" -ForegroundColor Green $adGroupDetails = @() $appVManagementPackage.ADGroups | ForEach-Object { $adDomain = $_.Domain $adName = $_.Name Write-Verbose "Processing AD group '$adDomain\$adName' for package '$($appVManagementPackage.Name)'" $adGroupName = '{0}\{1}' -f $adDomain, $adName $adGroupObject = [PSCustomObject]@{ Domain = $adDomain Name = $adName GroupName = $adGroupName Sid = "$($_.Sid)" UserConfigFilePath = "" } if (-not [string]::IsNullOrEmpty($($_.UserConfigurationXml))) { try { Write-Host "User configuration detected for AD group '$adGroupName'. Saving user configuration XML to appd file..." -ForegroundColor Cyan [xml]$userConfigXml = $($_.UserConfigurationXml) | Format-XML $xmlUserConfigFilePath = "$PackagePath".Replace('.appv', "_$($adName)_UserConfig.xml") Write-Verbose "Saving deployment configuration XML to appd file: $xmlUserConfigFilePath" $adGroupObject.UserConfigFilePath = $xmlUserConfigFilePath $userConfigXml.Save($xmlUserConfigFilePath) } catch { Write-Warning "Failed to save user configuration XML to appd file for AD group '$adGroupName': $($_.Exception.Message)" Write-AppVentiXDebugLog -Message 'Failed to save user configuration appd file' -ErrorRecord $_ -Data @{ Package = "$($appVManagementPackage.Name)" GroupName = "$adGroupName" xmlUserConfigFilePath = "$xmlUserConfigFilePath" } } } else { Write-Verbose "No user configuration detected for AD group '$adGroupName'." } $adGroupDetails += $adGroupObject } $createdTaskId = $null # Publish each group as its own task when any group produced a per-group user-config file. $publishSeparately = @($adGroupDetails | Where-Object { -not [string]::IsNullOrEmpty($_.UserConfigFilePath) }).Count -gt 0 if ($publishSeparately) { foreach ($adGroup in $adGroupDetails) { $publishingTaskParams = @{ MachineGroupFriendlyName = $currentPublishingGroups Group = $adGroup.GroupName ADGroupSID = @($adGroup.Sid) Path = $PackagePath } if ($adGroup.UserConfigFilePath) { $publishingTaskParams['DynamicUserConfigurationPath'] = $adGroup.UserConfigFilePath } Write-Verbose "Creating publishing task with parameters: $($publishingTaskParams | Out-String)" Write-Host "Creating publishing task..." try { $newTaskId = New-AppVentiXPublishingTask @publishingTaskParams } catch { Write-Error -Message "Failed to create publishing task for package '$($appVManagementPackage.Name)', AD group '$($adGroup.GroupName)': $($_.Exception.Message)" -ErrorId 'ImportPublishingTaskFailed' -Category $_.CategoryInfo.Category -TargetObject $adGroup.GroupName Write-AppVentiXDebugLog -Message 'Failed to create publishing task' -ErrorRecord $_ -Data @{ Package = "$($appVManagementPackage.Name)" GroupName = "$($adGroup.GroupName)" } continue } $createdTaskId = $newTaskId.Id Write-AppVentiXDebugLog -Message 'Created publishing task' -Level Timing -Data @{ Package = "$($appVManagementPackage.Name)" TaskId = "$($newTaskId.Id)" MachineGroupFriendlyName = "$currentMachineGroup" PublishingMachineGroupFriendlyName = "$($currentPublishingGroups -join ', ')" GroupCount = @($adGroupDetails).Count DynamicUserConfigurationPath = if ($adGroup.UserConfigFilePath) { "$($adGroup.UserConfigFilePath)" } else { "n/a" } } $userConfigPresent = $false if (-not [string]::IsNullOrEmpty($adGroup.UserConfigFilePath)) { $userConfigPresent = $true try { $userConfigFilename = Split-Path -Path $adGroup.UserConfigFilePath -Leaf } catch { $userConfigFilename = $adGroup.UserConfigFilePath } } else { $userConfigFilename = "" } $ImportResults.Add([PSCustomObject]@{ Name = $appVManagementPackage.Name Id = $newTaskId.Id UserConfig = $userConfigPresent UserConfigFilename = $userConfigFilename } ) } } else { $publishingTaskParams = @{ MachineGroupFriendlyName = $currentPublishingGroups Group = @($adGroupDetails.GroupName) ADGroupSID = @($adGroupDetails.Sid) Path = $PackagePath } Write-Verbose "Creating publishing task with parameters: $($publishingTaskParams | Out-String)" Write-Host "Creating publishing task..." try { $newTaskId = New-AppVentiXPublishingTask @publishingTaskParams } catch { Write-Error -Message "Failed to create publishing task for package '$($appVManagementPackage.Name)': $($_.Exception.Message)" -ErrorId 'ImportPublishingTaskFailed' -Category $_.CategoryInfo.Category -TargetObject $appVManagementPackage.Name Write-AppVentiXDebugLog -Message 'Failed to create publishing task' -ErrorRecord $_ -Data @{ Package = "$($appVManagementPackage.Name)" GroupName = "$($adGroupDetails.GroupName -join ', ')" } continue } $createdTaskId = $newTaskId.Id Write-AppVentiXDebugLog -Message 'Created publishing task' -Level Timing -Data @{ Package = "$($appVManagementPackage.Name)" TaskId = "$($newTaskId.Id)" MachineGroupFriendlyName = "$currentMachineGroup" PublishingMachineGroupFriendlyName = "$($currentPublishingGroups -join ', ')" ADGroupCount = @($adGroupDetails).Count } $ImportResults.Add([PSCustomObject]@{ Name = $appVManagementPackage.Name Id = $newTaskId.Id UserConfig = $false UserConfigFilename = "" } ) } try { if (-not [string]::IsNullOrEmpty($($appVManagementPackage.DeploymentConfigurationXml))) { Write-Host "Deployment configuration XML found. Saving XML data to appd file..." -ForegroundColor Cyan [xml]$DeploymentConfigXml = $($appVManagementPackage.DeploymentConfigurationXml) | Format-XML $appdFilePath = "$PackagePath".Replace('.appv', '_DeploymentConfig.appd') Write-Verbose "Saving deployment configuration XML to appd file: $appdFilePath" $DeploymentConfigXml.Save($appdFilePath) } } catch { Write-Warning "Failed to save deployment configuration XML to appd file: $($_.Exception.Message)" Write-AppVentiXDebugLog -Message 'Failed to save deployment configuration appd file' -ErrorRecord $_ -Data @{ Package = "$($appVManagementPackage.Name)" AppdFilePath = "$appdFilePath" } } } else { Write-Warning "The package could not be found at path '$PackagePath'. Skipping package import." continue } if ($null -eq $createdTaskId) { # Every per-group task failed; errors were already written. continue } Write-Host "Successfully created publishing task with Task ID '$createdTaskId'." -ForegroundColor Green } Write-Host "`r`n================================" -ForegroundColor Cyan Write-Host "`r`nFinished processing App-V Management Packages.`r`n" -ForegroundColor Cyan Write-AppVentiXDebugLog -Message 'Finished processing App-V Management packages' -Level Timing -Data @{ TotalPackages = @($AppVManagementPackageDetails).Count DisabledPackages = @($AppVManagementPackageDetails | Where-Object { $_.IsEnabled -eq $false }).Count TasksCreated = $ImportResults.Count TasksWithUserConfig = @($ImportResults | Where-Object { $_.UserConfig }).Count } # One entry for the import itself; New-AppVentiXPublishingTask logs each task it creates. Write-AppVentiXLogEntry -Feature Application -Action $($MyInvocation.MyCommand.Name) -Details "Imported $($ImportResults.Count) App-V Management package(s) from database '$SQLDatabase' on server '$SQLServer' into machine group '$MachineGroupFriendlyName'. Packages: $(@($ImportResults.Name) -join ', ')." -ConfigShare $ConfigShare $ImportResults } else { Write-Warning "No App-V Management Packages found in the database '$SQLDatabase' on server '$SQLServer'." return } } catch { Write-AppVentiXDebugLog -Message 'Import failed' -ErrorRecord $_ $ErrorMessage = @{ Message = "Failed to import App-V Management Packages: $($_.Exception.Message)" ErrorId = "ImportAppVManagementPackageFailed" Category = [System.Management.Automation.ErrorCategory]::NotSpecified TargetObject = $_.Exception } Write-Verbose $($_ | Get-ExceptionDetails -AsPlainText) Write-AppVentiXError @ErrorMessage } finally { # Clear the run id so later unrelated debug entries are not stamped with it. $Script:AppVentiXDebugRunId = $null } } else { throw "AppVentiX is not licensed. Please run the license setup before using this function." } } # SIG # Begin signature block # MII5zAYJKoZIhvcNAQcCoII5vTCCObkCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAQM17FlbPlJf7G # GYwn1vnoVHBR+WzE9hIX/xeFhI3T+qCCIfAwggXMMIIDtKADAgECAhBUmNLR1FsZ # lUgTecgRwIeZMA0GCSqGSIb3DQEBDAUAMHcxCzAJBgNVBAYTAlVTMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xSDBGBgNVBAMTP01pY3Jvc29mdCBJZGVu # dGl0eSBWZXJpZmljYXRpb24gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAy # MDAeFw0yMDA0MTYxODM2MTZaFw00NTA0MTYxODQ0NDBaMHcxCzAJBgNVBAYTAlVT # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xSDBGBgNVBAMTP01pY3Jv # c29mdCBJZGVudGl0eSBWZXJpZmljYXRpb24gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRo # b3JpdHkgMjAyMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALORKgeD # Bmf9np3gx8C3pOZCBH8Ppttf+9Va10Wg+3cL8IDzpm1aTXlT2KCGhFdFIMeiVPvH # or+Kx24186IVxC9O40qFlkkN/76Z2BT2vCcH7kKbK/ULkgbk/WkTZaiRcvKYhOuD # PQ7k13ESSCHLDe32R0m3m/nJxxe2hE//uKya13NnSYXjhr03QNAlhtTetcJtYmrV # qXi8LW9J+eVsFBT9FMfTZRY33stuvF4pjf1imxUs1gXmuYkyM6Nix9fWUmcIxC70 # ViueC4fM7Ke0pqrrBc0ZV6U6CwQnHJFnni1iLS8evtrAIMsEGcoz+4m+mOJyoHI1 # vnnhnINv5G0Xb5DzPQCGdTiO0OBJmrvb0/gwytVXiGhNctO/bX9x2P29Da6SZEi3 # W295JrXNm5UhhNHvDzI9e1eM80UHTHzgXhgONXaLbZ7LNnSrBfjgc10yVpRnlyUK # xjU9lJfnwUSLgP3B+PR0GeUw9gb7IVc+BhyLaxWGJ0l7gpPKWeh1R+g/OPTHU3mg # trTiXFHvvV84wRPmeAyVWi7FQFkozA8kwOy6CXcjmTimthzax7ogttc32H83rwjj # O3HbbnMbfZlysOSGM1l0tRYAe1BtxoYT2v3EOYI9JACaYNq6lMAFUSw0rFCZE4e7 # swWAsk0wAly4JoNdtGNz764jlU9gKL431VulAgMBAAGjVDBSMA4GA1UdDwEB/wQE # AwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTIftJqhSobyhmYBAcnz1AQ # T2ioojAQBgkrBgEEAYI3FQEEAwIBADANBgkqhkiG9w0BAQwFAAOCAgEAr2rd5hnn # LZRDGU7L6VCVZKUDkQKL4jaAOxWiUsIWGbZqWl10QzD0m/9gdAmxIR6QFm3FJI9c # Zohj9E/MffISTEAQiwGf2qnIrvKVG8+dBetJPnSgaFvlVixlHIJ+U9pW2UYXeZJF # xBA2CFIpF8svpvJ+1Gkkih6PsHMNzBxKq7Kq7aeRYwFkIqgyuH4yKLNncy2RtNwx # AQv3Rwqm8ddK7VZgxCwIo3tAsLx0J1KH1r6I3TeKiW5niB31yV2g/rarOoDXGpc8 # FzYiQR6sTdWD5jw4vU8w6VSp07YEwzJ2YbuwGMUrGLPAgNW3lbBeUU0i/OxYqujY # lLSlLu2S3ucYfCFX3VVj979tzR/SpncocMfiWzpbCNJbTsgAlrPhgzavhgplXHT2 # 6ux6anSg8Evu75SjrFDyh+3XOjCDyft9V77l4/hByuVkrrOj7FjshZrM77nq81YY # uVxzmq/FdxeDWds3GhhyVKVB0rYjdaNDmuV3fJZ5t0GNv+zcgKCf0Xd1WF81E+Al # GmcLfc4l+gcK5GEh2NQc5QfGNpn0ltDGFf5Ozdeui53bFv0ExpK91IjmqaOqu/dk # ODtfzAzQNb50GQOmxapMomE2gj4d8yu8l13bS3g7LfU772Aj6PXsCyM2la+YZr9T # 03u4aUoqlmZpxJTG9F9urJh4iIAGXKKy7aIwggalMIIEjaADAgECAhMzAAe4lMek # LWCItFCdAAAAB7iUMA0GCSqGSIb3DQEBDAUAMFoxCzAJBgNVBAYTAlVTMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzApBgNVBAMTIk1pY3Jvc29mdCBJ # RCBWZXJpZmllZCBDUyBBT0MgQ0EgMDQwHhcNMjYxMDA0MTczMjE4WhcNMjYxMDA3 # MTczMjE4WjBoMQswCQYDVQQGEwJOTDEWMBQGA1UECBMNTm9vcmQtSG9sbGFuZDEP # MA0GA1UEBxMGSGVpbG9vMRcwFQYDVQQKEw5BcHBWZW50aVggQi5WLjEXMBUGA1UE # AxMOQXBwVmVudGlYIEIuVi4wggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIB # gQCI1YwPphhyBceH5g229gOYpcXub+SVZ4RcqL0eDKgce/JNHuCeiSnOWTBpFjs1 # M7ammlzGHjOSSPR2izxQR7ASxv1A/x+qVkOzngfFAhjyz5P7CTpEr/tdd9GAsnQo # Y+ODSJodTGTTh+YW+z3Oa0UWgUTxwvJxwIiqwdQoV22YdqWUPmPGPPM2fjhMVOor # Z3dZvMLlOlQC/V73JLR4mrbZimOWNqzEiJzS81w7dsnx/MRDH12wFK5gK3u4W/9j # dGYTdV5hnViw/qhJRdWH2aJ0mCwbWbUsYyRElWvtKFRF6w+PP2CuKh6jXFG0jUgP # Fn2nwZu6glqWQnTSTy9x8JNMLufetz3vZCSeFooQ19AXoDVIOXxcRUhlMl0lVch+ # 1Ojvh6p4se7R8LUJo/ose5gRZpECDkzf1HzPclJSbelGvoFN/m5pfOmCdMDH9pa/ # IsPetrSGue/MoJL7TmOSpXxrMUTQxtD01UE0cm9cRnvxp0PvwI3Gk3844STwscT3 # 6VECAwEAAaOCAdQwggHQMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMDsG # A1UdJQQ0MDIGCisGAQQBgjdhAQAGCCsGAQUFBwMDBhorBgEEAYI3YaL8yC6uh9F6 # gZ2lyVCBwcKCJzAdBgNVHQ4EFgQU5rNL9Y/7gtCZHqApckPUAAeCboQwHwYDVR0j # BBgwFoAUayVB3vtrfP0YgAotf492XapzPbgwZwYDVR0fBGAwXjBcoFqgWIZWaHR0 # cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwSUQl # MjBWZXJpZmllZCUyMENTJTIwQU9DJTIwQ0ElMjAwNC5jcmwwdAYIKwYBBQUHAQEE # aDBmMGQGCCsGAQUFBzAChlhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3Bz # L2NlcnRzL01pY3Jvc29mdCUyMElEJTIwVmVyaWZpZWQlMjBDUyUyMEFPQyUyMENB # JTIwMDQuY3J0MFQGA1UdIARNMEswSQYEVR0gADBBMD8GCCsGAQUFBwIBFjNodHRw # Oi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0w # DQYJKoZIhvcNAQEMBQADggIBAIsYnfDTXNQ+Wp3KhJ7mSIeG0dQIM2TAij+/ZhSy # 2RipxwGLRta9V9XjkNONTFAabsrw1YRBGr1RjkTsthZZFyYl0QFUfmuYFSgIQpqJ # C74t5wR0fdpHHD4adj8qLc7QUHagHxHSYKYLxI6fdXLhO8s79QGyuhXUPOIX0UNk # mK4maA4bgSUL9+ZdN03PQdeAWWHNz0ak5j+jbE5p+eTyEe+eCye7QAubE5ggf8Og # lI4DTI21+uKEwjQfyliuiHerEliO0PJwRDQk/kt9Lzc68efLZmLzcMwLZjl7bcuW # oO85Tf01mY1hFnYWhnST4W4B5TSHyXaYGoBaY5Xd1Img1iq835gf4rG/R0SK9RWN # wD4GQCGcV0aUSrX3NXxHTSsa93vaSY0Xa3PJvmTPF4nnR95AU1SC2th0+t1P8kRr # RcXdz16m1g3dvR95yYZduiIcodCcE+NFMc5qhSjYB6zhErgYoFunWr0qB+HFFjkL # gxNk08Xu5AZ2HGWc2z8x6AuQN9mGTYqdUjMAZHMh9d1e5rMet94kEj6Yppc8RK36 # ii5VPkMAzToWgmU6paLw6ncNMIytJlK4wxvaiLgciIhFL4K7ksNkYT3eWQadYxzL # J3Kefyt+C2caCB8i7hXGarl83CSviKuMVQuoEoKEaeD681/BEpnQwUQiVa26HmsR # sq3GMIIGpTCCBI2gAwIBAgITMwAHuJTHpC1giLRQnQAAAAe4lDANBgkqhkiG9w0B # AQwFADBaMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMSswKQYDVQQDEyJNaWNyb3NvZnQgSUQgVmVyaWZpZWQgQ1MgQU9DIENBIDA0 # MB4XDTI2MTAwNDE3MzIxOFoXDTI2MTAwNzE3MzIxOFowaDELMAkGA1UEBhMCTkwx # FjAUBgNVBAgTDU5vb3JkLUhvbGxhbmQxDzANBgNVBAcTBkhlaWxvbzEXMBUGA1UE # ChMOQXBwVmVudGlYIEIuVi4xFzAVBgNVBAMTDkFwcFZlbnRpWCBCLlYuMIIBojAN # BgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAiNWMD6YYcgXHh+YNtvYDmKXF7m/k # lWeEXKi9HgyoHHvyTR7gnokpzlkwaRY7NTO2pppcxh4zkkj0dos8UEewEsb9QP8f # qlZDs54HxQIY8s+T+wk6RK/7XXfRgLJ0KGPjg0iaHUxk04fmFvs9zmtFFoFE8cLy # ccCIqsHUKFdtmHallD5jxjzzNn44TFTqK2d3WbzC5TpUAv1e9yS0eJq22Ypjljas # xIic0vNcO3bJ8fzEQx9dsBSuYCt7uFv/Y3RmE3VeYZ1YsP6oSUXVh9midJgsG1m1 # LGMkRJVr7ShUResPjz9grioeo1xRtI1IDxZ9p8GbuoJalkJ00k8vcfCTTC7n3rc9 # 72QknhaKENfQF6A1SDl8XEVIZTJdJVXIftTo74eqeLHu0fC1CaP6LHuYEWaRAg5M # 39R8z3JSUm3pRr6BTf5uaXzpgnTAx/aWvyLD3ra0hrnvzKCS+05jkqV8azFE0MbQ # 9NVBNHJvXEZ78adD78CNxpN/OOEk8LHE9+lRAgMBAAGjggHUMIIB0DAMBgNVHRMB # Af8EAjAAMA4GA1UdDwEB/wQEAwIHgDA7BgNVHSUENDAyBgorBgEEAYI3YQEABggr # BgEFBQcDAwYaKwYBBAGCN2Gi/MgurofReoGdpclQgcHCgicwHQYDVR0OBBYEFOaz # S/WP+4LQmR6gKXJD1AAHgm6EMB8GA1UdIwQYMBaAFGslQd77a3z9GIAKLX+Pdl2q # cz24MGcGA1UdHwRgMF4wXKBaoFiGVmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w # a2lvcHMvY3JsL01pY3Jvc29mdCUyMElEJTIwVmVyaWZpZWQlMjBDUyUyMEFPQyUy # MENBJTIwMDQuY3JsMHQGCCsGAQUFBwEBBGgwZjBkBggrBgEFBQcwAoZYaHR0cDov # L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBJRCUy # MFZlcmlmaWVkJTIwQ1MlMjBBT0MlMjBDQSUyMDA0LmNydDBUBgNVHSAETTBLMEkG # BFUdIAAwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3Br # aW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMA0GCSqGSIb3DQEBDAUAA4ICAQCLGJ3w # 01zUPlqdyoSe5kiHhtHUCDNkwIo/v2YUstkYqccBi0bWvVfV45DTjUxQGm7K8NWE # QRq9UY5E7LYWWRcmJdEBVH5rmBUoCEKaiQu+LecEdH3aRxw+GnY/Ki3O0FB2oB8R # 0mCmC8SOn3Vy4TvLO/UBsroV1DziF9FDZJiuJmgOG4ElC/fmXTdNz0HXgFlhzc9G # pOY/o2xOafnk8hHvngsnu0ALmxOYIH/DoJSOA0yNtfrihMI0H8pYroh3qxJYjtDy # cEQ0JP5LfS83OvHny2Zi83DMC2Y5e23LlqDvOU39NZmNYRZ2FoZ0k+FuAeU0h8l2 # mBqAWmOV3dSJoNYqvN+YH+Kxv0dEivUVjcA+BkAhnFdGlEq19zV8R00rGvd72kmN # F2tzyb5kzxeJ50feQFNUgtrYdPrdT/JEa0XF3c9eptYN3b0fecmGXboiHKHQnBPj # RTHOaoUo2Aes4RK4GKBbp1q9KgfhxRY5C4MTZNPF7uQGdhxlnNs/MegLkDfZhk2K # nVIzAGRzIfXdXuazHrfeJBI+mKaXPESt+oouVT5DAM06FoJlOqWi8Op3DTCMrSZS # uMMb2oi4HIiIRS+Cu5LDZGE93lkGnWMcyydynn8rfgtnGggfIu4Vxmq5fNwkr4ir # jFULqBKChGng+vNfwRKZ0MFEIlWtuh5rEbKtxjCCBygwggUQoAMCAQICEzMAAAAW # MZKNkgJle5oAAAAAABYwDQYJKoZIhvcNAQEMBQAwYzELMAkGA1UEBhMCVVMxHjAc # BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjE0MDIGA1UEAxMrTWljcm9zb2Z0 # IElEIFZlcmlmaWVkIENvZGUgU2lnbmluZyBQQ0EgMjAyMTAeFw0yNjAzMjYxODEx # MjlaFw0zMTAzMjYxODExMjlaMFoxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKzApBgNVBAMTIk1pY3Jvc29mdCBJRCBWZXJpZmll # ZCBDUyBBT0MgQ0EgMDQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDK # VfrI2+gJMM/0bQ5OVKNdvOASzLbUUMvXuf+Vl7YGuofPaZHVo3gMHF5inT+GMSpI # cfIZ9qtXU1UG68ry8vNbQtOL4Nm30ifXpqI1+ByiAWLO1YT0WnzG7XPOuoTeeWsN # Zv5FmjxCsReBZvyzyzCyXZbu1EQfJxWTH4ebUwtAiW9rqMf9eDj/wYhiEfNteJV3 # ZFeibD2ztCHr9JhFdd97XbnCHgQoTIqc02X5xlRKtUGBa++OtHBBjiJ/uwBnzTkq # u4FjpZjQeJtrmda+ur1CT2jflWIB/ypn7u7V9tvW9wJbJYt/H2EtJ0GONWxJZ7TE # u8jWPindOO3lzPP7UtzS/mVDV94HucWaltmsra6zSG8BoEJ87IM8QSb7vfm/O41F # hYkUv89WIj5ES2O4kxyiMSfe95CMivCuYrRP2hKvx7egPMrWgDDBkxMLgrKZO9hR # NUMm8vk3w5b9SogHOyJVhxyFm8aFXfIxgqDF4S0g4bhbhnzljmSlCLlumMZcXFGD # jpF2tNoAu3VGFGYtHtTSNVKvZpgB3b4ynaoDkbPf+Wg4523jt4VneasBgZhC1srZ # I2NCnCBBfgjLq04pqEKAWEohyW2K29KSkkHvt5VaE1ac3Yt+oyiOzMS57tXwQDJL # GvLg/OXFO0VNvczDndfIfXYExB/ab2PuMSwd5VIBOwIDAQABo4IB3DCCAdgwDgYD # VR0PAQH/BAQDAgGGMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRrJUHe+2t8 # /RiACi1/j3ZdqnM9uDBUBgNVHSAETTBLMEkGBFUdIAAwQTA/BggrBgEFBQcCARYz # aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnku # aHRtMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMBIGA1UdEwEB/wQIMAYBAf8C # AQAwHwYDVR0jBBgwFoAU2UEpsA8PY2zvadf1zSmepEhqMOYwcAYDVR0fBGkwZzBl # oGOgYYZfaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9z # b2Z0JTIwSUQlMjBWZXJpZmllZCUyMENvZGUlMjBTaWduaW5nJTIwUENBJTIwMjAy # MS5jcmwwfQYIKwYBBQUHAQEEcTBvMG0GCCsGAQUFBzAChmFodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMElEJTIwVmVyaWZp # ZWQlMjBDb2RlJTIwU2lnbmluZyUyMFBDQSUyMDIwMjEuY3J0MA0GCSqGSIb3DQEB # DAUAA4ICAQAG1VBeVHTVRBljlcZD3IiMxwPyMjQyLNaEnVu5mODm2hRBJfH8GsBL # ATmrHAc8F47jmk5CnpUPiIguCbw6Z/KVj4Dsoiq228NSLMLewFfGMri7uwNGLISC # 5ccp8vUdADDEIsS2dE+QI9OwkDpv3XuUD7d+hAgcLVcMOl1AsfEZtsZenhGvSYUr # m/FuLq0BqEGL9GXM5c+Ho9q8o+Vn/S+GWQN2y+gkRO15s0kI05nUpq/dOD4ri9rg # Vs6tipEd0YZqGgD+CZNiaZWrDTOQbNPncd2F9qOsUa20miYruoT5PwJAaI+QQiTE # 2ZJeMJOkOpzhTUgqVMZwZidEUZKCqudaeQA08WwnkQMfKyHzaU8j48ULcU4hUwvM # sv7fSurOe9GAdRQCPvF8WcSK5oDHe8VVJM4tv6KKCm91HqLx9JamBgRI6R2SfY3n # u26EGznu0rCg/769z8xWm4PVcC2ZaL6VlKVqFp1NsN8YqMyf5t+bbGVb09noFKcJ # G/UwyGlxRmQBlfeBUQx5/ytlzZzsEnhrJF9fTAfje8j3OdX5lEnePTFQLRlvzZFB # qUXnIeQKv3fHQjC9m2fo/Z01DII/qp3d8LhGVUW0BCG04fRwHJNH8iqqCG/qofMv # +kym2AxBDnHzNgRjL60JOFiBgiurvLhYQNhB95KWojFA6shQnggkMTCCB54wggWG # oAMCAQICEzMAAAAHh6M0o3uljhwAAAAAAAcwDQYJKoZIhvcNAQEMBQAwdzELMAkG # A1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjFIMEYGA1UE # AxM/TWljcm9zb2Z0IElkZW50aXR5IFZlcmlmaWNhdGlvbiBSb290IENlcnRpZmlj # YXRlIEF1dGhvcml0eSAyMDIwMB4XDTIxMDQwMTIwMDUyMFoXDTM2MDQwMTIwMTUy # MFowYzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjE0MDIGA1UEAxMrTWljcm9zb2Z0IElEIFZlcmlmaWVkIENvZGUgU2lnbmluZyBQ # Q0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALLwwK8ZiCji # 3VR6TElsaQhVCbRS/3pK+MHrJSj3Zxd3KU3rlfL3qrZilYKJNqztA9OQacr1AwoN # cHbKBLbsQAhBnIB34zxf52bDpIO3NJlfIaTE/xrweLoQ71lzCHkD7A4As1Bs076I # u+mA6cQzsYYH/Cbl1icwQ6C65rU4V9NQhNUwgrx9rGQ//h890Q8JdjLLw0nV+ayQ # 2Fbkd242o9kH82RZsH3HEyqjAB5a8+Ae2nPIPc8sZU6ZE7iRrRZywRmrKDp5+Tcm # JX9MRff241UaOBs4NmHOyke8oU1TYrkxh+YeHgfWo5tTgkoSMoayqoDpHOLJs+qG # 8Tvh8SnifW2Jj3+ii11TS8/FGngEaNAWrbyfNrC69oKpRQXY9bGH6jn9NEJv9weF # xhTwyvx9OJLXmRGbAUXN1U9nf4lXezky6Uh/cgjkVd6CGUAf0K+Jw+GE/5VpIVbc # Nr9rNE50Sbmy/4RTCEGvOq3GhjITbCa4crCzTTHgYYjHs1NbOc6brH+eKpWLtr+b # Gecy9CrwQyx7S/BfYJ+ozst7+yZtG2wR461uckFu0t+gCwLdN0A6cFtSRtR8bvxV # FyWwTtgMMFRuBa3vmUOTnfKLsLefRaQcVTgRnzeLzdpt32cdYKp+dhr2ogc+qM6K # 4CBI5/j4VFyC4QFeUP2YAidLtvpXRRo3AgMBAAGjggI1MIICMTAOBgNVHQ8BAf8E # BAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNlBKbAPD2Ns72nX9c0p # nqRIajDmMFQGA1UdIARNMEswSQYEVR0gADBBMD8GCCsGAQUFBwIBFjNodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0wGQYJ # KwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSME # GDAWgBTIftJqhSobyhmYBAcnz1AQT2ioojCBhAYDVR0fBH0wezB5oHegdYZzaHR0 # cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwSWRl # bnRpdHklMjBWZXJpZmljYXRpb24lMjBSb290JTIwQ2VydGlmaWNhdGUlMjBBdXRo # b3JpdHklMjAyMDIwLmNybDCBwwYIKwYBBQUHAQEEgbYwgbMwgYEGCCsGAQUFBzAC # hnVodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29m # dCUyMElkZW50aXR5JTIwVmVyaWZpY2F0aW9uJTIwUm9vdCUyMENlcnRpZmljYXRl # JTIwQXV0aG9yaXR5JTIwMjAyMC5jcnQwLQYIKwYBBQUHMAGGIWh0dHA6Ly9vbmVv # Y3NwLm1pY3Jvc29mdC5jb20vb2NzcDANBgkqhkiG9w0BAQwFAAOCAgEAfyUqnv7U # q+rdZgrbVyNMul5skONbhls5fccPlmIbzi+OwVdPQ4H55v7VOInnmezQEeW4LqK0 # wja+fBznANbXLB0KrdMCbHQpbLvG6UA/Xv2pfpVIE1CRFfNF4XKO8XYEa3oW8oVH # +KZHgIQRIwAbyFKQ9iyj4aOWeAzwk+f9E5StNp5T8FG7/VEURIVWArbAzPt9ThVN # 3w1fAZkF7+YU9kbq1bCR2YD+MtunSQ1Rft6XG7b4e0ejRA7mB2IoX5hNh3UEauY0 # byxNRG+fT2MCEhQl9g2i2fs6VOG19CNep7SquKaBjhWmirYyANb0RJSLWjinMLXN # OAga10n8i9jqeprzSMU5ODmrMCJE12xS/NWShg/tuLjAsKP6SzYZ+1Ry358ZTFcx # 0FS/mx2vSoU8s8HRvy+rnXqyUJ9HBqS0DErVLjQwK8VtsBdekBmdTbQVoCgPCqr+ # PDPB3xajYnzevs7eidBsM71PINK2BoE2UfMwxCCX3mccFgx6UsQeRSdVVVNSyALQ # e6PT12418xon2iDGE81OGCreLzDcMAZnrUAx4XQLUz6ZTl65yPUiOh3k7Yww94lD # f+8oG2oZmDh5O1Qe38E+M3vhKwmzIeoB1dVLlz4i3IpaDcR+iuGjH2TdaC1ZOmBX # iCRKJLj4DT2uhJ04ji+tHD6n58vhavFIrmcxghcyMIIXLgIBATBxMFoxCzAJBgNV # BAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzApBgNVBAMT # Ik1pY3Jvc29mdCBJRCBWZXJpZmllZCBDUyBBT0MgQ0EgMDQCEzMAB7iUx6QtYIi0 # UJ0AAAAHuJQwDQYJYIZIAWUDBAIBBQCgXjAQBgorBgEEAYI3AgEMMQIwADAZBgkq # hkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAvBgkqhkiG9w0BCQQxIgQgB+eaD7gxsnwW # KOi2dMYSma868XtV3wKK7U3uolJbn3EwDQYJKoZIhvcNAQEBBQAEggGAdspSRhft # BS5u6f731qN/p04HVL6Ok6JAT8aiiMGiK0W3DDHigloM/E6PxMmkbA+nEjEOBWFN # wVJyOotm2YWmi4LgaQgmiPzJdQjsebPnnN2pdgu5OMagmqkQwIIdwpFj9pHREDMp # 9Yf3Yztl6jKff4ocjqod1rglo230EEsGJejOOwjWpDbRuS2YYGeNDp1Gl26jHylZ # NmFcil0/WcAZHFXUfxbTtUGYPYFUXF/ATYj2eUgIg+ArRkAModSiB+CibRbQE+l7 # JOoVu0pWHEHsCSralQLwRdlXmFeeUxgVkSTLu0F2vLolTjX7WJRluoWGmYMYZtua # em1k/4dOKcELP1Z6qviJxr1+6JsHvF5HBpuxsQp2bepmVXGnOXijlLvj6XZFx+sC # qWtUac5CD+YGvQCZ6pk4HHFsILX5E3cfqrdLcFKtkVvh1ZwfNfzEINxhSPMqNASS # MNn6W5orRmD2mcpeHAdxTBvhJ2bqPCGi2ur2d5GA8C6C6/SsaDzzfKSIoYIUsjCC # FK4GCisGAQQBgjcDAwExghSeMIIUmgYJKoZIhvcNAQcCoIIUizCCFIcCAQMxDzAN # BglghkgBZQMEAgEFADCCAWoGCyqGSIb3DQEJEAEEoIIBWQSCAVUwggFRAgEBBgor # BgEEAYRZCgMBMDEwDQYJYIZIAWUDBAIBBQAEIJIH/ptywWo1A82DyFypAJz9PgsC # lAiAwDfDnVnNZZWwAgZqv6L1oD8YEzIwMjYxMDA2MDkxMjEyLjUyNFowBIACAfSg # gemkgeYwgeMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTAr # BgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEnMCUG # A1UECxMeblNoaWVsZCBUU1MgRVNOOjdCMUEtMDVFMC1EOTQ3MTUwMwYDVQQDEyxN # aWNyb3NvZnQgUHVibGljIFJTQSBUaW1lIFN0YW1waW5nIEF1dGhvcml0eaCCDykw # ggeCMIIFaqADAgECAhMzAAAABeXPD/9mLsmHAAAAAAAFMA0GCSqGSIb3DQEBDAUA # MHcxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # SDBGBgNVBAMTP01pY3Jvc29mdCBJZGVudGl0eSBWZXJpZmljYXRpb24gUm9vdCBD # ZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAyMDAeFw0yMDExMTkyMDMyMzFaFw0zNTEx # MTkyMDQyMzFaMGExCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBQdWJsaWMgUlNBIFRpbWVzdGFt # cGluZyBDQSAyMDIwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnnzn # UmP94MWfBX1jtQYioxwe1+eXM9ETBb1lRkd3kcFdcG9/sqtDlwxKoVIcaqDb+omF # io5DHC4RBcbyQHjXCwMk/l3TOYtgoBjxnG/eViS4sOx8y4gSq8Zg49REAf5huXhI # kQRKe3Qxs8Sgp02KHAznEa/Ssah8nWo5hJM1xznkRsFPu6rfDHeZeG1Wa1wISvlk # pOQooTULFm809Z0ZYlQ8Lp7i5F9YciFlyAKwn6yjN/kR4fkquUWfGmMopNq/B8U/ # pdoZkZZQbxNlqJOiBGgCWpx69uKqKhTPVi3gVErnc/qi+dR8A2MiAz0kN0nh7SqI # NGbmw5OIRC0EsZ31WF3Uxp3GgZwetEKxLms73KG/Z+MkeuaVDQQheangOEMGJ4pQ # ZH55ngI0Tdy1bi69INBV5Kn2HVJo9XxRYR/JPGAaM6xGl57Ei95HUw9NV/uC3yFj # rhc087qLJQawSC3xzY/EXzsT4I7sDbxOmM2rl4uKK6eEpurRduOQ2hTkmG1hSuWY # BunFGNv21Kt4N20AKmbeuSnGnsBCd2cjRKG79+TX+sTehawOoxfeOO/jR7wo3liw # kGdzPJYHgnJ54UxbckF914AqHOiEV7xTnD1a69w/UTxwjEugpIPMIIE67SFZ2PMo # 27xjlLAHWW3l1CEAFjLNHd3EQ79PUr8FUXetXr0CAwEAAaOCAhswggIXMA4GA1Ud # DwEB/wQEAwIBhjAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQUa2koOjUvSGNA # z3vYr0npPtk92yEwVAYDVR0gBE0wSzBJBgRVHSAAMEEwPwYIKwYBBQUHAgEWM2h0 # dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0 # bTATBgNVHSUEDDAKBggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMA # QTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFMh+0mqFKhvKGZgEByfPUBBP # aKiiMIGEBgNVHR8EfTB7MHmgd6B1hnNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20v # cGtpb3BzL2NybC9NaWNyb3NvZnQlMjBJZGVudGl0eSUyMFZlcmlmaWNhdGlvbiUy # MFJvb3QlMjBDZXJ0aWZpY2F0ZSUyMEF1dGhvcml0eSUyMDIwMjAuY3JsMIGUBggr # BgEFBQcBAQSBhzCBhDCBgQYIKwYBBQUHMAKGdWh0dHA6Ly93d3cubWljcm9zb2Z0 # LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwSWRlbnRpdHklMjBWZXJpZmlj # YXRpb24lMjBSb290JTIwQ2VydGlmaWNhdGUlMjBBdXRob3JpdHklMjAyMDIwLmNy # dDANBgkqhkiG9w0BAQwFAAOCAgEAX4h2x35ttVoVdedMeGj6TuHYRJklFaW4sTQ5 # r+k77iB79cSLNe+GzRjv4pVjJviceW6AF6ycWoEYR0LYhaa0ozJLU5Yi+LCmcrdo # vkl53DNt4EXs87KDogYb9eGEndSpZ5ZM74LNvVzY0/nPISHz0Xva71QjD4h+8z2X # MOZzY7YQ0Psw+etyNZ1CesufU211rLslLKsO8F2aBs2cIo1k+aHOhrw9xw6JCWON # NboZ497mwYW5EfN0W3zL5s3ad4Xtm7yFM7Ujrhc0aqy3xL7D5FR2J7x9cLWMq7eb # 0oYioXhqV2tgFqbKHeDick+P8tHYIFovIP7YG4ZkJWag1H91KlELGWi3SLv10o4K # Gag42pswjybTi4toQcC/irAodDW8HNtX+cbz0sMptFJK+KObAnDFHEsukxD+7jFf # EV9Hh/+CSxKRsmnuiovCWIOb+H7DRon9TlxydiFhvu88o0w35JkNbJxTk4MhF/Kg # aXn0GxdH8elEa2Imq45gaa8D+mTm8LWVydt4ytxYP/bqjN49D9NZ81coE6aQWm88 # TwIf4R4YZbOpMKN0CyejaPNN41LGXHeCUMYmBx3PkP8ADHD1J2Cr/6tjuOOCztfp # +o9Nc+ZoIAkpUcA/X2gSMkgHAPUvIdtoSAHEUKiBhI6JQivRepyvWcl+JYbYbBh7 # pmgAXVswggefMIIFh6ADAgECAhMzAAAAWXzacemNXvXAAAAAAABZMA0GCSqGSIb3 # DQEBDAUAMGExCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y # YXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBQdWJsaWMgUlNBIFRpbWVzdGFtcGlu # ZyBDQSAyMDIwMB4XDTI2MDEwODE4NTkwMVoXDTI3MDEwNzE4NTkwMVowgeMxCzAJ # BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jv # c29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEnMCUGA1UECxMeblNoaWVs # ZCBUU1MgRVNOOjdCMUEtMDVFMC1EOTQ3MTUwMwYDVQQDEyxNaWNyb3NvZnQgUHVi # bGljIFJTQSBUaW1lIFN0YW1waW5nIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEB # BQADggIPADCCAgoCggIBAKYu5/40eEX+hT+5jFa146bid3dA4LnXYntvkP3CGw4L # GARFhnvLMSJ/VtsubzDaeFnm7yb2KSM70WmHQprdCVqpvUH7l0uB4jNw7urLoAR9 # kKHLE0VlMlDStDSxUBI3qwsdrjvdmvV0k+9/njuDEiSlzJTf7Dowd1K3bO4beRya # FhR+Y8tymECOqlOAffYrG2wZdVM51+QSBSe+PEykr8C6OnnqSipuF8fZvCb6/huk # 0Zm6ZwsaixSHIAT2IEGvS7c63Im8jV3a8R0K6i2yiw0NNlnTSpwy/Zfv7iwsLBwh # fbjBTn+XOl6mPzDXQQ3V+SRP9xXbGKOsBTxzGid7aKAHw3o4Ahl9UGWLH9kNP3VU # okE6JYkjlfpuUGZ6gQyqDewfxD4VoYIlopt4HZ0xQvqajuJx+cr8LR/IZ56gLLmw # yMzde5+vtjBoilry/gSZwVGwgkvkIgpKPBQHGsSB0y3szr7Y7wEb6v0yZal1XUvW # nnz3inTaSWsCFrLPVwVmXy3ncY5/d25VpOkht+m697GWNbvsNOhAOHRaftE9j/hh # koM6RsyJfBLnhqMcA/wcavf5oj5NeyRQdGZeLKcls9csKS3sBUzPidxx2iiNH9CP # aDq/bLJEOXasYohXMnRinu+fUk81s8VO7DQSF6ffn5oqSHoV8lf1Ax6u+kdShb8B # AgMBAAGjggHLMIIBxzAdBgNVHQ4EFgQUj5bnC18D0vlnSRhCOiODGGuXNnYwHwYD # VR0jBBgwFoAUa2koOjUvSGNAz3vYr0npPtk92yEwbAYDVR0fBGUwYzBhoF+gXYZb # aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIw # UHVibGljJTIwUlNBJTIwVGltZXN0YW1waW5nJTIwQ0ElMjAyMDIwLmNybDB5Bggr # BgEFBQcBAQRtMGswaQYIKwYBBQUHMAKGXWh0dHA6Ly93d3cubWljcm9zb2Z0LmNv # bS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwUHVibGljJTIwUlNBJTIwVGltZXN0 # YW1waW5nJTIwQ0ElMjAyMDIwLmNydDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQM # MAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQEAwIHgDBmBgNVHSAEXzBdMFEGDCsGAQQB # gjdMg30BATBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20v # cGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0wCAYGZ4EMAQQCMA0GCSqGSIb3DQEB # DAUAA4ICAQBEMhzC/ZcjpG/zURE7z2Yp5vrUxUjsE5Xa3t/2RGvESwvbmsk3bLHh # SFAajgo2XQ8xoGDP3sUhKCLPeICSbkVv6V8sSp8fJ8Jos6yrawf2YVis8tcV+OO7 # U9S6JGPQzpmPncfzQc4ne1fqZ4+HiKabIDEoFdddQT2Egkk9fzxCY/EZ52avJ27d # SfrI/IDmyn9V10O3iQpg2F+C9vNTrk7nVgoDoHa9+Q3pYr0IHGnSmt5irgGT436z # o5WnXP8FxMhswH1aiyiSZiVzhor10C9C52cP3C8/PEoMKUXstLjoPO0TMkeW/1Fr # 186KXD45QRgBo0xImgtWTdzWFnlD+p7+iDBIuSrNcRXDRYuq/aYZaDhWSI0SYdPI # WVh5XvXuWA31a8oQ0SO+oPa3Nk80k0864wiiyJ1KsbSnaaefg9vspeghrpY8ljCw # xfCUtx5HQRNgAJOI8IKACK4d014Mk0hlRO0lQVRHegqIg29K6Xqkc360W2ZJGUcs # tlKokkVj6KAHjGyrLRPzepYfiZUJq4gXyxbpvKb1XJ2FN2682aUoNXo9RyRK1ch0 # f66k6+yj88kzvuC7+vJWtNDs/UpIM6Hhm0kU64JUJ7MMEQcAc7kpft7Gm7YeRK+o # KgqUgYXCfmzbX8nJXJZnPa8ADWVsIqsuNAxCI0CZXkULofqo5Be6zzGCA9QwggPQ # AgEBMHgwYTELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh # dGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGltZXN0YW1waW5n # IENBIDIwMjACEzMAAABZfNpx6Y1e9cAAAAAAAFkwDQYJYIZIAWUDBAIBBQCgggEt # MBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgpQyF # nwhWKz1uRf8AO6DR/7Awz0zFQf85TInWos94gwwwgd0GCyqGSIb3DQEJEAIvMYHN # MIHKMIHHMIGgBCDLRbqx24bpscXEJ+Hjj9xrcUVw7R8OyyMfSB2YGK3+vDB8MGWk # YzBhMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u # MTIwMAYDVQQDEylNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1lc3RhbXBpbmcgQ0Eg # MjAyMAITMwAAAFl82nHpjV71wAAAAAAAWTAiBCDLkyfCJA0w5uF8CXFkUHgsD1W5 # 8jKnqU3A3I83umxNdjANBgkqhkiG9w0BAQsFAASCAgCkRt15m3oySdqK4M/DBl3e # bjhyjpy8LVfGBdYsWprn6F8+vcK1jIfpTZ7AKpBNGUakNZRWUX2uJ7IYCf1deRy+ # /XSaQkkPWG86094/uhEH4uF1L4GyEG1uvviqTDf5pUOqutb/LQZoUmiIHex2gr/X # ivKWrfUNZh7ReQ/FMQOkkxh72OX1nIAyjQj6ltq+rMipKRrsi8llwmb0uMj+DWgU # a+exVYUy6cORoqUq5MA2WuyqWH3d0WPjaTNKyJF1SJHiC6Uj960FF+OtsreABopY # pTGOo4egwgzAj6Pf2AW/1HVbrgW/D4j8t/uT6mihzWuitPJC+EYvdNWSDSyGYRyy # LVsck8KmQgu07yP7PuoSdW5uZZEDoveUhrzA4F3+qMRy3g04Cd7UTqbnCXgYRnEZ # hNsD4LNlAwYoajbNC9GcWWFDSdNy+polA0/fgNuLDn+1FiDrCUFH393tZ4EPoENK # yyoxbYL3Caz8rxk2fhXJydjjnz2UewnKqwJUaez00KFjIpwPFN1ldyM5XnOLW0WL # MXVqxXpxDgeSXGhcgmYRoUqAcjm8bw3hTZ6OMOdewYgn39NxSo0IA+8jcyYjLq3C # akDMDzP0M44jIU6vHg8xrhB2F6/n+AhrK2I1vYmp5viuO3pAyGS3l/XSgz6Gaq+f # F6xBJNgsok1kZPIehSBxCw== # SIG # End signature block |