WindowsCompatibility.psm1
########################################################################################### # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. using namespace System.Management.Automation. using namespace System.Management.Automation.Runspaces Set-StrictMode -Version latest ########################################################################################### # A list of modules native to PowerShell Core that should never be imported $NeverImportList = @( "PSReadLine", "PackageManagement", "PowerShellGet", "Microsoft.PowerShell.Archive", "Microsoft.PowerShell.Host", "WindowsCompatibility" ) ########################################################################################### # The following is a list of modules native to PowerShell Core that don't have all of # the functionality of Windows PowerShell 5.1 versions. These modules can be imported but # will not overwrite any existing PowerShell Core commands $NeverClobberList = @( "Microsoft.PowerShell.Management", "Microsoft.PowerShell.Utility", "Microsoft.PowerShell.Security", "Microsoft.PowerShell.Diagnostics" ) ########################################################################################### # A list of compatibile modules that exist in Windows PowerShell that aren't available # to PowerShell Core by default. These modules, along with CIM modules can be installed # in the PowerShell Core module repository using the Copy-WinModule command. $CompatibleModules = @( "AppBackgroundTask", "AppLocker", "Appx", "AssignedAccess", "BitLocker", "BranchCache", "CimCmdlets", "ConfigCI", "Defender", "DeliveryOptimization", "DFSN", "DFSR", "DirectAccessClientComponents", "Dism", "EventTracingManagement", "GroupPolicy", "Hyper-V", "International", "IscsiTarget", "Kds", "MMAgent", "MsDtc", "NetAdapter", "NetConnection", "NetSecurity", "NetTCPIP", "NetworkConnectivityStatus", "NetworkControllerDiagnostics", "NetworkLoadBalancingClusters", "NetworkSwitchManager", "NetworkTransition", "PKI", "PnpDevice", "PrintManagement", "ProcessMitigations", "Provisioning", "PSScheduledJob", "ScheduledTasks", "SecureBoot", "SmbShare", "Storage", "TrustedPlatformModule", "VpnClient", "Wdac", "WindowsDeveloperLicense", "WindowsErrorReporting", "WindowsSearch", "WindowsUpdate" ) # Module-scope variable to hold the active compatibility session name $SessionName = $null # Specifies the default configuration to connect to when creating the compatibility session $DefaultConfigurationName = 'Microsoft.PowerShell' # Specifies the default name of the computer on which to create the compatibility session $DefaultComputerName = 'localhost' function Initialize-WinSession { [CmdletBinding()] [OutputType([System.Management.Automation.Runspaces.PSSession])] Param ( # If you don't want to use the default compatibility session, use # this parameter to specify the name of the computer on which to create # the compatibility session. # (Defaults to 'localhost') [Parameter(Mandatory=$false,Position=0)] [String] [Alias("Cn")] $ComputerName = $script:DefaultComputerName, # Specifies the configuration to connect to when creating the compatibility session # (Defaults to 'Microsoft.PowerShell') [Parameter()] [String] $ConfigurationName = $script:DefaultConfigurationName, # The credential to use when connecting to the target machine/configuration [Parameter()] [PSCredential] $Credential, # If present, the specified session object will be returned [Parameter()] [Switch] $PassThru ) [bool] $verboseFlag = $PSBoundParameters['Verbose'] if ($ComputerName -eq ".") { $ComputerName = "localhost" } Write-Verbose -Verbose:$verboseFlag "Initializing the compatibility session on host '$ComputerName'." if ($Credential) { $script:SessionName = "win-$($Credential.UserName)" } else { $script:SessionName = "win-$([environment]::UserName)" } Write-Verbose -Verbose:$verboseFlag "The compatibility session name is '$script:SessionName'." # BUGBUG - need to deal with the possibilities of multiple sessions $session = Get-PSSession | Where-Object { $_.ComputerName -eq $ComputerName -and $_.ConfigurationName -eq $ConfigurationName -and $_.Name -eq $script:SessionName } if ($session -and $session.State -ne "Opened") { Write-Verbose -Verbose:$verboseFlag "Removing closed compatibility session." Remove-PSSession $session $session = $null } if (-not $session) { $newPSSessionParameters = @{ Verbose = $verboseFlag ComputerName = $ComputerName Name = $script:sessionName ConfigurationName = $configurationName ErrorAction = "Stop" } if ($Credential) { $newPSSessionParameters.Credential = $Credential } if ($ComputerName -eq "localhost" -or $ComputerName -eq [environment]::MachineName) { $newPSSessionParameters.EnableNetworkAccess = $true } Write-Verbose -Verbose:$verboseFlag "Creating a new compatibility session." ##BUGBUG need to deal with the case where there might be multiple sessions because someone hit ctrl-C $session = New-PSSession @newPSSessionParameters } else { Write-Verbose -Verbose:$verboseFlag "Reusing the existing compatibility session." } if ($PassThru) { return $session } } function Add-WinFunction { [CmdletBinding()] [OutputType([void])] Param ( # The name of the function to define [Parameter(Mandatory,Position=0)] [String] $FunctionName, # Scriptblock to use as the body of the function [Parameter(Mandatory,Position=1)] [ScriptBlock] $ScriptBlock, # If you don't want to use the default compatibility session, use # this parameter to specify the name of the computer on which to create # the compatibility session. # (Defaults to 'localhost') [Parameter()] [String] [Alias("Cn")] $ComputerName = $script:DefaultComputerName, # Specifies the configuration to connect to when creating the compatibility session # (Defaults to 'Microsoft.PowerShell') [Parameter()] [String] $ConfigurationName = $script:DefaultConfigurationName, # The credential to use when creating the compatibility session # using the target machine/configuration [Parameter()] [PSCredential] $Credential ) # Make sure the session is initialized [void] $PSBoundParameters.Remove('FunctionName') [void] $PSBoundParameters.Remove('ScriptBlock') Initialize-WinSession @PSBoundParameters $localSessionName = $script:SessionName $wrapper = { $session = Get-PSsession -Name $localSessionName Invoke-Command -Session $session -Scriptblock $ScriptBlock -ArgumentList $args } Set-item function:Global:$FunctionName $wrapper.GetNewClosure(); } function Invoke-WinCommand { [CmdletBinding()] [OutputType([PSObject])] Param ( # The scriptblock to invoke in the compatibility session [Parameter(Mandatory, Position=0)] [ScriptBlock] $ScriptBlock, # If you don't want to use the default compatibility session, use # this parameter to specify the name of the computer on which to create # the compatibility session. # (Defaults to 'localhost') [Parameter()] [String] [Alias("cn")] $ComputerName = $script:DefaultComputerName, # Specifies the configuration to connect to when creating the compatibility session # (Defaults to 'Microsoft.PowerShell') [Parameter()] [String] $ConfigurationName = $script:DefaultConfigurationName, # The credential to use when connecting to the compatibility session. [Parameter()] [PSCredential] $Credential, # Arguments to pass to the scriptblock [Parameter(ValueFromRemainingArguments)] [object[]] $ArgumentList ) [void] $PSBoundParameters.Remove('ScriptBlock') [void] $PSBoundParameters.Remove('ArgumentList') # Make sure the session is initialized [PSSession] $session = Initialize-WinSession @PSBoundParameters -PassThru # And invoke the scriptblock in the session Invoke-Command -Session $session -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList } function Get-WinModule { [CmdletBinding()] [OutputType([PSObject])] Param ( # Pattern to filter module names by [Parameter(Mandatory=$false, Position=0)] [String[]] $Name='*', # If you don't want to use the default compatibility session, use # this parameter to specify the name of the computer on which to create # the compatibility session. # (Defaults to 'localhost') [Alias("cn")] [String] $ComputerName = $script:DefaultComputerName, # Specifies the configuration to connect to when creating the compatibility session # (Defaults to 'Microsoft.PowerShell') [Parameter()] [String] $ConfigurationName = $script:DefaultConfigurationName, # The credential to use when creating the compatibility session # using the target machine/configuration [Parameter()] [PSCredential] $Credential, # If specified, the complete deserialized module object # will be returned instead of the abbreviated form returned # by default. [Parameter()] [Switch] $Full ) [bool] $verboseFlag = $PSBoundParameters['Verbose'] Write-Verbose -Verbose:$verboseFlag 'Connecting to compatibility session.' $initializeWinSessionParameters = @{ Verbose = $verboseFlag ComputerName = $ComputerName ConfigurationName = $ConfigurationName Credential = $Credential PassThru = $true } [PSSession] $session = Initialize-WinSession @initializeWinSessionParameters if ($name -ne '*') { Write-Verbose -Verbose:$verboseFlag "Getting the list of available modules matching '$name'." } else { Write-Verbose -Verbose:$verboseFlag 'Getting the list of available modules.' } $propertiesToReturn = if ($Full) { '*' } else {'Name', 'Version', 'Description'} Invoke-Command -Session $session -ScriptBlock { Get-Module -ListAvailable -Name $using:Name | Where-Object Name -notin $using:NeverImportList | Select-Object $using:propertiesToReturn } | Select-Object $propertiesToReturn | Sort-Object Name } function Import-WinModule { [CmdletBinding()] [OutputType([PSObject])] Param ( # Specifies the name of the module to be imported. Wildcards can be used. [Parameter(Mandatory=$False,Position=0)] [String[]] $Name="*", # A list of wildcard patterns matching the names of modules that # should not be imported. [Parameter()] [string[]] $Exclude = "", # If you don't want to use the default compatibility session, use # this parameter to specify the name of the computer on which to create # the compatibility session. # (Defaults to 'localhost') [Parameter()] [String] [Alias("cn")] $ComputerName = $script:DefaultComputerName, # Specifies the configuration to connect to when creating the compatibility session # (Defaults to 'Microsoft.PowerShell') [Parameter()] [String] $ConfigurationName = $script:DefaultConfigurationName, # Prefix to prepend to the imported command names [Parameter()] [string] $Prefix = "", # Disable warnings about non-standard verbs [Parameter()] [Switch] $DisableNameChecking, # Don't overwrite any existing function definitions [Parameter()] [Switch] $NoClobber, # Force reloading the module [Parameter()] [Switch] $Force, # The credential to use when creating the compatibility session # using the target machine/configuration [Parameter()] [PSCredential] $Credential, # If present, the ModuleInfo objects will be written to the output pipe # as deserialized (PSObject) objects [Parameter()] [Switch] $PassThru ) [bool] $verboseFlag = $PSBoundParameters['Verbose'] Write-Verbose -Verbose:$verboseFlag "Connecting to compatibility session." $initializeWinSessionParameters = @{ Verbose = $verboseFlag ComputerName = $ComputerName ConfigurationName = $ConfigurationName Credential = $Credential PassThru = $true } [PSSession] $session = Initialize-WinSession @initializeWinSessionParameters # Mapping wildcards to a regex $Exclude = ($Exclude -replace "\*",".*") -join "|" Write-Verbose -Verbose:$verboseFlag "Getting module list..." $importNames = Invoke-Command -Session $session { # Running on the Remote Machine $m = (Get-Module -ListAvailable -Name $using:Name). Where{ $_.Name -notin $using:NeverImportList } # These can use wildcards e.g. Az*,x* will probably be common if ($using:Exclude) { $m = $m.Where{ $_.Name -NotMatch $using:Exclude } } $m.Name | Select-Object -Unique } Write-Verbose -Verbose:$verboseFlag "Importing modules..." $importModuleParameters = @{ Global = $true Force = $Force Verbose = $verboseFlag PSSession = $session PassThru = $PassThru DisableNameChecking = $DisableNameChecking } if ($Prefix) { $importModuleParameters.Prefix = $Prefix } if ($PassThru) { $importModuleParameters.PassThru = $PassThru } if ($importNames) { # Extract the 'never clobber' modules from the list $noClobberNames = $importNames.where{ $_ -in $script:NeverClobberList } $importNames = $importNames.where{ $_ -notin $script:NeverClobberList } if ($importNames) { Import-Module -Name $ImportNames -NoClobber:$NoClobber @importModuleParameters } if ($noClobberNames) { $importModuleParameters.PassThru = $true foreach ($name in $noClobberNames) { $module = Import-Module -Name $name -NoClobber @importModuleParameters # Hack using private reflection to keep the proxy module from shadowing the real module. $null = [PSModuleInfo]. GetMethod('SetName',[System.Reflection.BindingFlags]'Instance, NonPublic'). Invoke($module, @($module.Name + '.WinModule')) if($PassThru.IsPresent) { $module } } } } else { Write-Verbose -Verbose:$verboseFlag "No matching modules were found; nothing was imported" } } function Compare-WinModule { [CmdletBinding()] [OutputType([PSObject])] Param ( # Specifies the names or name patterns of for the modules to compare. # Wildcard characters are permitted. [Parameter(Position=0)] [String[]] $Name="*", # If you don't want to use the default compatibility session, use # this parameter to specify the name of the computer on which to create # the compatibility session. # (Defaults to 'localhost') [Parameter()] [String] [Alias("cn")] $ComputerName = $script:DefaultComputerName, # Specifies the configuration to connect to when creating the compatibility session # (Defaults to 'Microsoft.PowerShell') [Parameter()] [String] $ConfigurationName = $script:DefaultConfigurationName, # If needed, use this parameter to specify credentials for the compatibility session [Parameter()] [PSCredential] $Credential ) [bool] $verboseFlag = $PSBoundParameters['Verbose'] Write-Verbose -Verbose:$verboseFlag "Initializing compatibility session" $initializeWinSessionParameters = @{ Verbose = $verboseFlag ComputerName = $ComputerName ConfigurationName = $ConfigurationName Credential = $Credential PassThru = $true } [PSSession] $session = Initialize-WinSession @initializeWinSessionParameters Write-Verbose -Verbose:$verboseFlag "Getting local modules..." $LocalModule = (Get-Module -ListAvailable -Verbose:$false).Where{$_.Name -like $Name} Write-Verbose -Verbose:$verboseFlag "Getting remote modules..." # Use Invoke-Command here instead of the -PSSession option on Get-Module because # we're only returning a subset of the data $RemoteModule = @(Invoke-Command -Session $session { (Get-Module -ListAvailable). Where{$_.Name -notin $using:NeverImportList -and $_.Name -like $using:Name} | Select-Object Name, Version }) Write-Verbose -Verbose:$verboseFlag "Comparing module set..." Compare-Object $LocalModule $RemoteModule -Property Name,Version | Where-Object SideIndicator -eq "=>" } function Copy-WinModule { [CmdletBinding(SupportsShouldProcess)] [OutputType([void])] Param ( # Specifies names or name patterns of modules that will be copied. # Wildcard characters are permitted. [Parameter(Mandatory=$false,Position=0)] [String[]] $Name="*", # If you don't want to use the default compatibility session, use # this parameter to specify the name of the computer on which to create # the compatibility session. # (Defaults to 'localhost') [Parameter()] [String] [Alias("cn")] $ComputerName = $script:DefaultComputerName, # Specifies the configuration to connect to when creating the compatibility session # (Defaults to 'Microsoft.PowerShell') [Parameter()] [String] $ConfigurationName = $script:DefaultConfigurationName, # If needed, use this parameter to specify credentials for the compatibility session [Parameter()] [PSCredential] $Credential, # The location where compatible modules should be copied to [Parameter()] [String] $Destination ) [bool] $verboseFlag = $PSBoundParameters['Verbose'] [bool] $whatIfFlag = $PSBoundParameters['WhatIf'] [bool] $confirmFlag = $PSBoundParameters['Confirm'] if (-not $Destination) { # If the user hasn't specified a destination, default to the user module directory $parts = [environment]::GetFolderPath([System.Environment+SpecialFolder]::MyDocuments), "PowerShell", "Modules" $Destination = Join-Path @parts } # Resolve the path which also verifies that the path exists $resolvedDestination = Resolve-Path $Destination -ErrorAction SilentlyContinue if (-not $?) { throw "The destination path '$Destination' could not be resolved. Please ensure that the path exists and try the command again" } # Make sure it's a FileSystem location if ($resolvedDestination.provider.ImplementingType -ne [Microsoft.PowerShell.Commands.FileSystemProvider] ) { throw "Modules can only be installed to paths in the filesystem. Please choose a different location and try the command again" } $Destination = $resolvedDestination.Path $initializeWinSessionParameters = @{ Verbose = $verboseFlag ComputerName = $ComputerName ConfigurationName = $ConfigurationName Credential = $Credential PassThru = $true } [PSSession] $session = Initialize-WinSession @initializeWinSessionParameters $copyItemParameters = @{ WhatIf = $whatIfFlag Verbose = $verboseFlag Confirm = $confirmFlag Recurse = $true } if ($ComputerName -ne "localhost" -and $ComputerName -ne ".") { $copyItemParameters.FromSession = $session } Write-Verbose -Verbose:$verboseFlag "Searching for compatible modules..." $modulesToCopy = Invoke-Command $session { Get-Module -ListAvailable -Name $using:CompatibleModules | Select-Object Name, ModuleBase } Write-Verbose -Verbose:$verboseFlag "Searching for CIM modules..." $modulesToCopy += Invoke-Command $session { Get-Module -ListAvailable | Where-Object { $_.NestedModules[0].path -match '\.cdxml$' } | Select-Object Name,ModuleBase } Write-Verbose -Verbose:$verboseFlag "Copying modules to path '$Destination'" $modulesToCopy = $modulesToCopy | Sort-Object -Unique -Property Name foreach ($m in $modulesToCopy) { # Skip modules that aren't on the named module list if (-not ($name.Where{$m.Name -like $_})) { continue } $fullDestination = Join-Path $Destination $m.name if (-not (Test-Path $fullDestination)) { Copy-Item -Path $m.ModuleBase -Destination $fullDestination @copyItemParameters } else { Write-Verbose -Verbose:$verboseFlag "Skipping module '$($m.Name)'; module directory already exists" } } } function Add-WindowsPSModulePath { if ($PSVersionTable.PSEdition -eq 'Core' -and -not $IsWindows) { throw "This cmdlet is only supported on Windows" } if ($PSVersionTable.PSEdition -eq 'Desktop') { return } $WindowsPSModulePath = [System.Environment]::GetEnvironmentVariable("psmodulepath", [System.EnvironmentVariableTarget]::Machine) if (-not ($env:PSModulePath).Contains($WindowsPSModulePath)) { $env:PSModulePath += ";${env:userprofile}\Documents\WindowsPowerShell\Modules;${env:programfiles}\WindowsPowerShell\Modules;${WindowsPSModulePath}" } } # SIG # Begin signature block # MIIdhwYJKoZIhvcNAQcCoIIdeDCCHXQCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUjsNz+0Hylp+Xk9LfzkwqqXyF # RF6gghhVMIIEwzCCA6ugAwIBAgITMwAAAMvZUgZTvz4qWQAAAAAAyzANBgkqhkiG # 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw # HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTYwOTA3MTc1ODU1 # WhcNMTgwOTA3MTc1ODU1WjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNO # OjU4NDctRjc2MS00RjcwMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT # ZXJ2aWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAum1PI6z8Uk7O # jh+jHA7qkPtqGl+g9Ol0qKYBPtGxZ7SgIuZrXcfSVifUjgXwk+0q9TQFSiD+/b5L # akoaE7Onwvh5GKjFrZH4Ymu8lwmIQBFCa5liePRTnVQybA/c18S/8AhhXn7t3/QL # gh27vP7FnFy3lDYiVoEhxM40kv6vM0OuiBlFTxwWfBWzwHDem0cAw99IgtE4Ufac # ftfmmIVMazRTlX1M1SLYTHo0u5yaOiU1ac1i2q/K30PewG+997QJHkpC6umA9XwH # r4emFX3hqAChAO/fHrd3bRM0OMNH2sAFYTz41jH0D7ojyeRiRgMi+wAUtL1u+WTa # 3RQ3NOF7VQIDAQABo4IBCTCCAQUwHQYDVR0OBBYEFJjHnFnzwMDY0qoqcv3dfXL2 # PD/mMB8GA1UdIwQYMBaAFCM0+NlSRnAK7UD7dvuzK7DDNbMPMFQGA1UdHwRNMEsw # SaBHoEWGQ2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3Rz # L01pY3Jvc29mdFRpbWVTdGFtcFBDQS5jcmwwWAYIKwYBBQUHAQEETDBKMEgGCCsG # AQUFBzAChjxodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY3Jv # c29mdFRpbWVTdGFtcFBDQS5jcnQwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI # hvcNAQEFBQADggEBAEsgBVRD0msYysh0uEFNws3dDP5riqpVsamGJpWCMlJGNl4+ # LX4JIv283MTsCU37LaNIbGjhTuSi4ifVyvs73xsicr4tPiGK7IYBRthKL/3tEjeM # /mGWSfY2rZRgSwUKbPIGVz1IgHaOm089sb6Q6yC4EkEOAxTrhBS/4SZeTM2RuT0o # 8rFtffWR4sW8SrpgvRQuz28WAu2wDZ3XTTvAmiF+cjumrx6fA8UaLhYG6LWvJZT6 # zrlsZ8DcTZMwZLnw6tKSiqvb6gvIcyDTcVj25GRul3yzLfgsmLWGTRN7woCSGzfd # gykqBndYo4OS6E0ssxjPR8zJw0DbhJjvUMtJ/egwggYBMIID6aADAgECAhMzAAAA # xOmJ+HqBUOn/AAAAAADEMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp # Z25pbmcgUENBIDIwMTEwHhcNMTcwODExMjAyMDI0WhcNMTgwODExMjAyMDI0WjB0 # MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk # bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK # AoIBAQCIirgkwwePmoB5FfwmYPxyiCz69KOXiJZGt6PLX4kvOjMuHpF4+nypH4IB # tXrLGrwDykbrxZn3+wQd8oUK/yJuofJnPcUnGOUoH/UElEFj7OO6FYztE5o13jhw # VG877K1FCTBJwb6PMJkMy3bJ93OVFnfRi7uUxwiFIO0eqDXxccLgdABLitLckevW # eP6N+q1giD29uR+uYpe/xYSxkK7WryvTVPs12s1xkuYe/+xxa8t/CHZ04BBRSNTx # AMhITKMHNeVZDf18nMjmWuOF9daaDx+OpuSEF8HWyp8dAcf9SKcTkjOXIUgy+MIk # ogCyvlPKg24pW4HvOG6A87vsEwvrAgMBAAGjggGAMIIBfDAfBgNVHSUEGDAWBgor # BgEEAYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUy9ZihM9gOer/Z8Jc0si7q7fD # E5gwUgYDVR0RBEswSaRHMEUxDTALBgNVBAsTBE1PUFIxNDAyBgNVBAUTKzIzMDAx # MitjODA0YjVlYS00OWI0LTQyMzgtODM2Mi1kODUxZmEyMjU0ZmMwHwYDVR0jBBgw # FoAUSG5k5VAF04KqFzc3IrVtqMp1ApUwVAYDVR0fBE0wSzBJoEegRYZDaHR0cDov # L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljQ29kU2lnUENBMjAxMV8y # MDExLTA3LTA4LmNybDBhBggrBgEFBQcBAQRVMFMwUQYIKwYBBQUHMAKGRWh0dHA6 # Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljQ29kU2lnUENBMjAx # MV8yMDExLTA3LTA4LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IC # AQAGFh/bV8JQyCNPolF41+34/c291cDx+RtW7VPIaUcF1cTL7OL8mVuVXxE4KMAF # RRPgmnmIvGar27vrAlUjtz0jeEFtrvjxAFqUmYoczAmV0JocRDCppRbHukdb9Ss0 # i5+PWDfDThyvIsoQzdiCEKk18K4iyI8kpoGL3ycc5GYdiT4u/1cDTcFug6Ay67Sz # L1BWXQaxFYzIHWO3cwzj1nomDyqWRacygz6WPldJdyOJ/rEQx4rlCBVRxStaMVs5 # apaopIhrlihv8cSu6r1FF8xiToG1VBpHjpilbcBuJ8b4Jx/I7SCpC7HxzgualOJq # nWmDoTbXbSD+hdX/w7iXNgn+PRTBmBSpwIbM74LBq1UkQxi1SIV4htD50p0/GdkU # ieeNn2gkiGg7qceATibnCCFMY/2ckxVNM7VWYE/XSrk4jv8u3bFfpENryXjPsbtr # j4Nsh3Kq6qX7n90a1jn8ZMltPgjlfIOxrbyjunvPllakeljLEkdi0iHv/DzEMQv3 # Lz5kpTdvYFA/t0SQT6ALi75+WPbHZ4dh256YxMiMy29H4cAulO2x9rAwbexqSajp # lnbIvQjE/jv1rnM3BrJWzxnUu/WUyocc8oBqAU+2G4Fzs9NbIj86WBjfiO5nxEmn # L9wliz1e0Ow0RJEdvJEMdoI+78TYLaEEAo5I+e/dAs8DojCCBgcwggPvoAMCAQIC # CmEWaDQAAAAAABwwDQYJKoZIhvcNAQEFBQAwXzETMBEGCgmSJomT8ixkARkWA2Nv # bTEZMBcGCgmSJomT8ixkARkWCW1pY3Jvc29mdDEtMCsGA1UEAxMkTWljcm9zb2Z0 # IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTA3MDQwMzEyNTMwOVoXDTIx # MDQwMzEzMDMwOVowdzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEhMB8GA1UEAxMYTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBMIIBIjANBgkqhkiG # 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn6Fssd/bSJIqfGsuGeG94uPFmVEjUK3O3RhO # JA/u0afRTK10MCAR6wfVVJUVSZQbQpKumFwwJtoAa+h7veyJBw/3DgSY8InMH8sz # JIed8vRnHCz8e+eIHernTqOhwSNTyo36Rc8J0F6v0LBCBKL5pmyTZ9co3EZTsIbQ # 5ShGLieshk9VUgzkAyz7apCQMG6H81kwnfp+1pez6CGXfvjSE/MIt1NtUrRFkJ9I # AEpHZhEnKWaol+TTBoFKovmEpxFHFAmCn4TtVXj+AZodUAiFABAwRu233iNGu8Qt # VJ+vHnhBMXfMm987g5OhYQK1HQ2x/PebsgHOIktU//kFw8IgCwIDAQABo4IBqzCC # AacwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUIzT42VJGcArtQPt2+7MrsMM1 # sw8wCwYDVR0PBAQDAgGGMBAGCSsGAQQBgjcVAQQDAgEAMIGYBgNVHSMEgZAwgY2A # FA6sgmBAVieX5SUT/CrhClOVWeSkoWOkYTBfMRMwEQYKCZImiZPyLGQBGRYDY29t # MRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQDEyRNaWNyb3NvZnQg # Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCEHmtFqFKoKWtTHNY9AcTLmUwUAYD # VR0fBEkwRzBFoEOgQYY/aHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwv # cHJvZHVjdHMvbWljcm9zb2Z0cm9vdGNlcnQuY3JsMFQGCCsGAQUFBwEBBEgwRjBE # BggrBgEFBQcwAoY4aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9N # aWNyb3NvZnRSb290Q2VydC5jcnQwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI # hvcNAQEFBQADggIBABCXisNcA0Q23em0rXfbznlRTQGxLnRxW20ME6vOvnuPuC7U # EqKMbWK4VwLLTiATUJndekDiV7uvWJoc4R0Bhqy7ePKL0Ow7Ae7ivo8KBciNSOLw # UxXdT6uS5OeNatWAweaU8gYvhQPpkSokInD79vzkeJkuDfcH4nC8GE6djmsKcpW4 # oTmcZy3FUQ7qYlw/FpiLID/iBxoy+cwxSnYxPStyC8jqcD3/hQoT38IKYY7w17gX # 606Lf8U1K16jv+u8fQtCe9RTciHuMMq7eGVcWwEXChQO0toUmPU8uWZYsy0v5/mF # hsxRVuidcJRsrDlM1PZ5v6oYemIp76KbKTQGdxpiyT0ebR+C8AvHLLvPQ7Pl+ex9 # teOkqHQ1uE7FcSMSJnYLPFKMcVpGQxS8s7OwTWfIn0L/gHkhgJ4VMGboQhJeGsie # IiHQQ+kr6bv0SMws1NgygEwmKkgkX1rqVu+m3pmdyjpvvYEndAYR7nYhv5uCwSdU # trFqPYmhdmG0bqETpr+qR/ASb/2KMmyy/t9RyIwjyWa9nR2HEmQCPS2vWY+45CHl # tbDKY7R4VAXUQS5QrJSwpXirs6CWdRrZkocTdSIvMqgIbqBbjCW/oO+EyiHW6x5P # yZruSeD3AWVviQt9yGnI5m7qp5fOMSn/DsVbXNhNG6HY+i+ePy5VFmvJE6P9MIIH # ejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UE # BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc # BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0 # IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5 # WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDEx # MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00 # uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kN # eWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/n # qwhQz7NEt13YxC4Ddato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3V # XHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6x # jF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5k # f1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4c # I6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bys # AoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexN # STCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93 # KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX # 3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEA # MB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4K # AFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSME # GDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRw # Oi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJB # dXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcw # AoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJB # dXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcu # AzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9w # cy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEA # bABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3 # DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74 # w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQ # sP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6Sp # BQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd # 8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJx # Jxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9 # Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEG # sXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AA # KcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/ # 1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EK # sT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCBJww # ggSYAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # KDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAADE # 6Yn4eoFQ6f8AAAAAAMQwCQYFKw4DAhoFAKCBsDAZBgkqhkiG9w0BCQMxDAYKKwYB # BAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0B # CQQxFgQUASnAoQMybdNiTNgwCKFQyEipN5EwUAYKKwYBBAGCNwIBDDFCMECgFoAU # AFAAbwB3AGUAcgBTAGgAZQBsAGyhJoAkaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L1Bvd2VyU2hlbGwgMA0GCSqGSIb3DQEBAQUABIIBAGrryBtyM2L3vF174VZptHky # oDQxwsfCiZdcgefOd+Bug9MzOrvqh8WtGk3ulQc46RXMTUpYXSxMP84JSt4wSm+Y # l57fkq4pqbqeH0kVwZ6SB5UMgNOCVaE+3uclRF3p/fRCWOlxnWlxXeiSR5R7qrHi # s8N2OguUmdtEZGwW8N2xFZaozrpbk1S2VYllO9zR1kNUFV/C1JTzG1JiGUSaP6ED # rVX5/KyZMugJ0WRuRXsOhA20+uLa6GvHnBn69Z2NDtrzF9mV48HkScriCqIfgBwx # bpesZS7AJZwvIZrRXBrGO9DJCzRfNc/+hYXxgwV2IpOMx4DFkV20/MoTsRUdHcOh # ggIoMIICJAYJKoZIhvcNAQkGMYICFTCCAhECAQEwgY4wdzELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEhMB8GA1UEAxMYTWljcm9zb2Z0IFRpbWUt # U3RhbXAgUENBAhMzAAAAy9lSBlO/PipZAAAAAADLMAkGBSsOAwIaBQCgXTAYBgkq # hkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xODA2MDcxNzAx # MzhaMCMGCSqGSIb3DQEJBDEWBBTNuLeVLZh/VlVSD5hNKOVr9VURVjANBgkqhkiG # 9w0BAQUFAASCAQC3sE7aUC5mqP30nls2yv2UPwLpCrTsdyr0fTx1qV6fcROMhnMS # B5eZJE+SF1RU3aQ0b1eI2GDU6OlWMvsc8UlvVMjs12h9FZnDmWd9uo27Qo3bhgA7 # 8DoBZUK/3KsO+uH6xENpeYZEgUgYGC5UshCgy7qTaLPeX89ppoZcnliIxrMTE9re # d6E9hCngr7L9dbbcvftJMzuCtO9FWy8oSOCuzgdnjQKmTzYqlmKcCcpeU1p0VNuk # eh+/Fxx6HkJ7T1dMWQY3O1NCkni/yXtAUmxWgQoNr4pfeSq1+yc3yajqh+6JKLnB # 8Awa6QInNfysozcuOMJfhwHobOO+xvmeZpnh # SIG # End signature block |