Src/Private/Private.ps1
<#
.SYNOPSIS Ensures a directory path exists, creating it if necessary #> function Assert-Directory { [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [System.String] $Path ) process { if (Test-Path -Path $Path -PathType Leaf) { throw ($localized.SpecifiedPathIsNotEmptyError -f $Path) } elseif (-not (Test-Path -Path $Path)) { Write-Verbose -Message ($localized.CreatingPackageDirectory -f $Path) $parentPath = Split-Path -Path $Path -Parent $childPath = Split-Path -Path $Path -Leaf try { $null = New-Item -Path $parentPath -Name $childPath -ItemType Directory -Force } catch { Write-Error -ErrorRecord $_ -ErrorAction Stop } } } } <# .SYNOPSIS Ensures that package metadata file is (or isn't) cached locally. #> function Assert-EvergreenPackageCacheMetadata { [CmdletBinding()] param ( ## Evergreen package(s) metadata to cache [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [System.String] $Name, ## Evergreen Api subscription key [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Default')] [System.String] $SubscriptionKey, ## Enumerate customer package repository. [Parameter(ValueFromPipelineByPropertyName)] [System.String] $CustomerCode, ## Web proxy credential [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Default')] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $Credential, ## Force refresh of cached package metadata [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'Default')] [System.Management.Automation.SwitchParameter] $Force, [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $NotPresent ) process { $metadataFilename = '{0}.psd1' -f $Name $destinationPath = Join-Path -Path $cachedMetadataPath -ChildPath $metadataFilename $metadataItem = Get-Item -Path $destinationPath -ErrorAction SilentlyContinue if ($NotPresent) { if ($null -ne $metadataItem) { Write-Verbose -Message ($localized.RemovingCachedPackageMetadata -f $metadataItem.FullName) Remove-Item -Path $metadataItem.FullName -Force } } else { if ($Force -or ($null -eq $metadataItem)) { $invokeEvergreenMetadataDownloadParams = @{ Name = $Name SubscriptionKey = $SubscriptionKey Force = $Force } if ($PSBoundParameters.ContainsKey('CustomerCode')) { $invokeEvergreenMetadataDownloadParams['CustomerCode'] = $CustomerCode } if ($PSBoundParameters.ContainsKey('Credential')) { $invokeEvergreenMetadataDownloadParams['Credential'] = $Credential } $null = Invoke-EvergreenMetadataDownload @invokeEvergreenMetadataDownloadParams } } } } <# .SYNOPSIS Downloads an Evergreen package (if not already cached) and extracting any Zip packages. #> function Assert-EvergreenPackageDownload { [CmdletBinding()] [OutputType()] param ( [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.String] $Name, [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.String] $SubscriptionKey, ## Enumerate customer package repository. [Parameter(ValueFromPipelineByPropertyName)] [System.String] $CustomerCode, ## Web proxy credential [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $Credential, ## Refresh cached package metadata [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Force, ## Download retry attempts [Parameter(ValueFromPipelineByPropertyName)] [System.Uint32] $Retry = 3 ) process { Write-Verbose -Message ($localized.FindingPackage -f $packageName) $saveEvergreenPackageParams = @{ Name = $Name SubscriptionKey = $SubscriptionKey Path = $cachedMetadataPath Force = $Force Platform = 'Exe' Retry = $Retry } if ($PSBoundParameters.ContainsKey('CustomerCode')) { $saveEvergreenPackageParams['CustomerCode'] = $CustomerCode } if ($PSBoundParameters.ContainsKey('Credential')) { $saveEvergreenPackageParams['Credential'] = $Credential } $exePackagePath = Save-EvergreenPackage @saveEvergreenPackageParams -ErrorAction SilentlyContinue if ($null -ne $exePackagePath) { Write-Output -InputObject ([PSCustomObject] @{ Name = $Name Path = $exePackagePath TempPath = $null }) } else { $saveEvergreenPackageParams['Platform'] = 'Zip' Write-Verbose -Message ($localized.TryingZipPackageBackup -f $Name) $zipPackagePath = Save-EvergreenPackage @saveEvergreenPackageParams -ErrorAction Stop ## Extract Zip to temporary folder $tempDirectory = New-TempDirectory Write-Verbose -Message ($localized.ExtractingArchive -f $tempDirectory) Expand-Archive -Path $zipPackagePath -DestinationPath $tempDirectory $exePackagePath = Get-Item -Path (Join-Path $tempDirectory -ChildPath 'Deploy-Application.exe') Write-Output -InputObject ([PSCustomObject] @{ Name = $Name Path = $exePackagePath TempPath = $tempDirectory }) } } } <# .SYNOPSIS Returns file version from the specified path. .NOTES Required to test 'Test-PackageDetectionRuleFile' #> function Get-FileVersion { [CmdletBinding()] [OutputType([System.Version])] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.String] $Path ) process { [System.Diagnostics.FileVersionInfo]::GetVersionInfo($path).FileVersionRaw -as [System.Version] } } <# .SYNOPSIS Returns unique package identifier name from package metadata, e.g. 'Virtual-Engine-ACE' or 'Microsoft-Visual-Studio-Code-x64'. #> function Get-PackageMetadataPackageIdentifier { [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformationAttribute()] [System.Collections.Hashtable] $Metadata ) process { $properties = $Metadata.Properties $packageName = $properties.Name if (-not (Test-PackageExcludeArchitecture -Metadata $Metadata) -and $properties.Contains('Architecture')) { $packageName = '{0} {1}' -f $packageName, $properties.Architecture.ToLower() } if (-not (Test-PackageExcludePublisher -Metadata $metadata)) { $packageName = '{0} {1}' -f $properties.Publisher, $packageName } if (-not (Test-PackageExcludeStoreApp -Metadata $metadata)) { $packageName = '{0} UWP' -f $packageName } return $packageName.Replace(' ', '-') } } <# .SYNOPSIS Downloads an Evergreen package's metadata. #> function Invoke-EvergreenMetadataDownload { [CmdletBinding()] param ( ## Evergreen package(s) metadata to cache [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 [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 { ## Download metadata file Write-Verbose -Message ($localized.DownloadingPackageMetadata -f $Name) $invokeEvergreenPackageApiParams = @{ Name = $Name SubscriptionKey = $SubscriptionKey Platform = 'Exe' Force = $Force } if ($PSBoundParameters.ContainsKey('CustomerCode')) { $invokeEvergreenPackageApiParams['CustomerCode'] = $CustomerCode } if ($PSBoundParameters.ContainsKey('Credential')) { $invokeEvergreenPackageApiParams['Credential'] = $Credential } $package = Invoke-EvergreenPackageApi @invokeEvergreenPackageApiParams ## Evergreen API returns an empty string if there are no matching packages if ($package -is [System.String]) { ## We have no Exe package so try Zip as backup $invokeEvergreenPackageApiParams['Platform'] = 'Zip' Write-Verbose -Message ($localized.TryingZipPackageBackup -f $Name) $package = Invoke-EvergreenPackageApi @invokeEvergreenPackageApiParams } if ($package -is [System.String]) { throw ($localized.CannotFindPackageError -f $Name) } $invokeEvergreenPackageDownloadParams = @{ Path = $cachedMetadataPath Package = $package Metadata = $true Force = $Force } Invoke-EvergreenPackageDownload @invokeEvergreenPackageDownloadParams } } <# .SYNOPSIS Calls the Virtual Engine Evergreen Api. #> function Invoke-EvergreenPackageApi { [CmdletBinding()] param ( [Parameter(ValueFromPipelineByPropertyName)] [System.String] $Name, [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.String] $SubscriptionKey, ## Enumerate customer package repository. [Parameter(ValueFromPipelineByPropertyName)] [System.String] $CustomerCode, [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 [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $Credential, [Parameter(ValueFromRemainingArguments)] $RemainingArgs ) process { $uri = 'https://virtualengine.azure-api.net/evergreen/packages' $filters = @() if ($PSBoundParameters.ContainsKey('Name')) { $filters += 'name={0}' -f $Name } if ($PSBoundParameters.ContainsKey('CustomerCode')) { $filters += 'customercode={0}' -f $CustomerCode } if ($PSBoundParameters.ContainsKey('Platform')) { $filters += 'platform={0}' -f $Platform } if ($All) { $filters += 'all={0}' -f $All.ToBool().ToString().ToLower() } if ($Hidden) { $filters += 'hidden={0}' -f $Hidden.ToBool().ToString().ToLower() } if ($filters.Count -gt 0) { $filterString = [System.String]::Join('&', $filters) $uri = '{0}?{1}' -f $uri, $filterString } $invokeRestMethodParams = @{ Uri = $uri Headers = @{ 'Ocp-Apim-Subscription-Key' = $SubscriptionKey } Verbose = $false } if ($PSBoundParameters.ContainsKey('Credential')) { $invokeRestMethodParams['ProxyCredential'] = $Credential } Write-Verbose ($localized.InvokingEvergreenApi -f $uri) Invoke-RestMethod @invokeRestMethodParams } } <# .SYNOPSIS Downloads an Evergreen package if not already cached. #> function Invoke-EvergreenPackageDownload { [CmdletBinding()] param ( ## Evergreen package [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [System.Management.Automation.PSObject] $Package, ## Destination folder path [Parameter(Mandatory, ValueFromPipeline)] [System.String] $Path, ## Web proxy credential [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $Credential, ## Download from the MetadataUri property [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Metadata, ## Refresh cached package metadata [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $Force, ## Download retry attempts [Parameter(ValueFromPipelineByPropertyName)] [System.Uint32] $Retry = 3, [Parameter(ValueFromRemainingArguments)] $RemainingArgs ) process { $destinationFilename = ($Package.Uri -as [System.Uri]).AbsolutePath.Split('/')[-1] $uri = $Package.Uri if ($Metadata) { $destinationFilename = '{0}.psd1' -f $Package.Name $uri = $Package.MetadataUri } $destinationPath = Join-Path -Path $Path -ChildPath $destinationFilename if ($Force -or (-not (Test-Path -Path $destinationPath))) { $invokeWebClientDownloadParams = @{ DestinationPath = $destinationPath Uri = $uri Retry = $Retry } if ($PSBoundParameters.ContainsKey('Credential')) { $invokeWebClientDownloadParams['Credential'] = $Credential } Write-Verbose -Message ($localized.StartingDownload -f $destinationPath) Invoke-WebClientDownload @invokeWebClientDownloadParams } else { Write-Verbose -Message ($localized.ReturningCachedDownload -f $destinationPath) Get-Item -Path $destinationPath } } } function Invoke-WebClientDownload { <# .SYNOPSIS Downloads a (web) resource using System.Net.WebClient. .NOTES This solves issues when downloading resources using BITS under alternative credentials. .LINK https://github.com/VirtualEngine/Lability/blob/dev/Src/Private/Invoke-WebClientDownload.ps1 #> [CmdletBinding()] [OutputType([System.IO.FileInfo])] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.String] $DestinationPath, [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.String] $Uri, [Parameter(ValueFromPipelineByPropertyName)] [System.UInt32] $BufferSize = 64KB, ## Download retry attempts [Parameter(ValueFromPipelineByPropertyName)] [System.Uint32] $Retry = 3, [Parameter(ValueFromPipelineByPropertyName)] [AllowNull()] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $Credential ) process { for ($attempt = 1; $attempt -le $Retry; $attempt++) { try { [System.Net.WebClient] $webClient = New-Object -TypeName 'System.Net.WebClient' $webClient.Headers.Add('user-agent', $labDefaults.ModuleName) $webClient.Proxy = [System.Net.WebRequest]::GetSystemWebProxy() if (-not $webClient.Proxy.IsBypassed($Uri)) { $proxyInfo = $webClient.Proxy.GetProxy($Uri) if ($null -ne $proxyInfo.AbsoluteUri) { Write-Verbose -Message ($localized.UsingProxyServer -f $proxyInfo.AbsoluteUri) } } if ($Credential) { $webClient.Credentials = $Credential $webClient.Proxy.Credentials = $Credential } else { $webClient.UseDefaultCredentials = $true $webClient.Proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials } [System.IO.Stream] $inputStream = $webClient.OpenRead($Uri) [System.UInt64] $contentLength = $webClient.ResponseHeaders['Content-Length'] $path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($DestinationPath) [System.IO.Stream] $outputStream = [System.IO.File]::Create($path) [System.Byte[]] $buffer = New-Object -TypeName System.Byte[] -ArgumentList $BufferSize [System.UInt64] $bytesRead = 0 [System.UInt64] $totalBytes = 0 $writeProgessActivity = $localized.DownloadingActivity -f $Uri do { $iteration ++ $bytesRead = $inputStream.Read($buffer, 0, $buffer.Length) $totalBytes += $bytesRead $outputStream.Write($buffer, 0, $bytesRead) ## Avoid divide by zero if ($contentLength -gt 0) { if ($iteration % 30 -eq 0) { [System.Byte] $percentComplete = ($totalBytes / $contentLength) * 100 $writeProgressParams = @{ Activity = $writeProgessActivity PercentComplete = $percentComplete Status = $localized.DownloadStatus -f $totalBytes, $contentLength, $percentComplete } Write-Progress @writeProgressParams } } } while ($bytesRead -ne 0) $outputStream.Close() return (Get-Item -Path $path) } catch { $sleepSeconds = [System.Math]::Pow(5, $attempt) Write-Warning -Message ($localized.WebResourceDownloadFailedWarning -f $Uri, $sleepSeconds) Start-Sleep -Seconds $sleepSeconds } finally { if ($null -ne $writeProgressActivity) { Write-Progress -Activity $writeProgessActivity -Completed } if ($null -ne $outputStream) { $outputStream.Close() } if ($null -ne $inputStream) { $inputStream.Close() } if ($null -ne $webClient) { $webClient.Dispose() } } } ## if we get here we have failed all attempts throw ($localized.WebResourceDownloadFailedError -f $Uri) } } <# .SYNOPSIS Creates a new temporary directory. #> function New-TempDirectory { [Cmdletbinding()] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] param ( ) begin { if ($PSBoundParameters.ContainsKey('Debug')) { $DebugPreference = 'Continue' } } process { do { $path = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) } while ([System.IO.Directory]::Exists($path)) Write-Debug ("Generated temp directory path '{0}'" -f $path) return [System.IO.Directory]::CreateDirectory($path).FullName } } <# .SYNOPSIS Writes byte array ([Byte[]]) to a file. #> function Set-FileBytes { [CmdletBinding()] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns','')] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] [OutputType()] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Byte[]] $InputObject, [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.String] $Path ) process { if ($PSVersionTable['PSEdition'] -eq 'Core') { Set-Content -Path $Path -Value $InputObject -AsByteStream } else { Set-Content -Path $Path -Value $InputObject -Encoding Byte } } } <# .SYNOPSIS Starts and waits for a process to exit. #> function Start-WaitProcess { [CmdletBinding(SupportsShouldProcess)] [OutputType([System.Int32])] param ( # Path to process to start. [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [System.String] $FilePath, # Arguments (if any) to apply to the process. [Parameter()] [AllowNull()] [System.String[]] $ArgumentList, # Credential to start the process as. [Parameter()] [AllowNull()] [System.Management.Automation.PSCredential] [System.Management.Automation.CredentialAttribute()] $Credential, # Working directory [Parameter()] [ValidateNotNullOrEmpty()] [System.String] $WorkingDirectory = (Split-Path -Path $FilePath -Parent) ) process { $startProcessParams = @{ FilePath = $FilePath WorkingDirectory = $WorkingDirectory NoNewWindow = $true PassThru = $true } $displayParams = '<None>' if ($ArgumentList) { $displayParams = [System.String]::Join(' ', $ArgumentList) $startProcessParams['ArgumentList'] = $ArgumentList } Write-Verbose ($localized.StartingProcess -f $FilePath, $displayParams) if ($Credential) { Write-Verbose ($localized.StartingProcessAs -f $Credential.UserName); $startProcessParams['Credential'] = $Credential } $exitCode = 0 if ($PSCmdlet.ShouldProcess($FilePath, 'Start Process')) { $process = Start-Process @startProcessParams -ErrorAction Stop Write-Verbose ($localized.ProcessLaunched -f $process.Id) Wait-Process -InputObject $process $exitCode = [System.Convert]::ToInt32($process.ExitCode) Write-Verbose ($localized.ProcessExited -f $process.Id, $exitCode) } return $exitCode } } <# .SYNOPSIS Tests whether a package is installed using the package metadata Intune detection rules. #> function Test-PackageDetectionRule { [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Collections.Hashtable] [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformationAttribute()] $Metadata, ## Package name used for message output. [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [System.String] $PackageName, [Parameter(ValueFromPipelineByPropertyName)] [System.String] $Path = $PWD, ## Does not fail application test if no detection rules are defined. [Parameter(ValueFromPipelineByPropertyName)] [System.Management.Automation.SwitchParameter] $IgnoreEmptyDetectionRule ) process { if (($Metadata.Contains('Intune')) -and ($Metadata.Intune.Contains('DetectionRules'))) { foreach ($detectionRule in $Metadata.Intune.DetectionRules) { switch ($detectionRule.Type) { File { if (-not (Test-PackageDetectionRuleFile -DetectionRule $detectionRule)) { return $false } } Registry { if (-not (Test-PackageDetectionRuleRegistry -DetectionRule $detectionRule)) { return $false } } Msi { if (-not (Test-PackageDetectionRuleMsi -DetectionRule $detectionRule)) { return $false } } PowerShell { if (-not (Test-PackageDetectionRuleScript -DetectionRule $detectionRule -Path $Path)) { return $false } } Default { Write-Warning -Message ($localized.UnsupportedPackageDectectionRuleWarning -f $detectionRule.Type) } } } } else { if ($IgnoreEmptyDetectionRule) { ## Unable to determine installation state so return nothing Write-Verbose -Message ($localized.NoPackageDectectionRuleDefinedWarning -f $PackageName) return $null } else { throw ($localized.CannotFindPackageDetectionRuleError -f $PackageName) } } return $true } } <# .SYNOPSIS Tests a package metadata Intune file detection rule. #> function Test-PackageDetectionRuleFile { [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Collections.Hashtable] $DetectionRule ) process { $path = $DetectionRule.Path if (([System.Environment]::Is64BitOperatingSystem) -and ($DetectionRule.ContainsKey('Check32BitOn64System')) -and ($DetectionRule.Check32BitOn64System -eq $true)) { $path = $path -replace '%ProgramFiles%', '%ProgramFiles(x86)%' } $path = '{0}\{1}' -f [System.Environment]::ExpandEnvironmentVariables($path).TrimEnd('\'), $DetectionRule.FileOrFolderName switch ($DetectionRule.FileDetectionType) { NotConfigured { Write-Verbose -Message ($localized.EnumeratedFilePathNotConfigured -f $path) } Exists { Write-Verbose -Message ($localized.EnumeratingPathDoesExist -f $path) if (Test-Path -Path $path) { Write-Verbose -Message ($localized.EnumeratedPathDoesExist -f $path) return $true } else { Write-Verbose -Message ($localized.EnumeratedPathDoesNotExist -f $path) return $false } } DoesNotExist { Write-Verbose -Message ($localized.EnumeratingPathDoesNotExist -f $path) if (-not (Test-Path -Path $path)) { Write-Verbose -Message ($localized.EnumeratedPathDoesNotExist -f $path) return $true } else { Write-Verbose -Message ($localized.EnumeratedPathDoesExist -f $path) return $false } } Version { if (-not (Test-Path -Path $path -PathType Leaf)) { Write-Verbose -Message ($localized.EnumeratingPathDoesExist -f $path) Write-Verbose -Message ($localized.EnumeratedPathDoesNotExist -f $path) return $false } $version = $DetectionRule.FileDetectionValue -as [System.Version] $currentVersion = Get-FileVersion -Path $path $detectionOperator = $DetectionRule.FileDetectionOperator Write-Verbose -Message ($localized.EnumeratingFileVersion -f $path, $detectionOperator, $version) switch ($DetectionRule.FileDetectionOperator) { NotConfigurated { } Equal { if ($currentVersion -eq $Version) { Write-Verbose -Message ($localized.EnumeratedFileVersionIs -f $currentVersion, $detectionOperator, $version) return $true } else { Write-Verbose -Message ($localized.EnumeratedFileVersionIsNot -f $currentVersion, $detectionOperator, $version) return $false } } NotEqual { if ($currentVersion -ne $version) { Write-Verbose -Message ($localized.EnumeratedFileVersionIs -f $currentVersion, $detectionOperator, $version) return $true } else { Write-Verbose -Message ($localized.EnumeratedFileVersionIsNot -f $currentVersion, $detectionOperator, $version) return $false } } GreaterThan { if ($currentVersion -gt $version) { Write-Verbose -Message ($localized.EnumeratedFileVersionIs -f $currentVersion, $detectionOperator, $version) return $true } else { Write-Verbose -Message ($localized.EnumeratedFileVersionIsNot -f $currentVersion, $detectionOperator, $version) return $false } } GreaterThanOrEqual { if ($currentVersion -ge $version) { Write-Verbose -Message ($localized.EnumeratedFileVersionIs -f $currentVersion, $detectionOperator, $version) return $true } else { Write-Verbose -Message ($localized.EnumeratedFileVersionIsNot -f $currentVersion, $detectionOperator, $version) return $false } } LessThan { if ($currentVersion -lt $version) { Write-Verbose -Message ($localized.EnumeratedFileVersionIs -f $currentVersion, $detectionOperator, $version) return $true } else { Write-Verbose -Message ($localized.EnumeratedFileVersionIsNot -f $currentVersion, $detectionOperator, $version) return $false } } LessThanOrEqual { if ($currentVersion -le $version) { Write-Verbose -Message ($localized.EnumeratedFileVersionIs -f $currentVersion, $detectionOperator, $version) return $true } else { Write-Verbose -Message ($localized.EnumeratedFileVersionIsNot -f $currentVersion, $detectionOperator, $version) return $false } } } } Default { Write-Warning -Message ($localized.UnsupportedFileDectectionRuleWarning -f $DetectionRule.FileDetectionType) } } return $true } } <# .SYNOPSIS Tests a package metadata Intune MSI detection rule. #> function Test-PackageDetectionRuleMsi { [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Collections.Hashtable] $DetectionRule ) process { $paths = 'HKLM:\Software\Microsoft\windows\CurrentVersion\Uninstall\*', 'HKLM:\Software\WOW6432Node\Microsoft\windows\CurrentVersion\Uninstall\*' Write-Verbose -Message ($localized.EnumeratingMSIProductCodeDoesExist -f $DetectionRule.MsiProductCode) $package = Get-ItemProperty -Path $paths -ErrorAction SilentlyContinue | Where-Object { $_.PSChildName -eq $DetectionRule.MsiProductCode } if ($null -ne $package) { Write-Verbose -Message ($localized.EnumeratedMSIProductCodeDoesExist -f $DetectionRule.MsiProductCode) return $true } else { Write-Verbose -Message ($localized.EnumeratedMSIProductCodeDoesNotExist -f $DetectionRule.MsiProductCode) return $false } } } <# .SYNOPSIS Tests a package metadata Intune registry detection rule. #> function Test-PackageDetectionRuleRegistry { [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Collections.Hashtable] $DetectionRule ) process { $path = $DetectionRule.RegistryKeyPath -replace 'HKEY_LOCAL_MACHINE', 'HKLM:' -replace 'HKEY_CURRENT_USER', 'HKCU:' -replace 'HKEY_CLASSES_ROOT', 'HKCR:' if (([System.Environment]::Is64BitOperatingSystem) -and ($DetectionRule.ContainsKey('Check32BitOn64System')) -and ($DetectionRule.Check32BitOn64System -eq $true)) { $path = $path -replace '\\SOFTWARE\\', '\\SOFTWARE\\WOW6432Node\\' } if (($DetectionRule.ContainsKey('RegistryValue')) -and (-not [System.String]::IsNullOrEmpty($DetectionRule.RegistryValue))) { $registryValue = Get-ItemProperty -Path $path -Name $DetectionRule.RegistryValue -ErrorAction SilentlyContinue | Select-Object -ExpandProperty $DetectionRule.RegistryValue -ErrorAction SilentlyContinue } switch ($DetectionRule.RegistryDetectionType) { NotConfigured { Write-Verbose -Message ($localized.EnumeratedRegistryPathNotConfigured -f $path) } Exists { if (($DetectionRule.ContainsKey('RegistryValue')) -and (-not [System.String]::IsNullOrEmpty($DetectionRule.RegistryValue))) { Write-Verbose -Message ($localized.EnumeratingPathItemDoesExist -f $path, $DetectionRule.RegistryValue) if ($null -ne $registryValue) { Write-Verbose -Message ($localized.EnumeratedPathItemDoesExist -f $path, $DetectionRule.RegistryValue) return $true } else { Write-Verbose -Message ($localized.EnumeratedPathItemDoesNotExist -f $path, $DetectionRule.RegistryValue) return $false } } else { Write-Verbose -Message ($localized.EnumeratingPathExists -f $path) if (Test-Path -Path $path) { Write-Verbose -Message ($localized.EnumeratedPathExists -f $path, $DetectionRule.RegistryValue) return $true } else { Write-Verbose -Message ($localized.EnumeratedPathDoesNotExist -f $path, $DetectionRule.RegistryValue) return $false } } } DoesNotExist { if (($DetectionRule.ContainsKey('RegistryValue')) -and (-not [System.String]::IsNullOrEmpty($DetectionRule.RegistryValue))) { Write-Verbose -Message ($localized.EnumeratingPathItemDoesNotExist -f $path, $DetectionRule.RegistryValue) if ($null -eq $registryValue) { Write-Verbose -Message ($localized.EnumeratedPathItemDoesNotExist -f $path, $DetectionRule.RegistryValue) return $true } else { Write-Verbose -Message ($localized.EnumeratedPathItemDoesExist -f $path, $DetectionRule.RegistryValue) return $false } } else { Write-Verbose -Message ($localized.EnumeratingPathDoesNotExist -f $path) if (-not (Test-Path -Path $path)) { Write-Verbose -Message ($localized.EnumeratedPathDoesNotExist -f $path) return $true } else { Write-Verbose -Message ($localized.EnumeratedPathDoesExist -f $path) return $false } } } Default { if ($null -eq $registryValue) { Write-Verbose -Message ($localized.EnumeratedRegistryPathValueDoesNotExist -f $path, $DetectionRule.RegistryValue) return $false } Write-Verbose -Message ($localized.EnumeratingRegistryPathValue -f $path, $DetectionRule.RegistryValue) switch ($DetectionRule.RegistryDetectionType) { Version { $value = $DetectionRule.RegistryDetectionValue -as [System.Version] $currentValue = $registryValue -as [System.Version] } Integer { $value = $DetectionRule.RegistryDetectionValue -as [System.Int32] $currentValue = $registryValue -as [System.Int32] } String { $value = $DetectionRule.RegistryDetectionValue -as [System.String] $currentValue = $registryValue -as [System.String] } } $registryDetectionOperator = $DetectionRule.RegistryDetectionOperator Write-Verbose -Message ($localized.EnumeratingRegistryValue -f $currentValue, $registryDetectionOperator, $value) switch ($DetectionRule.RegistryDetectionOperator) { NotConfigurated { Write-Verbose -Message ($localized.EnumeratedRegistryPathNotConfigured -f $path) } Equal { if ($currentValue -eq $value) { Write-Verbose -Message ($localized.EnumeratedRegistryValueIs -f $currentValue, $registryDetectionOperator, $value) return $true } else { Write-Verbose -Message ($localized.EnumeratedRegistryValueIsNot -f $currentValue, $registryDetectionOperator, $value) return $false } } NotEqual { if ($currentValue -ne $value) { Write-Verbose -Message ($localized.EnumeratedRegistryValueIsNot -f $currentValue, $registryDetectionOperator, $value) return $true } else { Write-Verbose -Message ($localized.EnumeratedRegistryValueIs -f $currentValue, $registryDetectionOperator, $value) return $false } } GreaterThan { if ($currentValue -gt $value) { Write-Verbose -Message ($localized.EnumeratedRegistryValueIs -f $currentValue, $registryDetectionOperator, $value) return $true } else { Write-Verbose -Message ($localized.EnumeratedRegistryValueIsNot -f $currentValue, $registryDetectionOperator, $value) return $false } } GreaterThanOrEqual { if ($currentValue -ge $value) { Write-Verbose -Message ($localized.EnumeratedRegistryValueIs -f $currentValue, $registryDetectionOperator, $value) return $true } else { Write-Verbose -Message ($localized.EnumeratedRegistryValueIsNot -f $currentValue, $registryDetectionOperator, $value) return $false } } LessThan { if ($currentValue -lt $value) { Write-Verbose -Message ($localized.EnumeratedRegistryValueIs -f $currentValue, $registryDetectionOperator, $value) return $true } else { Write-Verbose -Message ($localized.EnumeratedRegistryValueIsNot -f $currentValue, $registryDetectionOperator, $value) return $false } } LessThanOrEqual { if ($currentValue -le $value) { Write-Verbose -Message ($localized.EnumeratedRegistryValueIs -f $currentValue, $registryDetectionOperator, $value) return $true } else { Write-Verbose -Message ($localized.EnumeratedRegistryValueIsNot -f $currentValue, $registryDetectionOperator, $value) return $false } } } } } return $true } } <# .SYNOPSIS Tests a package metadata Intune detection/requirement script rule. #> function Test-PackageDetectionRuleScript { [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Collections.Hashtable] $DetectionRule, [Parameter(ValueFromPipelineByPropertyName)] [System.String] $Path = $PWD ) process { $isEncodedCommand = $false try { $encodedBytes = [System.Convert]::FromBase64String($DetectionRule.ScriptFile) $isEncodedCommand = $true Write-Verbose -Message $localized.EncodedPowerShellScriptDetected $scriptPath = [System.IO.Path]::GetTempFileName().Replace('.tmp','.ps1') Write-Verbose -Message ($localized.CreatingTemporaryFile -f $scriptPath) Set-FileBytes -Path $scriptPath -InputObject $encodedBytes } catch { $scriptPath = Join-Path -Path $Path -ChildPath $DetectionRule.ScriptFile Write-Verbose -Message ($localized.EnumeratingPathDoesExist -f $scriptPath) } $isCompliant = $true if (Test-Path -Path $scriptPath -PathType Leaf) { Write-Verbose -Message ($localized.EnumeratedPathDoesExist -f $scriptPath) $scriptPathItem = Get-Item -Path $scriptPath Write-Verbose -Message ($localized.EnumeratingPowerShellScript -f $scriptPathItem.Name) $powerShellPath = "$env:WINDIR\System32\WindowsPowerShell\v1.0\powershell.exe" if ($DetectionRule.RunAs32Bit -eq $true) { ## Assumption here is that we are running a x64 PowerShell process to launch a x86 process $powerShellPath = "$env:WINDIR\SysWOW64\WindowsPowerShell\v1.0\powershell.exe" } Write-Verbose -Message ($localized.LaunchingPowerShellProcess -f $powerShellPath) $scriptOutput = & $powerShellPath -NoProfile -NonInteractive -NoLogo -File "$scriptPath" if ($LASTEXITCODE -eq 0) { Write-Verbose -Message ($localized.EnumeratedScriptExitCodeIsValid -f $LASTEXITCODE) } else { $isCompliant = $false Write-Verbose -Message ($localized.EnumeratedScriptExitCodeIsNotValid -f $LASTEXITCODE) } if ($null -ne $scriptOutput) { Write-Verbose -Message ($localized.EnumeratedScriptOutputIsValid) $scriptOutput -split "(`r)?`n" | ForEach-Object { Write-Verbose -Message " $_" } } else { $isCompliant = $false Write-Verbose -Message ($localized.EnumeratedScriptOutputIsNotValid) } if ($DetectionRule.EnforceSignatureCheck -eq $true) { Write-Verbose -Message ($localized.EnumeratingPowerShellScriptSignature -f $scriptPathItem.Name) $authenticodeSignature = Get-AuthenticodeSignature -FilePath $scriptPath -ErrorAction SilentlyContinue if (($null -eq $authenticodeSignature) -or ($authenticodeSignature.Status -ne 'Signed')) { Write-Verbose -Message ($localized.InvalidDetectionScriptSignatureWarning -f $scriptPathItem.Name) $isCompliant = $false } elseif ($authenticodeSignature.Status -eq 'Signed') { Write-Verbose -Message ($localized.DetectionScriptSignatureIsValid -f $scriptPathItem.Name) } } } else { Write-Verbose -Message ($localized.EnumeratedPathDoesNotExist -f $scriptPath) $isCompliant = $false } if($isEncodedCommand) { Write-Verbose -Message ($localized.RemovingTemporaryFile -f $scriptPath) Remove-Item -Path $scriptPath -Force } return $isCompliant } } <# .SYNOPSIS Tests metadata whether architecture should be excluded from package name. #> function Test-PackageExcludeArchitecture { [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformationAttribute()] [System.Collections.Hashtable] $Metadata ) process { if ((($Metadata.Properties.ContainsKey('ExcludeArchitecture')) -and ($Metadata.Properties.ExcludeArchitecture -eq $true))) { return $true } else { return $false } } } <# .SYNOPSIS Tests metadata whether publisher name should be excluded. #> function Test-PackageExcludePublisher { [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformationAttribute()] [System.Collections.Hashtable] $Metadata ) process { if ((($Metadata.Properties.ContainsKey('ExcludePublisher')) -and ($Metadata.Properties.ExcludePublisher -eq $true)) -or (($Metadata.Properties.ContainsKey('ExcludePublisherName')) -and ($Metadata.Properties.ExcludePublisherName -eq $true))) { return $true } else { return $false } } } <# .SYNOPSIS Tests metadata whether the UWP moniker should be excluded. #> function Test-PackageExcludeStoreApp { [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [Microsoft.PowerShell.DesiredStateConfiguration.ArgumentToConfigurationDataTransformationAttribute()] [System.Collections.Hashtable] $Metadata ) process { if ($Metadata.Properties.ContainsKey('IsStoreApp') -and ($Metadata.Properties.IsStoreApp -eq $true)) { if ($Metadata.Properties.ContainsKey('ExcludeStoreApp') -and ($Metadata.Properties.ExcludeStoreApp -eq $true)) { return $true } else { return $false } } else { ## Not a store app so always exclude return $true } } } # SIG # Begin signature block # MIIuwAYJKoZIhvcNAQcCoIIusTCCLq0CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCqb5bqWRW8EkLe # 3jwx2fhd8kPxJdn2i3x/TLdUr8FnXaCCE6QwggWQMIIDeKADAgECAhAFmxtXno4h # 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 # DAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgCYVf58Ajf/uBEL2oFe8HdEoZ # 5JV8HgIS5mCuXKRtefUwDQYJKoZIhvcNAQEBBQAEggIAmVdHfke78i5covUYwVkW # 5sF/5wZlsdmyx18TVx1v6GdInAApzzG2I534g87cPBH6G7trDBW4+rFb9VDzFo11 # C1aY70r5vD9W0EYNSrFYOKwEwCppmbqbE4kMUnlMB81BN4jD1UN5eLLwPpLUeaB/ # v0oy8kST8k5b3Icwo0CliHNB+xhyLYluzIpRsHHbTibiv6+1ymbJQ+HGevTyqihf # adxtFQ22W6l+uP1HJ8UnpRaT/gBVWuvwRZYpETsDd7wTsELxNVXON/6Ignbh8lVe # lJ+7j50AGzvGMhAXIw4ER76jEvVg8jRJhIHrIBFJr0FJKFlsE6ZMeo9YTpl85DdG # 5zl54YiZ8bX6BIfXOcJBqLK0K75xGioZMUfurcHMTI8/ia0SjEKLrSpSg0xqCTQb # 8Ft6OCbkvT4l3Gsg9HyFQFHLSneRyJVlfDgE58pVWAuG3exmsZf2T0Phy5WCKsy7 # +qufDXEcRcZtOvQfFoZzVVhw7c0t9k71R9f2H7rSb9gtqF+GXxeTp8onvs17yYdO # zaplKYN2iJUh1VpQ6rM46NrEPuD8GFmsHs5VSdefJ2BUF40uK5NQ7rs8WyQf5E+s # u5oa924cBuknXbRDigujEn5UsYKrT75af3Rl22xLMhsIjLMLdR61MYgtdHqddBwt # JallFRn2lnNSTCJxBtp4z5mhghc/MIIXOwYKKwYBBAGCNwMDATGCFyswghcnBgkq # hkiG9w0BBwKgghcYMIIXFAIBAzEPMA0GCWCGSAFlAwQCAQUAMHcGCyqGSIb3DQEJ # EAEEoGgEZjBkAgEBBglghkgBhv1sBwEwMTANBglghkgBZQMEAgEFAAQg48zU6tAZ # d4UtYCXJpdJyuuREgcGBMzIQLnETkyo7VNgCEFQO2XIngDpuMihRNdFk8kwYDzIw # MjMxMjEzMTIwODI4WqCCEwkwggbCMIIEqqADAgECAhAFRK/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 # MjEzMTIwODI4WjArBgsqhkiG9w0BCRACDDEcMBowGDAWBBRm8CsywsLJD4JdzqqK # ycZPGZzPQDAvBgkqhkiG9w0BCQQxIgQg9DLOlCtvvSgbOSaa13+HcGmL91I5r9Nl # 09S1nIA/dO4wNwYLKoZIhvcNAQkQAi8xKDAmMCQwIgQg0vbkbe10IszR1EBXaEE2 # b4KK2lWarjMWr00amtQMeCgwDQYJKoZIhvcNAQEBBQAEggIAAeJrxptQaD3BV2yd # h37vXT05uoO30gsFjYeTTfjGkOsQ5xP4hpzDcZRBJe/KKM9ObZzLxxt2NfZQfDH8 # e02pr0+4nbVCRMzmgdKtt8TK9KSuEWGXv7Wzgk+lLvpB6rikYAajEwQDGMrQpGjx # V2R9aTjfM0bP4RUH0O/6EEqBWbkQEVadtHJVCm4ku3UYRhiWryP1pKm8eSEW0wPF # /nFChatcRmg4wmRWbK9WhFTEbtaWHc928IYl3rL+VADki84/ehCbNTGsjsdIDKDJ # LMqrRubAYV1RxRsjC1MN5s9XlhAk6h04i/4uKxcn9UAx513N5WJ7Blcfdto8dSnG # VxKr3vqSSlvkgKS/PvUKhcdSgaFco+h4KpyNOgZD2Vq4LzAUb2iMgLlAKtIX431j # 7gKjR7pqT3B3taV2xCkvGYSMHs1nLVG0/IswdZuPe+ZXgHEnvdW3yAPZ3p9SwRAS # 8+nsUL0HwkUrxPcVevmlxwYqFR12x3pvvVxEIcf+lZSyUZvk8Fdx8vxYUlStgm7/ # OWSM9/5vSYio7EK1TnxlNT6/ZSUh2VNdalil0beYqRPTjDQZHug6z4l4rgDz4+8f # zrwyIwKtTFI4ZbAyQaTHt4t1eBl1bEuiN0KIffhnxD8/21suZB8a9Q8d86bXr5At # nQkHEDf4oLYQpBO9u7onS1s7GyM= # SIG # End signature block |