# 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 = @" "@ $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