Src/Public/Public.ps1
<#
.SYNOPSIS Clears cached Virtual Engine Evergreen package files. .DESCRIPTION Removes all Evergreen package installer and metadata files from the '%ALLUSERSPROFILE%\VirtualEngine\Evergreen' directory. .EXAMPLE Clear-EvergreenPackageCache Removes all cached Evergreen packages and package metadata. #> function Clear-EvergreenPackageCache { [CmdletBinding(SupportsShouldProcess)] param ( ## Force cached file removal. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Force ) process { Remove-Item -Path "$cachedMetadataPath\*" -Exclude 'AppDeployToolkit*' -Force:$Force -Recurse } } <# .SYNOPSIS Searches for available Virtual Engine Evergreen packages. .DESCRIPTION Queries the Virtual Engine Evergreen API for one or more available packages. .EXAMPLE Find-EvergreenPackage -Name 7-Zip-x64 -SubscriptionKey abcdef1234567890 Name : 7-Zip-x64 Version : 19.0 Platform : Intune Uri : https://../7-Zip-x64/7-Zip-x64.19.0.intunewin?sv=2019-02-02 MetadataUri : https://../7-Zip-x64/7-Zip-x64.19.0.intunewin.metadata?sv=2019-02-02 Name : 7-Zip-x64 Version : 19.0 Platform : Zip Uri : https://../7-Zip-x64/7-Zip-x64.19.0.zip?sv=2019-02-02 MetadataUri : Searches for the latest 7-Zip-x64 package. .EXAMPLE Find-EvergreenPackage -Name 7-Zip-x64 -Platform Zip Name : 7-Zip-x64 Version : 19.0 Platform : Zip Uri : https://../7-Zip-x64/7-Zip-x64.19.0.zip?sv=2019-02-02 MetadataUri : Searches for the latest 7-Zip-x64 archive package. .EXAMPLE Find-EvergreenPackage -Name Google-Chrome-Enterprise-x64 -Platform Intune -All Name : Google-Chrome-Enterprise-x64 Version : 81.0.4044.129 Platform : Intune Uri : https://../Google-Chrome-Enterprise-x64/Google-Chrome-Enterprise-x64.81.0.4044.129.intunewin?sv=2019-02-02 MetadataUri : https://../Google-Chrome-Enterprise-x64/Google-Chrome-Enterprise-x64.81.0.4044.129.intunewin.metadata?sv=2019-02-02 Name : Google-Chrome-Enterprise-x64 Version : 81.0.4044.138 Platform : Intune Uri : https://../Google-Chrome-Enterprise-x64/Google-Chrome-Enterprise-x64.81.0.4044.138.intunewin?sv=2019-02-02 MetadataUri : https://../Google-Chrome-Enterprise-x64/Google-Chrome-Enterprise-x64.81.0.4044.138.intunewin.metadata?sv=2019-02-02 Name : Google-Chrome-Enterprise-x64 Version : 81.0.4044.92 Platform : Intune Uri : https://../Google-Chrome-Enterprise-x64/Google-Chrome-Enterprise-x64.81.0.4044.92.intunewin?sv=2019-02-02 MetadataUri : https://../Google-Chrome-Enterprise-x64/Google-Chrome-Enterprise-x64.81.0.4044.92.intunewin.metadata?sv=2019-02-02 Name : Google-Chrome-Enterprise-x64 Version : 83.0.4103.61 Platform : Intune Uri : https://../Google-Chrome-Enterprise-x64/Google-Chrome-Enterprise-x64.83.0.4103.61.intunewin?sv=2019-02-02 MetadataUri : https://../Google-Chrome-Enterprise-x64/Google-Chrome-Enterprise-x64.83.0.4103.61.intunewin.metadata?sv=2019-02-02 Searches for all available Google-Chrome-Enterprise-x64 Intune packages. #> function Find-EvergreenPackage { [CmdletBinding()] [OutputType([System.Management.Automation.PSObject])] param ( ## Evergreen package(s) to search for. [Parameter(ValueFromPipelineByPropertyName)] [System.String[]] $Name, ## Virtual Engine Evergreen Api subscription key. [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [Alias('Key')] [System.String] $SubscriptionKey, ## Enumerate customer package repository. [Parameter(ValueFromPipelineByPropertyName)] [System.String] $CustomerCode, ## Evergreen package format. [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('Exe', 'Zip', 'Intune')] [System.String] $Platform, ## Return all available package versions. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $All, ## Include hidden (test) packages. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Hidden, ## Web proxy credential required to access Virtual Engine Evergreen Api. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $Credential ) process { $invokeEvergreenPackageApiParams = @{ SubscriptionKey = $SubscriptionKey Hidden = $Hidden All = $All } if ($PSBoundParameters.ContainsKey('CustomerCode')) { $invokeEvergreenPackageApiParams['CustomerCode'] = $CustomerCode } if ($PSBoundParameters.ContainsKey('Credential')) { $invokeEvergreenPackageApiParams['Credential'] = $Credential } if ($PSBoundParameters.ContainsKey('Platform')) { $invokeEvergreenPackageApiParams['Platform'] = $Platform } if ($PSBoundParameters.ContainsKey('Name')) { foreach ($packageName in $Name) { $invokeEvergreenPackageApiParams['Name'] = $packageName $packages = Invoke-EvergreenPackageApi @invokeEvergreenPackageApiParams ## Evergreen API returns an empty string if there are no matching packages if ($packages -is [System.String]) { Write-Error ($localized.CannotFindPackageError -f $packageName) } elseif (($packages -is [System.Array]) -and ($packages.Count -eq 0)) { Write-Error ($localized.CannotFindPackageError -f $packageName) } else { $packages | ForEach-Object { $package = [PSCustomObject] @{ Name = $_.Name Description = $_.Description Version = $_.Version -as [System.Version] Revision = $_.Revision -as [System.Int32] Platform = $_.Platform PublishDate = $_.Date Uri = $_.Uri MetadataUri = $_.MetadataUri IsCustom = $PSBoundParameters.ContainsKey('CustomerCode') } $package.PSObject.TypeNames.Insert(0, 'VirtualEngine.Evergreen.Package') Write-Output -InputObject $package } } } } else { $packages = Invoke-EvergreenPackageApi @invokeEvergreenPackageApiParams if (($packages -is [System.Object[]]) -and ($packages[0] -isnot [System.Management.Automation.PSObject])) { ## Deserialisation sometimes results in an [Object[]] with a nested [PSObject]s $packages[0] | ForEach-Object { $package = [PSCustomObject] @{ Name = $_.Name Description = $_.Description Version = $_.Version -as [System.Version] Revision = $_.Revision -as [System.Int32] Platform = $_.Platform PublishDate = $_.Date Uri = $_.Uri MetadataUri = $_.MetadataUri IsCustom = $PSBoundParameters.ContainsKey('CustomerCode') } $package.PSObject.TypeNames.Insert(0, 'VirtualEngine.Evergreen.Package') Write-Output -InputObject $package } } else { $packages | ForEach-Object { $package = [PSCustomObject] @{ Name = $_.Name Description = $_.Description Version = $_.Version -as [System.Version] Revision = $_.Revision -as [System.Int32] Platform = $_.Platform PublishDate = $_.Date Uri = $_.Uri MetadataUri = $_.MetadataUri IsCustom = $PSBoundParameters.ContainsKey('CustomerCode') } $package.PSObject.TypeNames.Insert(0, 'VirtualEngine.Evergreen.Package') Write-Output -InputObject $package } } } } } <# .SYNOPSIS Returns Virtual Engine Evergreen package metadata. .DESCRIPTION Returns the cached Virtual Engine Evergreen package metadata. Downloads package metadata to the local file system if necessary. #> function Get-EvergreenPackageCacheMetadata { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( ## Evergreen package name. [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.String] $Name, ## Evergreen Api subscription key. [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.String] $SubscriptionKey, ## Enumerate customer package repository. [Parameter(ValueFromPipelineByPropertyName)] [System.String] $CustomerCode, ## Web proxy credential required to access Virtual Engine Evergreen Api. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $Credential, ## Force refresh of cached package metadata. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Force ) process { Write-Verbose -Message ($localized.RetrievingPackageMetadata -f $Name) try { $destinationFilename = '{0}.psd1' -f $Name $destinationPath = Join-Path -Path $cachedMetadataPath -ChildPath $destinationFilename $metadataPath = (Get-Item -Path $destinationPath -ErrorAction SilentlyContinue).FullName if ($Force -or ($null -eq $metadataPath)) { $invokeEvergreenMetadataDownloadParams = @{ Name = $Name SubscriptionKey = $SubscriptionKey Force = $Force } if ($PSBoundParameters.ContainsKey('CustomerCode')) { $invokeEvergreenMetadataDownloadParams['CustomerCode'] = $CustomerCode } if ($PSBoundParameters.ContainsKey('Credential')) { $invokeEvergreenMetadataDownloadParams['Credential'] = $Credential } $metadataPath = (Invoke-EvergreenMetadataDownload @invokeEvergreenMetadataDownloadParams).FullName } else { Write-Verbose -Message ($localized.UsingCachedPackage -f $metadataPath) } return Import-PowerShellDataFile -Path $metadataPath } catch { Write-Error $_ } } } <# .SYNOPSIS Installs one or more Virtual Engine Evergreen package(s). .DESCRIPTION Downloads Virtual Engine Evergreen packages to the local file system and intitiates the install. .EXAMPLE Install-EvergreenPackage -Name 7-Zip-x64 -SubscriptionKey abcdef1234567890 Downloads and installs the latest '7-Zip-x64' Evergreen package. .EXAMPLE Install-EvergreenPackage -Name 7-Zip-x64 -SubscriptionKey abcdef1234567890 -Verify Downloads, installs the latest '7-Zip-x64' Evergreen package and tests the installation is detected successfully. Throws an error when no Intune detection rule has been defined or defined Intune detection rule(s) fail. .EXAMPLE Install-EvergreenPackage -Name 7-Zip-x64 -SubscriptionKey abcdef1234567890 -Verify -IgnoreEmptyDetectionRule Downloads, installs the latest '7-Zip-x64' Evergreen package and tests the installation is detected successfully. Does not throw an error when no Intune detection rule has been defined. #> function Install-EvergreenPackage { [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Default')] param ( ## Evergreen package(s) to install. [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.String[]] $Name, ## Evergreen Api subscription key. [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.String] $SubscriptionKey, ## Run the application's detection rules after installation to verify the application is detected as installed. [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Verify')] [System.Management.Automation.SwitchParameter] $Verify, ## Enumerate customer package repository. [Parameter(ValueFromPipelineByPropertyName)] [System.String] $CustomerCode, ## Custom package installation arguments. [Parameter(ValueFromPipelineByPropertyName)] [System.String] $Arguments, ## Web proxy credential required to access Virtual Engine Evergreen Api. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $Credential, ## Force refresh of cached package metadata. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Force, ## Does not fail application verification if no detection rules are defined. [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Verify')] [System.Management.Automation.SwitchParameter] $IgnoreEmptyDetectionRule, ## Download retry attempts. [Parameter(ValueFromPipelineByPropertyName)] [System.Uint32] $Retry = 3 ) process { foreach ($packageName in $Name) { try { ## Cache metadata file to ensure any future Test-EvergreenPackage uses the ## detection rules used when initially installing the package $assertEvergreenPackageCacheMetadataParams = @{ Name = $packageName SubscriptionKey = $SubscriptionKey Force = $Force.ToBool() } if ($PSBoundParameters.ContainsKey('CustomerCode')) { $assertEvergreenPackageCacheMetadataParams['CustomerCode'] = $CustomerCode } if ($PSBoundParameters.ContainsKey('Credential')) { $assertEvergreenPackageCacheMetadataParams['Credential'] = $Credential } Assert-EvergreenPackageCacheMetadata @assertEvergreenPackageCacheMetadataParams ## Test to see if the package is already installed (assuming detection rules are defined) $isEvergreenPackageInstalled = Test-EvergreenPackage @assertEvergreenPackageCacheMetadataParams -IgnoreEmptyDetectionRule if ($null -eq $isEvergreenPackageInstalled) { ## Unable to determine installation state if ($Verify -and (-not $IgnoreEmptyDetectionRule)) { ## We have a package with no detection rules and we are not ignoring them throw ($localized.CannotFindPackageDetectionRuleError -f $packageName) } } elseif ($isEvergreenPackageInstalled -eq $true) { ## Already installed/present continue } ## Ensure the binaries are cached $assertEvergreenPackageDownloadParams = @{ Name = $packageName SubscriptionKey = $SubscriptionKey Force = $Force.ToBool() Retry = $Retry ErrorAction = 'Stop' } if ($PSBoundParameters.ContainsKey('CustomerCode')) { $assertEvergreenPackageDownloadParams['CustomerCode'] = $CustomerCode } if ($PSBoundParameters.ContainsKey('Credential')) { $assertEvergreenPackageDownloadParams['Credential'] = $Credential } $package = Assert-EvergreenPackageDownload @assertEvergreenPackageDownloadParams ## Start the installation $customArguments = @() if ($null -eq $package.TempPath) { ## We have a self-extracting .EXE $customArguments += '-y' } $customArguments += '-DeploymentType','Install' if ($PSBoundParameters.ContainsKey('Arguments')) { $customArguments += $Arguments } $startWaitProcessParams = @{ FilePath = $package.Path ArgumentList = $customArguments } Write-Verbose -Message ($localized.InstallingPackage -f $packageName) $null = Start-WaitProcess @startWaitProcessParams if ($Verify) { $assertEvergreenPackageCacheMetadataParams['IgnoreEmptyDetectionRule'] = $IgnoreEmptyDetectionRule.ToBool() $isEvergreenPackageInstalled = Test-EvergreenPackage @assertEvergreenPackageCacheMetadataParams if ($isEvergreenPackageInstalled -eq $false) { throw ($localized.PackageIsNotInstalled -f $packageName) } } } catch { Write-Error $_ } finally { ## Clear up temp files, folders and downloads if ($null -ne $package) { if (($null -ne $package.TempPath) -and (Test-Path -Path $package.TempPath)) { Write-Verbose -Message ($localized.RemoveTemporaryDirectory -f $package.TempPath) Remove-Item -Path $package.TempPath -Force -Recurse -WhatIf:$false } } } } } } <# .SYNOPSIS Saves Virtual Engine Evergreen packages. .DESCRIPTION Downloads Virtual Engine evergreen packages to the local file system. .EXAMPLE Save-EvergreenPackage -Name 7-Zip-x64 -SubscriptionKey abcdef1234567890 Directory: C:\Users\User\Documents Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 20/05/2020 12:56 2095648 7-Zip-x64.19.0.intunewin -a---- 20/05/2020 12:56 17519 7-Zip-x64.19.0.intunewin.metadata -a---- 20/05/2020 12:56 2056278 7-Zip-x64.19.0.zip Saves all available formats of the latest 7-Zip-x64 packages to the current path. .EXAMPLE Save-EvergreenPackage -Name 7-Zip-x64,KeePass -SubscriptionKey abcdef1234567890 -Platform Zip -Path ~\Downloads Directory: C:\Users\User\Downloads Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 20/05/2020 13:41 2056278 7-Zip-x64.19.0.zip -a---- 20/05/2020 13:41 3533503 KeePass.2.45.zip Saves the latest 7-Zip-x64 and KeePass archive packages to the user's Downloads directory. #> function Save-EvergreenPackage { [CmdletBinding()] [OutputType([System.IO.FileInfo])] param ( ## Evergreen package name. [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.String[]] $Name, ## Evergreen Api subscription key. [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.String] $SubscriptionKey, ## Enumerate customer package repository. [Parameter(ValueFromPipelineByPropertyName)] [System.String] $CustomerCode, ## Evergreen package format. [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('Exe', 'Zip', 'Intune')] [System.String] $Platform, ## Return all available package versions. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $All, ## File path to save the packages. [Parameter(ValueFromPipelineByPropertyName)] [System.String] $Path = (Get-Location -PSProvider Filesystem).Path, ## Web proxy credential required to access Virtual Engine Evergreen Api. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $Credential, ## Overwrite any existing file(s). [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Force, ## Download retry attempts. [Parameter(ValueFromPipelineByPropertyName)] [System.Uint32] $Retry = 3, ## Save package metadata. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Metadata ) begin { if (-not (Test-Path -Path $Path -PathType Container)) { throw ($localized.InvalidDestinationPathError -f $Path) } } process { $null = $PSBoundParameters.Remove('Path') $null = $PSBoundParameters.Remove('Force') $null = $PSBoundParameters.Remove('Metadata') $null = $PSBoundParameters.Remove('Retry') $availablePackages = Find-EvergreenPackage @PSBoundParameters foreach ($package in $availablePackages) { if ($Metadata) { $destinationPath = Join-Path -Path $Path -ChildPath ($package.MetadataUri -as [System.Uri]).AbsolutePath.Split('/')[-1] } else { $destinationPath = Join-Path -Path $Path -ChildPath ($package.Uri -as [System.Uri]).AbsolutePath.Split('/')[-1] } if (-not $Force -and (Test-Path -Path $destinationPath)) { Write-Verbose -Message ($localized.UsingCachedPackage -f $destinationPath) Get-Item -Path $destinationPath } else { if (-not $Metadata) { ## Download primary package $uri = $package.Uri -as [System.Uri] $filename = $uri.AbsolutePath.Split('/')[-1] Write-Verbose -Message ($localized.DownloadingActivity -f $filename) $invokeWebClientDownloadParams = @{ Uri = $package.Uri DestinationPath = $destinationPath Retry = $Retry } if ($PSBoundParameters.ContainsKey('Credential')) { $invokeWebClientDownloadParams['Credential'] = $Credential } Invoke-WebClientDownload @invokeWebClientDownloadParams } if ($Metadata -or ($Platform -eq 'Intune')) { $uri = $package.MetadataUri -as [System.Uri] $filename = $uri.AbsolutePath.Split('/')[-1] $destinationPath = Join-Path -Path $Path -ChildPath $filename if (-not $Force -and (Test-Path -Path $destinationPath)) { Write-Verbose -Message ($localized.UsingCachedPackage -f $destinationPath) Get-Item -Path $destinationPath } else { Write-Verbose -Message ($localized.DownloadingActivity -f $filename) $invokeWebClientDownloadParams = @{ Uri = $package.MetadataUri DestinationPath = $destinationPath Retry = $Retry } if ($PSBoundParameters.ContainsKey('Credential')) { $invokeWebClientDownloadParams['Credential'] = $Credential } Invoke-WebClientDownload @invokeWebClientDownloadParams } } } } } } <# .SYNOPSIS Tests whether one or more Virtual Engine Evergreen packages are installed. .DESCRIPTION Downloads Virtual Engine Evergreen package metadata to the local file system to test whether the package is installed (or not). .EXAMPLE Test-EvergreenPackage -Name 7-Zip-x64 -SubscriptionKey abcdef1234567890 False Downloads and latest '7-Zip-x64' Evergreen package metadata and tests wheter the package is installed. #> function Test-EvergreenPackage { [CmdletBinding(DefaultParameterSetName = 'Default')] [OutputType([System.Boolean])] param ( ## Evergreen package name. [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Default')] [System.String[]] $Name, ## Evergreen Api subscription key. [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Default')] [System.String] $SubscriptionKey, ## Enumerate customer package repository. [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Default')] [System.String] $CustomerCode, ## Evergreen package metadata file path. [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Metadata')] [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformationAttribute()] [System.Collections.Hashtable] $Metadata, ## Test all applications in the local cache. [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Cached')] [System.Management.Automation.SwitchParameter] $Cached, ## Web proxy credential required to access Virtual Engine Evergreen Api. [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Default')] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $Credential, ## Does not fail application test if no detection rules are defined. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $IgnoreEmptyDetectionRule, ## Force refresh of cached package metadata. [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Default')] [System.Management.Automation.SwitchParameter] $Force ) process { $metadataCollection = @() if ($PSCmdlet.ParameterSetName -eq 'Cached') { $cachedMetadataItems = Get-ChildItem -Path $cachedMetadataPath -Filter '*.psd1' foreach ($cachedMetadataItem in $cachedMetadataItems) { $metadataCollection += Import-PowerShellDataFile -Path $cachedMetadataItem.FullName } } elseif ($PSCmdlet.ParameterSetName -eq 'Default') { foreach ($packageName in $Name) { $getEvergreenPackageCacheMetadataParams = @{ Name = $packageName SubscriptionKey = $SubscriptionKey Force = $Force.ToBool() } if ($PSBoundParameters.ContainsKey('CustomerCode')) { $getEvergreenPackageCacheMetadataParams['CustomerCode'] = $CustomerCode } if ($PSBoundParameters.ContainsKey('Credential')) { $getEvergreenPackageCacheMetadataParams['Credential'] = $Credential } $metadataCollection += Get-EvergreenPackageCacheMetadata @getEvergreenPackageCacheMetadataParams } } else { $metadataCollection += $Metadata } $isCompliant = $true foreach ($metadataItem in $metadataCollection) { $packageName = Get-PackageMetadataPackageIdentifier -Metadata $metadataItem Write-Verbose -Message ($localized.TestingPackage -f $packageName) $testPackageDetectionRuleParams = @{ Metadata = $metadataItem PackageName = $packageName IgnoreEmptyDetectionRule = $IgnoreEmptyDetectionRule.ToBool() } $isInstalled = Test-PackageDetectionRule @testPackageDetectionRuleParams if (($null -eq $isInstalled) -and ($IgnoreEmptyDetectionRule -eq $true)) { Write-Warning -Message ($localized.UnableToDetermineInstallStateWarning -f $packageName) $isCompliant = $null } elseif ($isInstalled -eq $true) { Write-Verbose -Message ($localized.PackageIsInstalled -f $packageName) } else { Write-Verbose -Message ($localized.PackageIsNotInstalled -f $packageName) $isCompliant = $false } } return $isCompliant } } <# .SYNOPSIS Uninstalls one or more Virtual Engine Evergreen package(s). .DESCRIPTION Downloads Virtual Engine Evergreen packages to the local file system and intitiates the uninstall. .EXAMPLE Uninstall-EvergreenPackage -Name 7-Zip-x64 -SubscriptionKey abcdef1234567890 Downloads and uninstalls the '7-Zip-x64' package. .EXAMPLE Uninstall-EvergreenPackage -Name 7-Zip-x64 -SubscriptionKey abcdef1234567890 -Verify Uninstalls the cached '7-Zip-x64' Evergreen package and tests the installation is no longer detected successfully. Throws an error when no Intune detection rule has been defined or defined Intune detection rule(s) pass. .EXAMPLE Uninstall-EvergreenPackage -Name 7-Zip-x64 -SubscriptionKey abcdef1234567890 -Verify -IgnoreEmptyDetectionRule Uninstalls the cached '7-Zip-x64' Evergreen package and tests the installation is no longer detected successfully. Does not throw an error when no Intune detection rule has been defined. #> function Uninstall-EvergreenPackage { [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Default')] param ( ## Evergreen package(s) to uninstall. [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.String[]] $Name, ## Evergreen Api subscription key. [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.String] $SubscriptionKey, ## Run the application's detection rules after installation to verify the application is detected as installed. [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Verify')] [System.Management.Automation.SwitchParameter] $Verify, ## Enumerate customer package repository. [Parameter(ValueFromPipelineByPropertyName)] [System.String] $CustomerCode, ## Custom package installation arguments. [Parameter(ValueFromPipelineByPropertyName)] [System.String] $Arguments, ## Web proxy credential required to access Virtual Engine Evergreen Api. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $Credential, ## Force refresh of cached package metadata. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Force, ## Does not fail application verification if no detection rules are defined. [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Verify')] [System.Management.Automation.SwitchParameter] $IgnoreEmptyDetectionRule, ## Download retry attempts. [Parameter(ValueFromPipelineByPropertyName)] [System.Uint32] $Retry = 3, ## Cached files are removed after uninstall if the process exit code matches/is successful. [Parameter(ValueFromPipelineByPropertyName)] [System.UInt32[]] $ValidExitCodes = @(0, 3010) ) process { foreach ($packageName in $Name) { try { ## Cache metadata file to ensure any future Test-EvergreenPackage uses the ## detection rules used when initially installing the package $assertEvergreenPackageCacheMetadataParams = @{ Name = $packageName SubscriptionKey = $SubscriptionKey Force = $Force.ToBool() } if ($PSBoundParameters.ContainsKey('CustomerCode')) { $assertEvergreenPackageCacheMetadataParams['CustomerCode'] = $CustomerCode } if ($PSBoundParameters.ContainsKey('Credential')) { $assertEvergreenPackageCacheMetadataParams['Credential'] = $Credential } Assert-EvergreenPackageCacheMetadata @assertEvergreenPackageCacheMetadataParams ## Test to see if the package is already installed (assuming detection rules are defined) $isEvergreenPackageInstalled = Test-EvergreenPackage @assertEvergreenPackageCacheMetadataParams -IgnoreEmptyDetectionRule if ($null -eq $isEvergreenPackageInstalled) { ## Unable to determine installation state if ($Verify -and (-not $IgnoreEmptyDetectionRule)) { ## We have a package with no detection rules and we are not ignoring them throw ($localized.CannotFindPackageDetectionRuleError -f $packageName) } } elseif ($isEvergreenPackageInstalled -eq $false) { ## Already uninstalled/not present continue } ## Ensure the binaries are cached $assertEvergreenPackageDownloadParams = @{ Name = $packageName SubscriptionKey = $SubscriptionKey Force = $Force.ToBool() Retry = $Retry ErrorAction = 'Stop' } if ($PSBoundParameters.ContainsKey('CustomerCode')) { $assertEvergreenPackageDownloadParams['CustomerCode'] = $CustomerCode } if ($PSBoundParameters.ContainsKey('Credential')) { $assertEvergreenPackageDownloadParams['Credential'] = $Credential } $package = Assert-EvergreenPackageDownload @assertEvergreenPackageDownloadParams ## Start the uninstallation $customArguments = @() if ($null -eq $package.TempPath) { ## We have a self-extracting .EXE $customArguments += '-y' } $customArguments += '-DeploymentType','Uninstall' if ($PSBoundParameters.ContainsKey('Arguments')) { $customArguments += $Arguments } $startWaitProcessParams = @{ FilePath = $package.Path ArgumentList = $customArguments } Write-Verbose -Message ($localized.UninstallingPackage -f $package.Name) $exitCode = Start-WaitProcess @startWaitProcessParams if ($Verify) { $assertEvergreenPackageCacheMetadataParams['IgnoreEmptyDetectionRule'] = $IgnoreEmptyDetectionRule.ToBool() $isEvergreenPackageInstalled = Test-EvergreenPackage @assertEvergreenPackageCacheMetadataParams if ($isEvergreenPackageInstalled -eq $true) { throw ($localized.PackageIsInstalled -f $packageName) } } if ($exitCode -in $ValidExitCodes) { ## Remove any cached metadata files to ensure any future Test-EvergreenPackage ## detection rules are downloaded again $assertEvergreenPackageCacheMetadataParams = @{ Name = $packageName SubscriptionKey = $SubscriptionKey Force = $true NotPresent = $true } Assert-EvergreenPackageCacheMetadata @assertEvergreenPackageCacheMetadataParams } } catch { Write-Error $_ } finally { ## Clear up temp files, folders and downloads if ($null -ne $package) { if (($null -ne $package.TempPath) -and (Test-Path -Path $package.TempPath)) { Write-Verbose -Message ($localized.RemoveTemporaryDirectory -f $package.TempPath) Remove-Item -Path $package.TempPath -Force -Recurse -WhatIf:$false } } } } } } # SIG # Begin signature block # MIIuwAYJKoZIhvcNAQcCoIIusTCCLq0CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBHEWWdMwA+ydvg # zImSnYq8UPnKFTZxZmhfHcFRnEyw8qCCE6QwggWQMIIDeKADAgECAhAFmxtXno4h # MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV # BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z # ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ # bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0 # IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB # AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z # G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ # anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s # Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL # 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb # BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3 # JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c # AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx # YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0 # viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL # T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud # EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf # Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk # aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS # PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK # 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB # cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp # 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg # dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri # RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7 # 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5 # nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3 # i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H # EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G # CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ # bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0 # IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla # MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE # AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz # ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C # 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce # 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da # E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T # SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA # FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh # D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM # 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z # 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05 # huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY # mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP # /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T # AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD # VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG # A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY # aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj # ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV # HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU # cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN # BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry # sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL # IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf # Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh # OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh # dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV # 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j # wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH # Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC # XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l # /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW # eE4wggdYMIIFQKADAgECAhAIfHT3o/FeY5ksO94AUhTmMA0GCSqGSIb3DQEBCwUA # MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE # AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz # ODQgMjAyMSBDQTEwHhcNMjMxMDE4MDAwMDAwWhcNMjYxMjE2MjM1OTU5WjBgMQsw # CQYDVQQGEwJHQjEPMA0GA1UEBxMGTG9uZG9uMR8wHQYDVQQKExZWaXJ0dWFsIEVu # Z2luZSBMaW1pdGVkMR8wHQYDVQQDExZWaXJ0dWFsIEVuZ2luZSBMaW1pdGVkMIIC # IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtyhrsCMi6pgLcX5sWY7I09dO # WKweRHfDwW5AN6ffgLCYO9dqWWxvqu95FqnNVRyt1VNzEl3TevKVhRE0GGdirei3 # VqnFFjLDwD2jHhGY8qoSYyfffj/WYq2DkvNI62C3gUwSeP3FeqKRalc2c3V2v4jh # yEYhrgG3nfnWQ/Oq2xzuiCqHy1E4U+IKKDtrXls4JX2Z4J/uAHZIAyKfrcTRQOhZ # R4ZS1cQkeSBU9Urx578rOmxL0si0GAoaYQC49W7OimRelbahxZw/R+f5ch+C1ycU # CpeXLg+bFhpa0+EXnkGidlILZbJiZJn7qvMQTZgipQKZ8nhX3rtJLqTeodPWzcCk # tXQUy0q5fxhR3e6Ls7XQesq/G2yMcCMTCd6eltze0OgabvL6Xkhir5lATHaJtnmw # FlcKzRr1YXK1k1D84hWfKSAdUg8T1O72ztimbqFLg6WoC8M2qqsHcm2DOc9hM3i2 # CWRpegikitRvZ9i1wkA9arGh7+a7UD+nLh2hnGmO06wONLNqABOEn4JOVnNrQ1gY # eDeH9FDx7IYuAvMsfXG9Bo+I97TR2VfwDAx+ccR+UQLON3aQyFZ3BefYnvUu0gUR # ikEAnAS4Jnc3BHizgb0voz0iWRDjFoTTmCmrInCVDGc+5KMy0xyoUwdQvYvRGAWB # 61OCWnXBXbAEPniTZ80CAwEAAaOCAgMwggH/MB8GA1UdIwQYMBaAFGg34Ou2O/hf # EYb7/mF7CIhl9E5CMB0GA1UdDgQWBBRuAv58K4EDYLmb7WNcxt5+r4NfnzA+BgNV # HSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2lj # ZXJ0LmNvbS9DUFMwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMD # MIG1BgNVHR8Ega0wgaowU6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9E # aWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEu # Y3JsMFOgUaBPhk1odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz # dGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCBlAYIKwYB # BQUHAQEEgYcwgYQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNv # bTBcBggrBgEFBQcwAoZQaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lD # ZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQw # CQYDVR0TBAIwADANBgkqhkiG9w0BAQsFAAOCAgEAnXMg6efkBrwLIvd1Xmuh0dam # 9FhUtDEj+P5SIqdP/U4veOv66NEQhBHLbW2Dvrdm6ec0HMj9b4e8pt4ylKFzHIPj # fpuRffHVR9JQSx8qpryN6pP49DfCkAYeZGqjY3pGRzd/xQ0cfwcuYbUF+vwVk7tj # q8c93VHCM0rb5M4N2hD1Ze1pvZxtaf9QnFKFzgXZjr02K6bswQc2+n5jFCp7zV1f # KTstyb68rhSJBWKK1tMeFk6a6HXr5buTD3skluC0oyPmD7yAd97r2owjDMEveEso # kADP/z7XQk7wqbwbpi4W6Uju2qHK/9UUsVRF5KTVEAIzVw2V1Aq/Jh3JuSV7b7C1 # 4CghNekltBb+w7YVp8/IFcj7axqnpNQ/+f7RVc3A5hyjV+MkoSwn8Sg7a7hn6SzX # jec/TfRVvWCmG94MQHko+6206uIXrZnmQ6UQYFyOHRlyKDEozzkZhIcVlsZloUjL # 3FZ5V/l8TIIzbc3bkEnu4iByksNvRxI6c5264OLauYlWv50ZUPwXmZ9gX8bs3BqZ # avbGUrOW2PIjtWhtvs4zHhMBCoU1Z0OMvXcF9rUDqefmVCZK46xz3DGKVkDQnxY6 # UWQ3GL60/lEzju4YS99LJVQks2UGmP6LAzlEZ1dnGqi1aQ51OidCYEs39B75PsvO # By2iAR8pBVi/byWBypExghpyMIIabgIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYD # VQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBH # NCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEzODQgMjAyMSBDQTECEAh8dPej8V5j # mSw73gBSFOYwDQYJYIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAA # oQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4w # DAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgDzBLT0pqIBjtUdDMwbxb68nU # hKF1D006llMzDNhoYc4wDQYJKoZIhvcNAQEBBQAEggIATItlTEevgW2ryuigWyfS # t5xir7AWJLNCHUXiJQl0JNf4yDewz/fTdrk5wcMRGHXCUH+4247KyMYKVSBgDRbN # 3ddqxZuILyW9M91wqaeagRAU7VJ53U4l4GPX7VeALYmhTG0JPotqvHrGCXLhZKvB # n3FvLGdYcDVIAo2jy3UjseYcs7EFKXDLg63FguohjIcXtEdxAQhCiwOlquFBPjPP # RYgOiZvxbaYBOkTpT7oojLhrzZSmRSTSQWH/FPxTn2srU/X3uEGLKvhXXgq+kRvM # eIrJ+MsHNMCIU7AoZ05l9U1q2uIJ7vlHj4ZjzuB4AZBwBETy+ltPHGbBUyXLGbUW # RdNnwiYRCX7qjpPB2CkmZJ2BTFf32K31nc0leJ3I3KWZA80Flp8nKC3FOXScedO8 # 3vQEh5tozbk5n4DUnB72Be3JsSdqtt1eUdva6naIPa1E6PoVgMTvXaU596TpB1BN # sxa7mwZE68z1wauz7eF03ssDdBmiak/5yyIXAUGTV8vBheEWsq3qMTidyxgkQeMm # eJP88lbxO/72aIMa4EqfnHDAwdxSzmMi9Q27nx0w2wTPrhx+L2Q1BopcTUaKIJDd # dsr7UUKWqNEbyIcUp7i7MnkmqIi6CD1ZbLS/L3+vlvt79/5qp9NQptTLrXsEdlcC # FvWrFtFUhwaTaCwymKtcwQChghc/MIIXOwYKKwYBBAGCNwMDATGCFyswghcnBgkq # hkiG9w0BBwKgghcYMIIXFAIBAzEPMA0GCWCGSAFlAwQCAQUAMHcGCyqGSIb3DQEJ # EAEEoGgEZjBkAgEBBglghkgBhv1sBwEwMTANBglghkgBZQMEAgEFAAQgWDz3LQ7v # OSlphqKH5il7amBBjq8567vSMhLnZ7F481QCEEUAfV+utSmIDxi99GB+cbQYDzIw # MjMxMjEzMTIwODMwWqCCEwkwggbCMIIEqqADAgECAhAFRK/zlJ0IOaa/2z9f5WEW # MA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2Vy # dCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNI # QTI1NiBUaW1lU3RhbXBpbmcgQ0EwHhcNMjMwNzE0MDAwMDAwWhcNMzQxMDEzMjM1 # OTU5WjBIMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xIDAe # BgNVBAMTF0RpZ2lDZXJ0IFRpbWVzdGFtcCAyMDIzMIICIjANBgkqhkiG9w0BAQEF # AAOCAg8AMIICCgKCAgEAo1NFhx2DjlusPlSzI+DPn9fl0uddoQ4J3C9Io5d6Oyqc # Z9xiFVjBqZMRp82qsmrdECmKHmJjadNYnDVxvzqX65RQjxwg6seaOy+WZuNp52n+ # W8PWKyAcwZeUtKVQgfLPywemMGjKg0La/H8JJJSkghraarrYO8pd3hkYhftF6g1h # bJ3+cV7EBpo88MUueQ8bZlLjyNY+X9pD04T10Mf2SC1eRXWWdf7dEKEbg8G45lKV # tUfXeCk5a+B4WZfjRCtK1ZXO7wgX6oJkTf8j48qG7rSkIWRw69XloNpjsy7pBe6q # 9iT1HbybHLK3X9/w7nZ9MZllR1WdSiQvrCuXvp/k/XtzPjLuUjT71Lvr1KAsNJvj # 3m5kGQc3AZEPHLVRzapMZoOIaGK7vEEbeBlt5NkP4FhB+9ixLOFRr7StFQYU6mII # E9NpHnxkTZ0P387RXoyqq1AVybPKvNfEO2hEo6U7Qv1zfe7dCv95NBB+plwKWEwA # PoVpdceDZNZ1zY8SdlalJPrXxGshuugfNJgvOuprAbD3+yqG7HtSOKmYCaFxsmxx # rz64b5bV4RAT/mFHCoz+8LbH1cfebCTwv0KCyqBxPZySkwS0aXAnDU+3tTbRyV8I # pHCj7ArxES5k4MsiK8rxKBMhSVF+BmbTO77665E42FEHypS34lCh8zrTioPLQHsC # AwEAAaOCAYswggGHMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYGA1Ud # JQEB/wQMMAoGCCsGAQUFBwMIMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG # /WwHATAfBgNVHSMEGDAWgBS6FtltTYUvcyl2mi91jGogj57IbzAdBgNVHQ4EFgQU # pbbvE+fvzdBkodVWqWUxo97V40kwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDovL2Ny # bDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0UlNBNDA5NlNIQTI1NlRp # bWVTdGFtcGluZ0NBLmNybDCBkAYIKwYBBQUHAQEEgYMwgYAwJAYIKwYBBQUHMAGG # GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBYBggrBgEFBQcwAoZMaHR0cDovL2Nh # Y2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0UlNBNDA5NlNIQTI1 # NlRpbWVTdGFtcGluZ0NBLmNydDANBgkqhkiG9w0BAQsFAAOCAgEAgRrW3qCptZgX # vHCNT4o8aJzYJf/LLOTN6l0ikuyMIgKpuM+AqNnn48XtJoKKcS8Y3U623mzX4WCc # K+3tPUiOuGu6fF29wmE3aEl3o+uQqhLXJ4Xzjh6S2sJAOJ9dyKAuJXglnSoFeoQp # mLZXeY/bJlYrsPOnvTcM2Jh2T1a5UsK2nTipgedtQVyMadG5K8TGe8+c+njikxp2 # oml101DkRBK+IA2eqUTQ+OVJdwhaIcW0z5iVGlS6ubzBaRm6zxbygzc0brBBJt3e # WpdPM43UjXd9dUWhpVgmagNF3tlQtVCMr1a9TMXhRsUo063nQwBw3syYnhmJA+rU # kTfvTVLzyWAhxFZH7doRS4wyw4jmWOK22z75X7BC1o/jF5HRqsBV44a/rCcsQdCa # M0qoNtS5cpZ+l3k4SF/Kwtw9Mt911jZnWon49qfH5U81PAC9vpwqbHkB3NpE5jre # ODsHXjlY9HxzMVWggBHLFAx+rrz+pOt5Zapo1iLKO+uagjVXKBbLafIymrLS2Dq4 # sUaGa7oX/cR3bBVsrquvczroSUa31X/MtjjA2Owc9bahuEMs305MfR5ocMB3CtQC # 4Fxguyj/OOVSWtasFyIjTvTs0xf7UGv/B3cfcZdEQcm4RtNsMnxYL2dHZeUbc7aZ # +WssBkbvQR7w8F/g29mtkIBEr4AQQYowggauMIIElqADAgECAhAHNje3JFR82Ees # /ShmKl5bMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxE # aWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMT # GERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMjAzMjMwMDAwMDBaFw0zNzAz # MjIyMzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5j # LjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBU # aW1lU3RhbXBpbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDG # hjUGSbPBPXJJUVXHJQPE8pE3qZdRodbSg9GeTKJtoLDMg/la9hGhRBVCX6SI82j6 # ffOciQt/nR+eDzMfUBMLJnOWbfhXqAJ9/UO0hNoR8XOxs+4rgISKIhjf69o9xBd/ # qxkrPkLcZ47qUT3w1lbU5ygt69OxtXXnHwZljZQp09nsad/ZkIdGAHvbREGJ3Hxq # V3rwN3mfXazL6IRktFLydkf3YYMZ3V+0VAshaG43IbtArF+y3kp9zvU5EmfvDqVj # bOSmxR3NNg1c1eYbqMFkdECnwHLFuk4fsbVYTXn+149zk6wsOeKlSNbwsDETqVcp # licu9Yemj052FVUmcJgmf6AaRyBD40NjgHt1biclkJg6OBGz9vae5jtb7IHeIhTZ # girHkr+g3uM+onP65x9abJTyUpURK1h0QCirc0PO30qhHGs4xSnzyqqWc0Jon7ZG # s506o9UD4L/wojzKQtwYSH8UNM/STKvvmz3+DrhkKvp1KCRB7UK/BZxmSVJQ9FHz # NklNiyDSLFc1eSuo80VgvCONWPfcYd6T/jnA+bIwpUzX6ZhKWD7TA4j+s4/TXkt2 # ElGTyYwMO1uKIqjBJgj5FBASA31fI7tk42PgpuE+9sJ0sj8eCXbsq11GdeJgo1gJ # ASgADoRU7s7pXcheMBK9Rp6103a50g5rmQzSM7TNsQIDAQABo4IBXTCCAVkwEgYD # VR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUuhbZbU2FL3MpdpovdYxqII+eyG8w # HwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGG # MBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcw # AYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8v # Y2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBD # BgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNl # cnRUcnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgB # hv1sBwEwDQYJKoZIhvcNAQELBQADggIBAH1ZjsCTtm+YqUQiAX5m1tghQuGwGC4Q # TRPPMFPOvxj7x1Bd4ksp+3CKDaopafxpwc8dB+k+YMjYC+VcW9dth/qEICU0MWfN # thKWb8RQTGIdDAiCqBa9qVbPFXONASIlzpVpP0d3+3J0FNf/q0+KLHqrhc1DX+1g # tqpPkWaeLJ7giqzl/Yy8ZCaHbJK9nXzQcAp876i8dU+6WvepELJd6f8oVInw1Ypx # dmXazPByoyP6wCeCRK6ZJxurJB4mwbfeKuv2nrF5mYGjVoarCkXJ38SNoOeY+/um # nXKvxMfBwWpx2cYTgAnEtp/Nh4cku0+jSbl3ZpHxcpzpSwJSpzd+k1OsOx0ISQ+U # zTl63f8lY5knLD0/a6fxZsNBzU+2QJshIUDQtxMkzdwdeDrknq3lNHGS1yZr5Dhz # q6YBT70/O3itTK37xJV77QpfMzmHQXh6OOmc4d0j/R0o08f56PGYX/sr2H7yRp11 # LB4nLCbbbxV7HhmLNriT1ObyF5lZynDwN7+YAN8gFk8n+2BnFqFmut1VwDophrCY # oCvtlUG3OtUVmDG0YgkPCr2B2RP+v6TR81fZvAT6gt4y3wSJ8ADNXcL50CN/AAvk # dgIm2fBldkKmKYcJRyvmfxqkhQ/8mJb2VVQrH4D6wPIOK+XW+6kvRBVK5xMOHds3 # OBqhK/bt1nz8MIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv21DiCEAYWjANBgkqhkiG # 9w0BAQwFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkw # FwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1 # cmVkIElEIFJvb3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcNMzExMTA5MjM1OTU5WjBi # MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 # d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg # RzQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAi # MGkz7MKnJS7JIT3yithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnny # yhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE # 5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm # 7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5 # w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsD # dV14Ztk6MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1Z # XUJ2h4mXaXpI8OCiEhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS0 # 0mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hk # pjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m8 # 00ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+i # sX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo4IBOjCCATYwDwYDVR0TAQH/BAUwAwEB # /zAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wHwYDVR0jBBgwFoAUReui # r/SSy4IxLVGLp6chnfNtyA8wDgYDVR0PAQH/BAQDAgGGMHkGCCsGAQUFBwEBBG0w # azAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUF # BzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVk # SURSb290Q0EuY3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2lj # ZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwEQYDVR0gBAowCDAG # BgRVHSAAMA0GCSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXnOF+go3QbPbYW1/e/Vwe9 # mqyhhyzshV6pGrsi+IcaaVQi7aSId229GhT0E0p6Ly23OO/0/4C5+KH38nLeJLxS # A8hO0Cre+i1Wz/n096wwepqLsl7Uz9FDRJtDIeuWcqFItJnLnU+nBgMTdydE1Od/ # 6Fmo8L8vC6bp8jQ87PcDx4eo0kxAGTVGamlUsLihVo7spNU96LHc/RzY9HdaXFSM # b++hUD38dglohJ9vytsgjTVgHAIDyyCwrFigDkBjxZgiwbJZ9VVrzyerbHbObyMt # 9H5xaiNrIv8SuFQtJ37YOtnwtoeW/VvRXKwYw02fc7cBqZ9Xql4o4rmUMYIDdjCC # A3ICAQEwdzBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4x # OzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGlt # ZVN0YW1waW5nIENBAhAFRK/zlJ0IOaa/2z9f5WEWMA0GCWCGSAFlAwQCAQUAoIHR # MBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMjMx # MjEzMTIwODMwWjArBgsqhkiG9w0BCRACDDEcMBowGDAWBBRm8CsywsLJD4JdzqqK # ycZPGZzPQDAvBgkqhkiG9w0BCQQxIgQgREAytERzRfin1FwJU4LDUWvLHRKqoryh # 0697xeH/y7MwNwYLKoZIhvcNAQkQAi8xKDAmMCQwIgQg0vbkbe10IszR1EBXaEE2 # b4KK2lWarjMWr00amtQMeCgwDQYJKoZIhvcNAQEBBQAEggIATmOnslVX+04GwbN0 # /z/WB1sGwA8h9c8o1CJ08a+BJjsmcjaBNP57rFY7vxtc0r4u7DxyvFGY2fP69a99 # LyxONEBR+MGRk6bnbYVd7kPkqKz0C8zjsQxa5uzaNYTKLmGCb2w5Qjueunbj9V7K # 3f+qJL5YrDMSQiIA+//kcNjyRcjhZ8KLQVWn7UOtET4NKMTsOygcQmg5uirQd4ls # O2ZjaQokYPro4yTv362EDC12jy4S42vTjZD7APeJSSiXPf/+iM9pPfc4BiNnBpTm # wMq6s8IZY42lDe2tdgc8JZa9o9ms3tyEXRwIP4HhtSAboW2tuAhot0yDf7wz3doh # MevGRkxc0DTDcIGJvscNeE6oR6gx0c0aixA8PLuBpU/cwSGBqp5okUYp6OnlB+yo # ReR4RKgLpU3w6xYqvTSc4BK2hRHpU02X+EagbQWTtVe2oIY//Lx3cLa+5XcVTStT # U3NfpF2omWjijmSQJbJN0R82gnCr4Ay6HEPEMt0S1NDQipFPqqx/eDiw+K3i9lT2 # +SHj/A211C3syYm/wHaUq1NY3twirfif0N4zm5F+6nJvNdERexc5mAF4WQRvsrAW # Ze0gADgy3/wcZriZtQGUq30ubU/O/VQi0qClCEmFqnaq1TSeQCXsvQuMbtNicxzK # 1fTaVtnH4M46cTTjUxZOhbGD914= # SIG # End signature block |