AWS.Tools.Installer.psm1
using namespace System using namespace System.Collections.Generic using namespace System.IO using namespace System.IO.Compression using namespace System.Management.Automation using namespace System.Net using namespace System.Net.Http using namespace System.Threading.Tasks Microsoft.PowerShell.Core\Set-StrictMode -Version 3 $script:AWSToolsSignatureSubject = 'CN="Amazon.com, Inc.", O="Amazon.com, Inc.", L=Seattle, S=Washington, C=US' $script:AWSToolsTempRepoName = 'AWSToolsTemp' $script:CurrentMinAWSToolsInstallerVersion = '0.0.0.0' $script:ExpectedModuleCompanyName = 'aws-dotnet-sdk-team' $script:MaxModulesToFindIndividually = 3 $script:ParallelDownloaderClassCode = @" using System; using System.IO; using System.Net.Http; using System.Threading; using System.Threading.Tasks; public class ParallelDownloader { private readonly HttpClient Client; private readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource(); public ParallelDownloader(HttpClient client) { Client = client; } public async Task DownloadToFile(string uri, string filePath) { using (var httpResponseMessage = await Client.GetAsync(uri, CancellationTokenSource.Token)) using (var stream = await httpResponseMessage.EnsureSuccessStatusCode().Content.ReadAsStreamAsync()) using (var fileStream = new FileStream(filePath, FileMode.Create)) { await stream.CopyToAsync(fileStream, 81920, CancellationTokenSource.Token); } } public void Cancel() { CancellationTokenSource.Cancel(); } } "@ function Get-CleanVersion { Param( [Parameter(ValueFromPipelineByPropertyName, ValueFromPipeline, Mandatory, Position = 0)] [AllowNull()] [Version] $Version ) Process { if ($null -eq $Version) { $Version } else { [int]$major = $Version.Major [int]$minor = $Version.Minor [int]$build = $Version.Build [int]$revision = $Version.Revision #PowerShell modules version numbers can have missing fields, that would create problems with #matching and sorting versions. Replacing missing fields with 0s if ($major -lt 0) { $major = 0 } if ($minor -lt 0) { $minor = 0 } if ($build -lt 0) { $build = 0 } if ($revision -lt 0) { $revision = 0 } [Version]::new($major, $minor, $build, $revision) } } } function Get-AWSToolsModule { Param( [Parameter()] [Switch] $SkipIfInvalidSignature ) Process { [PSModuleInfo[]]$installedAwsToolsModules = Microsoft.PowerShell.Core\Get-Module -Name 'AWS.Tools.*' -ListAvailable -Verbose:$false if ($installedAwsToolsModules -and ($PSVersionTable.PSVersion.Major -lt 6 -or $IsWindows)) { $installedAwsToolsModules = $installedAwsToolsModules | Where-Object { [Signature]$signature = Microsoft.PowerShell.Security\Get-AuthenticodeSignature -FilePath $_.Path ($signature.Status -eq 'Valid' -or $SkipIfInvalidSignature) -and $signature.SignerCertificate.Subject -eq $script:AWSToolsSignatureSubject } } $installedAwsToolsModules | Where-Object { $_.Name -ne 'AWS.Tools.Installer' } } } <# .Synopsis Uninstalls all currently installed AWS.Tools modules. .Description This cmdlet uses Uninstall-Module to uninstall all currently installed AWS.Tools modules. .Notes .Example Uninstall-AWSToolsModule -ExceptVersion 4.0.0.0 This example uninstalls all versions of all AWS.Tools modules except for version 4.0.0.0. #> function Uninstall-AWSToolsModule { [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( ## Specifies the minimum version of the modules to uninstall. [Parameter(ValueFromPipelineByPropertyName)] [Version] $MinimumVersion, ## Specifies exact version number of the module to uninstall. [Parameter(ValueFromPipelineByPropertyName)] [Version] $RequiredVersion, ## Specifies the maximum version of the modules to uninstall. [Parameter(ValueFromPipelineByPropertyName)] [Version] $MaximumVersion, ## Specifies that you want to uninstall all of the other available versions of AWS Tools except this one. [Parameter(ValueFromPipelineByPropertyName)] [Version] $ExceptVersion, ## Forces Uninstall-AWSToolsModule to run without asking for user confirmation [Parameter(ValueFromPipelineByPropertyName)] [Switch] $Force ) Begin { $MinimumVersion = Get-CleanVersion $MinimumVersion $RequiredVersion = Get-CleanVersion $RequiredVersion $MaximumVersion = Get-CleanVersion $MaximumVersion $ExceptVersion = Get-CleanVersion $ExceptVersion Write-Verbose "[$($MyInvocation.MyCommand)] ConfirmPreference=$ConfirmPreference WhatIfPreference=$WhatIfPreference VerbosePreference=$VerbosePreference Force=$Force" } Process { $ErrorActionPreference = 'Stop' Write-Verbose "[$($MyInvocation.MyCommand)] Searching installed modules" [PSModuleInfo[]]$InstalledAwsToolsModules = Get-AWSToolsModule if ($MinimumVersion) { $InstalledAwsToolsModules = $InstalledAwsToolsModules | Where-Object { $_.Version -ge $MinimumVersion } } if ($MaximumVersion) { $InstalledAwsToolsModules = $InstalledAwsToolsModules | Where-Object { $_.Version -le $MaximumVersion } } if ($RequiredVersion) { $InstalledAwsToolsModules = $InstalledAwsToolsModules | Where-Object { $_.Version -eq $RequiredVersion } } if ($ExceptVersion) { $InstalledAwsToolsModules = $InstalledAwsToolsModules | Where-Object { $_.Version -ne $ExceptVersion } } $versions = $InstalledAwsToolsModules | Group-Object Version if ($versions -and ($Force -or $WhatIfPreference -or $PSCmdlet.ShouldProcess("AWS Tools version $([string]::Join(', ', $versions.Name))"))) { $ConfirmPreference = 'None' $versions | ForEach-Object { Write-Host "Uninstalling AWS.Tools version $($_.Name)" [PSModuleInfo[]]$versionModules = $_.Group while ($versionModules) { [string[]]$dependencyNames = $versionModules | Select-Object -ExpandProperty RequiredModules | Select-Object -ExpandProperty Name | Sort-Object -Unique if ($dependencyNames) { [PSModuleInfo[]]$removableModules = $versionModules | Where-Object { -not $dependencyNames.Contains($_.Name) } } else { [PSModuleInfo[]]$removableModules = $versionModules } if (-not $removableModules) { Write-Error "Remaining modules for version $($_.Name) cannot be removed" break } $removableModules | ForEach-Object { if ($WhatIfPreference) { Write-Host "What if: Uninstalling module $($_.Name)" } else { Write-Host "Uninstalling module $($_.Name)" #We need to use -Force to work around https://github.com/PowerShell/PowerShellGet/issues/542 $uninstallModuleParams = @{ Name = $_.Name RequiredVersion = $_.Version Force = $true Confirm = $false ErrorAction = 'Continue' } PowerShellGet\Uninstall-Module @uninstallModuleParams } } $versionModules = $versionModules | Where-Object { $_.Name -notin ($removableModules | Select-Object -ExpandProperty Name) } } } } } End { Write-Verbose "[$($MyInvocation.MyCommand)] End" } } function Find-AWSToolsModule { Param( ## Specifies a proxy server for the request, rather than connecting directly to an internet resource. [Parameter(ValueFromPipelineByPropertyName, ValueFromPipeline, Mandatory, Position = 0)] [string[]] $Name, ## Specifies a proxy server for the request, rather than connecting directly to an internet resource. [Parameter(ValueFromPipelineByPropertyName)] [Uri] $Proxy, ## Specifies a user account that has permission to use the proxy server specified by the Proxy parameter. [Parameter(ValueFromPipelineByPropertyName)] [PSCredential] $ProxyCredential ) Begin { $RequiredVersion = Get-CleanVersion $RequiredVersion $MaximumVersion = Get-CleanVersion $MaximumVersion Write-Verbose "[$($MyInvocation.MyCommand)] ConfirmPreference=$ConfirmPreference WhatIfPreference=$WhatIfPreference VerbosePreference=$VerbosePreference Name=($Name)" } Process { $proxyParams = @{ } if ($Proxy) { $proxyParams['Proxy'] = $Proxy } if ($ProxyCredential) { $proxyParams['ProxyCredential'] = $ProxyCredential } [PSObject[]]$availableModules = @() [string[]]$missingModules = $Name #'Find-Module AWS.Tools.*' is only slightly slower than Find-Module for a single module if ($Name.Count -gt $script:MaxModulesToFindIndividually) { $availableModules += PowerShellGet\Find-Module -Name 'AWS.Tools.*' -Repository 'PSGallery' @proxyParams -ErrorAction 'Stop' | Where-Object { $_.Name -in $Name -and $_.CompanyName -ceq $script:ExpectedModuleCompanyName } $missingModules = $Name | Where-Object { $_ -notin ($availableModules | Select-Object -ExpandProperty Name) } if ($missingModules) { Write-Verbose "[$($MyInvocation.MyCommand)] Retrying Find-Module on ($missingModules)" } } if ($missingModules) { #'Find-Module AWS.Tools.*' doesn't always return all modules, so we have to retry missing ones $missingModules | ForEach-Object { $availableModules += PowerShellGet\Find-Module -Name $_ -Repository 'PSGallery' @proxyParams -ErrorAction 'Ignore' | Where-Object { $_.Name -in $Name -and $_.CompanyName -ceq $script:ExpectedModuleCompanyName } } $missingModules = $Name | Where-Object { $_ -notin ($availableModules | Select-Object -ExpandProperty Name) } if ($missingModules) { throw "Could not find AWS.Tools module on PSGallery: $([string]::Join(', ', $missingModules))." } } $availableModules } End { Write-Verbose "[$($MyInvocation.MyCommand)] End" } } function Get-AWSToolsModuleDependenciesAndValidate { Param( ## Path of the manifest file to validate [Parameter(ValueFromPipelineByPropertyName, Mandatory)] [string] $Path, ## Name of the module to validate [Parameter(ValueFromPipelineByPropertyName, Mandatory)] [string] $Name ) Begin { Write-Verbose "[$($MyInvocation.MyCommand)] ConfirmPreference=$ConfirmPreference WhatIfPreference=$WhatIfPreference VerbosePreference=$VerbosePreference Name=$Name Path=$Path" } Process { $ErrorActionPreference = 'Stop' Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction Stop Add-Type -AssemblyName System.IO.Compression -ErrorAction Stop [Stream]$manifestFileStream = $null [Stream]$entryStream = $null [ZipArchive]$zipArchive = $null [string]$temporaryManifestFilePath = $null try { $zipArchive = [ZipFile]::OpenRead($Path) [ZipArchiveEntry]$entry = $zipArchive.GetEntry("$($Name).psd1") $entryStream = $entry.Open() $temporaryManifestFilePath = Join-Path ([Path]::GetTempPath()) "$([Path]::GetRandomFileName()).psd1" $manifestFileStream = [File]::OpenWrite($temporaryManifestFilePath) $entryStream.CopyTo($manifestFileStream) $manifestFileStream.Close(); if ($PSVersionTable.PSVersion.Major -lt 6 -or $IsWindows) { [Signature]$manifestSignature = Microsoft.PowerShell.Security\Get-AuthenticodeSignature -FilePath $temporaryManifestFilePath if ($manifestSignature.Status -eq 'Valid' -and $manifestSignature.SignerCertificate.Subject -eq $script:AWSToolsSignatureSubject) { Write-Verbose "[$($MyInvocation.MyCommand)] Manifest signature correctly validated" } else { throw "Error validating manifest signature for $($Name)" } } else { Write-Verbose "[$($MyInvocation.MyCommand)] Authenticode signature can only be verified on Windows, skipping" } [PSObject]$manifestData = Microsoft.PowerShell.Utility\Import-PowerShellDataFile $temporaryManifestFilePath if ($manifestData.PrivateData.ContainsKey('MinAWSToolsInstallerVersion')) { [Version]$minVersion = Get-CleanVersion $manifestData.PrivateData.MinAWSToolsInstallerVersion if ($minVersion -gt $script:CurrentMinAWSToolsInstallerVersion) { throw "$Name version $($manifestData.ModuleVersion) requires at least AWS.Tools.Installer version $minVersion. Run 'Update-Module AWS.Tools.Installer'." } } $manifestData.RequiredModules | ForEach-Object { Write-Verbose "[$($MyInvocation.MyCommand)] Found dependency $($_.ModuleName)" $_.ModuleName } } finally { if ($manifestFileStream) { $manifestFileStream.Dispose() } if ($entryStream) { $entryStream.Dispose() } if ($zipArchive) { $zipArchive.Dispose() } if ($temporaryManifestFilePath) { Microsoft.PowerShell.Management\Remove-Item -Path $temporaryManifestFilePath -WhatIf:$false } } } End { Write-Verbose "[$($MyInvocation.MyCommand)] End" } } <# .Synopsis Install AWS.Tools modules. .Description This cmdlet uses Install-Module to install AWS.Tools modules. Unless -SkipUpdate is specified, this cmdlet also updates all other currently installed AWS.Tools modules to the version being installed. .Notes This cmdlet uses the PSRepository named PSGallery as source. Use 'Get-PSRepository -Name PSGallery' for information on the PSRepository used by Update-AWSToolsModule. This cmdlet downloads all modules from https://www.powershellgallery.com/api/v2/package/ and considers it a trusted source. .Example Install-AWSToolsModule EC2,S3 -RequiredVersion 4.0.0.0 This example installs version 4.0.0.0 of AWS.Tools.EC2, AWS.Tools.S3 and their dependencies. #> function Install-AWSToolsModule { [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( ## Specifies names of the AWS.Tools modules to install. ## The names can be listed either with or without the "AWS.Tools." prefix (i.e. "AWS.Tools.Common" or simply "Common"). [Parameter(ValueFromPipelineByPropertyName, ValueFromPipeline, Mandatory, Position = 0)] [string[]] $Name, ## Specifies exact version number of the module to install. [Parameter(ValueFromPipelineByPropertyName, Position = 1)] [Version] $RequiredVersion, ## Specifies the minimum version of the modules to install. [Parameter(ValueFromPipelineByPropertyName)] [Version] $MinimumVersion, ## Specifies the maximum version of the modules to install. [Parameter(ValueFromPipelineByPropertyName)] [Version] $MaximumVersion, ## Specifies that, after a successful install, all other versions of the AWS Tools modules should be uninstalled. [Parameter(ValueFromPipelineByPropertyName)] [Switch] $CleanUp, ## Install-AWSToolsModule by default also updates all currently installed AWS.Tools modules. -SkipUpdate disables the update. [Parameter(ValueFromPipelineByPropertyName)] [Switch] $SkipUpdate, ## Specifies the installation scope of the module. The acceptable values for this parameter are AllUsers and CurrentUser. ## The AllUsers scope installs modules in a location that is accessible to all users of the computer: ## $env:ProgramFiles\PowerShell\Modules ## The CurrentUser installs modules in a location that is accessible only to the current user of the computer: ## $home\Documents\PowerShell\Modules ## When no Scope is defined, the default is CurrentUser. [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('CurrentUser', 'AllUsers')] [string] $Scope = 'CurrentUser', ## Overrides warning messages about installation conflicts about existing commands on a computer. [Parameter(ValueFromPipelineByPropertyName)] [Switch] $AllowClobber, ## Specifies a proxy server for the request, rather than connecting directly to an internet resource. [Parameter(ValueFromPipelineByPropertyName)] [Uri] $Proxy, ## Specifies a user account that has permission to use the proxy server specified by the Proxy parameter. [Parameter(ValueFromPipelineByPropertyName)] [PSCredential] $ProxyCredential, ## Forces an install of each specified module without a prompt to request confirmation [Parameter(ValueFromPipelineByPropertyName)] [Switch] $Force ) Begin { $RequiredVersion = Get-CleanVersion $RequiredVersion $MaximumVersion = Get-CleanVersion $MaximumVersion Write-Verbose "[$($MyInvocation.MyCommand)] ConfirmPreference=$ConfirmPreference WhatIfPreference=$WhatIfPreference VerbosePreference=$VerbosePreference Force=$Force Name=($Name) RequiredVersion=$RequiredVersion SkipUpdate=$SkipUpdate CleanUp=$CleanUp" } Process { $ErrorActionPreference = 'Stop' $Name = $Name | ForEach-Object { if ($_.Contains('.')) { $_ } else { "AWS.Tools.$_" } } | Sort-Object -Unique if ($Name -notlike 'AWS.Tools.*') { throw "The Name parameter must contain only AWS.Tools modules." } if ($Name -eq 'AWS.Tools.Installer') { throw "AWS.Tools.Installer cannot be used to install AWS.Tools.Installer. Use Update-Module instead." } [PSObject[]]$availableModulesToInstall = Find-AWSToolsModule -Name $Name -Proxy $Proxy -ProxyCredential $ProxyCredential [Version]$availableVersion = [Version[]]$availableModulesToInstall.Version | Measure-Object -Minimum | Select-Object -Expand Minimum if ($MinimumVersion -and $MinimumVersion -gt $availableVersion) { throw "The maximum version available is $availableVersion." } if ($RequiredVersion -and $RequiredVersion -gt $availableVersion) { throw "The maximum version available is $availableVersion." } if ($MinimumVersion -and $RequiredVersion -and $MinimumVersion -gt $RequiredVersion) { throw 'Parameter MinimumVersion is greater than RequiredVersion.' } if ($MaximumVersion -and $RequiredVersion -and $MaximumVersion -lt $RequiredVersion) { throw 'Parameter MaximumVersion is less than RequiredVersion.' } if ($MaximumVersion -and -not $RequiredVersion -and $MaximumVersion -lt $availableVersion) { $RequiredVersion = Find-Module -Name 'AWS.Tools.Common' -MaximumVersion $MaximumVersion | Select-Object -Expand Version } if (-not $RequiredVersion) { $RequiredVersion = $availableVersion } Write-Verbose "[$($MyInvocation.MyCommand)] Installing AWS Tools version $RequiredVersion" [string[]]$modulesToInstall = $availableModulesToInstall.Name if (-not $SkipUpdate) { Write-Verbose "[$($MyInvocation.MyCommand)] Searching installed modules" [PSModuleInfo[]]$installedAwsToolsModules = Get-AWSToolsModule -SkipIfInvalidSignature if ($installedAwsToolsModules) { $modulesToInstall = ($modulesToInstall + ($installedAwsToolsModules | Select-Object -Expand Name)) | Sort-Object -Unique Write-Verbose "[$($MyInvocation.MyCommand)] Merging existing modules into the list of modules to install: ($modulesToInstall)" } } $modulesToInstall = $modulesToInstall | Where-Object { -not (Get-Module $_ -ListAvailable -Verbose:$false | Where-Object { $_.Version -eq $RequiredVersion }) } Write-Verbose "[$($MyInvocation.MyCommand)] Removing already installed modules from the. Final list of modules to install: ($modulesToInstall)" if ($modulesToInstall) { if ($Force -or $WhatIfPreference -or $PSCmdlet.ShouldProcess("AWS Tools version $RequiredVersion")) { $ConfirmPreference = 'None' [string]$temporaryRepoDirectory = Join-Path ([Path]::GetTempPath()) ([Path]::GetRandomFileName()) Write-Verbose "[$($MyInvocation.MyCommand)] Create folder for temporary repository $temporaryRepoDirectory" Microsoft.PowerShell.Management\New-Item -ItemType Directory -Path $temporaryRepoDirectory -WhatIf:$false | Out-Null try { if (-not $WhatIfPreference) { PowerShellGet\Unregister-PSRepository -Name $script:AWSToolsTempRepoName -ErrorAction 'SilentlyContinue' Write-Verbose "[$($MyInvocation.MyCommand)] Registering temporary repository $script:AWSToolsTempRepoName" PowerShellGet\Register-PSRepository -Name $script:AWSToolsTempRepoName -SourceLocation $temporaryRepoDirectory -ErrorAction 'Stop' PowerShellGet\Set-PSRepository -Name $script:AWSToolsTempRepoName -InstallationPolicy Trusted } Add-Type -AssemblyName System.Net.Http -ErrorAction Stop [HttpClient]$httpClient = $null [HttpClientHandler]$httpClientHandler = $null [List[PSCustomObject]]$tasks = @() Write-Verbose "[$($MyInvocation.MyCommand)] Downloading modules to temporary repository" try { $httpClientHandler = [HttpClientHandler]::new() if ($Proxy) { $httpClientHandler.Proxy = [WebProxy]::new($Proxy) if ($ProxyCredential) { $httpClientHandler.Proxy.Credentials = $ProxyCredential.GetNetworkCredential() } } $httpClient = [HttpClient]::new($httpClientHandler); Add-Type $script:ParallelDownloaderClassCode -ReferencedAssemblies System.Net.Http,System.Threading.Tasks [ParallelDownloader]$parallelDownloader = [ParallelDownloader]::new($httpClient) [string[]]$modulesToDownload = $modulesToInstall [HashSet[string]]$savedModules = New-Object -TypeName System.Collections.Generic.HashSet[string] Write-Verbose "[$($MyInvocation.MyCommand)] Downloading modules ($modulesToDownload)" while ($modulesToDownload) { [string[]]$dependencies = @() $tasks = $modulesToDownload | Where-Object { $savedModules.Add($_) } | ForEach-Object { [string]$nupkgFilePath = Join-Path $temporaryRepoDirectory "$_.$($RequiredVersion).nupkg" Write-Verbose "[$($MyInvocation.MyCommand)] Downloading module $_ to $TemporaryRepoDirectory" [PSCustomObject]@{ Task = $parallelDownloader.DownloadToFile("https://www.powershellgallery.com/api/v2/package/$_/$RequiredVersion", $nupkgFilePath) ModuleName = $_ Path = $nupkgFilePath } } while ($tasks) { [int]$taskIndex = [Task]::WaitAny($tasks.Task) [PSObject]$task = $tasks[$taskIndex] $tasks.RemoveAt($taskIndex) if ($task.Task.IsCompleted) { $dependencies += Get-AWSToolsModuleDependenciesAndValidate -Path $task.Path -Name $task.ModuleName } else { throw "Error downloading $($task.ModuleName): $($task.Task.Exception)" } } $modulesToDownload = $dependencies | Sort-Object -Unique } } finally { if ($tasks) { Write-Verbose "[$($MyInvocation.MyCommand)] Cancelling $($tasks.Count) tasks" $parallelDownloader.Cancel() try { [Task]::WaitAll($tasks.Task) } catch { } } if ($httpClient) { $httpClient.Dispose() } if ($httpClientHandler) { $httpClientHandler.Dispose() } } Write-Verbose "[$($MyInvocation.MyCommand)] Installing modules ($modulesToInstall)" $installModuleParams = @{ RequiredVersion = $RequiredVersion Scope = $Scope Repository = $script:AWSToolsTempRepoName AllowClobber = $AllowClobber Confirm = $false ErrorAction = 'Stop' } $modulesToInstall | ForEach-Object { if (-not $WhatIfPreference) { Write-Host "Installing module $_ version $RequiredVersion" PowerShellGet\Install-Module -Name $_ @installModuleParams } else { Write-Host "What if: Installing module $_ version $RequiredVersion" } } Write-Verbose "[$($MyInvocation.MyCommand)] Modules install complete" } finally { if (-not $WhatIfPreference) { Write-Verbose "[$($MyInvocation.MyCommand)] Unregistering temporary repository $script:AWSToolsTempRepoName" PowerShellGet\Unregister-PSRepository -Name $script:AWSToolsTempRepoName -ErrorAction 'Continue' } Write-Verbose "[$($MyInvocation.MyCommand)] Delete repository folder $temporaryRepoDirectory" Microsoft.PowerShell.Management\Remove-Item -Path $temporaryRepoDirectory -Recurse -WhatIf:$false } } } else { Write-Verbose "[$($MyInvocation.MyCommand)] All modules are up to date" } if ($CleanUp) { Uninstall-AWSToolsModule -ExceptVersion $RequiredVersion } } End { Write-Verbose "[$($MyInvocation.MyCommand)] End" } } <# .Synopsis Updates all currently installed AWS.Tools modules. .Description This cmdlet uses Install-Module to update all AWS.Tools modules. .Notes This cmdlet uses the PSRepository named PSGallery as source. Use 'Get-PSRepository -Name PSGallery' for information on the PSRepository used by Update-AWSToolsModule. This cmdlet downloads all modules from https://www.powershellgallery.com/api/v2/package/ and considers it a trusted source. .Example Update-AWSToolsModule -CleanUp This example updates all installed AWS.Tools modules to the latest version available on the PSGallery and uninstalls all other versions. #> function Update-AWSToolsModule { [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] Param( ## Specifies the exact version of the modules to update to. [Parameter(ValueFromPipelineByPropertyName, Position = 0)] [Version] $RequiredVersion, ## Specifies the maximum version of the modules to update to. [Parameter(ValueFromPipelineByPropertyName)] [Version] $MaximumVersion, ## Specifies that, after a successful install, all other versions of the AWS Tools modules should be uninstalled. [Parameter(ValueFromPipelineByPropertyName)] [Switch] $CleanUp, #Specifies the installation scope of the module. The acceptable values for this parameter are AllUsers and CurrentUser. #The AllUsers scope installs modules in a location that is accessible to all users of the computer: # $env:ProgramFiles\PowerShell\Modules #The CurrentUser installs modules in a location that is accessible only to the current user of the computer: # $home\Documents\PowerShell\Modules #When no Scope is defined, the default is CurrentUser. [Parameter(ValueFromPipelineByPropertyName)] [ValidateSet('CurrentUser', 'AllUsers')] [string] $Scope = 'CurrentUser', #Overrides warning messages about installation conflicts about existing commands on a computer. [Parameter(ValueFromPipelineByPropertyName)] [Switch] $AllowClobber, #Specifies a proxy server for the request, rather than connecting directly to an internet resource. [Parameter(ValueFromPipelineByPropertyName)] [Uri] $Proxy, ## Specifies a user account that has permission to use the proxy server specified by the Proxy parameter. [Parameter(ValueFromPipelineByPropertyName)] [PSCredential] $ProxyCredential, ## Forces an update of each specified module without a prompt to request confirmation [Parameter(ValueFromPipelineByPropertyName)] [Switch] $Force ) Begin { $RequiredVersion = Get-CleanVersion $RequiredVersion $MaximumVersion = Get-CleanVersion $MaximumVersion Write-Verbose "[$($MyInvocation.MyCommand)] ConfirmPreference=$ConfirmPreference WhatIfPreference=$WhatIfPreference VerbosePreference=$VerbosePreference Force=$Force" } Process { $ErrorActionPreference = 'Stop' Write-Verbose "[$($MyInvocation.MyCommand)] Searching installed modules" [PSModuleInfo[]]$installedAwsToolsModules = Get-AWSToolsModule -SkipIfInvalidSignature [string[]]$installedAwsToolsModuleNames = $installedAwsToolsModules | Select-Object -Expand Name | Sort-Object -Unique if ($installedAwsToolsModuleNames) { Write-Verbose "[$($MyInvocation.MyCommand)] Found modules ($installedAwsToolsModuleNames)" $installAWSToolsModuleParams = @{ Name = $installedAwsToolsModuleNames RequiredVersion = $RequiredVersion MaximumVersion = $MaximumVersion Scope = $Scope AllowClobber = $AllowClobber CleanUp = $CleanUp Force = $Force SkipUpdate = $true Proxy = $Proxy ProxyCredential = $ProxyCredential } Install-AWSToolsModule @installAWSToolsModuleParams } } End { Write-Verbose "[$($MyInvocation.MyCommand)] End" } } # SIG # Begin signature block # MIIcUAYJKoZIhvcNAQcCoIIcQTCCHD0CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDBBpOwOQPj8Vg2 # F2MthTd1WOMHBVtrtVmRjFzOdRNGnqCCF0cwggS5MIIDoaADAgECAhArd4OFAE9M # ppHAfzWQwHt/MA0GCSqGSIb3DQEBCwUAMIGEMQswCQYDVQQGEwJVUzEdMBsGA1UE # ChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0 # IE5ldHdvcmsxNTAzBgNVBAMTLFN5bWFudGVjIENsYXNzIDMgU0hBMjU2IENvZGUg # U2lnbmluZyBDQSAtIEcyMB4XDTE3MDcxMDAwMDAwMFoXDTIwMDcxMDIzNTk1OVow # ajELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1Nl # YXR0bGUxGTAXBgNVBAoMEEFtYXpvbi5jb20sIEluYy4xGTAXBgNVBAMMEEFtYXpv # bi5jb20sIEluYy4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC18TJW # m2/aGpvb+TP2CZMg49X/5bjDAgPxWlAs7bqDyZnRYJork4rLmejpeJu+2BhRjZeK # OirT4XveddBsdL1/TR+FKp8BXEsrm0wcR4yT6mNHJ9yCgC1YBNG91bZ75kRIT+46 # chbC7eNI5703wi8ejxe2KvvOERppBTaFStVJuAHab69dvFma8qE3s7wbqPdQ5eTI # +Xm0bXp8cObS+vj+hf3N2pfDNWM8ITva3kbowGoCW0rKzpf7fBGtBOKnOCCSL0yC # AOwLlFkslemVyrT1/HTDjOTKCro016HxOPddA4cefvr2ZhGlRZQQHg7wMdG7TpZX # ueQ6LoS9UxlzCYHFAgMBAAGjggE+MIIBOjAJBgNVHRMEAjAAMA4GA1UdDwEB/wQE # AwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzBhBgNVHSAEWjBYMFYGBmeBDAEEATBM # MCMGCCsGAQUFBwIBFhdodHRwczovL2Quc3ltY2IuY29tL2NwczAlBggrBgEFBQcC # AjAZDBdodHRwczovL2Quc3ltY2IuY29tL3JwYTAfBgNVHSMEGDAWgBTUwAYiSes5 # S92T4lyhuEd2CXIDWDArBgNVHR8EJDAiMCCgHqAchhpodHRwOi8vcmIuc3ltY2Iu # Y29tL3JiLmNybDBXBggrBgEFBQcBAQRLMEkwHwYIKwYBBQUHMAGGE2h0dHA6Ly9y # Yi5zeW1jZC5jb20wJgYIKwYBBQUHMAKGGmh0dHA6Ly9yYi5zeW1jYi5jb20vcmIu # Y3J0MA0GCSqGSIb3DQEBCwUAA4IBAQC+C4TcK44ph2QQK/1f65jOR23DtSBC3y9a # bzRHdo4qxmcAW5ot69os7GJfzgVsA5lh1IT4+aMuGYA4GTcF6iTSOMgFSRwP8urI # N2BprsWuMJsQ7+Flo3PBRN3dU6idOlFKOfuRxgIHRn47t2yRan6XTNhfiWl84DrD # NjSTnk4c72Gzu0hiwQB9OTsf8CQP3Shb3ZzcAOmeUB01TFoJU34PfJpKlKQZeQIi # W5WdPPr1G/0cAHgejDHtdNYcSqIWfoGeYgCxUg1IFpp1VmPlqb/de8QKONzPDK6/ # 5hulSGqGgpRmEkwGGJiQeOB51GxYZRCPq3hN3UJ6N0A+hYzj7yspMIIFRzCCBC+g # AwIBAgIQfBs1NUrn23TnQV8RacprqDANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE # BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln # biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5j # LiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBV # bml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNDA3MjIw # MDAwMDBaFw0yNDA3MjEyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEdMBsGA1UEChMU # U3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5l # dHdvcmsxNTAzBgNVBAMTLFN5bWFudGVjIENsYXNzIDMgU0hBMjU2IENvZGUgU2ln # bmluZyBDQSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA15VD # 1NzfZ645+1KktiYxBHDpt45bKro3aTWVj7vAMOeG2HO73+vRdj+KVo7rLUvwVxhO # sY2lM9MLdSPVankn3aPT9w6HZbXerRzx9TW0IlGvIqHBXUuQf8BZTqudeakC1x5J # sTtNh/7CeKu/71KunK8I2TnlmlE+aV8wEE5xY2xY4fAgMxsPdL5byxLh24zEgJRy # u/ZFmp7BJQv7oxye2KYJcHHswEdMj33D3hnOPu4Eco4X0//wsgUyGUzTsByf/qV4 # IEJwQbAmjG8AyDoAEUF6QbCnipEEoJl49He082Aq5mxQBLcUYP8NUfSoi4T+Idpc # Xn31KXlPsER0b21y/wIDAQABo4IBeDCCAXQwLgYIKwYBBQUHAQEEIjAgMB4GCCsG # AQUFBzABhhJodHRwOi8vcy5zeW1jZC5jb20wEgYDVR0TAQH/BAgwBgEB/wIBADBm # BgNVHSAEXzBdMFsGC2CGSAGG+EUBBxcDMEwwIwYIKwYBBQUHAgEWF2h0dHBzOi8v # ZC5zeW1jYi5jb20vY3BzMCUGCCsGAQUFBwICMBkaF2h0dHBzOi8vZC5zeW1jYi5j # b20vcnBhMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9zLnN5bWNiLmNvbS91bml2 # ZXJzYWwtcm9vdC5jcmwwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDgYDVR0PAQH/BAQD # AgEGMCkGA1UdEQQiMCCkHjAcMRowGAYDVQQDExFTeW1hbnRlY1BLSS0xLTcyNDAd # BgNVHQ4EFgQU1MAGIknrOUvdk+JcobhHdglyA1gwHwYDVR0jBBgwFoAUtnf6aUhH # n1MS1cLqBzJ2B9GXBxkwDQYJKoZIhvcNAQELBQADggEBAH/ryqfqi3ZC6z6OIFQw # 47e53PpIPhbHD0WVEM0nhqNm8wLtcfiqwlWXkXCD+VJ+Umk8yfHglEaAGLuh1KRW # pvMdAJHVhvNIh+DLxDRoIF60y/kF7ZyvcFMnueg+flGgaXGL3FHtgDolMp9Er25D # KNMhdbuX2IuLjP6pBEYEhfcVnEsRjcQsF/7Vbn+a4laS8ZazrS359N/aiZnOsjhE # wPdHe8olufoqaDObUHLeqJ/UzSwLNL2LMHhA4I2OJxuQbxq+CBWBXesv4lHnUR7J # eCnnHmW/OO8BSgEJJA4WxBR5wUE3NNA9kVKUneFo7wjw4mmcZ26QCxqTcdQmAsPA # WiMwggZqMIIFUqADAgECAhADAZoCOv9YsWvW1ermF/BmMA0GCSqGSIb3DQEBBQUA # MGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT # EHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQg # Q0EtMTAeFw0xNDEwMjIwMDAwMDBaFw0yNDEwMjIwMDAwMDBaMEcxCzAJBgNVBAYT # AlVTMREwDwYDVQQKEwhEaWdpQ2VydDElMCMGA1UEAxMcRGlnaUNlcnQgVGltZXN0 # YW1wIFJlc3BvbmRlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKNk # Xfx8s+CCNeDg9sYq5kl1O8xu4FOpnx9kWeZ8a39rjJ1V+JLjntVaY1sCSVDZg85v # Zu7dy4XpX6X51Id0iEQ7Gcnl9ZGfxhQ5rCTqqEsskYnMXij0ZLZQt/USs3OWCmej # vmGfrvP9Enh1DqZbFP1FI46GRFV9GIYFjFWHeUhG98oOjafeTl/iqLYtWQJhiGFy # GGi5uHzu5uc0LzF3gTAfuzYBje8n4/ea8EwxZI3j6/oZh6h+z+yMDDZbesF6uHjH # yQYuRhDIjegEYNu8c3T6Ttj+qkDxss5wRoPp2kChWTrZFQlXmVYwk/PJYczQCMxr # 7GJCkawCwO+k8IkRj3cCAwEAAaOCAzUwggMxMA4GA1UdDwEB/wQEAwIHgDAMBgNV # HRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMIIBvwYDVR0gBIIBtjCC # AbIwggGhBglghkgBhv1sBwEwggGSMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5k # aWdpY2VydC5jb20vQ1BTMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4AeQAgAHUA # cwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQAZQAgAGMA # bwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUAIABvAGYA # IAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAAYQBuAGQA # IAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcAcgBlAGUA # bQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIAaQBsAGkA # dAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQAZQBkACAA # aABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMAsGCWCGSAGG # /WwDFTAfBgNVHSMEGDAWgBQVABIrE5iymQftHt+ivlcNK2cCzTAdBgNVHQ4EFgQU # YVpNJLZJMp1KKnkag0v0HonByn0wfQYDVR0fBHYwdDA4oDagNIYyaHR0cDovL2Ny # bDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5jcmwwOKA2oDSG # Mmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRENBLTEu # Y3JsMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGln # aWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5j # b20vRGlnaUNlcnRBc3N1cmVkSURDQS0xLmNydDANBgkqhkiG9w0BAQUFAAOCAQEA # nSV+GzNNsiaBXJuGziMgD4CH5Yj//7HUaiwx7ToXGXEXzakbvFoWOQCd42yE5FpA # +94GAYw3+puxnSR+/iCkV61bt5qwYCbqaVchXTQvH3Gwg5QZBWs1kBCge5fH9j/n # 4hFBpr1i2fAnPTgdKG86Ugnw7HBi02JLsOBzppLA044x2C/jbRcTBu7kA7YUq/OP # Q6dxnSHdFMoVXZJB2vkPgdGZdA0mxA5/G7X1oPHGdwYoFenYk+VVFvC7Cqsc21xI # J2bIo4sKHOWV2q7ELlmgYd3a822iYemKC23sEhi991VUQAOSK2vCUcIKSK+w1G7g # 9BQKOhvjjz3Kr2qNe9zYRDCCBs0wggW1oAMCAQICEAb9+QOWA63qAArrPye7uhsw # DQYJKoZIhvcNAQEFBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0 # IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNl # cnQgQXNzdXJlZCBJRCBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTIxMTExMDAw # MDAwMFowYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcG # A1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgQXNzdXJl # ZCBJRCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6IItmfnK # wkKVpYBzQHDSnlZUXKnE0kEGj8kz/E1FkVyBn+0snPgWWd+etSQVwpi5tHdJ3InE # Ctqvy15r7a2wcTHrzzpADEZNk+yLejYIA6sMNP4YSYL+x8cxSIB8HqIPkg5QycaH # 6zY/2DDD/6b3+6LNb3Mj/qxWBZDwMiEWicZwiPkFl32jx0PdAug7Pe2xQaPtP77b # lUjE7h6z8rwMK5nQxl0SQoHhg26Ccz8mSxSQrllmCsSNvtLOBq6thG9IhJtPQLnx # TPKvmPv2zkBdXPao8S+v7Iki8msYZbHBc63X8djPHgp0XEK4aH631XcKJ1Z8D2Kk # PzIUYJX9BwSiCQIDAQABo4IDejCCA3YwDgYDVR0PAQH/BAQDAgGGMDsGA1UdJQQ0 # MDIGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF # BQcDCDCCAdIGA1UdIASCAckwggHFMIIBtAYKYIZIAYb9bAABBDCCAaQwOgYIKwYB # BQUHAgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9y # eS5odG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYA # IAB0AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkA # dAB1AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAA # RABpAGcAaQBDAGUAcgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAA # UgBlAGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAA # dwBoAGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4A # ZAAgAGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkA # bgAgAGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wCwYJYIZIAYb9bAMVMBIGA1Ud # EwEB/wQIMAYBAf8CAQAweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRw # Oi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRz # LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1Ud # HwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFz # c3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNv # bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwHQYDVR0OBBYEFBUAEisTmLKZ # B+0e36K+Vw0rZwLNMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0G # CSqGSIb3DQEBBQUAA4IBAQBGUD7Jtygkpzgdtlspr1LPUukxR6tWXHvVDQtBs+/s # dR90OPKyXGGinJXDUOSCuSPRujqGcq04eKx1XRcXNHJHhZRW0eu7NoR3zCSl8wQZ # Vann4+erYs37iy2QwsDStZS9Xk+xBdIOPRqpFFumhjFiqKgz5Js5p8T1zh14dpQl # c+Qqq8+cdkvtX8JLFuRLcEwAiR78xXm8TBJX/l/hHrwCXaj++wc4Tw3GXZG5D2dF # zdaD7eeSDY2xaYxP+1ngIw/Sqq4AfO6cQg7PkdcntxbuD8O9fAqg7iwIVYUiuOsY # Gk38KiGtSTGDR5V3cdyxG0tLHBCcdxTBnU8vWpUIKRAmMYIEXzCCBFsCAQEwgZkw # gYQxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEf # MB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazE1MDMGA1UEAxMsU3ltYW50 # ZWMgQ2xhc3MgMyBTSEEyNTYgQ29kZSBTaWduaW5nIENBIC0gRzICECt3g4UAT0ym # kcB/NZDAe38wDQYJYIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAA # oQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4w # DAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgYDhhQvuT8ECxmbn+jXO8NOS0 # EjOJWFENngVMt+z/IhwwDQYJKoZIhvcNAQEBBQAEggEAjCnzwVECDXlRcwxfrkiF # OeDScihqXdEoqY5dSlaM8/QvL6cpPEE5hcKT4ndClV3GCBzRVPOkAbpXThzL0oQG # 3JatV8pTOnBZHVGxIGoEsjz0N7ozkEa9g8Ln1rM+fLD3TMHSr05e+5cuKcn73Hoo # CHqMoatChIIlG2Yawo4/R6n8PsWjjE8ll8idLFZibszPg2mZQi8vCZqrdgeQ1ZHZ # nVb3X8DccBh663UkXZKKYOppwzjhlmZQBt8qWxsdrCUYjVn7IkuhbltRhMfLlQ1a # ZUuCheYj1yAqLM5jjsWI5xZGc+QbVYy9cTn7FSVGywC6/RLZHbtp2/uIG1E4CCp8 # x6GCAg8wggILBgkqhkiG9w0BCQYxggH8MIIB+AIBATB2MGIxCzAJBgNVBAYTAlVT # MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j # b20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMQIQAwGaAjr/WLFr # 1tXq5hfwZjAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAc # BgkqhkiG9w0BCQUxDxcNMTkxMjEyMjMxNTU4WjAjBgkqhkiG9w0BCQQxFgQUr7Jh # Lve5B4sUDPq8l5wn2LUGhxowDQYJKoZIhvcNAQEBBQAEggEAReSX3tAy8EOZrUb4 # RxZ0hWfqgbtb6aZI83tZSa6AUT9Q1TBp+3U/gu9REyVwmnqWZDhzsxobCrkOPjyH # esBpGaavS10diOdaRc/tBGHo9gujr+d6T4bPAq+/hgHxbg4syspmsQ2zNCklOBXJ # J+b3jNGiws7a+0icsz+oU9vJTWqrJKZ5osElsNcK53uavboq7wVjhFpqOt5QXYgK # wxAtRr7+Z/a8DeBVFGBcKIpokQ0c884oXIhUU/oM3vcvH0z7hrGB4C3nKlJ/jRGE # VXMTDZZ/Rgt/6gShMSpBmU3cflhGPywiRM3AoBr9T6F5dt+c5p3SEjYxcrin5x4J # 4P8R3Q== # SIG # End signature block |