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