Public/Install-AWSToolsModule.ps1
|
<#
.Synopsis Installs AWS Tools for PowerShell modules and cleans up previous versions. .Description This cmdlet installs AWS Tools for PowerShell modules from the AWS CloudFront-hosted AWS.Tools.zip file. By default, it installs all available AWS Tools modules to the latest version. Installation is skipped if the target version of AWS.Tools.Common is already installed unless Force is specified. Note: AWS.Tools.Installer is excluded from this cmdlet and must be managed separately using Install-AWSToolsInstaller and Uninstall-AWSToolsInstaller. .Parameter Name Specifies names of the AWS.Tools modules to install. All AWS Tools for PowerShell modules are installed by default. The names can be listed either with or without the "AWS.Tools." prefix (i.e. "AWS.Tools.Common" or simply "Common"). Note: AWS.Tools.Common is automatically included when specific modules are requested, as it is required for all AWS.Tools modules to function properly. Note: AWS.Tools.Installer cannot be specified here. Use Install-AWSToolsInstaller to manage the installer module. .Parameter Version Specifies the version to install. Supports exact versions (e.g., 5.0.151) or major version wildcards (e.g., 4.*, 5.*). Minor version wildcards (e.g., 5.0.*, 5.1.*) are not supported. Latest version is installed by default when not specified. .Parameter MinimumVersion Specifies the minimum version of the modules to install. This parameter is deprecated and should no longer be used as it will be removed in the next major version. .Parameter MaximumVersion Specifies the maximum version of the modules to install. This parameter is deprecated and should no longer be used as it will be removed in the next major version. .Parameter SkipUpdate This parameter is deprecated and should no longer be used as it will be removed in the next major version. The parameter is ignored and has no effect in AWS.Tools.Installer 2.0.0. This version of AWS.Tools.Installer attempts to install all available modules for a version unless the Name parameter is used to specify certain modules. .Parameter SkipPublisherCheck This parameter is deprecated and should no longer be used as it will be removed in the next major version. The parameter is ignored and has no effect in AWS.Tools.Installer 2.0.0. There is no publisher check applied by this version of AWS.Tools.Installer. .Parameter AllowClobber This parameter is deprecated and should no longer be used as it will be removed in the next major version. The parameter is ignored and has no effect in AWS.Tools.Installer 2.0.0. Existing cmdlets will be clobbered if their names conflict with cmdlets installed by AWS Tools for PowerShell modules. .Parameter Proxy This parameter is deprecated and should no longer be used as it will be removed in the next major version. The parameter is ignored and has no effect in AWS.Tools.Installer 2.0.0. Proxy support has been removed in favor of CloudFront-based distribution. Previously this parameter specified a proxy server for the request, rather than connecting directly to an internet resource. .Parameter ProxyCredential This parameter is deprecated and should no longer be used as it will be removed in the next major version. The parameter is ignored and has no effect in AWS.Tools.Installer 2.0.0. Proxy support has been removed in favor of CloudFront-based distribution. Previously this parameter specified a user account that had permission to use the proxy server specified by the Proxy parameter. .Parameter Cleanup Switch that uninstalls other versions of installed AWS Tools modules after successful installation like when using <code>Uninstall-AWSToolsModule -ExceptVersion ...</code> Otherwise, by default, other versions remain installed. When the Name parameter is specified, cleanup only affects the named modules. When Name is not specified, all AWS Tools modules (except installer) are cleaned up. .Parameter CleanUpLegacyModuleScope Runs a separate cleanup of all instances of AWSPowerShell and AWSPowerShell.NetCore found in the specified scope. Warns on failure. The acceptable values for this parameter are AllUsers and CurrentUser. .Parameter Scope Specifies the installation scope of the module as well as scope for module cleanup. 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. The CurrentUser installs modules in a location that is accessible only to the current user of the computer. Default value is 'CurrentUser'. .Parameter SourceZipPath Path to a local AWS.Tools.zip file for offline/air-gapped installation. Must be a file path, not a directory. Requires either -SourceHashPath for SHA512 validation or -SkipIntegrityCheck to bypass validation. .Parameter SourceHashPath Path to the SHA512 hash file for validating the integrity of the zip file specified with -SourceZipPath. When using -SourceZipPath, you must either provide this parameter for validation or use -SkipIntegrityCheck to bypass validation. .Parameter SkipIntegrityCheck Switch that bypasses SHA512 validation when specified. By default, SHA512 validation is performed to ensure the integrity of downloaded files. .Parameter Force Forces installation even if the same version is already installed. Without this parameter, installation is skipped if the target version already exists. .Parameter Prerelease Allows installation of prerelease versions. When specified, the Version parameter can include a prerelease tag (e.g., 5.0.0-preview001). This switch must be specified when installing prerelease versions; otherwise, an error will be thrown. .Parameter Timeout Specifies the timeout in seconds for downloading the AWS.Tools.zip file. Default is 300 seconds (5 minutes). Increase this value for slower internet connections or when downloading large files. .Notes This cmdlet sources modules from the AWS CloudFront-hosted AWS.Tools.zip file for improved speed and reliability. There is no support for installing AWSPowerShell or AWSPowerShell.NetCore as these are legacy modules. AWS.Tools.Installer is also excluded and must be managed using Install-AWSToolsInstaller. This cmdlet does not support installation from a PSRepository including PSGallery but examples to accomplish this using alternative cmdlets are provided below. .Example Install-AWSToolsModule -Version 5.* This example installs the latest modules with major version 5 from CloudFront-hosted AWS.Tools.zip. Using major version wildcards reduces the risk of breaking changes while still receiving bug fixes and new features within the major version. .Example Install-AWSToolsModule -Version 5.0.0-preview001 -Prerelease This example installs a preview/prerelease version of AWS Tools modules. The -Prerelease switch must be specified when installing versions with prerelease tags (e.g., 5.0.0-preview001). This is useful for testing upcoming releases before they are generally available. .Example Install-AWSToolsModule -Version 5.0.103 -Cleanup This example installs version 5.0.103 of all AWS Tools modules from AWS.Tools.zip and removes all other versions. Specifying exact versions ensures predictable behavior and is recommended for production workloads. .Example Install-AWSToolsModule -WarningAction SilentlyContinue This example installs the latest available version of all AWS Tools modules from CloudFront-hosted AWS.Tools.zip. The WarningAction parameter suppresses the production workload warning when you intentionally want the latest version. .Example Install-AWSToolsModule -Cleanup -Confirm:$false This example installs all AWS Tools modules from CloudFront-hosted AWS.Tools.zip and removes all non-latest AWS Tools for PowerShell modules without requiring interactive confirmation. Note: Installing without a version constraint may introduce unexpected changes in production workloads. .Example Install-AWSToolsModule -Name EC2,S3 This example installs only the EC2 and S3 modules (and their dependencies). .Example Install-AWSToolsModule -Name EC2,S3 -Cleanup This example installs only the EC2 and S3 modules (and their dependencies) and removes all other versions of EC2 and S3 only, leaving other AWS Tools modules untouched. .Example Install-AWSToolsModule -Timeout 600 This example installs all AWS Tools modules with a 10-minute download timeout for slower connections. .Example Install-AWSToolsModule -Force This example forces reinstallation of AWS Tools modules even if the same version is already installed. .Example Install-AWSToolsModule -Scope AllUsers This example installs AWS Tools modules to the system-wide location accessible to all users. .Example Install-AWSToolsModule -SourceZipPath "C:\Downloads\AWS.Tools.zip" This example installs AWS Tools modules from a local zip file for offline scenarios. .Example Install-AWSToolsModule -Path "C:\DevModules" This example installs AWS Tools modules to a custom directory for testing or development. .Example Install-AWSToolsModule -WhatIf This example performs a what-if analysis to preview what would be installed without making changes. .Example Install-AWSToolsModule -CleanUpLegacyModuleScope CurrentUser -Confirm:$false This example installs AWS Tools modules and cleans up legacy AWSPowerShell modules from the current user scope. .Example $ProgressPreference = 'SilentlyContinue' Install-AWSToolsModule $ProgressPreference = 'Continue' This example suppresses progress bar output during installation, which can significantly reduce execution time, particularly in PowerShell 5.1. The progress preference is restored after installation completes. Currently unable to add support for -ProgressAction. .Example $common = Install-Module AWS.Tools.Common -PassThru Get-AWSService | ForEach-Object {Install-Module -Name $_.ModuleName -RequiredVersion $common.Version} This example shows how to install active modules for the latest version from PSGallery. A private repository can be specified with the <code>-Repository</code> parameter for both commands. This alternative approach take many magnitudes longer to execute than the equivalent <code>Install-AWSToolsModule</code> command and may fail for some modules during the daily release. Note: PSGallery if not advised as a dependency for production workloads. There is no SLA for the availability of PSGallery. .Example $common = Install-PSResource AWS.Tools.Common -PassThru Get-AWSService | ForEach-Object {Install-PSResource -Name $_.ModuleName -Version $common.Version -SkipDependencyCheck} This example is similar to the previous but uses the <code>Microsoft.PowerShell.PSResourceGet</code>. This is several magnitudes faster than the previous example but still several magnitudes slower than the equivalent <code>Install-AWSToolsModule</code> command and may fail for some modules during the daily release. Note: PSGallery if not advised as a dependency for production workloads. There is no SLA for the availability of PSGallery. #> function Install-AWSToolsModule { [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium', DefaultParameterSetName = 'ManagedCloudFront')] Param( [Parameter(Position = 0)] [string[]] [Alias('ModuleName')] $Name, [Parameter(ParameterSetName = 'ManagedCloudFront', Position = 1)] [Parameter(ParameterSetName = 'Legacy', Position = 1)] [string] [Alias('RequiredVersion')] $Version, [Parameter(ParameterSetName = 'Legacy')] [Version] $MinimumVersion, [Parameter(ParameterSetName = 'Legacy')] [Version] $MaximumVersion, [Parameter()] [Switch] $Cleanup, [Parameter()] [ValidateSet('CurrentUser', 'AllUsers')] [string] $CleanUpLegacyModuleScope, [Parameter()] [ValidateSet('CurrentUser', 'AllUsers')] [string] $Scope = 'CurrentUser', [Parameter(ParameterSetName = 'SourceZipPath', Mandatory)] [string] $SourceZipPath, [Parameter(ParameterSetName = 'SourceZipPath')] [string] $SourceHashPath, [Parameter()] [Switch] $SkipIntegrityCheck, [Parameter()] [string] $Path, [Parameter()] [Switch] $Force, [Parameter()] [int] $Timeout = $script:Config.network.DefaultTimeout, [Parameter()] [switch] $SkipUpdate, [Parameter()] [switch] $SkipPublisherCheck, [Parameter()] [switch] $AllowClobber, [Parameter()] [uri] $Proxy, [Parameter()] [pscredential] $ProxyCredential, [Parameter(ParameterSetName = 'ManagedCloudFront')] [Alias('AllowPrerelease')] [switch] $Prerelease ) Begin { # Initialize progress bar with ID to prevent it from being overridden by other progress bars Write-Progress -Id 1 -Activity "Install-AWSToolsModule" -Status "Initializing..." -PercentComplete 0 # Parameter validation for version constraints if ($PSCmdlet.ParameterSetName -eq 'ManagedCloudFront' -or $PSCmdlet.ParameterSetName -eq 'Legacy') { # Deprecation warnings for restored parameters if ($MinimumVersion) { Write-Warning "The MinimumVersion parameter is deprecated and should no longer be used as it will be removed in the next major version." } if ($MaximumVersion) { Write-Warning "The MaximumVersion parameter is deprecated and should no longer be used as it will be removed in the next major version." } # Deprecation warnings for legacy parameters (ignored) if ($SkipUpdate) { Write-Warning "The SkipUpdate parameter is deprecated and should no longer be used as it will be removed in the next major version." } if ($SkipPublisherCheck) { Write-Warning "The SkipPublisherCheck parameter is deprecated and should no longer be used as it will be removed in the next major version." } if ($AllowClobber) { Write-Warning "The AllowClobber parameter is deprecated and should no longer be used as it will be removed in the next major version." } if ($Proxy) { Write-Warning "The Proxy parameter is deprecated and should no longer be used as it will be removed in the next major version." } if ($ProxyCredential) { Write-Warning "The ProxyCredential parameter is deprecated and should no longer be used as it will be removed in the next major version." } # Issue warning if version is unspecified (no version constraint at all) $isUnspecifiedVersion = [string]::IsNullOrWhiteSpace($Version) if ($isUnspecifiedVersion) { Write-Warning @" Installing the latest module without a version constraint may introduce breaking changes or unexpected behavior in production workloads. For production environments, consider specifying a major version wildcard (e.g., -Version '5.*') or an exact version (e.g., -Version '5.0.103'). To suppress this warning, specify a version constraint. Alternatively, you can suppress ALL warnings using -WarningAction SilentlyContinue. "@ } # 1.0.3-compatible validation: Parameters can be used together if ($MinimumVersion -and $MaximumVersion -and $MinimumVersion -gt $MaximumVersion) { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( ([System.ArgumentException]"Parameter MinimumVersion ('$MinimumVersion') cannot be greater than MaximumVersion ('$MaximumVersion')."), 'InvalidVersionRange', [System.Management.Automation.ErrorCategory]::InvalidArgument, $MinimumVersion ) ) } if ($MinimumVersion -and $Version) { # Validate MinimumVersion <= Version (1.0.3 behavior) # Extract base version for comparison (handle semver strings) $versionForComparison = $Version if ($Version -match '^(\d+\.\d+\.\d+)') { $versionForComparison = $Matches[1] } $versionObj = [Version]$versionForComparison if ($MinimumVersion -gt $versionObj) { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( ([System.ArgumentException]"Parameter MinimumVersion ('$MinimumVersion') cannot be greater than Version ('$Version')."), 'InvalidParameterCombination', [System.Management.Automation.ErrorCategory]::InvalidArgument, $MinimumVersion ) ) } } if ($MaximumVersion -and $Version) { # Validate MaximumVersion >= Version (1.0.3 behavior) # Extract base version for comparison (handle semver strings) $versionForComparison = $Version if ($Version -match '^(\d+\.\d+\.\d+)') { $versionForComparison = $Matches[1] } $versionObj = [Version]$versionForComparison if ($MaximumVersion -lt $versionObj) { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( ([System.ArgumentException]"Parameter MaximumVersion ('$MaximumVersion') cannot be less than Version ('$Version')."), 'InvalidParameterCombination', [System.Management.Automation.ErrorCategory]::InvalidArgument, $MaximumVersion ) ) } } # Handle MaximumVersion without explicit Version (like 1.0.3) if ($MaximumVersion -and -not $Version) { # Try to get latest for the major version of MaximumVersion $majorVersion = $MaximumVersion.Major $wildcardVersion = "$majorVersion.*" try { $latestForMajor = Resolve-AWSToolsVersion -Name 'AWS.Tools' -Version $wildcardVersion -Timeout $Timeout -ErrorAction Stop if ($latestForMajor -and $latestForMajor -le $MaximumVersion) { # Check MinimumVersion constraint if specified if (-not $MinimumVersion -or $latestForMajor -ge $MinimumVersion) { $Version = $latestForMajor Write-Verbose ("[$($MyInvocation.MyCommand)] Using latest version '$Version' within MaximumVersion constraint") } else { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( ([System.ArgumentException]"Version '$latestForMajor' would be installed but a maximum version of '$MaximumVersion' was specified. The minimum version '$MinimumVersion' requirement is satisfied."), 'NoVersionsInRange', [System.Management.Automation.ErrorCategory]::ObjectNotFound, @($MinimumVersion, $MaximumVersion) ) ) } } else { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( ([System.ArgumentException]"Version '$latestForMajor' would be installed but a maximum version of '$MaximumVersion' was specified."), 'MaximumVersionConstraintNotSatisfied', [System.Management.Automation.ErrorCategory]::ObjectNotFound, $MaximumVersion ) ) } } catch { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( ([System.ArgumentException]"Unable to find a version within maximum version constraint '$MaximumVersion': $($_.Exception.Message)"), 'MaximumVersionResolutionFailed', [System.Management.Automation.ErrorCategory]::ObjectNotFound, $MaximumVersion ) ) } } # Handle MinimumVersion without explicit Version or MaximumVersion if ($MinimumVersion -and -not $Version -and -not $MaximumVersion) { # Just validate MinimumVersion exists, then use latest (1.0.3 behavior) try { $minVersionCheck = Resolve-AWSToolsVersion -Name 'AWS.Tools' -Version $MinimumVersion.ToString() -Timeout $Timeout -ErrorAction Stop if (-not $minVersionCheck) { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( ([System.ArgumentException]"The minimum version '$MinimumVersion' is not available online."), 'MinimumVersionNotFound', [System.Management.Automation.ErrorCategory]::ObjectNotFound, $MinimumVersion ) ) } Write-Verbose ("[$($MyInvocation.MyCommand)] MinimumVersion '$MinimumVersion' verified, using latest version") # Leave $Version as null to use latest } catch { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( ([System.ArgumentException]"The minimum version '$MinimumVersion' is not available online."), 'MinimumVersionNotFound', [System.Management.Automation.ErrorCategory]::ObjectNotFound, $MinimumVersion ) ) } } # Validate prerelease parameter usage try { $versionFlags = Test-PrereleaseParameterValidation -Version $Version -Prerelease $Prerelease -ModuleName 'AWS.Tools' $isPreReleaseVersion = $versionFlags.IsPreReleaseVersion $isWildcardVersion = $versionFlags.IsWildcardVersion } catch { $PSCmdlet.ThrowTerminatingError($_) } # Resolve version using standard logic $resolveVersionParams = @{ Name = 'AWS.Tools' Version = $Version Timeout = $Timeout } if ($Prerelease) { $resolveVersionParams.Prerelease = $true } try { $resolvedVersion = Resolve-AWSToolsVersion @resolveVersionParams # Store the resolved version object separately (don't assign to $Version which is typed as [string]) $resolvedVersionObj = $resolvedVersion } catch { # Re-throw using ThrowTerminatingError to preserve original error record $PSCmdlet.ThrowTerminatingError($_) } # Handle both standard [Version] objects and semver PSCustomObjects (for preview versions) if ($resolvedVersionObj) { Write-Debug "[$($MyInvocation.MyCommand)] ResolvedVersion type: $($resolvedVersionObj.GetType().FullName)" Write-Debug "[$($MyInvocation.MyCommand)] ResolvedVersion value: $resolvedVersionObj" $hasSemVerString = $null -ne $resolvedVersionObj.PSObject.Properties['SemVerString'] Write-Debug "[$($MyInvocation.MyCommand)] Has SemVerString property: $hasSemVerString" if ($hasSemVerString) { # Preview version - use the full semver string $targetVersionToInstall = $resolvedVersionObj.SemVerString Write-Debug "[$($MyInvocation.MyCommand)] Set targetVersionToInstall to SemVerString: $targetVersionToInstall (type: $($targetVersionToInstall.GetType().Name))" } else { # Standard version $targetVersionToInstall = $resolvedVersionObj.ToString() Write-Debug "[$($MyInvocation.MyCommand)] Set targetVersionToInstall to Version.ToString(): $targetVersionToInstall (type: $($targetVersionToInstall.GetType().Name))" } } else { $targetVersionToInstall = "latest" Write-Debug "[$($MyInvocation.MyCommand)] Set targetVersionToInstall to 'latest'" } } else { # SourceZipPath - version will be determined from zip contents during installation $targetVersionToInstall = "from local zip file" } Write-Verbose ("[$($MyInvocation.MyCommand)] ParameterSetName=$($PSCmdlet.ParameterSetName) " + "ConfirmPreference=$ConfirmPreference WhatIfPreference=$WhatIfPreference " + "VerbosePreference=$VerbosePreference Name=($Name) Version=$Version Cleanup=$Cleanup") } Process { $ErrorActionPreference = 'Stop' try { # Update progress - starting version check and preparation Write-Progress -Id 1 -Activity "Install-AWSToolsModule" -Status "Resolving and downloading required files..." -PercentComplete 10 # Validate and normalize module names $resolvedNames = Resolve-AWSToolsModuleNames -Name $Name # Get target installation path if ($Path) { $targetPath = $Path Write-Verbose ("[$($MyInvocation.MyCommand)] Using specified installation path: " + "$targetPath") } else { $targetPath = Get-PSModulePath -Scope $Scope Write-Verbose ("[$($MyInvocation.MyCommand)] Using $Scope scope installation " + "path: $targetPath") } # Build target description for ShouldProcess if ($PSCmdlet.ParameterSetName -eq 'ManagedCloudFront') { $versionText = " version $targetVersionToInstall" } else { $versionText = " from local zip file" } if ($Name) { $moduleText = ($Name -join ', ') } else { $moduleText = "all AWS Tools modules" } $target = "$moduleText$versionText to $targetPath" # Initialize installedVersion variable $installedVersion = $null if ($PSCmdlet.ShouldProcess($target, "Install AWS Tools modules")) { # Check if version is already installed BEFORE downloading to avoid unnecessary network calls # This check happens AFTER ShouldProcess so WhatIf users see what would be installed if (-not $SourceZipPath -and -not $Force -and $resolvedVersionObj) { # Compute the target version string once for comparison. $targetVersionString = if ($resolvedVersionObj.PSObject.Properties['SemVerString']) { $resolvedVersionObj.SemVerString } else { $resolvedVersionObj.ToString() } # Enumerate installed AWS.Tools modules once (excludes AWS.Tools.Installer). $installedModulesOnDisk = Get-InstalledAWSToolsModule -Path $targetPath -Name 'AWS.Tools' if ($resolvedNames) { # Subset install (-Name specified): only skip when EVERY requested module is # already present at the target version. Using AWS.Tools.Common as a proxy # would wrongly skip when Common is present but a requested service module # (e.g. AWS.Tools.S3) is not yet installed. $latestByName = @{} foreach ($installedModule in $installedModulesOnDisk) { $existing = $latestByName[$installedModule.Name] if (-not $existing -or $installedModule.Version -gt $existing.Version) { $latestByName[$installedModule.Name] = $installedModule } } $allRequestedPresentAtTarget = $true foreach ($requestedName in $resolvedNames) { $candidate = $latestByName[$requestedName] if (-not $candidate -or $candidate.Version.ToString() -ne $targetVersionString) { $allRequestedPresentAtTarget = $false break } } if ($allRequestedPresentAtTarget) { Write-Verbose ("[$($MyInvocation.MyCommand)] All requested AWS Tools modules already installed at version $targetVersionString, skipping installation") Write-Host "Skipped installation: requested AWS.Tools modules version $targetVersionToInstall already installed in $targetPath. Use -Force to reinstall." return } } else { # Install-all (no -Name): every module in a release shares the release # version, so AWS.Tools.Common is a valid proxy for the whole set. $installedCommon = $installedModulesOnDisk | Where-Object { $_.Name -eq 'AWS.Tools.Common' } | Sort-Object Version -Descending | Select-Object -First 1 if ($installedCommon -and $installedCommon.Version.ToString() -eq $targetVersionString) { Write-Verbose ("[$($MyInvocation.MyCommand)] AWS Tools version $targetVersionString already installed, skipping installation") Write-Host "Skipped installation: AWS.Tools.Common version $targetVersionToInstall already installed in $targetPath. Use -Force to overwrite or add missing modules." return } } } # Create temporary directory $tempPath = [System.IO.Path]::GetTempPath() $randomName = [System.IO.Path]::GetRandomFileName() $tempDir = Join-Path $tempPath $randomName New-Item -ItemType 'Directory' -Path $tempDir -Force | Out-Null try { # Update progress - preparing for installation Write-Progress -Id 1 -Activity "Install-AWSToolsModule" -Status "Preparing to install modules..." -PercentComplete 30 # Resolve zip source (local file or download) $resolveZipParams = @{ Name = 'AWS.Tools' SourceZipPath = $SourceZipPath TempDir = $tempDir Timeout = $Timeout } if ($PSBoundParameters.ContainsKey('SkipIntegrityCheck')) { $resolveZipParams.SkipIntegrityCheck = $SkipIntegrityCheck } if ($PSBoundParameters.ContainsKey('SourceHashPath')) { $resolveZipParams.SourceHashPath = $SourceHashPath } # Pass the resolved version object (not the original string which might be a wildcard) if ($resolvedVersionObj) { $resolveZipParams.Version = $resolvedVersionObj } $zipPath = Resolve-AWSToolsZipSource @resolveZipParams # Debug output to check the value of $zipPath Write-Verbose "[$($MyInvocation.MyCommand)] ZipPath value: $zipPath" # Ensure $zipPath is a string if ($null -eq $zipPath) { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( ([System.InvalidOperationException]"ZipPath is null. Cannot proceed with installation."), 'NullZipPath', [System.Management.Automation.ErrorCategory]::InvalidOperation, $null ) ) } Write-Progress -Id 1 -Activity "Install-AWSToolsModule" -Status "Installing modules..." -PercentComplete 40 # Install modules from zip if ($resolvedNames) { Write-Verbose ("[$($MyInvocation.MyCommand)] Installing specified " + "AWS Tools modules: $($resolvedNames -join ', ')") } else { Write-Verbose ("[$($MyInvocation.MyCommand)] Installing all " + "AWS Tools modules") } $installFromZipParams = @{ Name = 'AWS.Tools' ZipPath = $zipPath ModuleNames = $resolvedNames TargetPath = $targetPath ErrorAction = 'Stop' } # Best-effort: PS 7.4+ exposes $PSCmdlet.StoppingToken; older shells # (notably Windows PowerShell 5.1) do not. Pass it when present so # Ctrl+C can interrupt the native extract loop. if ($PSCmdlet.PSObject.Properties['StoppingToken']) { $installFromZipParams.CancellationToken = $PSCmdlet.StoppingToken } $installResult = Install-AWSToolsModuleFromZip @installFromZipParams # Extract version and modules from structured result $installedVersion = $installResult.Version $installedVersionString = if ($installResult.VersionString) { $installResult.VersionString } else { $installedVersion } $installedModules = $installResult.Modules $moduleNames = $installedModules | ForEach-Object { $_.Name } Write-Verbose ("[$($MyInvocation.MyCommand)] Installed modules: " + "$($moduleNames -join ', ') with version $installedVersion") # For SourceZipPath parameter set, capture the version from the installed modules if ($PSCmdlet.ParameterSetName -eq 'SourceZipPath' -and $installedVersion) { $Version = $installedVersion Write-Verbose ("[$($MyInvocation.MyCommand)] Captured version from local zip: $Version") } Write-Verbose ("[$($MyInvocation.MyCommand)] AWS Tools modules installation " + "completed successfully") # Provide installation summary via Write-Host Write-Host "Installed AWS Tools version $installedVersionString to $targetPath" # Update progress - installation complete, preparing for cleanup Write-Progress -Id 1 -Activity "Install-AWSToolsModule" -Status "Installation complete, cleaning up previous versions..." -PercentComplete 70 } finally { # Clean up temporary directory if (Test-Path $tempDir) { Write-Verbose ("[$($MyInvocation.MyCommand)] Cleaning up temporary " + "directory: $tempDir") $removeItemParams = @{ Path = $tempDir Recurse = $true Force = $true ErrorAction = 'SilentlyContinue' } # Temporarily suppress progress just for this operation $savedProgressPreference = $ProgressPreference $ProgressPreference = 'SilentlyContinue' try { Remove-Item @removeItemParams } finally { $ProgressPreference = $savedProgressPreference } } } } # Determine version to keep for cleanup if ($installedVersion) { # Use the version that was actually installed $versionToKeep = $installedVersion } elseif ($Version) { # Use the specified version - handle both [Version] and semver objects # Check if it's a string first to avoid triggering type resolution if ($Version -is [string]) { # For string versions (including wildcards and semver strings), use directly $versionToKeep = $Version } elseif ($Version.PSObject.Properties['SemVerString']) { $versionToKeep = $Version.SemVerString } else { $versionToKeep = $Version.ToString() } } else { # For ManagedCloudFront with "latest", we can't do cleanup without knowing the actual version # For SourceZipPath, no cleanup without installed version $versionToKeep = $null } # Only call cleanup if we have installed modules and cleanup is requested if ($installedModules -and $installedModules.Count -gt 0 -and $Cleanup) { try { # Use the Modules array directly as ExceptModules (already in correct format) Write-Verbose ("[$($MyInvocation.MyCommand)] Using ExceptModules for cleanup " + "with $($installedModules.Count) module-version pairs") # Use Uninstall-AWSToolsModule for cleanup $uninstallParams = @{ ExceptModules = $installedModules } # If Name was specified, only clean up those specific modules # This aligns with V1 behavior and avoids surprising destructive cleanup if ($Name) { $uninstallParams.Name = $Name Write-Verbose ("[$($MyInvocation.MyCommand)] Cleanup constrained to " + "named modules: $($Name -join ', ')") } # Pass WhatIf preference if it's set if ($PSBoundParameters.ContainsKey('WhatIf')) { $uninstallParams.WhatIf = $WhatIfPreference } # Pass scope or custom path if ($Path) { $uninstallParams.Path = $Path } else { $uninstallParams.Scope = $Scope } # Pass legacy cleanup scope if specified if ($CleanUpLegacyModuleScope) { $uninstallParams.CleanUpLegacyScope = $CleanUpLegacyModuleScope } # Call Uninstall-AWSToolsModule for cleanup Uninstall-AWSToolsModule @uninstallParams | Out-Null } catch { # Cleanup errors are non-terminating - write error but continue Write-Error ("Failed to clean up previous AWS Tools versions: " + "$($_.Exception.Message)") -ErrorAction Continue Write-Verbose ("[$($MyInvocation.MyCommand)] Cleanup error details: " + "$($_.Exception.ToString())") } } elseif ($CleanUpLegacyModuleScope) { try { # If skipping AWS Tools cleanup but still need to clean up legacy modules $uninstallParams = @{ CleanUpLegacyScope = $CleanUpLegacyModuleScope } # Pass WhatIf preference if it's set if ($PSBoundParameters.ContainsKey('WhatIf')) { $uninstallParams.WhatIf = $WhatIfPreference } # Call Uninstall-AWSToolsModule for legacy cleanup only Uninstall-AWSToolsModule @uninstallParams } catch { # Legacy cleanup errors are non-terminating - write error but continue Write-Error ("Failed to clean up legacy AWSPowerShell modules: " + "$($_.Exception.Message)") -ErrorAction Continue Write-Verbose ("[$($MyInvocation.MyCommand)] Legacy cleanup error details: " + "$($_.Exception.ToString())") } } } catch { $PSCmdlet.ThrowTerminatingError($_) } } End { # Complete progress bar Write-Progress -Id 1 -Activity "Install-AWSToolsModule" -Completed Write-Verbose "[$($MyInvocation.MyCommand)] End" } } # SIG # Begin signature block # MIIutQYJKoZIhvcNAQcCoIIupjCCLqICAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDQNk2qJU/RspzU # UmX1LH+s+eckG7wb+ChjCd3ON12fBaCCE+owggXAMIIEqKADAgECAhAP0bvKeWvX # +N1MguEKmpYxMA0GCSqGSIb3DQEBCwUAMGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xKzApBgNV # BAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0EwHhcNMjIwMTEz # MDAwMDAwWhcNMzExMTA5MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMM # RGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQD # ExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqGSIb3DQEBAQUAA4IC # DwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEppz1Yq3aa # za57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllV # cq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT # +CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd # 463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+ # EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/CNdaSaTC5qmgZ92k # J7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtmmnTK3kse5w5j # rubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7 # f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJU # KSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+wh # X8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQAB # o4IBZjCCAWIwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5n # P+e6mK4cD08wHwYDVR0jBBgwFoAUsT7DaQP4v0cB1JgmGggC72NkK8MwDgYDVR0P # AQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMH8GCCsGAQUFBwEBBHMwcTAk # BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEkGCCsGAQUFBzAC # hj1odHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRIaWdoQXNzdXJh # bmNlRVZSb290Q0EuY3J0MEsGA1UdHwREMEIwQKA+oDyGOmh0dHA6Ly9jcmwzLmRp # Z2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5jcmwwHAYD # VR0gBBUwEzAHBgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcNAQELBQADggEBAEHx # qRH0DxNHecllao3A7pgEpMbjDPKisedfYk/ak1k2zfIe4R7sD+EbP5HU5A/C5pg0 # /xkPZigfT2IxpCrhKhO61z7H0ZL+q93fqpgzRh9Onr3g7QdG64AupP2uU7SkwaT1 # IY1rzAGt9Rnu15ClMlIr28xzDxj4+87eg3Gn77tRWwR2L62t0+od/P1Tk+WMieNg # GbngLyOOLFxJy34riDkruQZhiPOuAnZ2dMFkkbiJUZflhX0901emWG4f7vtpYeJa # 3Cgh6GO6Ps9W7Zrk9wXqyvPsEt84zdp7PiuTUy9cUQBY3pBIowrHC/Q7bVUx8ALM # R3eWUaNetbxcyEMRoacwggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G # 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 # eE4wggduMIIFVqADAgECAhACkbBFv37tbsDucbrJZmQsMA0GCSqGSIb3DQEBCwUA # MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE # AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz # ODQgMjAyMSBDQTEwHhcNMjYwNDEzMDAwMDAwWhcNMjcwNDEyMjM1OTU5WjCB9jET # MBEGCysGAQQBgjc8AgEDEwJVUzEZMBcGCysGAQQBgjc8AgECEwhEZWxhd2FyZTEd # MBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xEDAOBgNVBAUTBzQxNTI5NTQx # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdTZWF0 # dGxlMSIwIAYDVQQKExlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRcwFQYDVQQL # Ew5TREtzIGFuZCBUb29sczEiMCAGA1UEAxMZQW1hem9uIFdlYiBTZXJ2aWNlcywg # SW5jLjCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAKAYfPWODy5W775n # g6Qb7qn45ScvlDKKwv740oW7PjXysv4iETo57UU0QPbw1hCvKP5EbB7jGSSXvfnw # 6Zb1mhXb22f1EFgnsLdUHE3fGu1EBlkwd6lUu1G4rrsctxyYsJrrR2O044WI+WQQ # GkeRMnlxD5a9kveXWkEe5z9Dz52DetD1Z0dP/mgRsdOoT/tvKFN84LvT36oq0YFx # 8Cxn4XYe1VekrYMKZ//nk25GIswcdVTASFnWffSUPEWwPQgRyEE2EBLs1QlRDsRo # KGC4pelMzoZj+ka98JlyoWn3MFnwnO/i+XwVrdi9HgPvwI3QdmipMa6FXIIlGPhj # E73unbRwck60qHaBkh5uWkuJhJU0n6iy1aYCOONp/HUvFjrXI+U5+M3c6ZaJKneI # +z7oJI1c/ELbCMdNFNLgdZN+YbxKGBFYQAuqG4Y4KUMTkF/HD1y2JPm7aTHryrIE # TD1LT4vlwK+KPaODWpJgRSwHw6wEdjHbPgug+7kosXqqUGbC3QIDAQABo4ICAjCC # Af4wHwYDVR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYDVR0OBBYEFG/Y # ITb8e/nTXVg4U0Nrg0fSUPtVMD0GA1UdIAQ2MDQwMgYFZ4EMAQMwKTAnBggrBgEF # BQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMA4GA1UdDwEB/wQEAwIH # gDATBgNVHSUEDDAKBggrBgEFBQcDAzCBtQYDVR0fBIGtMIGqMFOgUaBPhk1odHRw # Oi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu # Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDBToFGgT4ZNaHR0cDovL2NybDQuZGln # aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hB # Mzg0MjAyMUNBMS5jcmwwgZQGCCsGAQUFBwEBBIGHMIGEMCQGCCsGAQUFBzABhhho # dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXAYIKwYBBQUHMAKGUGh0dHA6Ly9jYWNl # cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNB # NDA5NlNIQTM4NDIwMjFDQTEuY3J0MAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQAD # ggIBAIUBjjxjpjf9/pYOyXd+SkZl7jWePeWdmu6IsyuHrq26aL1ckfWVgGvoGJwt # bDzFbfcW0mRvSjdE6PyQ6zLwFgHhgSXbuuyjJZY2cfCxYYvVJ9rD8b80X3QS0RbI # C414xwTjB+D1wUMvY1GvvHFRnkixSPT4+/POuUWPtN8yELHGOwgwv3GTfrWfA2+k # t7iKGNeEVxP3SqNPsyEBWKnn2gVMsH1aGO3eqvSe5pM+Dm5uKACrbtofA7xE/7qo # WTqQ5UtxBD9hLWXMbxQkSuEh63jJGC1rL+LV2cC6iIHm/pu+V9edqCECxB2SKQ2o # Z4ObhHvmxinBYUBJ29XEHoAb8mooXYDFAxEH4o20M3yaoXkeCAYXW5m6UZR8Zpit # i9ZRYNwEvBhDlI5jJzxBdrUeOqLAXLdYk1elPp/hlLSF9t9P19iUSeWCJbdaZJkk # +hcTuxBsD4zXtUkxQYR1nbWCqjt0+H2hcXu5DcNBj7rv/hs7xMLLFY8PqL+QNFaq # 1/GamAZm07r1czJL+gHOTO6MQ01uxWlCTQ2QAkhd6yyS5jyz8LyHfKjiAxNbyLGZ # wqdVzxGI/3+ZgF+N/pcazWfL6XFiQcPJISPMDSRvAZQ0BV1Cz8C52nX0Sq8zzQ6r # 9+JNA/ti3V9KJc6vQwYMlOsjYj2T4Ph3vX/DJs3alLLwLVLRMYIaITCCGh0CAQEw # fTBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNV # BAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hB # Mzg0IDIwMjEgQ0ExAhACkbBFv37tbsDucbrJZmQsMA0GCWCGSAFlAwQCAQUAoHww # EAYKKwYBBAGCNwIBDDECMAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYK # KwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEICPBTbTy # uHTg3ILPWPy9qvYZLTjEGiGNygaYWpEBQws/MA0GCSqGSIb3DQEBAQUABIIBgFFg # osyMfEPUKCLAOtoQk+HL+8KSEr83L83ZFLI7MyvEm8/iooAxT6Ck1TKL16TihW76 # H7PhvAZ1YIwDpY4d6jPUmsx+7/mBFmgYrwjOhkpK3treI9VwI5w0QEuXbhlgYgib # i2qLNP0b1zR+8S4ylH215m+5XYSzRRCUzhFKTfI3FAHgNpr+lSOFuRPl+jQRbCZk # DweqVGXMrn7G7vQ+FX0BtCrCq6apfkkjvMpFvdBpzrWTYDI3tS53fCinTZ/tHEtL # WmGt+5FPE1wBd6UUkd1YYDjWcI3F/tU0XrEhUUOlM9jHfTGbTC2R4ieSRZYu8cwl # /+wpfx4/3VexQIy3GSWGRw7sfuVT4pvzSqSlOdi79OKC91mD43am9ee4EWKpLDkZ # x2yEVU3MNiSLIqfAJdIJW2tiQJNITI8qsSCzwZek7/xH8oizR8uYDHFv8wTeP/ES # 547zOKj0gFvIYLjC78CCdxaladb3DkQAhCbq83T1BUx8WNEkLo4MUC7b2bXTh6GC # F3cwghdzBgorBgEEAYI3AwMBMYIXYzCCF18GCSqGSIb3DQEHAqCCF1AwghdMAgED # MQ8wDQYJYIZIAWUDBAIBBQAweAYLKoZIhvcNAQkQAQSgaQRnMGUCAQEGCWCGSAGG # /WwHATAxMA0GCWCGSAFlAwQCAQUABCAeZgOg2edKX8iPKfzhD+SHW2sQ60A7OVMx # RR6wU6YgCgIRAPPQ1nUEP42q8/nuyVQZIHQYDzIwMjYwODAzMTkzNzA1WqCCEzow # ggbtMIIE1aADAgECAhAKgO8YS43xBYLRxHanlXRoMA0GCSqGSIb3DQEBCwUAMGkx # CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4 # RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYg # MjAyNSBDQTEwHhcNMjUwNjA0MDAwMDAwWhcNMzYwOTAzMjM1OTU5WjBjMQswCQYD # VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lD # ZXJ0IFNIQTI1NiBSU0E0MDk2IFRpbWVzdGFtcCBSZXNwb25kZXIgMjAyNSAxMIIC # IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0EasLRLGntDqrmBWsytXum9R # /4ZwCgHfyjfMGUIwYzKomd8U1nH7C8Dr0cVMF3BsfAFI54um8+dnxk36+jx0Tb+k # +87H9WPxNyFPJIDZHhAqlUPt281mHrBbZHqRK71Em3/hCGC5KyyneqiZ7syvFXJ9 # A72wzHpkBaMUNg7MOLxI6E9RaUueHTQKWXymOtRwJXcrcTTPPT2V1D/+cFllESvi # H8YjoPFvZSjKs3SKO1QNUdFd2adw44wDcKgH+JRJE5Qg0NP3yiSyi5MxgU6cehGH # r7zou1znOM8odbkqoK+lJ25LCHBSai25CFyD23DZgPfDrJJJK77epTwMP6eKA0kW # a3osAe8fcpK40uhktzUd/Yk0xUvhDU6lvJukx7jphx40DQt82yepyekl4i0r8OEp # s/FNO4ahfvAk12hE5FVs9HVVWcO5J4dVmVzix4A77p3awLbr89A90/nWGjXMGn7F # QhmSlIUDy9Z2hSgctaepZTd0ILIUbWuhKuAeNIeWrzHKYueMJtItnj2Q+aTyLLKL # M0MheP/9w6CtjuuVHJOVoIJ/DtpJRE7Ce7vMRHoRon4CWIvuiNN1Lk9Y+xZ66laz # s2kKFSTnnkrT3pXWETTJkhd76CIDBbTRofOsNyEhzZtCGmnQigpFHti58CSmvEyJ # cAlDVcKacJ+A9/z7eacCAwEAAaOCAZUwggGRMAwGA1UdEwEB/wQCMAAwHQYDVR0O # BBYEFOQ7/PIx7f391/ORcWMZUEPPYYzoMB8GA1UdIwQYMBaAFO9vU0rp5AZ8esri # kFb2L9RJ7MtOMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8EDDAKBggrBgEFBQcD # CDCBlQYIKwYBBQUHAQEEgYgwgYUwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp # Z2ljZXJ0LmNvbTBdBggrBgEFBQcwAoZRaHR0cDovL2NhY2VydHMuZGlnaWNlcnQu # Y29tL0RpZ2lDZXJ0VHJ1c3RlZEc0VGltZVN0YW1waW5nUlNBNDA5NlNIQTI1NjIw # MjVDQTEuY3J0MF8GA1UdHwRYMFYwVKBSoFCGTmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0 # LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFRpbWVTdGFtcGluZ1JTQTQwOTZTSEEyNTYy # MDI1Q0ExLmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJ # KoZIhvcNAQELBQADggIBAGUqrfEcJwS5rmBB7NEIRJ5jQHIh+OT2Ik/bNYulCrVv # hREafBYF0RkP2AGr181o2YWPoSHz9iZEN/FPsLSTwVQWo2H62yGBvg7ouCODwrx6 # ULj6hYKqdT8wv2UV+Kbz/3ImZlJ7YXwBD9R0oU62PtgxOao872bOySCILdBghQ/Z # LcdC8cbUUO75ZSpbh1oipOhcUT8lD8QAGB9lctZTTOJM3pHfKBAEcxQFoHlt2s9s # XoxFizTeHihsQyfFg5fxUFEp7W42fNBVN4ueLaceRf9Cq9ec1v5iQMWTFQa0xNqI # tH3CPFTG7aEQJmmrJTV3Qhtfparz+BW60OiMEgV5GWoBy4RVPRwqxv7Mk0Sy4QHs # 7v9y69NBqycz0BZwhB9WOfOu/CIJnzkQTwtSSpGGhLdjnQ4eBpjtP+XB3pQCtv4E # 5UCSDag6+iX8MmB10nfldPF9SVD7weCC3yXZi/uuhqdwkgVxuiMFzGVFwYbQsiGn # oa9F5AaAyBjFBtXVLcKtapnMG3VH3EmAp/jsJ3FVF3+d1SVDTmjFjLbNFZUWMXuZ # yvgLfgyPehwJVxwC+UpX2MSey2ueIu9THFVkT+um1vshETaWyQo8gmBto/m3acaP # 9QsuLj3FNwFlTxq25+T4QwX9xa6ILs84ZPvmpovq90K8eWyG2N01c4IhSOxqt81n # MIIGtDCCBJygAwIBAgIQDcesVwX/IZkuQEMiDDpJhjANBgkqhkiG9w0BAQsFADBi # MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 # d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg # RzQwHhcNMjUwNTA3MDAwMDAwWhcNMzgwMTE0MjM1OTU5WjBpMQswCQYDVQQGEwJV # UzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRy # dXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYgU0hBMjU2IDIwMjUgQ0ExMIIC # IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtHgx0wqYQXK+PEbAHKx126NG # aHS0URedTa2NDZS1mZaDLFTtQ2oRjzUXMmxCqvkbsDpz4aH+qbxeLho8I6jY3xL1 # IusLopuW2qftJYJaDNs1+JH7Z+QdSKWM06qchUP+AbdJgMQB3h2DZ0Mal5kYp77j # YMVQXSZH++0trj6Ao+xh/AS7sQRuQL37QXbDhAktVJMQbzIBHYJBYgzWIjk8eDrY # hXDEpKk7RdoX0M980EpLtlrNyHw0Xm+nt5pnYJU3Gmq6bNMI1I7Gb5IBZK4ivbVC # iZv7PNBYqHEpNVWC2ZQ8BbfnFRQVESYOszFI2Wv82wnJRfN20VRS3hpLgIR4hjzL # 0hpoYGk81coWJ+KdPvMvaB0WkE/2qHxJ0ucS638ZxqU14lDnki7CcoKCz6eum5A1 # 9WZQHkqUJfdkDjHkccpL6uoG8pbF0LJAQQZxst7VvwDDjAmSFTUms+wV/FbWBqi7 # fTJnjq3hj0XbQcd8hjj/q8d6ylgxCZSKi17yVp2NL+cnT6Toy+rN+nM8M7LnLqCr # O2JP3oW//1sfuZDKiDEb1AQ8es9Xr/u6bDTnYCTKIsDq1BtmXUqEG1NqzJKS4kOm # xkYp2WyODi7vQTCBZtVFJfVZ3j7OgWmnhFr4yUozZtqgPrHRVHhGNKlYzyjlroPx # ul+bgIspzOwbtmsgY1MCAwEAAaOCAV0wggFZMBIGA1UdEwEB/wQIMAYBAf8CAQAw # HQYDVR0OBBYEFO9vU0rp5AZ8esrikFb2L9RJ7MtOMB8GA1UdIwQYMBaAFOzX44LS # cV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEF # BQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp # Z2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQu # Y29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYy # aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5j # cmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEB # CwUAA4ICAQAXzvsWgBz+Bz0RdnEwvb4LyLU0pn/N0IfFiBowf0/Dm1wGc/Do7oVM # Y2mhXZXjDNJQa8j00DNqhCT3t+s8G0iP5kvN2n7Jd2E4/iEIUBO41P5F448rSYJ5 # 9Ib61eoalhnd6ywFLerycvZTAz40y8S4F3/a+Z1jEMK/DMm/axFSgoR8n6c3nuZB # 9BfBwAQYK9FHaoq2e26MHvVY9gCDA/JYsq7pGdogP8HRtrYfctSLANEBfHU16r3J # 05qX3kId+ZOczgj5kjatVB+NdADVZKON/gnZruMvNYY2o1f4MXRJDMdTSlOLh0HC # n2cQLwQCqjFbqrXuvTPSegOOzr4EWj7PtspIHBldNE2K9i697cvaiIo2p61Ed2p8 # xMJb82Yosn0z4y25xUbI7GIN/TpVfHIqQ6Ku/qjTY6hc3hsXMrS+U0yy+GWqAXam # 4ToWd2UQ1KYT70kZjE4YtL8Pbzg0c1ugMZyZZd/BdHLiRu7hAWE6bTEm4XYRkA6T # l4KSFLFk43esaUeqGkH/wyW4N7OigizwJWeukcyIPbAvjSabnf7+Pu0VrFgoiovR # Diyx3zEdmcif/sYQsfch28bZeUz2rtY/9TCA6TD8dC3JE3rYkrhLULy7Dc90G6e8 # BlqmyIjlgp2+VqsS9/wQD7yFylIz0scmbKvFoW2jNrbM1pD2T7m3XDCCBY0wggR1 # oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEMBQAwZTELMAkGA1UE # BhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2lj # ZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4X # DTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkGA1UEBhMCVVMxFTAT # BgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEh # MB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIICIjANBgkqhkiG9w0B # AQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zCpyUuySE98orYWcLh # Kac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf1gU8Ug9SH8aeFaV+ # vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x4i0MG+4g1ckgHWMp # Lc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEioZldXn1RYjgwrt0+n # MNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7axxLVqGDgDEI3Y1Dek # LgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZOjFEmjNAvwjXWkmk # wuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJl2l6SPDgohIbZpp0 # yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz2cXfSwQAzH0clcOP # 9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH4b235kOkGLimdwHh # D5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb5RBQ6zHFynIWIgnf # fEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ9eRpL5gdLfXZqbId # 5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOzX44LS # cV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgP # MA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0 # dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2Vy # dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDBFBgNV # HR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRB # c3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0gADANBgkqhkiG9w0B # AQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs7IVeqRq7IviHGmlU # Iu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq3votVs/59PesMHqa # i7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/Lwum6fI0POz3A8eH # qNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9/HYJaISfb8rbII01 # YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWojayL/ErhULSd+2DrZ # 8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3wwggN4AgEBMH0waTELMAkGA1UE # BhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2Vy # dCBUcnVzdGVkIEc0IFRpbWVTdGFtcGluZyBSU0E0MDk2IFNIQTI1NiAyMDI1IENB # MQIQCoDvGEuN8QWC0cR2p5V0aDANBglghkgBZQMEAgEFAKCB0TAaBgkqhkiG9w0B # CQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI2MDgwMzE5MzcwNVow # KwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU3WIwrIYKLTBr2jixaHlSMAf7QX4wLwYJ # KoZIhvcNAQkEMSIEILmpAQn5sVYxPbYjtg4MeItRQvL9pzwEgNhiOLBvBFITMDcG # CyqGSIb3DQEJEAIvMSgwJjAkMCIEIEqgP6Is11yExVyTj4KOZ2ucrsqzP+NtJpqj # NPFGEQozMA0GCSqGSIb3DQEBAQUABIICABih+eq5QZGFW9z6JlTWaGghnffkUypF # mvBute62++B/NRIFLqZ+3et2LYNgIhJ44ZYtct7sf5GB7rkDxxQK3ILGQccHmPus # FXL7fqkQm6sHIZkzYWGvX56ULd2olVWmUpmsndF68+zk6mnSb8CZDg+TnG0bIk8F # 4oBlToLLJZKOPbz2A1KqoYdhGFROwDEkM2llofbX+qmrGryk4AMTXu8pCBbseihC # lUakmjhMoevWiqsTNJVKwJg/05O/OOmk1aeG1gkMlS9cIumaUelUsmzMQ+MqbAOp # lNYG1onViTKdwlq4F7xP/DsANxvwDK2vks89C7z00wKwmQYQUbCjB2kkJ2Mycy/W # QUIkVzLalS41XG11nmR8eg1omjha5Wy3VYmaijSBwnuL4hRuSuWFDKJlH1thbESC # WojH/4tyoUG4Jlz3xag1dLRNrIkoGDmziQDcaM8R5KjwVMPFu8Q/gKaN/HJrly0s # LfpDwTjb0N9ylJAAyIHU2XUaNC3BN0UZrcuoj+DVIW3tuEnYu6pSKTvlAkDiB1Wl # FuwG9Ey7JK0RpyVoRTSu6b/1gOewg6q0dh4+kLWVY7ojz8AQhNV/RYhVKp0YXlju # w+weMjgLzvzTQt+VJjLokwPTbSztcegrWqzx4BAXAalaB1NWRkj8+PAyshUhg0Vg # yo2i77ZC0Im3 # SIG # End signature block |