PowerShell/src/powershell-native/Install-PowerShellRemoting.ps1

233 lines
8.5 KiB
PowerShell

# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
#####################################################################################################
#
# Registers the WinRM endpoint for this instance of PowerShell.
#
# If the parameters '-PowerShellHome' were specified, it means that the script will be registering
# an instance of PowerShell from another instance of PowerShell.
#
# If no parameter is specified, it means that this instance of PowerShell is registering itself.
#
# Assumptions:
# 1. The CoreCLR and the the PowerShell assemblies are side-by-side in $PSHOME
# 2. Plugins are registered by version number. Only one plugin can be automatically registered
# per PowerShell version. However, multiple endpoints may be manually registered for a given
# plugin.
#
#####################################################################################################
[CmdletBinding(DefaultParameterSetName = "NotByPath")]
param
(
[parameter(Mandatory = $true, ParameterSetName = "ByPath")]
[switch]$Force,
[string]
$PowerShellHome
)
Set-StrictMode -Version 3.0
if (! ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
{
Write-Error "WinRM registration requires Administrator rights. To run this cmdlet, start PowerShell with the `"Run as administrator`" option."
return
}
function Register-WinRmPlugin
{
param
(
#
# Expected Example:
# %windir%\\system32\\PowerShell\\6.0.0\\pwrshplugin.dll
#
[string]
[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
$pluginAbsolutePath,
#
# Expected Example: powershell.6.0.0-beta.3
#
[string]
[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
$pluginEndpointName
)
$regKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WSMAN\Plugin\$pluginEndpointName"
$pluginArchitecture = "64"
if ($env:PROCESSOR_ARCHITECTURE -match "x86" -or $env:PROCESSOR_ARCHITECTURE -eq "ARM")
{
$pluginArchitecture = "32"
}
$regKeyValueFormatString = @"
<PlugInConfiguration xmlns="http://schemas.microsoft.com/wbem/wsman/1/config/PluginConfiguration" Name="{0}" Filename="{1}"
SDKVersion="2" XmlRenderingType="text" Enabled="True" OutputBufferingMode="Block" ProcessIdleTimeoutSec="0" Architecture="{2}"
UseSharedProcess="false" RunAsUser="" RunAsPassword="" AutoRestart="false">
<InitializationParameters>
<Param Name="PSVersion" Value="7.0"/>
</InitializationParameters>
<Resources>
<Resource ResourceUri="http://schemas.microsoft.com/powershell/{0}" SupportsOptions="true" ExactMatch="true">
<Security Uri="http://schemas.microsoft.com/powershell/{0}" ExactMatch="true"
Sddl="O:NSG:BAD:P(A;;GA;;;BA)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)"/>
<Capability Type="Shell"/>
</Resource>
</Resources>
<Quotas IdleTimeoutms="7200000" MaxConcurrentUsers="5" MaxProcessesPerShell="15" MaxMemoryPerShellMB="1024" MaxShellsPerUser="25"
MaxConcurrentCommandsPerShell="1000" MaxShells="25" MaxIdleTimeoutms="43200000"/>
</PlugInConfiguration>
"@
$valueString = $regKeyValueFormatString -f $pluginEndpointName, $pluginAbsolutePath, $pluginArchitecture
New-Item $regKey -Force > $null
New-ItemProperty -Path $regKey -Name ConfigXML -Value $valueString > $null
}
function New-PluginConfigFile
{
[CmdletBinding(SupportsShouldProcess, ConfirmImpact="Medium")]
param
(
[string]
[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
$pluginFile,
[string]
[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
$targetPsHomeDir
)
# This always overwrites the file with a new version of it if the
# script is invoked multiple times.
Set-Content -Path $pluginFile -Value "PSHOMEDIR=$targetPsHomeDir" -ErrorAction Stop
Add-Content -Path $pluginFile -Value "CORECLRDIR=$targetPsHomeDir" -ErrorAction Stop
Write-Verbose "Created Plugin Config File: $pluginFile" -Verbose
}
function Install-PluginEndpoint {
[CmdletBinding(SupportsShouldProcess, ConfirmImpact="Medium")]
param (
[Parameter()] [bool] $Force,
[switch]
$VersionIndependent
)
######################
# #
# Install the plugin #
# #
######################
if (-not [String]::IsNullOrEmpty($PowerShellHome))
{
$targetPsHome = $PowerShellHome
$targetPsVersion = & "$targetPsHome\pwsh" -NoProfile -Command '$PSVersionTable.PSVersion.ToString()'
}
else
{
## Get the PSHome and PSVersion using the current powershell instance
$targetPsHome = $PSHOME
$targetPsVersion = $PSVersionTable.PSVersion.ToString()
}
Write-Verbose "PowerShellHome: $targetPsHome" -Verbose
# For default, not tied to the specific version endpoint, we apply
# only first number in the PSVersion string to the endpoint name.
# Example name: 'PowerShell.6'.
if ($VersionIndependent) {
$dotPos = $targetPsVersion.IndexOf(".")
if ($dotPos -ne -1) {
$targetPsVersion = $targetPsVersion.Substring(0, $dotPos)
}
}
Write-Verbose "Using PowerShell Version: $targetPsVersion" -Verbose
$pluginEndpointName = "PowerShell.$targetPsVersion"
$endPoint = Get-PSSessionConfiguration $pluginEndpointName -Force:$Force -ErrorAction silentlycontinue 2>&1
# If endpoint exists and -Force parameter was not used, the endpoint would not be overwritten.
if ($endpoint -and !$Force)
{
Write-Error -Category ResourceExists -ErrorId "PSSessionConfigurationExists" -Message "Endpoint $pluginEndpointName already exists."
return
}
if (!$PSCmdlet.ShouldProcess($pluginEndpointName)) {
return
}
if ($PSVersionTable.PSVersion -lt "6.0")
{
# This script is primarily used from Windows PowerShell for Win10 IoT and NanoServer to setup PSCore6 remoting endpoint
# so it's ok to hardcode to 'C:\Windows' for those systems
$pluginBasePath = Join-Path "C:\Windows\System32\PowerShell" $targetPsVersion
}
else
{
$pluginBasePath = Join-Path ([System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::Windows) + "\System32\PowerShell") $targetPsVersion
}
$resolvedPluginAbsolutePath = ""
if (! (Test-Path $pluginBasePath))
{
Write-Verbose "Creating $pluginBasePath"
$resolvedPluginAbsolutePath = New-Item -Type Directory -Path $pluginBasePath
}
else
{
$resolvedPluginAbsolutePath = Resolve-Path $pluginBasePath
}
$pluginPath = Join-Path $resolvedPluginAbsolutePath "pwrshplugin.dll"
# This is forced to ensure the the file is placed correctly
Copy-Item $targetPsHome\pwrshplugin.dll $resolvedPluginAbsolutePath -Force -Verbose -ErrorAction Stop
$pluginFile = Join-Path $resolvedPluginAbsolutePath "RemotePowerShellConfig.txt"
New-PluginConfigFile $pluginFile (Resolve-Path $targetPsHome)
# Register the plugin
Register-WinRmPlugin $pluginPath $pluginEndpointName
####################################################################
# #
# Validations to confirm that everything was registered correctly. #
# #
####################################################################
if (! (Test-Path $pluginFile))
{
throw "WinRM Plugin configuration file not created. Expected = $pluginFile"
}
if (! (Test-Path $resolvedPluginAbsolutePath\pwrshplugin.dll))
{
throw "WinRM Plugin DLL missing. Expected = $resolvedPluginAbsolutePath\pwrshplugin.dll"
}
try
{
Write-Host "`nGet-PSSessionConfiguration $pluginEndpointName" -ForegroundColor "green"
Get-PSSessionConfiguration $pluginEndpointName -ErrorAction Stop
}
catch [Microsoft.PowerShell.Commands.WriteErrorException]
{
throw "No remoting session configuration matches the name $pluginEndpointName."
}
}
Install-PluginEndpoint -Force $Force
Install-PluginEndpoint -Force $Force -VersionIndependent
Write-Host "Restarting WinRM to ensure that the plugin configuration change takes effect.`nThis is required for WinRM running on Windows SKUs prior to Windows 10." -ForegroundColor Magenta
Restart-Service winrm