Universal.psm1
|
Import-Module "$PSScriptRoot\UniversalDashboard.MaterialUI.psm1" -ErrorAction SilentlyContinue if (-not $IsCoreCLR) { [PowerShellUniversal.Cmdlets.AssemblyResolver]::Bind() } $TAType = [psobject].Assembly.GetType('System.Management.Automation.TypeAccelerators') $TAtype::Add('File', 'PowerShellUniversal.PSUFile') $TAtype::Add('Documentation', 'PowerShellUniversal.DocumentationAttribute') $TAtype::Add('Component', 'PowerShellUniversal.ComponentAttribute') function Start-PSUServer { [CmdletBinding(DefaultParameterSetName = "Service")] param( [Parameter(ParameterSetName = 'Path')] [string]$ExecutablePath, [Parameter(ParameterSetName = 'Path')] [string]$ListenAddress, [Parameter(ParameterSetName = 'Path')] [int]$Port, [Parameter(ParameterSetName = 'Path')] [ScriptBlock]$Configuration ) if ($MyInvocation.BoundParameters.Count -eq 0) { Start-Service 'PowerShellUniversal' return } if ([UniversalAutomation.RemoteCommand]::Configuration) { & $Configuration return } if (-not $PSBoundParameters.ContainsKey("ExecutablePath")) { $ExecutablePath = "Universal.Server" if ($PSVersionTable.PSEdition -eq 'Desktop' -or $IsWindows) { $ExecutablePath = "Universal.Server.exe" } } $Command = Get-Command $ExecutablePath -ErrorAction SilentlyContinue if ($null -eq $Command) { $ExecutablePath = Join-Path $PSScriptRoot $ExecutablePath $Command = Get-Command $ExecutablePath -ErrorAction SilentlyContinue if ($null -eq $Command) { throw 'Unable to locate the Universal Server executable. You can use Install-PSUServer the server for your platform. Use the -AddToPath parameter to add the installation directory the the PATH.' } } if ($PSVersionTable.PSEdition -ne 'Desktop' -and -not $IsWindows) { try { chmod +x $ExecutablePath } catch { Write-Warning "Failed to set executable flag. You may have to run 'chmod +x' yourself on $ExecutablePath. $_" } } if ($ListenAddress) { $Env:Kestrel__Endpoints__HTTP__Url = $ListenAddress } elseif ($PSBoundParameters.ContainsKey("Port")) { $Env:Kestrel__Endpoints__HTTP__Url = "http://*:$port" } if ($Configuration) { $scriptName = (Get-PSCallStack | Select-Object -Last 1).ScriptName if (-not $scriptName) { $scriptName = (Get-PSCallStack | Select-Object -Last 1 -Skip 1).ScriptName } $Env:Data__ConfigurationScript = $scriptName } $Process = Start-Process -FilePath $ExecutablePath -PassThru $Process } function Invoke-PSUMsiExec { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet('Install', 'Uninstall')] [string]$Action, [Parameter(Mandatory)] [string]$PackagePath, [Parameter()] [string]$ProductName = 'PowerShell Universal' ) $logPath = Join-Path ([System.IO.Path]::GetTempPath()) ("PowerShellUniversal.{0}.{1}.log" -f $Action.ToLowerInvariant(), [guid]::NewGuid().ToString('N')) $operation = if ($Action -eq 'Install') { '/i' } else { '/x' } $arguments = "$operation `"$PackagePath`" /quiet /qn /norestart /l*v `"$logPath`"" try { $process = Start-Process msiexec.exe -ArgumentList $arguments -Wait -PassThru -ErrorAction Stop } catch { throw "Failed to start Windows Installer while attempting to $($Action.ToLowerInvariant()) $ProductName. $($_.Exception.Message)" } switch ($process.ExitCode) { 0 { Remove-Item $logPath -Force -ErrorAction SilentlyContinue return } 3010 { Write-Warning "$ProductName $($Action.ToLowerInvariant()) completed successfully, but Windows requires a restart. Installer log: $logPath" return } 1641 { Write-Warning "$ProductName $($Action.ToLowerInvariant()) completed successfully and requested a restart. Installer log: $logPath" return } 1602 { throw "Windows Installer canceled the attempt to $($Action.ToLowerInvariant()) $ProductName. Review the installer log for details: $logPath" } 1603 { throw "Windows Installer encountered a fatal error while attempting to $($Action.ToLowerInvariant()) $ProductName. Review the installer log for details: $logPath" } 1618 { throw "Another Windows Installer operation is already in progress. Wait for it to finish and retry the operation on $ProductName. Installer log: $logPath" } 1619 { throw "Windows Installer could not open the package for $ProductName. Verify the downloaded MSI exists and is accessible. Installer log: $logPath" } 1620 { throw "Windows Installer reported that the package for $ProductName is invalid. Installer log: $logPath" } default { throw "Windows Installer failed while attempting to $($Action.ToLowerInvariant()) $ProductName. Exit code: $($process.ExitCode). Installer log: $logPath" } } } function Resolve-PSUPackageSource { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$FileName, [Parameter(Mandatory)] [string]$DefaultUrl ) $downloadSource = [Environment]::GetEnvironmentVariable('PSU_SERVER_DOWNLOAD_SOURCE') if ([string]::IsNullOrWhiteSpace($downloadSource)) { return [pscustomobject]@{ Location = $DefaultUrl IsLocalFile = $false IsOverride = $false } } $downloadSource = $downloadSource.Trim() $uri = $null if ([System.Uri]::TryCreate($downloadSource, [System.UriKind]::Absolute, [ref]$uri)) { if ($uri.IsFile) { $resolvedPath = $uri.LocalPath if ((Test-Path $resolvedPath -PathType Container) -or $resolvedPath.EndsWith([System.IO.Path]::DirectorySeparatorChar) -or $resolvedPath.EndsWith([System.IO.Path]::AltDirectorySeparatorChar)) { $resolvedPath = Join-Path $resolvedPath $FileName } return [pscustomobject]@{ Location = $resolvedPath IsLocalFile = $true IsOverride = $true } } $lastSegment = if ($uri.Segments.Count -gt 0) { $uri.Segments[$uri.Segments.Count - 1].TrimEnd('/') } else { [string]::Empty } $knownPackageExtensions = @('.msi', '.zip') $lastSegmentExtension = [System.IO.Path]::GetExtension($lastSegment) $isDirectoryUrl = $downloadSource.EndsWith('/') $isExpectedFileName = $lastSegment.Equals($FileName, [System.StringComparison]::OrdinalIgnoreCase) $hasKnownPackageExtension = -not [string]::IsNullOrEmpty($lastSegmentExtension) -and $knownPackageExtensions.Contains($lastSegmentExtension.ToLowerInvariant()) if ($isDirectoryUrl -or (-not $isExpectedFileName -and -not $hasKnownPackageExtension)) { $downloadSource = $downloadSource.TrimEnd('/') + "/$FileName" } return [pscustomobject]@{ Location = $downloadSource IsLocalFile = $false IsOverride = $true } } $resolvedPath = $downloadSource if ((Test-Path $downloadSource -PathType Container) -or $downloadSource.EndsWith([System.IO.Path]::DirectorySeparatorChar) -or $downloadSource.EndsWith([System.IO.Path]::AltDirectorySeparatorChar)) { $resolvedPath = Join-Path $downloadSource $FileName } return [pscustomobject]@{ Location = $resolvedPath IsLocalFile = $true IsOverride = $true } } function Save-PSUPackage { [CmdletBinding()] param( [Parameter(Mandatory)] [psobject]$Source, [Parameter(Mandatory)] [string]$DestinationPath ) if ($Source.IsLocalFile) { if (-not (Test-Path $Source.Location -PathType Leaf)) { throw "Unable to locate the package specified by PSU_SERVER_DOWNLOAD_SOURCE: $($Source.Location)" } $sourcePath = [System.IO.Path]::GetFullPath($Source.Location) $destination = [System.IO.Path]::GetFullPath($DestinationPath) $pathComparison = if ($IsCoreCLR -and -not $IsWindows) { [System.StringComparison]::Ordinal } else { [System.StringComparison]::OrdinalIgnoreCase } if (-not $sourcePath.Equals($destination, $pathComparison)) { Copy-Item $Source.Location $DestinationPath -Force -ErrorAction Stop } return } Invoke-WebRequest $Source.Location -UseBasicParsing -OutFile $DestinationPath } function Install-PSUServer { <# .SYNOPSIS Install the PowerShell Universal server. .DESCRIPTION Install the PowerShell Universal server. This is a convenience function that will install the server for your platform. On Windows, it will install the server as a Windows service. On Linux, it will install the server as a systemd service. On Mac, it will install the server as a launchd service. .PARAMETER Path The path to store the PowerShell Universal binaries. If not specified, the default installation path will be used. .PARAMETER AddToPath Whether to add the path to the PATH environment variable. .PARAMETER Version The version of PowerShell Universal to install. .PARAMETER LatestVersion Install the most recent version. .EXAMPLE Install-PSUServer #> [CmdletBinding(DefaultParameterSetName = "Version")] param( [Parameter()] [string]$Path, [Parameter(ParameterSetName = "Version")] [string]$Version, [Parameter(ParameterSetName = "Latest")] [Switch]$LatestVersion, [Parameter()] [string]$IISWebsite, [Parameter()] [string]$IISAppPool = "PowerShellUniversal", [Parameter()] [int]$IISPort ) if ([string]::IsNullOrEmpty($Version)) { $Version = $PSCmdlet.MyInvocation.MyCommand.Module.Version Write-Verbose "Version not specified. Using version $Version from the Universal module." } if ($IISWebsite -and ($IsLinux -or $IsMacOS)) { throw "IISWebsite is only supported on Windows." } if ($IISWebsite) { Import-Module WebAdministration -ErrorAction Stop } if ($platform -eq 'win-x64' -and -not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { throw 'You must be an administrator to install the Universal Server. Please run the command as an administrator.' } $platform = "win-x64"; $folder = 'CommonApplicationData' if ($PSVersionTable.PSEdition -eq 'Core') { if ($IsLinux) { $platform = "linux-x64" } elseif ($IsMacOS) { $folder = 'ApplicationData' $platform = "osx-x64" } } if (-not $Path -and -not $IISWebsite) { $ProgramData = [System.Environment]::GetFolderPath($folder) $Path = [System.IO.Path]::Combine($ProgramData, "PowerShellUniversal", "Server") } if (-not $Path -and $IISWebsite) { $Path = "C:\inetpub\wwwroot\PowerShellUniversal" New-Item $Path -ItemType Directory | Out-Null } # Determine whether this is a Devolutions-era version (major year >= 2026) $parsedVersion = [Version]::new(0, 0) [void][Version]::TryParse($Version, [ref]$parsedVersion) $isDevolutionsVersion = $parsedVersion.Major -gt 2000 if ($LatestVersion) { if ($isDevolutionsVersion) { Write-Verbose "Fetching latest version from Devolutions product info." $productInfo = (Invoke-WebRequest 'https://devolutions.net/productinfo.json' -UseBasicParsing).Content | ConvertFrom-Json $Version = $productInfo.PowerShellUniversal.Current.Version Write-Verbose "Latest Devolutions version: $Version" # Re-evaluate after fetching the latest version [void][Version]::TryParse($Version, [ref]$parsedVersion) $isDevolutionsVersion = $parsedVersion.Major -gt 2000 } else { $Version = (Invoke-WebRequest 'https://imsreleases.blob.core.windows.net/universal/production/v5-version.txt' -UseBasicParsing).Content } } Write-Verbose "Downloading version $Version" if ($isDevolutionsVersion) { # The Devolutions CDN uses a 4-octet version (e.g. 2026.1.2.0). # If the supplied version has only 3 parts, append .0. $cdnVersion = $Version if (($Version -split '\.').Count -eq 3) { $cdnVersion = "$Version.0" } if (($PSVersionTable.PSEdition -eq 'Desktop' -or $IsWindows) -and -not $IISWebsite) { $Temp = [System.IO.Path]::GetTempPath() $msiFileName = "Devolutions.PowerShellUniversal.$cdnVersion.msi" $Msi = (Join-Path $Temp $msiFileName) Remove-Item $Msi -Force -ErrorAction SilentlyContinue $msiUrl = "https://cdn.devolutions.net/download/Devolutions.PowerShellUniversal.$cdnVersion.msi" $msiSource = Resolve-PSUPackageSource -FileName $msiFileName -DefaultUrl $msiUrl $msiAction = if ($msiSource.IsLocalFile) { 'Copying' } else { 'Downloading' } $msiContext = if ($msiSource.IsOverride) { ' using PSU_SERVER_DOWNLOAD_SOURCE' } else { '' } Write-Verbose "$msiAction MSI from $($msiSource.Location)$msiContext" Save-PSUPackage -Source $msiSource -DestinationPath $Msi Write-Verbose "Download complete. Installing from MSI." Invoke-PSUMsiExec -Action Install -PackagePath $Msi -ProductName 'PowerShell Universal' } else { Write-Verbose "Installing server to $Path" $Temp = [System.IO.Path]::GetTempPath() $zipFileName = "Universal.$platform.$cdnVersion.zip" $Zip = (Join-Path $Temp $zipFileName) Remove-Item $Zip -Force -ErrorAction SilentlyContinue $zipUrl = "https://cdn.devolutions.net/download/Universal.$platform.$cdnVersion.zip" $zipSource = Resolve-PSUPackageSource -FileName $zipFileName -DefaultUrl $zipUrl $zipAction = if ($zipSource.IsLocalFile) { 'Copying' } else { 'Downloading' } $zipContext = if ($zipSource.IsOverride) { ' using PSU_SERVER_DOWNLOAD_SOURCE' } else { '' } Write-Verbose "$zipAction ZIP from $($zipSource.Location)$zipContext" Save-PSUPackage -Source $zipSource -DestinationPath $Zip Write-Verbose "Download complete. Unzipping to $Path" Expand-Archive -Path $Zip -DestinationPath $Path -Force Remove-Item $Zip -Force } } elseif (($PSVersionTable.PSEdition -eq 'Desktop' -or $IsWindows) -and -not $IISWebsite) { $Temp = [System.IO.Path]::GetTempPath() $msiFileName = "PowerShellUniversal.$Version.msi" $Msi = (Join-Path $Temp $msiFileName) Remove-Item $Msi -Force -ErrorAction SilentlyContinue $msiUrl = "https://imsreleases.blob.core.windows.net/universal/production/$Version/PowerShellUniversal.$Version.msi" $msiSource = Resolve-PSUPackageSource -FileName $msiFileName -DefaultUrl $msiUrl $msiAction = if ($msiSource.IsLocalFile) { 'Copying' } else { 'Downloading' } $msiContext = if ($msiSource.IsOverride) { ' using PSU_SERVER_DOWNLOAD_SOURCE' } else { '' } Write-Verbose "$msiAction MSI from $($msiSource.Location)$msiContext" Save-PSUPackage -Source $msiSource -DestinationPath $Msi Write-Verbose "Download complete. Installing from MSI." Invoke-PSUMsiExec -Action Install -PackagePath $Msi -ProductName 'PowerShell Universal' } else { Write-Verbose "Installing server to $Path" $Temp = [System.IO.Path]::GetTempPath() $zipFileName = "Universal.$platform.$Version.zip" $Zip = (Join-Path $Temp $zipFileName) Remove-Item $Zip -Force -ErrorAction SilentlyContinue $zipUrl = "https://imsreleases.blob.core.windows.net/universal/production/$Version/Universal.$platform.$Version.zip" $zipSource = Resolve-PSUPackageSource -FileName $zipFileName -DefaultUrl $zipUrl $zipAction = if ($zipSource.IsLocalFile) { 'Copying' } else { 'Downloading' } $zipContext = if ($zipSource.IsOverride) { ' using PSU_SERVER_DOWNLOAD_SOURCE' } else { '' } Write-Verbose "$zipAction ZIP from $($zipSource.Location)$zipContext" Save-PSUPackage -Source $zipSource -DestinationPath $Zip Write-Verbose "Download complete. Unzipping to $Path" Expand-Archive -Path $Zip -DestinationPath $Path -Force Remove-Item $Zip -Force } if ($IISWebsite) { New-WebAppPool -Name $IISAppPool | Out-Null New-Website -Name $IISWebsite -Port $IISPort -PhysicalPath $Path -ApplicationPool $IISAppPool | Out-Null Start-Website -Name $IISWebsite } if ($IsMacOS -or $IsLinux) { $ServerPath = Join-Path $Path "Universal.Server" /bin/chmod +x $ServerPath } if ($IsLinux) { Write-Verbose "Creating and starting PowerShellUniversal service" touch /etc/systemd/system/PowerShellUniversal.service chmod 664 /etc/systemd/system/PowerShellUniversal.service "[Unit] Description=PowerShell Universal [Service] ExecStart=$Path/Universal.Server [Install] WantedBy=multi-user.target" | Out-File /etc/systemd/system/PowerShellUniversal.service systemctl daemon-reload systemctl start PowerShellUniversal systemctl enable PowerShellUniversal } if ($PSVersionTable.PSEdition -eq 'Desktop' -or $IsWindows) { } else { Write-Verbose "Adding $Path to `$PATH variable" $PathSeparator = ":" $envPath = [Environment]::GetEnvironmentVariable('PATH') $newpath = $envPath + $PathSeparator + $Path [Environment]::SetEnvironmentVariable("PATH", $newpath) } $Env:PATH += $PathSeparator + $Path } function Update-PSUServer { <# .SYNOPSIS Update the PowerShell Universal server. .DESCRIPTION Update the PowerShell Universal server. This is a convenience function that will update the server for your platform. .PARAMETER Path The path for the PowerShell Universal binaries. If not specified, the path will attempt to be resolved. .PARAMETER Version The version to upgrade to. .PARAMETER LatestVersion Upgrade to the latest version. .EXAMPLE Update-PSUServer #> [CmdletBinding(DefaultParameterSetName = "Version")] param( [Parameter()] [string]$Path, [Parameter(ParameterSetName = "Version")] [string]$Version, [Parameter(ParameterSetName = "Latest")] [Switch]$LatestVersion, [Parameter()] [string]$IISWebsite ) if ([string]::IsNullOrEmpty($Version)) { # This module is either named Universal or Devolutions.PowerShellUniversal so use the version from the module if not specified. $Version = $PSCmdlet.MyInvocation.MyCommand.Module.Version Write-Verbose "Version not specified. Using version $Version from the Universal module." } $platform = "win-x64"; if ($PSVersionTable.PSEdition -eq 'Core') { if ($IsLinux) { $platform = "linux-x64" } elseif ($IsMacOS) { $platform = "osx-x64" } } if ($platform -eq 'win-x64' -and -not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { throw 'You must be an administrator to update the Universal Server. Please run the command as an administrator.' } if ($IISWebsite -and ($IsLinux -or $IsMacOS)) { throw "IISWebsite is only supported on Windows." } if ($IISWebsite) { Import-Module WebAdministration -ErrorAction Stop } # If the path isn't specified, attempt to resolve it. if (-not $Path -and ($IsLinux -or $IsMacOS)) { $ServerPath = Get-Command "Universal.Server" -ErrorAction SilentlyContinue if (-not $ServerPath) { throw "Unable to locate existing PowerShell Universal installation. Use the -Path parameter to specify the folder of the previous installation." } $Path = [System.IO.Path]::GetDirectoryName($ServerPath.Source) } if (-not $Path -and $IISWebsite) { $Path = (Get-Website -Name $IISWebsite).PhysicalPath } # Determine whether this is a Devolutions-era version (major year >= 2026) $parsedVersion = [Version]::new(0, 0) [void][Version]::TryParse($Version, [ref]$parsedVersion) $isDevolutionsVersion = $parsedVersion.Major -gt 2000 if ($LatestVersion) { if ($isDevolutionsVersion) { Write-Verbose "Fetching latest version from Devolutions product info." $productInfo = (Invoke-WebRequest 'https://devolutions.net/productinfo.json' -UseBasicParsing).Content | ConvertFrom-Json $Version = $productInfo.PowerShellUniversal.Current.Version Write-Verbose "Latest Devolutions version: $Version" # Re-evaluate after fetching the latest version [void][Version]::TryParse($Version, [ref]$parsedVersion) $isDevolutionsVersion = $parsedVersion.Major -gt 2000 } else { $Version = (Invoke-WebRequest 'https://imsreleases.blob.core.windows.net/universal/production/v5-version.txt' -UseBasicParsing).Content } } Write-Verbose "Downloading version $Version" if ($isDevolutionsVersion) { # The Devolutions CDN uses a 4-octet version (e.g. 2026.1.2.0). # If the supplied version has only 3 parts, append .0. $cdnVersion = $Version if (($Version -split '\.').Count -eq 3) { $cdnVersion = "$Version.0" } if (($PSVersionTable.PSEdition -eq 'Desktop' -or $IsWindows) -and -not $IISWebsite) { $Temp = [System.IO.Path]::GetTempPath() $msiFileName = "Devolutions.PowerShellUniversal.$cdnVersion.msi" $Msi = (Join-Path $Temp $msiFileName) Remove-Item $Msi -Force -ErrorAction SilentlyContinue $msiUrl = "https://cdn.devolutions.net/download/Devolutions.PowerShellUniversal.$cdnVersion.msi" $msiSource = Resolve-PSUPackageSource -FileName $msiFileName -DefaultUrl $msiUrl $msiAction = if ($msiSource.IsLocalFile) { 'Copying' } else { 'Downloading' } $msiContext = if ($msiSource.IsOverride) { ' using PSU_SERVER_DOWNLOAD_SOURCE' } else { '' } Write-Verbose "$msiAction MSI from $($msiSource.Location)$msiContext" Save-PSUPackage -Source $msiSource -DestinationPath $Msi Write-Verbose "Download complete. Installing from MSI." Invoke-PSUMsiExec -Action Install -PackagePath $Msi -ProductName 'PowerShell Universal' } else { Write-Verbose "Upgrading server installed at $Path" $Temp = [System.IO.Path]::GetTempPath() $zipFileName = "Universal.$platform.$cdnVersion.zip" $Zip = (Join-Path $Temp $zipFileName) Remove-Item $Zip -Force -ErrorAction SilentlyContinue if ($IISWebsite) { $AppPool = (Get-Website -Name $IISWebsite).ApplicationPool Stop-Website -Name $IISWebsite Stop-WebAppPool -Name $AppPool } if ($IsLinux) { Write-Verbose "Stopped PowerShellUniversal service" systemctl stop PowerShellUniversal systemctl disable PowerShellUniversal } Remove-Item $Path -Force -Recurse $zipUrl = "https://cdn.devolutions.net/download/Universal.$platform.$cdnVersion.zip" $zipSource = Resolve-PSUPackageSource -FileName $zipFileName -DefaultUrl $zipUrl $zipAction = if ($zipSource.IsLocalFile) { 'Copying' } else { 'Downloading' } $zipContext = if ($zipSource.IsOverride) { ' using PSU_SERVER_DOWNLOAD_SOURCE' } else { '' } Write-Verbose "$zipAction ZIP from $($zipSource.Location)$zipContext" Save-PSUPackage -Source $zipSource -DestinationPath $Zip Write-Verbose "Download complete. Extracting to $Path" Expand-Archive -Path $Zip -DestinationPath $Path -Force Remove-Item $Zip -Force if ($IISWebsite) { Get-ChildItem $Path -Recurse | Unblock-File Start-Website -Name $IISWebsite } if ($IsMacOS -or $IsLinux) { $ServerPath = Join-Path $Path "Universal.Server" /bin/chmod +x $ServerPath } if ($IsLinux) { Write-Verbose "Started PowerShellUniversal service" systemctl start PowerShellUniversal systemctl enable PowerShellUniversal } } } elseif (($PSVersionTable.PSEdition -eq 'Desktop' -or $IsWindows) -and -not $IISWebsite) { $Temp = [System.IO.Path]::GetTempPath() $msiFileName = "PowerShellUniversal.$Version.msi" $Msi = (Join-Path $Temp $msiFileName) Remove-Item $Msi -Force -ErrorAction SilentlyContinue $msiUrl = "https://imsreleases.blob.core.windows.net/universal/production/$Version/PowerShellUniversal.$Version.msi" $msiSource = Resolve-PSUPackageSource -FileName $msiFileName -DefaultUrl $msiUrl $msiAction = if ($msiSource.IsLocalFile) { 'Copying' } else { 'Downloading' } $msiContext = if ($msiSource.IsOverride) { ' using PSU_SERVER_DOWNLOAD_SOURCE' } else { '' } Write-Verbose "$msiAction MSI from $($msiSource.Location)$msiContext" Save-PSUPackage -Source $msiSource -DestinationPath $Msi Write-Verbose "Download complete. Installing from MSI." Invoke-PSUMsiExec -Action Install -PackagePath $Msi -ProductName 'PowerShell Universal' } else { Write-Verbose "Upgrading server installed at $Path" $Temp = [System.IO.Path]::GetTempPath() $zipFileName = "Universal.$platform.$Version.zip" $Zip = (Join-Path $Temp $zipFileName) Remove-Item $Zip -Force -ErrorAction SilentlyContinue if ($IISWebsite) { $AppPool = (Get-Website -Name $IISWebsite).ApplicationPool Stop-Website -Name $IISWebsite Stop-WebAppPool -Name $AppPool } if ($IsLinux) { Write-Verbose "Stopped PowerShellUniversal service" systemctl stop PowerShellUniversal systemctl disable PowerShellUniversal } Remove-Item $Path -Force -Recurse $zipUrl = "https://imsreleases.blob.core.windows.net/universal/production/$Version/Universal.$platform.$Version.zip" $zipSource = Resolve-PSUPackageSource -FileName $zipFileName -DefaultUrl $zipUrl $zipAction = if ($zipSource.IsLocalFile) { 'Copying' } else { 'Downloading' } $zipContext = if ($zipSource.IsOverride) { ' using PSU_SERVER_DOWNLOAD_SOURCE' } else { '' } Write-Verbose "$zipAction ZIP from $($zipSource.Location)$zipContext" Save-PSUPackage -Source $zipSource -DestinationPath $Zip Write-Verbose "Download complete. Extracting to $Path" Expand-Archive -Path $Zip -DestinationPath $Path -Force Remove-Item $Zip -Force if ($IISWebsite) { Get-ChildItem $Path -Recurse | Unblock-File Start-Website -Name $IISWebsite } if ($IsMacOS -or $IsLinux) { $ServerPath = Join-Path $Path "Universal.Server" /bin/chmod +x $ServerPath } if ($IsLinux) { Write-Verbose "Started PowerShellUniversal service" systemctl start PowerShellUniversal systemctl enable PowerShellUniversal } } } function Uninstall-PSUServer { <# .SYNOPSIS Removes the PowerShell Universal server. .DESCRIPTION Removes the PowerShell Universal server. This is a convenience function that will remove the server for your platform. .PARAMETER Path The path to the PowerShell Universal binaries. If not specified, the path will attempt to be resolved. .EXAMPLE Remove-PSUServer #> [Alias("Remove-PSUServer")] [CmdletBinding(DefaultParameterSetName = "Version")] param( [Parameter()] [string]$Path, [Parameter()] [string]$IISWebsite ) if (($PSVersionTable.PSEdition -eq 'Desktop' -or $IsWindows) -and -not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { throw 'You must be an administrator to remove the Universal Server. Please run the command as an administrator.' } if ($IISWebsite -and ($IsLinux -or $IsMacOS)) { throw "IISWebsite is only supported on Windows." } if ($IISWebsite) { Import-Module WebAdministration -ErrorAction Stop } if (($PSVersionTable.PSEdition -eq 'Desktop' -or $IsWindows) -and -not $IISWebsite) { Write-Verbose "Locating local package with Win32_Product" $Package = Get-CimInstance win32_product -Filter "Name like 'PowerShell Universal%'" | Select-Object -First 1 if ($Package) { Write-Verbose "Removing via msiexec." Invoke-PSUMsiExec -Action Uninstall -PackagePath $Package.LocalPackage -ProductName 'PowerShell Universal' } else { throw "Unable to locate PowerShell Universal installation." } } else { if (-not $Path -and ($IsLinux -or $IsMacOS)) { $ServerPath = Get-Command "Universal.Server" -ErrorAction SilentlyContinue if (-not $ServerPath) { throw "Unable to locate existing PowerShell Universal installation. Use the -Path parameter to specify the folder of the previous installation." } $Path = [System.IO.Path]::GetDirectoryName($ServerPath.Source) } if ($IISWebsite) { $Path = (Get-Website -Name $IISWebsite).PhysicalPath $AppPool = (Get-Website -Name $IISWebsite).ApplicationPool Stop-Website -Name $IISWebsite Remove-Website -Name $IISWebsite Remove-WebAppPool $AppPool Write-Verbose "Removing application files" Remove-Item $Path -Force -Recurse } if ($IsLinux) { Write-Verbose "Stopped PowerShellUniversal service" systemctl stop PowerShellUniversal systemctl disable PowerShellUniversal Remove-Item /etc/systemd/system/PowerShellUniversal.service -Force systemctl daemon-reload Write-Verbose "Removing application files" Remove-Item $Path -Force -Recurse } } } # SIG # Begin signature block # MIIvWQYJKoZIhvcNAQcCoIIvSjCCL0YCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBstjtrmYbClLus # lgmQpvF8iiebv04fb1xs9qZ5mGU6c6CCFBcwggVyMIIDWqADAgECAhB2U/6sdUZI # k/Xl10pIOk74MA0GCSqGSIb3DQEBDAUAMFMxCzAJBgNVBAYTAkJFMRkwFwYDVQQK # ExBHbG9iYWxTaWduIG52LXNhMSkwJwYDVQQDEyBHbG9iYWxTaWduIENvZGUgU2ln # bmluZyBSb290IFI0NTAeFw0yMDAzMTgwMDAwMDBaFw00NTAzMTgwMDAwMDBaMFMx # CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSkwJwYDVQQD # EyBHbG9iYWxTaWduIENvZGUgU2lnbmluZyBSb290IFI0NTCCAiIwDQYJKoZIhvcN # AQEBBQADggIPADCCAgoCggIBALYtxTDdeuirkD0DcrA6S5kWYbLl/6VnHTcc5X7s # k4OqhPWjQ5uYRYq4Y1ddmwCIBCXp+GiSS4LYS8lKA/Oof2qPimEnvaFE0P31PyLC # o0+RjbMFsiiCkV37WYgFC5cGwpj4LKczJO5QOkHM8KCwex1N0qhYOJbp3/kbkbuL # ECzSx0Mdogl0oYCve+YzCgxZa4689Ktal3t/rlX7hPCA/oRM1+K6vcR1oW+9YRB0 # RLKYB+J0q/9o3GwmPukf5eAEh60w0wyNA3xVuBZwXCR4ICXrZ2eIq7pONJhrcBHe # OMrUvqHAnOHfHgIB2DvhZ0OEts/8dLcvhKO/ugk3PWdssUVcGWGrQYP1rB3rdw1G # R3POv72Vle2dK4gQ/vpY6KdX4bPPqFrpByWbEsSegHI9k9yMlN87ROYmgPzSwwPw # jAzSRdYu54+YnuYE7kJuZ35CFnFi5wT5YMZkobacgSFOK8ZtaJSGxpl0c2cxepHy # 1Ix5bnymu35Gb03FhRIrz5oiRAiohTfOB2FXBhcSJMDEMXOhmDVXR34QOkXZLaRR # kJipoAc3xGUaqhxrFnf3p5fsPxkwmW8x++pAsufSxPrJ0PBQdnRZ+o1tFzK++Ol+ # A/Tnh3Wa1EqRLIUDEwIrQoDyiWo2z8hMoM6e+MuNrRan097VmxinxpI68YJj8S4O # JGTfAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0G # A1UdDgQWBBQfAL9GgAr8eDm3pbRD2VZQu86WOzANBgkqhkiG9w0BAQwFAAOCAgEA # Xiu6dJc0RF92SChAhJPuAW7pobPWgCXme+S8CZE9D/x2rdfUMCC7j2DQkdYc8pzv # eBorlDICwSSWUlIC0PPR/PKbOW6Z4R+OQ0F9mh5byV2ahPwm5ofzdHImraQb2T07 # alKgPAkeLx57szO0Rcf3rLGvk2Ctdq64shV464Nq6//bRqsk5e4C+pAfWcAvXda3 # XaRcELdyU/hBTsz6eBolSsr+hWJDYcO0N6qB0vTWOg+9jVl+MEfeK2vnIVAzX9Rn # m9S4Z588J5kD/4VDjnMSyiDN6GHVsWbcF9Y5bQ/bzyM3oYKJThxrP9agzaoHnT5C # JqrXDO76R78aUn7RdYHTyYpiF21PiKAhoCY+r23ZYjAf6Zgorm6N1Y5McmaTgI0q # 41XHYGeQQlZcIlEPs9xOOe5N3dkdeBBUO27Ql28DtR6yI3PGErKaZND8lYUkqP/f # obDckUCu3wkzq7ndkrfxzJF0O2nrZ5cbkL/nx6BvcbtXv7ePWu16QGoWzYCELS/h # AtQklEOzFfwMKxv9cW/8y7x1Fzpeg9LJsy8b1ZyNf1T+fn7kVqOHp53hWVKUQY9t # W76GlZr/GnbdQNJRSnC0HzNjI3c/7CceWeQIh+00gkoPP/6gHcH1Z3NFhnj0qinp # J4fGGdvGExTDOUmHTaCX4GUT9Z13Vunas1jHOvLAzYIwggboMIIE0KADAgECAhB3 # vQ4Ft1kLth1HYVMeP3XtMA0GCSqGSIb3DQEBCwUAMFMxCzAJBgNVBAYTAkJFMRkw # FwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSkwJwYDVQQDEyBHbG9iYWxTaWduIENv # ZGUgU2lnbmluZyBSb290IFI0NTAeFw0yMDA3MjgwMDAwMDBaFw0zMDA3MjgwMDAw # MDBaMFwxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMTIw # MAYDVQQDEylHbG9iYWxTaWduIEdDQyBSNDUgRVYgQ29kZVNpZ25pbmcgQ0EgMjAy # MDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMsg75ceuQEyQ6BbqYoj # /SBerjgSi8os1P9B2BpV1BlTt/2jF+d6OVzA984Ro/ml7QH6tbqT76+T3PjisxlM # g7BKRFAEeIQQaqTWlpCOgfh8qy+1o1cz0lh7lA5tD6WRJiqzg09ysYp7ZJLQ8LRV # X5YLEeWatSyyEc8lG31RK5gfSaNf+BOeNbgDAtqkEy+FSu/EL3AOwdTMMxLsvUCV # 0xHK5s2zBZzIU+tS13hMUQGSgt4T8weOdLqEgJ/SpBUO6K/r94n233Hw0b6nskEz # IHXMsdXtHQcZxOsmd/KrbReTSam35sOQnMa47MzJe5pexcUkk2NvfhCLYc+YVaMk # oog28vmfvpMusgafJsAMAVYS4bKKnw4e3JiLLs/a4ok0ph8moKiueG3soYgVPMLq # 7rfYrWGlr3A2onmO3A1zwPHkLKuU7FgGOTZI1jta6CLOdA6vLPEV2tG0leis1Ult # 5a/dm2tjIF2OfjuyQ9hiOpTlzbSYszcZJBJyc6sEsAnchebUIgTvQCodLm3HadNu # twFsDeCXpxbmJouI9wNEhl9iZ0y1pzeoVdwDNoxuz202JvEOj7A9ccDhMqeC5LYy # AjIwfLWTyCH9PIjmaWP47nXJi8Kr77o6/elev7YR8b7wPcoyPm593g9+m5XEEofn # GrhO7izB36Fl6CSDySrC/blTAgMBAAGjggGtMIIBqTAOBgNVHQ8BAf8EBAMCAYYw # EwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4E # FgQUJZ3Q/FkJhmPF7POxEztXHAOSNhEwHwYDVR0jBBgwFoAUHwC/RoAK/Hg5t6W0 # Q9lWULvOljswgZMGCCsGAQUFBwEBBIGGMIGDMDkGCCsGAQUFBzABhi1odHRwOi8v # b2NzcC5nbG9iYWxzaWduLmNvbS9jb2Rlc2lnbmluZ3Jvb3RyNDUwRgYIKwYBBQUH # MAKGOmh0dHA6Ly9zZWN1cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L2NvZGVzaWdu # aW5ncm9vdHI0NS5jcnQwQQYDVR0fBDowODA2oDSgMoYwaHR0cDovL2NybC5nbG9i # YWxzaWduLmNvbS9jb2Rlc2lnbmluZ3Jvb3RyNDUuY3JsMFUGA1UdIAROMEwwQQYJ # KwYBBAGgMgECMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24u # Y29tL3JlcG9zaXRvcnkvMAcGBWeBDAEDMA0GCSqGSIb3DQEBCwUAA4ICAQAldaAJ # yTm6t6E5iS8Yn6vW6x1L6JR8DQdomxyd73G2F2prAk+zP4ZFh8xlm0zjWAYCImbV # YQLFY4/UovG2XiULd5bpzXFAM4gp7O7zom28TbU+BkvJczPKCBQtPUzosLp1pnQt # pFg6bBNJ+KUVChSWhbFqaDQlQq+WVvQQ+iR98StywRbha+vmqZjHPlr00Bid/XSX # hndGKj0jfShziq7vKxuav2xTpxSePIdxwF6OyPvTKpIz6ldNXgdeysEYrIEtGiH6 # bs+XYXvfcXo6ymP31TBENzL+u0OF3Lr8psozGSt3bdvLBfB+X3Uuora/Nao2Y8nO # ZNm9/Lws80lWAMgSK8YnuzevV+/Ezx4pxPTiLc4qYc9X7fUKQOL1GNYe6ZAvytOH # X5OKSBoRHeU3hZ8uZmKaXoFOlaxVV0PcU4slfjxhD4oLuvU/pteO9wRWXiG7n9dq # cYC/lt5yA9jYIivzJxZPOOhRQAyuku++PX33gMZMNleElaeEFUgwDlInCI2Oor0i # xxnJpsoOqHo222q6YV8RJJWk4o5o7hmpSZle0LQ0vdb5QMcQlzFSOTUpEYck08T7 # qWPLd0jV+mL8JOAEek7Q5G7ezp44UCb0IXFl1wkl1MkHAHq4x/N36MXU4lXQ0x72 # f1LiSY25EXIMiEQmM2YBRN/kMw4h3mKJSAfa9TCCB7EwggWZoAMCAQICDHPTwzYD # /4u0QiTyXjANBgkqhkiG9w0BAQsFADBcMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQ # R2xvYmFsU2lnbiBudi1zYTEyMDAGA1UEAxMpR2xvYmFsU2lnbiBHQ0MgUjQ1IEVW # IENvZGVTaWduaW5nIENBIDIwMjAwHhcNMjMxMDMwMTc1MTE4WhcNMjYxMDMwMTc1 # MTE4WjCB8TEdMBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRpb24xEzARBgNVBAUT # CjExNjI1NDQ2ODkxEzARBgsrBgEEAYI3PAIBAxMCQ0ExFzAVBgsrBgEEAYI3PAIB # AhMGUXVlYmVjMQswCQYDVQQGEwJDQTEPMA0GA1UECBMGUXVlYmVjMRIwEAYDVQQH # EwlMYXZhbHRyaWUxGDAWBgNVBAoTD0Rldm9sdXRpb25zIEluYzEYMBYGA1UEAxMP # RGV2b2x1dGlvbnMgSW5jMScwJQYJKoZIhvcNAQkBFhhzZWN1cml0eUBkZXZvbHV0 # aW9ucy5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCfDk6c1eCL # 9rTvq1D1lq1GmU08ZKyYQJQ7Eb/mRFpRXqpOFiySnf8BysYbZ4y4MnIl7M2Wjc5n # 1JcXR9BPWmkJLnI7rFTwpq/O5xKUwW20/EYyOuF7TasRq6olljm73dcLjrt5z/a2 # u2gO+vMS8LVY6UXKAGZGIigMoPS92f2MkkKmdEmA5dpwbALUfvH9sy0qknUfQY6d # slpI8PbjTCx9GY5xqCTMtBQcWB5sBn/I0YAlp5yuOn+2ga4vUcucAZTVseoRI/Js # n5KWWb0iM9wrbv+DOCzcAtBF+Yj2Kp8wHRWfMCumu4YuYcwTY3hbIuNRoUi8j4nL # ptjGaz7R3UfAr4b/rH4Vg8/l9ufP61Z7bpSkZbIlnh3Gjy9UJCjw5wguQucnllSb # NNg5ZBd7v3DRUKwKvzF9TYoOERwGdeY8uS4fnSYP7XuGF9b+coZ/D5guGaebiJJE # odRJkGdiP5P+6jLO43dzgmB4hmWbuF5wofRYXd1ihFOf4aBH2qzHnFkDvp5zeclM # lgoLuxJVb4mU36Z84KnJuT7fPThK9RbNEoqPPzd1BYcCcRmVaLCYHw+6AgmVXm3b # gCsv4zM/DqkycfPX11sBXedYdTJ4tihtFo1eRqfQsXEivN+XYwUIJ/EdfHUmaHU+ # 7eYhgSPVynPm9Fq1mAAC3KqH+6RtIpEmpQIDAQABo4IB2zCCAdcwDgYDVR0PAQH/ # BAQDAgeAMIGfBggrBgEFBQcBAQSBkjCBjzBMBggrBgEFBQcwAoZAaHR0cDovL3Nl # Y3VyZS5nbG9iYWxzaWduLmNvbS9jYWNlcnQvZ3NnY2NyNDVldmNvZGVzaWduY2Ey # MDIwLmNydDA/BggrBgEFBQcwAYYzaHR0cDovL29jc3AuZ2xvYmFsc2lnbi5jb20v # Z3NnY2NyNDVldmNvZGVzaWduY2EyMDIwMFUGA1UdIAROMEwwQQYJKwYBBAGgMgEC # MDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9z # aXRvcnkvMAcGBWeBDAEDMAkGA1UdEwQCMAAwRwYDVR0fBEAwPjA8oDqgOIY2aHR0 # cDovL2NybC5nbG9iYWxzaWduLmNvbS9nc2djY3I0NWV2Y29kZXNpZ25jYTIwMjAu # Y3JsMCMGA1UdEQQcMBqBGHNlY3VyaXR5QGRldm9sdXRpb25zLm5ldDATBgNVHSUE # DDAKBggrBgEFBQcDAzAfBgNVHSMEGDAWgBQlndD8WQmGY8Xs87ETO1ccA5I2ETAd # BgNVHQ4EFgQU+cpn+IPqWRnE5rHeI+bO8b/X89owDQYJKoZIhvcNAQELBQADggIB # ABr7ukUZYHuRYKb0JdoVh9Lwngn45m/BBg90jTL5CF6ZP4xYB2kaKN366sfAbvmK # ThbgfcIvN26NjS1/cFXad5af6s5OzGUic+mAFZOhbpX81GedsAnxl1D4BKJs2+iW # h/eK2vba/K3J5V2Z7S7YFgHqF0vlmDtNxnBQ8jsI30zrbcuYJowft8WLjfW4hr0S # dAIk2F4X1CTGhtJVMuPcxyUuvrmknp1g2y99jc5eXA6qp0CiUbFC1R3C1kpZYT4s # xiu86B3kbY6JqTO2f08tjvpih36UeFCC/ByZBzb1D8FFIaKiErjlDHVMIBCY1XrE # EDEJpIyMRyobXsIuisyn4TpK8JqRb0C0opDzvE8BlKvqlqmHfafbOUXFH5gz/F9a # iJAMfHyh4ddUg9nFcF+YKWKp8hpdaIW+5ptlsC2LSS5tztMUXRisUf/zCTeLQ2MA # Xc7Vl0sc8ZD9Uqb9wm+tmK3ZGvnDKCikwE8YU+y96ogFUybGcEWXUYk3QvuXKeS0 # 9/v6QOwbgY3o5EkrNQyPUugI2HsyWtmLhTdDM/Pnj+O2NDNkPXvGiss2b0O8yUMV # kh9C0HG4WS3L/ExoM1keN1Yd54FaFhk1zQv3KQaC7MJU8uZrmrIJLPNdEPGKiFfI # 8CLIV/04jAIrR+A4SDaCpDTz+XDZF7kP42KGybJiSD1qMYIamDCCGpQCAQEwbDBc # MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEyMDAGA1UE # AxMpR2xvYmFsU2lnbiBHQ0MgUjQ1IEVWIENvZGVTaWduaW5nIENBIDIwMjACDHPT # wzYD/4u0QiTyXjANBglghkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKAC # gAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsx # DjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCCmdamHpF2XCfuZUZqd5Mc2 # zR7gd9OiNaUGtCb2tOTp0DANBgkqhkiG9w0BAQEFAASCAgBA0YhgWC0H4UZmf1Od # vdL5Uo9uzh6+4r7UCUcovm3XQfnyK3aRGSiF0I2YZOKxUpVhr5R/5QtS/3oE55PB # 8Opl/gE1osRxe/WqOCrXzEKePc0IObqwNlGleSy9XuhQ+KSDnd2ekTUoS83IMaQZ # Z3Elz1z3uOr+1/1Sxah6/Bt2d7ja1qkTXdIMUHQXEH91F3aXixupUCy4n3UCg1+Q # fJPTL3rWS/Iu4Uq1FKJvakYl/0u2SvlhewV5RjulbPh+1EfztjwfqehRnZEQ/vHP # dRfPbaIarre96DK5HR3lh8+Jot85dwdB2Cbj9reuHCH7INaZ+YR4wq/mTCqAPghI # gabipAQbm84Zhm7hqCQvQP2WdGJ7/oV7DGKFyOiMJQcboGtyMXww/iivUH13WQXc # 8A0oyTakbyD0LHNmoH4uFWbJHjQgr+MGJNKxUABopMXnsjNHIxX61ZhEglQAxBTF # 8dWx0JLV5mtvBY46wnLPHCx6CSgfTUKTbChZuIdy4frRFaDQlvOfI94zJjzvZpJ+ # oX1izfRD8a7vwQXfnZQqp1LbpSei/wJzjnVpHeVyP9F3JgxI+SjPHYUYT/iBJOG4 # ENazZlbrtixAL2Y29tFkCc0bGrMB6t6sQwv5T9JWGNpVftIJOvU1zicImf+HEQmc # eelomsjdusHgge7TAMDqXgFjr6GCF3YwghdyBgorBgEEAYI3AwMBMYIXYjCCF14G # CSqGSIb3DQEHAqCCF08wghdLAgEDMQ8wDQYJYIZIAWUDBAIBBQAwdwYLKoZIhvcN # AQkQAQSgaARmMGQCAQEGCWCGSAGG/WwHATAxMA0GCWCGSAFlAwQCAQUABCAWJghY # ToMAkFfOsipnGMEDiNjOaTflWF2oL/pJS0FnXgIQLjEiKDZAm8KrlUbx6sQgLBgP # MjAyNjA2MjYxNTAzNDdaoIITOjCCBu0wggTVoAMCAQICEAqA7xhLjfEFgtHEdqeV # dGgwDQYJKoZIhvcNAQELBQAwaTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD # ZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IFRpbWVTdGFt # cGluZyBSU0E0MDk2IFNIQTI1NiAyMDI1IENBMTAeFw0yNTA2MDQwMDAwMDBaFw0z # NjA5MDMyMzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg # SW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgU0hBMjU2IFJTQTQwOTYgVGltZXN0YW1w # IFJlc3BvbmRlciAyMDI1IDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC # AQDQRqwtEsae0OquYFazK1e6b1H/hnAKAd/KN8wZQjBjMqiZ3xTWcfsLwOvRxUwX # cGx8AUjni6bz52fGTfr6PHRNv6T7zsf1Y/E3IU8kgNkeECqVQ+3bzWYesFtkepEr # vUSbf+EIYLkrLKd6qJnuzK8Vcn0DvbDMemQFoxQ2Dsw4vEjoT1FpS54dNApZfKY6 # 1HAldytxNM89PZXUP/5wWWURK+IfxiOg8W9lKMqzdIo7VA1R0V3Zp3DjjANwqAf4 # lEkTlCDQ0/fKJLKLkzGBTpx6EYevvOi7XOc4zyh1uSqgr6UnbksIcFJqLbkIXIPb # cNmA98Oskkkrvt6lPAw/p4oDSRZreiwB7x9ykrjS6GS3NR39iTTFS+ENTqW8m6TH # uOmHHjQNC3zbJ6nJ6SXiLSvw4Smz8U07hqF+8CTXaETkVWz0dVVZw7knh1WZXOLH # gDvundrAtuvz0D3T+dYaNcwafsVCGZKUhQPL1naFKBy1p6llN3QgshRta6Eq4B40 # h5avMcpi54wm0i2ePZD5pPIssoszQyF4//3DoK2O65Uck5Wggn8O2klETsJ7u8xE # ehGifgJYi+6I03UuT1j7FnrqVrOzaQoVJOeeStPeldYRNMmSF3voIgMFtNGh86w3 # ISHNm0IaadCKCkUe2LnwJKa8TIlwCUNVwppwn4D3/Pt5pwIDAQABo4IBlTCCAZEw # DAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU5Dv88jHt/f3X85FxYxlQQ89hjOgwHwYD # VR0jBBgwFoAU729TSunkBnx6yuKQVvYv1Ensy04wDgYDVR0PAQH/BAQDAgeAMBYG # A1UdJQEB/wQMMAoGCCsGAQUFBwMIMIGVBggrBgEFBQcBAQSBiDCBhTAkBggrBgEF # BQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMF0GCCsGAQUFBzAChlFodHRw # Oi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRUaW1lU3Rh # bXBpbmdSU0E0MDk2U0hBMjU2MjAyNUNBMS5jcnQwXwYDVR0fBFgwVjBUoFKgUIZO # aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0VGltZVN0 # YW1waW5nUlNBNDA5NlNIQTI1NjIwMjVDQTEuY3JsMCAGA1UdIAQZMBcwCAYGZ4EM # AQQCMAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEAZSqt8RwnBLmuYEHs # 0QhEnmNAciH45PYiT9s1i6UKtW+FERp8FgXRGQ/YAavXzWjZhY+hIfP2JkQ38U+w # tJPBVBajYfrbIYG+Dui4I4PCvHpQuPqFgqp1PzC/ZRX4pvP/ciZmUnthfAEP1HSh # TrY+2DE5qjzvZs7JIIgt0GCFD9ktx0LxxtRQ7vllKluHWiKk6FxRPyUPxAAYH2Vy # 1lNM4kzekd8oEARzFAWgeW3az2xejEWLNN4eKGxDJ8WDl/FQUSntbjZ80FU3i54t # px5F/0Kr15zW/mJAxZMVBrTE2oi0fcI8VMbtoRAmaaslNXdCG1+lqvP4FbrQ6IwS # BXkZagHLhFU9HCrG/syTRLLhAezu/3Lr00GrJzPQFnCEH1Y58678IgmfORBPC1JK # kYaEt2OdDh4GmO0/5cHelAK2/gTlQJINqDr6JfwyYHXSd+V08X1JUPvB4ILfJdmL # +66Gp3CSBXG6IwXMZUXBhtCyIaehr0XkBoDIGMUG1dUtwq1qmcwbdUfcSYCn+Own # cVUXf53VJUNOaMWMts0VlRYxe5nK+At+DI96HAlXHAL5SlfYxJ7La54i71McVWRP # 66bW+yERNpbJCjyCYG2j+bdpxo/1Cy4uPcU3AWVPGrbn5PhDBf3Froguzzhk++am # i+r3Qrx5bIbY3TVzgiFI7Gq3zWcwgga0MIIEnKADAgECAhANx6xXBf8hmS5AQyIM # OkmGMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdp # Q2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERp # Z2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yNTA1MDcwMDAwMDBaFw0zODAxMTQy # MzU5NTlaMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFB # MD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5 # NiBTSEEyNTYgMjAyNSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC # AQC0eDHTCphBcr48RsAcrHXbo0ZodLRRF51NrY0NlLWZloMsVO1DahGPNRcybEKq # +RuwOnPhof6pvF4uGjwjqNjfEvUi6wuim5bap+0lgloM2zX4kftn5B1IpYzTqpyF # Q/4Bt0mAxAHeHYNnQxqXmRinvuNgxVBdJkf77S2uPoCj7GH8BLuxBG5AvftBdsOE # CS1UkxBvMgEdgkFiDNYiOTx4OtiFcMSkqTtF2hfQz3zQSku2Ws3IfDReb6e3mmdg # lTcaarps0wjUjsZvkgFkriK9tUKJm/s80FiocSk1VYLZlDwFt+cVFBURJg6zMUjZ # a/zbCclF83bRVFLeGkuAhHiGPMvSGmhgaTzVyhYn4p0+8y9oHRaQT/aofEnS5xLr # fxnGpTXiUOeSLsJygoLPp66bkDX1ZlAeSpQl92QOMeRxykvq6gbylsXQskBBBnGy # 3tW/AMOMCZIVNSaz7BX8VtYGqLt9MmeOreGPRdtBx3yGOP+rx3rKWDEJlIqLXvJW # nY0v5ydPpOjL6s36czwzsucuoKs7Yk/ehb//Wx+5kMqIMRvUBDx6z1ev+7psNOdg # JMoiwOrUG2ZdSoQbU2rMkpLiQ6bGRinZbI4OLu9BMIFm1UUl9VnePs6BaaeEWvjJ # SjNm2qA+sdFUeEY0qVjPKOWug/G6X5uAiynM7Bu2ayBjUwIDAQABo4IBXTCCAVkw # EgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU729TSunkBnx6yuKQVvYv1Ens # y04wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQD # AgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEF # BQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRw # Oi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNy # dDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGln # aUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglg # hkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBABfO+xaAHP4HPRF2cTC9vgvItTSm # f83Qh8WIGjB/T8ObXAZz8OjuhUxjaaFdleMM0lBryPTQM2qEJPe36zwbSI/mS83a # fsl3YTj+IQhQE7jU/kXjjytJgnn0hvrV6hqWGd3rLAUt6vJy9lMDPjTLxLgXf9r5 # nWMQwr8Myb9rEVKChHyfpzee5kH0F8HABBgr0UdqirZ7bowe9Vj2AIMD8liyrukZ # 2iA/wdG2th9y1IsA0QF8dTXqvcnTmpfeQh35k5zOCPmSNq1UH410ANVko43+Cdmu # 4y81hjajV/gxdEkMx1NKU4uHQcKfZxAvBAKqMVuqte69M9J6A47OvgRaPs+2ykgc # GV00TYr2Lr3ty9qIijanrUR3anzEwlvzZiiyfTPjLbnFRsjsYg39OlV8cipDoq7+ # qNNjqFzeGxcytL5TTLL4ZaoBdqbhOhZ3ZRDUphPvSRmMThi0vw9vODRzW6AxnJll # 38F0cuJG7uEBYTptMSbhdhGQDpOXgpIUsWTjd6xpR6oaQf/DJbg3s6KCLPAlZ66R # zIg9sC+NJpud/v4+7RWsWCiKi9EOLLHfMR2ZyJ/+xhCx9yHbxtl5TPau1j/1MIDp # MPx0LckTetiSuEtQvLsNz3Qbp7wGWqbIiOWCnb5WqxL3/BAPvIXKUjPSxyZsq8Wh # baM2tszWkPZPubdcMIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv21DiCEAYWjANBgkq # hkiG9w0BAQwFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j # MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBB # c3N1cmVkIElEIFJvb3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcNMzExMTA5MjM1OTU5 # WjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL # ExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJv # b3QgRzQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1K # PDAiMGkz7MKnJS7JIT3yithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2r # snnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C # 8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBf # sXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGY # QJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8 # rhsDdV14Ztk6MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaY # dj1ZXUJ2h4mXaXpI8OCiEhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+ # wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw # ++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+N # P8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7F # wI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo4IBOjCCATYwDwYDVR0TAQH/BAUw # AwEB/zAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wHwYDVR0jBBgwFoAU # Reuir/SSy4IxLVGLp6chnfNtyA8wDgYDVR0PAQH/BAQDAgGGMHkGCCsGAQUFBwEB # BG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsG # AQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1 # cmVkSURSb290Q0EuY3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRp # Z2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwEQYDVR0gBAow # CDAGBgRVHSAAMA0GCSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXnOF+go3QbPbYW1/e/ # Vwe9mqyhhyzshV6pGrsi+IcaaVQi7aSId229GhT0E0p6Ly23OO/0/4C5+KH38nLe # JLxSA8hO0Cre+i1Wz/n096wwepqLsl7Uz9FDRJtDIeuWcqFItJnLnU+nBgMTdydE # 1Od/6Fmo8L8vC6bp8jQ87PcDx4eo0kxAGTVGamlUsLihVo7spNU96LHc/RzY9Hda # XFSMb++hUD38dglohJ9vytsgjTVgHAIDyyCwrFigDkBjxZgiwbJZ9VVrzyerbHbO # byMt9H5xaiNrIv8SuFQtJ37YOtnwtoeW/VvRXKwYw02fc7cBqZ9Xql4o4rmUMYID # fDCCA3gCAQEwfTBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIElu # Yy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJT # QTQwOTYgU0hBMjU2IDIwMjUgQ0ExAhAKgO8YS43xBYLRxHanlXRoMA0GCWCGSAFl # AwQCAQUAoIHRMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0B # CQUxDxcNMjYwNjI2MTUwMzQ3WjArBgsqhkiG9w0BCRACDDEcMBowGDAWBBTdYjCs # hgotMGvaOLFoeVIwB/tBfjAvBgkqhkiG9w0BCQQxIgQgqApVW8dsMGVrAQEy+gd/ # NE37b2tbAkWZvRovU+5s/QIwNwYLKoZIhvcNAQkQAi8xKDAmMCQwIgQgSqA/oizX # XITFXJOPgo5na5yuyrM/420mmqM08UYRCjMwDQYJKoZIhvcNAQEBBQAEggIAJ202 # IahahZQiZBs6EAoug4KOSPal+c9o90EeDLeCR65LIOLrWGf5numiGNG5wnMm7Qy7 # OaXy5b1uqvv3AvNOWtMXipu2g6yctzrJGIS1Dv+24tHDxBf7zF6DsB7QyM6CGXcZ # atkNNFdDrqu/4wKaNkkjjRVWNwQPI4ojdEgZ4toeHTsOu6w8HlEO28+iBehnFY9K # dZWIXmZIxWnM1upy/37SIydcQOZxs9VhraNzIWlz3Vc/fXzNkrMmQZY2yS2NxK9A # 9hRG0yj5Y/5bIYqUpPXrf0kC04wA9/Abt5+XpRbg5YGT2MdjOysSjD5bS57cgAOE # 5zutQJLQY4LdA64MZUaBYFRW4R3blpnj0su0F8O6CDnUr/JnZPhHTKYWquATmeHI # IxzGxeWkuyXfXh8gaHICL012shA5iSTurRIdSckatJDpd2l1oxY6ooyaPy3qf3Ig # 4Y/U+xjUk/mq1FLUP3xb23/23eS3KlhWU25Dvhy21oiQlA8f+PMNPrFMOOSDPW0V # H5aXy9J2V67q1lC5KrOsb3qyshKK/2B03+3tbnxsuN4CAXBeHrMhtMY3JgLJxHYp # L1yUTvWgCFfY8GknH8A7tVRpr2DpVd1bAr7LQpWctPnNZKgOx+sjltCDCVEqq4zs # H9j16gjHCDWqkhtSmzKuBqC776OX77OPiQkcsW8= # SIG # End signature block |