2016-03-30 14:20:52 -07:00
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
using System ;
using System.Collections ;
using System.Collections.ObjectModel ;
using System.Text ;
using System.Globalization ;
using System.Threading ;
using System.Security.AccessControl ;
using System.IO ;
using System.Diagnostics.CodeAnalysis ;
using System.Management.Automation ;
using System.Management.Automation.Internal ;
using System.Management.Automation.Runspaces ;
using System.Management.Automation.Remoting ;
using System.Management.Automation.Language ;
using Dbg = System . Management . Automation . Diagnostics ;
using WSManNativeApi = System . Management . Automation . Remoting . Client . WSManNativeApi ;
using PowerShellApi = System . Management . Automation . PowerShell ;
using Microsoft.Win32 ;
using System.Security ;
using System.Collections.Generic ;
using System.Security.Principal ;
#if CORECLR
// Some APIs are missing from System.Environment. We use System.Management.Automation.Environment as a proxy type:
// - for missing APIs, System.Management.Automation.Environment has extension implementation.
// - for existing APIs, System.Management.Automation.Environment redirect the call to System.Environment.
2016-07-29 13:02:49 -07:00
using Environment = System . Management . Automation . Environment ;
2016-03-30 14:20:52 -07:00
#endif
namespace Microsoft.PowerShell.Commands
{
#region Register - PSSessionConfiguration cmdlet
/// <summary>
/// Class implemeting Register-PSSessionConfiguration
/// </summary>
[Cmdlet(VerbsLifecycle.Register, RemotingConstants.PSSessionConfigurationNoun,
DefaultParameterSetName = PSSessionConfigurationCommandBase.NameParameterSetName,
SupportsShouldProcess = true,
ConfirmImpact = ConfirmImpact.Medium, HelpUri = "http://go.microsoft.com/fwlink/?LinkID=144306")]
public sealed class RegisterPSSessionConfigurationCommand : PSSessionConfigurationCommandBase
{
#region Private Data
// To Escape " -- ""
private const string newPluginSbFormat = @"
function Register-PSSessionConfiguration
{{
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact=""Medium"")]
param(
[string] $filepath,
[string] $pluginName,
[bool] $shouldShowUI,
[bool] $force,
[string] $restartWSManTarget,
[string] $restartWSManAction,
[string] $restartWSManRequired,
[string] $runAsUserName,
[system.security.securestring] $runAsPassword,
[System.Management.Automation.Runspaces.PSSessionConfigurationAccessMode] $accessMode,
[bool] $isSddlSpecified,
[string] $configTableSddl
)
begin
{{
## Construct SID for network users
[system.security.principal.wellknownsidtype]$evst = ""NetworkSid""
$networkSID = new-object system.security.principal.securityidentifier $evst,$null
## If all session configurations have Network Access disabled,
## then we create this endpoint as Local as well.
$newSDDL = $null
$foundRemoteEndpoint = $false;
Get-PSSessionConfiguration -Force:$force | Foreach-Object {{
if ($_.Enabled)
{{
$sddl = $null
if ($_.psobject.members[""SecurityDescriptorSddl""])
{{
$sddl = $_.psobject.members[""SecurityDescriptorSddl""].Value
}}
if($sddl)
{{
# See if it has 'Disable Network Access'
$sd = new-object system.security.accesscontrol.commonsecuritydescriptor $false,$false,$sddl
$disableNetworkExists = $false
$sd.DiscretionaryAcl | % {{
if (($_.acequalifier -eq ""accessdenied"") -and ($_.securityidentifier -match $networkSID) -and ($_.AccessMask -eq 268435456))
{{
$disableNetworkExists = $true
}}
}}
if(-not $disableNetworkExists) {{ $foundRemoteEndpoint = $true }}
}}
}}
}}
if(-not $foundRemoteEndpoint)
{{
$newSDDL = ""{1}""
}}
}}
process
{{
if ($force)
{{
if (Test-Path WSMan:\localhost\Plugin\""$pluginName"")
{{
Unregister-PSSessionConfiguration -name ""$pluginName"" -force
}}
}}
new-item -path WSMan:\localhost\Plugin -file ""$filepath"" -name ""$pluginName""
# $? is to make sure the last operation is succeeded
if ($? -and $runAsUserName)
{{
try {{
$runAsCredential = new-object system.management.automation.PSCredential($runAsUserName, $runAsPassword)
set-item -WarningAction SilentlyContinue WSMan:\localhost\Plugin\""$pluginName""\RunAsUser $runAsCredential -confirm:$false
}} catch {{
remove-item WSMan:\localhost\Plugin\""$pluginName"" -recurse -force
write-error $_
# Do not add anymore clean up code after Write-Error, because if EA=Stop is set by user
# any code at this point will not execute.
return
}}
}}
## Replace the SDDL with any groups or restrictions defined in the PSSessionConfigurationFile
if($? -and $configTableSddl -and (-not $isSddlSpecified))
{{
$null = Set-PSSessionConfiguration -Name $pluginName -SecurityDescriptorSddl $configTableSddl -Force:$force
}}
if ($? -and $shouldShowUI)
{{
$null = winrm configsddl ""{0}$pluginName""
# if AccessMode is Disabled OR the winrm configsddl failed, we just return
if ([System.Management.Automation.Runspaces.PSSessionConfigurationAccessMode]::Disabled.Equals($accessMode) -or !$?)
{{
return
}}
}} # end of if ($shouldShowUI)
if ($?)
{{
# if AccessMode is Local or Remote, we need to check the SDDL the user set in the UI or passed in to the cmdlet.
$newSDDL = $null
$curPlugin = Get-PSSessionConfiguration -Name $pluginName -Force:$force
$curSDDL = $curPlugin.SecurityDescriptorSddl
if (!$curSDDL)
{{
if ([System.Management.Automation.Runspaces.PSSessionConfigurationAccessMode]::Local.Equals($accessMode))
{{
$newSDDL = ""{1}""
}}
}}
else
{{
# Construct SID for network users
[system.security.principal.wellknownsidtype]$evst = ""NetworkSid""
$networkSID = new-object system.security.principal.securityidentifier $evst,$null
$sd = new-object system.security.accesscontrol.commonsecuritydescriptor $false,$false,$curSDDL
$haveDisableACE = $false
$securityIdentifierToPurge = $null
$sd.DiscretionaryAcl | % {{
if (($_.acequalifier -eq ""accessdenied"") -and ($_.securityidentifier -match $networkSID) -and ($_.AccessMask -eq 268435456))
{{
$haveDisableACE = $true
$securityIdentifierToPurge = $_.securityidentifier
}}
}}
if (([System.Management.Automation.Runspaces.PSSessionConfigurationAccessMode]::Local.Equals($accessMode) -or
([System.Management.Automation.Runspaces.PSSessionConfigurationAccessMode]::Remote.Equals($accessMode)-and $disableNetworkExists)) -and
!$haveDisableACE)
{{
# Add network deny ACE for local access or remote access with PSRemoting disabled ($disableNetworkExists)
$sd.DiscretionaryAcl.AddAccess(""deny"", $networkSID, 268435456, ""None"", ""None"")
$newSDDL = $sd.GetSddlForm(""all"")
}}
if ([System.Management.Automation.Runspaces.PSSessionConfigurationAccessMode]::Remote.Equals($accessMode) -and -not $disableNetworkExists -and $haveDisableACE)
{{
# Remove the specific ACE
$sd.discretionaryacl.RemoveAccessSpecific('Deny', $securityIdentifierToPurge, 268435456, 'none', 'none')
# if there is no discretionaryacl..add Builtin Administrators and Remote Management Users
# to the DACL group as this is the default WSMan behavior
if ($sd.discretionaryacl.count -eq 0)
{{
[system.security.principal.wellknownsidtype]$bast = ""BuiltinAdministratorsSid""
$basid = new-object system.security.principal.securityidentifier $bast,$null
$sd.DiscretionaryAcl.AddAccess('Allow',$basid, 268435456, 'none', 'none')
# Remote Management Users, Win8+ only
if ([System.Environment]::OSVersion.Version -ge ""6.2.0.0"")
{{
$rmSidId = new-object system.security.principal.securityidentifier ""{2}""
$sd.DiscretionaryAcl.AddAccess('Allow', $rmSidId, 268435456, 'none', 'none')
}}
# Interactive Users
$iaSidId = new-object system.security.principal.securityidentifier ""{3}""
$sd.DiscretionaryAcl.AddAccess('Allow', $iaSidId, 268435456, 'none', 'none')
}}
$newSDDL = $sd.GetSddlForm(""all"")
}}
}} # end of if(!$curSDDL)
}} # end of if ($?)
if ($? -and $newSDDL)
{{
try {{
if ($runAsUserName)
{{
$runAsCredential = new-object system.management.automation.PSCredential($runAsUserName, $runAsPassword)
$null = Set-PSSessionConfiguration -Name $pluginName -SecurityDescriptorSddl $newSDDL -NoServiceRestart -force -WarningAction 0 -RunAsCredential $runAsCredential
}}
else
{{
$null = Set-PSSessionConfiguration -Name $pluginName -SecurityDescriptorSddl $newSDDL -NoServiceRestart -force -WarningAction 0
}}
}} catch {{
remove-item WSMan:\localhost\Plugin\""$pluginName"" -recurse -force
write-error $_
# Do not add anymore clean up code after Write-Error, because if EA=Stop is set by user
# any code at this point will not execute.
return
}}
}}
if ($?){{
try{{
$s = New-PSSession -ComputerName localhost -ConfigurationName $pluginName -ErrorAction Stop
2016-04-28 13:41:25 -07:00
# session is ok, no need to restart WinRM service
2016-03-30 14:20:52 -07:00
Remove-PSSession $s -Confirm:$false
}}catch{{
# session is NOT ok, we need to restart winrm if -Force was specified, otherwise show a warning
if ($force){{
Restart-Service -Name WinRM -Force -Confirm:$false
}}else{{
$warningWSManRestart = [Microsoft.PowerShell.Commands.Internal.RemotingErrorResources]::WinRMRestartWarning -f $PSCmdlet.MyInvocation.MyCommand.Name
Write-Warning $warningWSManRestart
}}
}}
}}
}}
}}
if ($args[14] -eq $null)
{{
Register-PSSessionConfiguration -filepath $args[0] -pluginName $args[1] -shouldShowUI $args[2] -force $args[3] -whatif:$args[4] -confirm:$args[5] -restartWSManTarget $args[6] -restartWSManAction $args[7] -restartWSManRequired $args[8] -runAsUserName $args[9] -runAsPassword $args[10] -accessMode $args[11] -isSddlSpecified $args[12] -configTableSddl $args[13]
}}
else
{{
Register-PSSessionConfiguration -filepath $args[0] -pluginName $args[1] -shouldShowUI $args[2] -force $args[3] -whatif:$args[4] -confirm:$args[5] -restartWSManTarget $args[6] -restartWSManAction $args[7] -restartWSManRequired $args[8] -runAsUserName $args[9] -runAsPassword $args[10] -accessMode $args[11] -isSddlSpecified $args[12] -configTableSddl $args[13] -erroraction $args[14]
}}
" ;
2016-07-29 13:02:49 -07:00
private static readonly ScriptBlock s_newPluginSb ;
2016-03-30 14:20:52 -07:00
private const string pluginXmlFormat = @"
<PlugInConfiguration xmlns='http://schemas.microsoft.com/wbem/wsman/1/config/PluginConfiguration'
Name='{0}'
Filename='%windir%\system32\{1}'
SDKVersion='{12}'
XmlRenderingType='text' {2} {6} {7} {8} {9} {10}>
<InitializationParameters>
{3}
</InitializationParameters>
<Resources>
<Resource ResourceUri='{4}' SupportsOptions='true' ExactMatch='true'>
{5}
<Capability Type='Shell' />
</Resource>
</Resources>
{11}
</PlugInConfiguration>
" ;
private const string architectureAttribFormat = @"
Architecture='{0}'" ;
private const string sharedHostAttribFormat = @"
UseSharedProcess='{0}'" ;
private const string runasVirtualAccountAttribFormat = @"
RunAsVirtualAccount='{0}'" ;
private const string runAsVirtualAccountGroupsAttribFormat = @"
RunAsVirtualAccountGroups='{0}'" ;
private const string allowRemoteShellAccessFormat = @"
Enabled='{0}'" ;
private const string initParamFormat = @"
<Param Name='{0}' Value='{1}' />{2}" ;
private const string privateDataFormat = @"<Param Name='PrivateData'>{0}</Param>" ;
private const string securityElementFormat = "<Security Uri='{0}' ExactMatch='true' Sddl='{1}' />" ;
private const string SessionConfigDataFormat = @"<SessionConfigurationData>{0}</SessionConfigurationData>" ;
2016-07-29 13:02:49 -07:00
private string _gmsaAccount ;
private string _configTableSDDL ;
2016-03-30 14:20:52 -07:00
// true if there are errors running the wsman's configuration
// command
2016-07-29 13:02:49 -07:00
private bool _isErrorReported ;
2016-03-30 14:20:52 -07:00
#endregion
#region Parameters
/// <summary>
/// Parameter used to specify the Processor Architecture that this shell targets.
/// On a 64bit base OS, specifying a value of 32 means that the shell is configured
/// to launch like a 32bit process (WOW64).
/// </summary>
[Parameter()]
[Alias("PA")]
[ValidateNotNullOrEmpty]
[ValidateSet("x86", "amd64")]
2016-08-02 13:29:09 -07:00
public string ProcessorArchitecture { get ; set ; }
2016-03-30 14:20:52 -07:00
/// <summary>
/// SessionType
/// </summary>
/// Only want this on non assemblyName parameter set, since assembly decides the sessiontype.
2016-07-29 13:02:49 -07:00
[Parameter(ParameterSetName = PSSessionConfigurationCommandBase.NameParameterSetName)]
2016-03-30 14:20:52 -07:00
public PSSessionType SessionType
{
get
{
return sessionType ;
}
set
{
sessionType = value ;
}
}
internal PSSessionType sessionType = PSSessionType . DefaultRemoteShell ;
#endregion
#region Constructors
static RegisterPSSessionConfigurationCommand ()
{
string localSDDL = GetLocalSddl ();
// compile the script block statically and reuse the same instance
// everytime the command is run..This will save on parsing time.
string newPluginSbString = string . Format ( CultureInfo . InvariantCulture ,
newPluginSbFormat ,
WSManNativeApi . ResourceURIPrefix , localSDDL , RemoteManagementUsersSID , InteractiveUsersSID );
2016-07-29 13:02:49 -07:00
s_newPluginSb = ScriptBlock . Create ( newPluginSbString );
s_newPluginSb . LanguageMode = PSLanguageMode . FullLanguage ;
2016-03-30 14:20:52 -07:00
}
#endregion
#region Cmdlet Overrides
/// <summary>
///
/// </summary>
/// <exception cref="InvalidOperationException">
/// 1. Either both "AssemblyName" and "ConfigurationTypeName" must be specified
/// or both must not be specified.
/// </exception>
protected override void BeginProcessing ()
{
if ( isSddlSpecified && showUISpecified )
{
string message = StringUtil . Format ( RemotingErrorIdStrings . ShowUIAndSDDLCannotExist ,
"SecurityDescriptorSddl" ,
"ShowSecurityDescriptorUI" );
throw new PSInvalidOperationException ( message );
}
if ( isRunAsCredentialSpecified )
{
WriteWarning ( RemotingErrorIdStrings . RunAsSessionConfigurationSecurityWarning );
}
if ( isSddlSpecified )
{
// Constructor call should succeed. The sddl is check in the property setter
CommonSecurityDescriptor descriptor = new CommonSecurityDescriptor ( false , false , sddl );
SecurityIdentifier networkSidIdentifier = new SecurityIdentifier ( WellKnownSidType . NetworkSid , null );
bool networkDenyAllExists = false ;
foreach ( CommonAce ace in descriptor . DiscretionaryAcl )
{
if ( ace . AceQualifier . Equals ( AceQualifier . AccessDenied ) && ace . SecurityIdentifier . Equals ( networkSidIdentifier ) && ace . AccessMask == 268435456 )
{
networkDenyAllExists = true ;
break ;
}
}
switch ( AccessMode )
{
case PSSessionConfigurationAccessMode . Local :
if (! networkDenyAllExists )
{
descriptor . DiscretionaryAcl . AddAccess ( AccessControlType . Deny , networkSidIdentifier , 268435456 , InheritanceFlags . None , PropagationFlags . None );
sddl = descriptor . GetSddlForm ( AccessControlSections . All );
}
break ;
case PSSessionConfigurationAccessMode . Remote :
if ( networkDenyAllExists )
{
// Remove the specific ACE
descriptor . DiscretionaryAcl . RemoveAccessSpecific ( AccessControlType . Deny , networkSidIdentifier , 268435456 , InheritanceFlags . None , PropagationFlags . None );
// If the discretionaryAcl becomes empty, add the BA and RM which is the default WinRM behavior
if ( descriptor . DiscretionaryAcl . Count == 0 )
{
// BA
SecurityIdentifier baSidIdentifier = new SecurityIdentifier ( WellKnownSidType . BuiltinAdministratorsSid , null );
descriptor . DiscretionaryAcl . AddAccess ( AccessControlType . Allow , baSidIdentifier , 268435456 , InheritanceFlags . None , PropagationFlags . None );
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
// Only for Win8+
if ( Environment . OSVersion . Version >= new Version ( 6 , 2 ))
{
// Remote Management Users
SecurityIdentifier rmSidIdentifier = new SecurityIdentifier ( RemoteManagementUsersSID );
descriptor . DiscretionaryAcl . AddAccess ( AccessControlType . Allow , rmSidIdentifier , 268435456 , InheritanceFlags . None , PropagationFlags . None );
}
// Interactive Users
SecurityIdentifier iaSidIdentifier = new SecurityIdentifier ( InteractiveUsersSID );
descriptor . DiscretionaryAcl . AddAccess ( AccessControlType . Allow , iaSidIdentifier , 268435456 , InheritanceFlags . None , PropagationFlags . None );
}
sddl = descriptor . GetSddlForm ( AccessControlSections . All );
}
break ;
case PSSessionConfigurationAccessMode . Disabled :
break ;
}
}
if (! isSddlSpecified && ! showUISpecified )
{
if ( AccessMode . Equals ( PSSessionConfigurationAccessMode . Local ))
{
// If AccessMode is Local or Disabled and no SDDL specified, use the default local SDDL
sddl = GetLocalSddl ();
}
else if ( AccessMode . Equals ( PSSessionConfigurationAccessMode . Remote ))
{
// If AccessMode is Remote and no SDDL specified then use the default remote SDDL
sddl = GetRemoteSddl ();
}
}
// check if we have compatible WSMan
RemotingCommandUtil . CheckRemotingCmdletPrerequisites ();
PSSessionConfigurationCommandUtilities . ThrowIfNotAdministrator ();
WSManConfigurationOption wsmanOption = transportOption as WSManConfigurationOption ;
if ( wsmanOption != null )
{
if ( wsmanOption . ProcessIdleTimeoutSec != null && ! isUseSharedProcessSpecified )
{
PSInvalidOperationException ioe = new PSInvalidOperationException (
StringUtil . Format ( RemotingErrorIdStrings . InvalidConfigurationXMLAttribute , "ProcessIdleTimeoutSec" ,
"UseSharedProcess" ));
ThrowTerminatingError ( ioe . ErrorRecord );
}
}
}
/// <summary>
/// For each record, execute it, and push the results into the
/// success stream.
/// </summary>
protected override void ProcessRecord ()
{
WriteVerbose ( StringUtil . Format ( RemotingErrorIdStrings . NcsScriptMessageV , newPluginSbFormat ));
if (! force )
{
string shouldProcessAction = StringUtil . Format ( RemotingErrorIdStrings . CSShouldProcessAction ,
this . CommandInfo . Name );
string shouldProcessTarget ;
if ( isSddlSpecified )
{
shouldProcessTarget = StringUtil . Format ( RemotingErrorIdStrings . NcsShouldProcessTargetSDDL , Name , sddl );
}
else
{
shouldProcessTarget = StringUtil . Format ( RemotingErrorIdStrings . CSShouldProcessTargetAdminEnable , Name );
}
string action = StringUtil . Format ( RemotingErrorIdStrings . CSShouldProcessAction ,
this . CommandInfo . Name );
WriteWarning ( StringUtil . Format ( RemotingErrorIdStrings . WinRMRestartWarning , action ));
if (! ShouldProcess ( shouldProcessTarget , shouldProcessAction ))
{
return ;
}
}
// Configuration file copy information.
string srcConfigFilePath ;
string destConfigFilePath ;
// construct plugin config file.
string pluginContent = ConstructPluginContent ( out srcConfigFilePath , out destConfigFilePath );
// Create temporary file with the content.
string file = ConstructTemporaryFile ( pluginContent );
// Move the WinRM service to its own service host if the endpoint is given elevated credentials.
if ( isRunAsCredentialSpecified || RunAsVirtualAccountSpecified )
{
PSSessionConfigurationCommandUtilities . MoveWinRmToIsolatedServiceHost ( RunAsVirtualAccountSpecified );
}
// Use the Group Managed Service Account if provided.
2016-07-29 13:02:49 -07:00
if (! isRunAsCredentialSpecified && ! string . IsNullOrEmpty ( _gmsaAccount ))
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
runAsCredential = PSSessionConfigurationCommandUtilities . CreateGMSAAccountCredentials ( _gmsaAccount );
2016-03-30 14:20:52 -07:00
}
try
{
// restart-service winrm to make the changes effective.
string restartServiceAction = RemotingErrorIdStrings . RestartWSManServiceAction ;
string restartServiceTarget = StringUtil . Format ( RemotingErrorIdStrings . RestartWSManServiceTarget , "WinRM" );
string restartWSManRequiredForUI = StringUtil . Format ( RemotingErrorIdStrings . RestartWSManRequiredShowUI ,
string . Format ( CultureInfo . InvariantCulture ,
"Set-PSSessionConfiguration {0} -ShowSecurityDescriptorUI" ,
shellName ));
// gather -WhatIf, -Confirm parameter data and pass it to the script block
bool whatIf = false ;
// confirm is always true to start with
bool confirm = true ;
PSSessionConfigurationCommandUtilities . CollectShouldProcessParameters ( this , out whatIf , out confirm );
// gather -ErrorAction parameter data and pass it to the script block. if -ErrorAction is not set, pass $null in
object errorAction = null ;
if ( Context . CurrentCommandProcessor . CommandRuntime . IsErrorActionSet )
{
errorAction = Context . CurrentCommandProcessor . CommandRuntime . ErrorAction ;
}
ArrayList errorList = ( ArrayList ) Context . DollarErrorVariable ;
int errorCountBefore = errorList . Count ;
2016-07-29 13:02:49 -07:00
if ( force &&
2016-04-28 13:41:25 -07:00
this . Context != null &&
this . Context . EngineHostInterface != null &&
this . Context . EngineHostInterface . ExternalHost != null &&
this . Context . EngineHostInterface . ExternalHost is System . Management . Automation . Remoting . ServerRemoteHost )
{
WriteWarning ( RemotingErrorIdStrings . WinRMForceRestartWarning );
}
2016-07-29 13:02:49 -07:00
s_newPluginSb . InvokeUsingCmdlet (
2016-03-30 14:20:52 -07:00
contextCmdlet : this ,
useLocalScope : true ,
errorHandlingBehavior : ScriptBlock . ErrorHandlingBehavior . WriteToCurrentErrorPipe ,
dollarUnder : AutomationNull . Value ,
input : Utils . EmptyArray < object >(),
scriptThis : AutomationNull . Value ,
args : new object [] {
file ,
shellName ,
ShowSecurityDescriptorUI . ToBool (),
force ,
whatIf ,
confirm ,
restartServiceTarget ,
restartServiceAction ,
restartWSManRequiredForUI ,
runAsCredential != null ? runAsCredential . UserName : null ,
runAsCredential != null ? runAsCredential . Password : null ,
AccessMode ,
isSddlSpecified ,
2016-07-29 13:02:49 -07:00
_configTableSDDL ,
2016-03-30 14:20:52 -07:00
errorAction
});
errorList = ( ArrayList ) Context . DollarErrorVariable ;
2016-07-29 13:02:49 -07:00
_isErrorReported = errorList . Count > errorCountBefore ;
2016-03-30 14:20:52 -07:00
}
finally
{
DeleteFile ( file );
}
// If the file no longer exists then re-copy the configuration file to the dest location after
// newPluginSb script is run the file no longer exists.
if (( srcConfigFilePath != null ) && ( destConfigFilePath != null ) &&
! File . Exists ( destConfigFilePath ))
{
try
{
File . Copy ( srcConfigFilePath , destConfigFilePath , true );
}
catch ( IOException ) { }
catch ( ArgumentException ) { }
catch ( NotSupportedException ) { }
catch ( UnauthorizedAccessException ) { }
}
}
/// <summary>
/// </summary>
protected override void EndProcessing ()
{
System . Management . Automation . Tracing . Tracer tracer = new System . Management . Automation . Tracing . Tracer ();
tracer . EndpointRegistered ( this . Name , this . sessionType . ToString (), WindowsIdentity . GetCurrent (). Name );
}
#endregion
#region Private Methods
/// <summary>
/// </summary>
/// <param name="tmpFileName"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException">
/// 1. New shell successfully registered. However cannot delete temporary plugin file {0}.
/// Reason for failure: {1}.
/// </exception>
private void DeleteFile ( string tmpFileName )
{
Dbg . Assert (! string . IsNullOrEmpty ( tmpFileName ), "tmpFile cannot be null or empty." );
Exception e = null ;
try
{
File . Delete ( tmpFileName );
//WriteWarning(tmpFileName);
}
catch ( UnauthorizedAccessException uae )
{
e = uae ;
}
catch ( ArgumentException ae )
{
e = ae ;
}
catch ( PathTooLongException pe )
{
e = pe ;
}
catch ( DirectoryNotFoundException dnfe )
{
e = dnfe ;
}
catch ( IOException ioe )
{
e = ioe ;
}
catch ( NotSupportedException nse )
{
e = nse ;
}
if ( e != null )
{
throw PSTraceSource . NewInvalidOperationException ( RemotingErrorIdStrings . NcsCannotDeleteFileAfterInstall ,
tmpFileName ,
e . Message );
}
}
/// <summary>
/// </summary>
/// <param name="pluginContent"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException">
/// 1. Cannot delete temporary file {0}. Try again. Reason for failure: {1}.
/// 2. Cannot write shell configuration data into temporary file {0}. Try again.
/// Reason for failure: {1}.
/// </exception>
private string ConstructTemporaryFile ( string pluginContent )
{
// Path.GetTempFileName creates a temporary file whereas GetRandomFileName does not.
string tmpFileName = System . IO . Path . Combine ( System . IO . Path . GetTempPath (), System . IO . Path . GetRandomFileName ()) + "psshell.xml" ;
Exception e = null ;
// Remove the temp file if it exists.
if ( File . Exists ( tmpFileName ))
{
FileInfo destfile = new FileInfo ( tmpFileName );
if ( destfile != null )
{
try
{
//Make sure the file is not read only
destfile . Attributes = destfile . Attributes & ~( FileAttributes . ReadOnly | FileAttributes . Hidden );
destfile . Delete ();
}
catch ( FileNotFoundException fnf )
{
e = fnf ;
}
catch ( DirectoryNotFoundException dnf )
{
e = dnf ;
}
catch ( UnauthorizedAccessException uac )
{
e = uac ;
}
catch ( System . Security . SecurityException se )
{
e = se ;
}
catch ( ArgumentNullException ane )
{
e = ane ;
}
catch ( ArgumentException ae )
{
e = ae ;
}
catch ( PathTooLongException pe )
{
e = pe ;
}
catch ( NotSupportedException ns )
{
e = ns ;
}
catch ( IOException ioe )
{
e = ioe ;
}
if ( e != null )
{
throw PSTraceSource . NewInvalidOperationException ( RemotingErrorIdStrings . NcsCannotDeleteFile ,
tmpFileName ,
e . Message );
}
}
}
try
{
StreamWriter fileStream = File . CreateText ( tmpFileName );
fileStream . Write ( pluginContent );
fileStream . Flush ();
fileStream . Dispose ();
}
catch ( UnauthorizedAccessException uae )
{
e = uae ;
}
catch ( ArgumentException ae )
{
e = ae ;
}
catch ( PathTooLongException pe )
{
e = pe ;
}
catch ( DirectoryNotFoundException dnfe )
{
e = dnfe ;
}
if ( e != null )
{
throw PSTraceSource . NewInvalidOperationException ( RemotingErrorIdStrings . NcsCannotWritePluginContent ,
tmpFileName ,
e . Message );
}
return tmpFileName ;
}
private string ConstructPluginContent ( out string srcConfigFilePath , out string destConfigFilePath )
{
srcConfigFilePath = null ;
destConfigFilePath = null ;
StringBuilder initParameters = new StringBuilder ();
bool assemblyAndTypeTokensSet = false ;
if ( sessionType == PSSessionType . Workflow )
{
initParameters . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
ConfigurationDataFromXML . ENDPOINTCONFIGURATIONTYPE ,
sessionType ,
Environment . NewLine ));
initParameters . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
ConfigurationDataFromXML . ASSEMBLYTOKEN ,
ConfigurationDataFromXML . WORKFLOWCOREASSEMBLY ,
Environment . NewLine ));
initParameters . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
ConfigurationDataFromXML . SHELLCONFIGTYPETOKEN ,
ConfigurationDataFromXML . WORKFLOWCORETYPENAME ,
Environment . NewLine ));
assemblyAndTypeTokensSet = true ;
}
// DISC endpoint
if ( Path != null )
{
ProviderInfo provider = null ;
PSDriveInfo drive ;
string filePath = SessionState . Path . GetUnresolvedProviderPathFromPSPath ( Path , out provider , out drive );
if (! provider . NameEquals ( Context . ProviderNames . FileSystem ) || ! filePath . EndsWith ( StringLiterals . PowerShellDISCFileExtension , StringComparison . OrdinalIgnoreCase ))
{
string message = StringUtil . Format ( RemotingErrorIdStrings . InvalidPSSessionConfigurationFilePath , filePath );
InvalidOperationException ioe = new InvalidOperationException ( message );
ErrorRecord er = new ErrorRecord ( ioe , "InvalidPSSessionConfigurationFilePath" ,
ErrorCategory . InvalidArgument , Path );
ThrowTerminatingError ( er );
}
Guid sessionGuid = Guid . Empty ;
// Load session GUID from config file
string scriptName ;
ExternalScriptInfo scriptInfo = null ;
Hashtable configTable = null ;
try
{
scriptInfo = DISCUtils . GetScriptInfoForFile ( this . Context , filePath , out scriptName );
configTable = DISCUtils . LoadConfigFile ( this . Context , scriptInfo );
}
catch ( RuntimeException rte )
{
string message = StringUtil . Format ( RemotingErrorIdStrings . InvalidPSSessionConfigurationFileErrorProcessing , filePath , rte . Message );
InvalidOperationException ioe = new InvalidOperationException ( message , rte );
ErrorRecord er = new ErrorRecord ( ioe , "InvalidPSSessionConfigurationFilePath" ,
ErrorCategory . InvalidArgument , Path );
ThrowTerminatingError ( er );
}
if ( configTable == null )
{
string message = StringUtil . Format ( RemotingErrorIdStrings . InvalidPSSessionConfigurationFile , filePath );
InvalidOperationException ioe = new InvalidOperationException ( message );
ErrorRecord er = new ErrorRecord ( ioe , "InvalidPSSessionConfigurationFile" ,
ErrorCategory . InvalidArgument , Path );
ThrowTerminatingError ( er );
}
else
{
if ( configTable . ContainsKey ( ConfigFileConstants . Guid ))
{
try
{
if ( configTable [ ConfigFileConstants . Guid ] != null )
{
sessionGuid = Guid . Parse ( configTable [ ConfigFileConstants . Guid ]. ToString ());
}
else
{
InvalidOperationException invalidOperationException = new InvalidOperationException ( StringUtil . Format ( RemotingErrorIdStrings . ErrorParsingTheKeyInPSSessionConfigurationFile , ConfigFileConstants . Guid , filePath ));
ThrowTerminatingError ( new ErrorRecord ( invalidOperationException , "InvalidGuidInPSSessionConfigurationFile" , ErrorCategory . InvalidOperation , null ));
}
}
catch ( FormatException e )
{
ThrowTerminatingError ( new ErrorRecord ( e , "InvalidGuidInPSSessionConfigurationFile" , ErrorCategory . InvalidOperation , null ));
}
}
if ( configTable . ContainsKey ( ConfigFileConstants . PowerShellVersion ))
{
if ( isPSVersionSpecified == false )
{
try
{
PSVersion = new Version ( configTable [ ConfigFileConstants . PowerShellVersion ]. ToString ());
}
catch ( ArgumentException e )
{
ThrowTerminatingError ( new ErrorRecord ( e , "InvalidPowerShellVersion" , ErrorCategory . InvalidOperation , null ));
}
catch ( FormatException e )
{
ThrowTerminatingError ( new ErrorRecord ( e , "InvalidPowerShellVersion" , ErrorCategory . InvalidOperation , null ));
}
catch ( OverflowException e )
{
ThrowTerminatingError ( new ErrorRecord ( e , "InvalidPowerShellVersion" , ErrorCategory . InvalidOperation , null ));
}
}
}
if ( configTable . ContainsKey ( ConfigFileConstants . RunAsVirtualAccount ))
{
this . RunAsVirtualAccount = LanguagePrimitives . ConvertTo < bool >( configTable [ ConfigFileConstants . RunAsVirtualAccount ]);
this . RunAsVirtualAccountSpecified = true ;
}
if ( configTable . ContainsKey ( ConfigFileConstants . RunAsVirtualAccountGroups ))
{
this . RunAsVirtualAccountGroups = PSSessionConfigurationCommandUtilities . GetRunAsVirtualAccountGroupsString (
DISCPowerShellConfiguration . TryGetStringArray ( configTable [ ConfigFileConstants . RunAsVirtualAccountGroups ]));
}
if ( configTable . ContainsKey ( ConfigFileConstants . GMSAAccount ))
{
2016-07-29 13:02:49 -07:00
_gmsaAccount = configTable [ ConfigFileConstants . GMSAAccount ] as string ;
2016-03-30 14:20:52 -07:00
}
// Get role account and group restriction SDDL from configuration table, if any.
ErrorRecord error ;
2016-07-29 13:02:49 -07:00
_configTableSDDL = PSSessionConfigurationCommandUtilities . ComputeSDDLFromConfiguration (
2016-03-30 14:20:52 -07:00
configTable ,
AccessMode ,
out error );
if ( error != null )
{
WriteError ( error );
}
// Update default Sddl with any group membership requirements.
2016-07-29 13:02:49 -07:00
if ( string . IsNullOrEmpty ( _configTableSDDL ) && ! this . isSddlSpecified && ! string . IsNullOrEmpty ( sddl ))
2016-03-30 14:20:52 -07:00
{
string configGroupMemberShipACE = PSSessionConfigurationCommandUtilities . CreateConditionalACEFromConfig ( configTable );
if (! string . IsNullOrEmpty ( configGroupMemberShipACE ))
{
sddl = PSSessionConfigurationCommandUtilities . UpdateSDDLUsersWithGroupConditional ( sddl , configGroupMemberShipACE );
}
}
try
{
DISCUtils . ValidateAbsolutePaths ( SessionState , configTable , Path );
}
catch ( InvalidOperationException e )
{
ThrowTerminatingError ( new ErrorRecord ( e , "RelativePathsNotSupported" , ErrorCategory . InvalidOperation , null ));
}
try
{
DISCUtils . ValidateExtensions ( configTable , Path );
}
catch ( InvalidOperationException e )
{
ThrowTerminatingError ( new ErrorRecord ( e , "FileExtensionNotSupported" , ErrorCategory . InvalidOperation , null ));
}
}
string destPath = System . IO . Path . Combine ( Utils . GetApplicationBase ( Utils . DefaultPowerShellShellID ), "SessionConfig" ,
shellName + "_" + sessionGuid . ToString () + StringLiterals . PowerShellDISCFileExtension );
2016-07-29 13:02:49 -07:00
if ( string . Equals ( ProcessorArchitecture , "x86" , StringComparison . OrdinalIgnoreCase ))
2016-03-30 14:20:52 -07:00
{
string procArch = Environment . GetEnvironmentVariable ( "PROCESSOR_ARCHITECTURE" );
if ( string . Equals ( procArch , "amd64" , StringComparison . OrdinalIgnoreCase ) ||
string . Equals ( procArch , "ia64" , StringComparison . OrdinalIgnoreCase ))
{
// syswow64 is applicable only on 64 bit platforms.
destPath = destPath . ToLowerInvariant (). Replace ( "\\system32\\" , "\\syswow64\\" );
}
}
// Return configuration file path names for later copy operation, if needed.
// We need to copy the file again if after running Register-PSSessionConfiguration
// removes the file which can happen if the endpoint already exists.
srcConfigFilePath = filePath ;
destConfigFilePath = destPath ;
// Copy File.
File . Copy ( srcConfigFilePath , destConfigFilePath , true );
initParameters . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
ConfigurationDataFromXML . CONFIGFILEPATH_CamelCase ,
destPath ,
Environment . NewLine ));
}
if (! assemblyAndTypeTokensSet )
{
if (! string . IsNullOrEmpty ( configurationTypeName ))
{
initParameters . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
ConfigurationDataFromXML . SHELLCONFIGTYPETOKEN ,
configurationTypeName ,
Environment . NewLine ));
}
if (! string . IsNullOrEmpty ( assemblyName ))
{
initParameters . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
ConfigurationDataFromXML . ASSEMBLYTOKEN ,
assemblyName ,
Environment . NewLine ));
}
}
if (! string . IsNullOrEmpty ( applicationBase ))
{
initParameters . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
ConfigurationDataFromXML . APPBASETOKEN ,
applicationBase ,
Environment . NewLine ));
}
if (! string . IsNullOrEmpty ( configurationScript ))
{
initParameters . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
ConfigurationDataFromXML . STARTUPSCRIPTTOKEN ,
configurationScript ,
Environment . NewLine ));
}
if ( maxCommandSizeMB . HasValue )
{
initParameters . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
ConfigurationDataFromXML . MAXRCVDCMDSIZETOKEN ,
maxCommandSizeMB . Value ,
Environment . NewLine ));
}
if ( maxObjectSizeMB . HasValue )
{
initParameters . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
ConfigurationDataFromXML . MAXRCVDOBJSIZETOKEN ,
maxObjectSizeMB . Value ,
Environment . NewLine ));
}
#if ! CORECLR // No ApartmentState In CoreCLR
if ( threadAptState . HasValue )
{
initParameters . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
ConfigurationDataFromXML . THREADAPTSTATETOKEN ,
threadAptState . Value ,
Environment . NewLine ));
}
#endif
if ( threadOptions . HasValue )
{
initParameters . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
ConfigurationDataFromXML . THREADOPTIONSTOKEN ,
threadOptions . Value ,
Environment . NewLine ));
}
// Default value for PSVersion
if ( isPSVersionSpecified == false )
{
psVersion = PSVersionInfo . PSVersion ;
}
if ( psVersion != null )
{
initParameters . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
ConfigurationDataFromXML . PSVERSIONTOKEN ,
PSSessionConfigurationCommandUtilities . ConstructVersionFormatForConfigXml ( psVersion ),
Environment . NewLine ));
// Calculate MaxPSVersion from PSVersion
MaxPSVersion = PSSessionConfigurationCommandUtilities . CalculateMaxPSVersion ( psVersion );
if ( MaxPSVersion != null )
{
initParameters . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
ConfigurationDataFromXML . MAXPSVERSIONTOKEN ,
PSSessionConfigurationCommandUtilities . ConstructVersionFormatForConfigXml ( MaxPSVersion ),
Environment . NewLine ));
}
}
string securityParameters = "" ;
if (! string . IsNullOrEmpty ( sddl ))
{
securityParameters = string . Format ( CultureInfo . InvariantCulture ,
securityElementFormat ,
WSManNativeApi . ResourceURIPrefix + shellName ,
SecurityElement . Escape ( sddl ));
}
string architectureParameter = string . Empty ;
2016-08-02 13:29:09 -07:00
if (! string . IsNullOrEmpty ( ProcessorArchitecture ))
2016-03-30 14:20:52 -07:00
{
string tempValue = "32" ;
2016-08-02 13:29:09 -07:00
switch ( ProcessorArchitecture . ToLowerInvariant ())
2016-03-30 14:20:52 -07:00
{
case "x86" :
tempValue = "32" ;
break ;
case "amd64" :
tempValue = "64" ;
break ;
}
architectureParameter = string . Format ( CultureInfo . InvariantCulture ,
architectureAttribFormat ,
tempValue );
}
if ( sessionType == PSSessionType . Workflow && ! isUseSharedProcessSpecified )
{
UseSharedProcess = true ;
}
string sharedHostParameter = string . Empty ;
if ( isUseSharedProcessSpecified )
{
sharedHostParameter = string . Format ( CultureInfo . InvariantCulture ,
sharedHostAttribFormat , UseSharedProcess . ToString ()
);
}
string runAsVirtualAccountParameter = string . Empty ;
string runAsVirtualAccountGroupsParameter = string . Empty ;
if ( RunAsVirtualAccount )
{
runAsVirtualAccountParameter = string . Format ( CultureInfo . InvariantCulture ,
runasVirtualAccountAttribFormat , RunAsVirtualAccount . ToString ()
);
// Include virtual account groups if any.
if (! string . IsNullOrEmpty ( RunAsVirtualAccountGroups ))
{
runAsVirtualAccountGroupsParameter = string . Format ( CultureInfo . InvariantCulture ,
runAsVirtualAccountGroupsAttribFormat , RunAsVirtualAccountGroups );
}
}
string allowRemoteShellAccessParameter = string . Empty ;
switch ( AccessMode )
{
case PSSessionConfigurationAccessMode . Disabled :
allowRemoteShellAccessParameter = string . Format ( CultureInfo . InvariantCulture ,
allowRemoteShellAccessFormat , false . ToString ());
break ;
case PSSessionConfigurationAccessMode . Local :
case PSSessionConfigurationAccessMode . Remote :
allowRemoteShellAccessParameter = string . Format ( CultureInfo . InvariantCulture ,
allowRemoteShellAccessFormat , true . ToString ());
break ;
}
StringBuilder sessionConfigurationData = new StringBuilder ();
if ( sessionType == PSSessionType . Workflow )
{
List < object > modifiedModulePath = new List < object >( modulesToImport ?? Utils . EmptyArray < string >());
modifiedModulePath . Insert ( 0 , ConfigurationDataFromXML . PSWORKFLOWMODULE );
modulesToImport = modifiedModulePath . ToArray ();
}
if ( modulesToImport != null && modulesToImport . Length > 0 )
{
sessionConfigurationData . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
PSSessionConfigurationData . ModulesToImportToken ,
PSSessionConfigurationCommandUtilities . GetModulePathAsString ( modulesToImport ),
string . Empty ));
}
if ( sessionTypeOption != null )
{
//TODO: This should probably be a terminating exception for Win8
string privateData = this . sessionTypeOption . ConstructPrivateData ();
if (! string . IsNullOrEmpty ( privateData ))
{
sessionConfigurationData . Append ( string . Format ( CultureInfo . InvariantCulture , privateDataFormat , privateData ));
}
}
if ( sessionConfigurationData . Length > 0 )
{
string sessionConfigData = string . Format ( CultureInfo . InvariantCulture ,
SessionConfigDataFormat ,
sessionConfigurationData );
string encodedSessionConfigData = SecurityElement . Escape ( sessionConfigData );
initParameters . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
ConfigurationDataFromXML . SESSIONCONFIGTOKEN ,
encodedSessionConfigData ,
string . Empty ));
}
if ( transportOption == null )
{
transportOption = new WSManConfigurationOption ();
}
else
{
transportOption = transportOption . Clone () as PSTransportOption ;
}
transportOption . LoadFromDefaults ( sessionType , true );
// If useSharedHost is set to false, we need to set hostIdleTimeout to 0 as well, else WS-Man throws error
if ( isUseSharedProcessSpecified && ! UseSharedProcess )
{
( transportOption as WSManConfigurationOption ). ProcessIdleTimeoutSec = 0 ;
}
string result = string . Format ( CultureInfo . InvariantCulture ,
pluginXmlFormat ,
shellName , /* {0} */
RemotingConstants . PSPluginDLLName , /* {1} */
architectureParameter , /* {2} */
initParameters . ToString (), /* {3} */
WSManNativeApi . ResourceURIPrefix + shellName , /* {4} */
securityParameters , /* {5} */
sharedHostParameter , /* {6} */
runAsVirtualAccountParameter , /* {7} */
runAsVirtualAccountGroupsParameter , /* {8} */
allowRemoteShellAccessParameter , /* {9} */
transportOption . ConstructOptionsAsXmlAttributes (), /* {10} */
transportOption . ConstructQuotas (), /* {11} */
( psVersion . Major < 3 ) ? 1 : 2 /* {12} - Pass in SDK version. */
);
return result ;
}
#endregion
}
#endregion Register - PSSessionConfiguration cmdlet
#region Utils
/// <summary>
/// Utilities for Custom shell commands
/// </summary>
internal static class PSSessionConfigurationCommandUtilities
{
internal const string restartWSManFormat = "restart-service winrm -force -confirm:$false" ;
#region Internal Methods / Properties
internal const string PSCustomShellTypeName = "Microsoft.PowerShell.Commands.PSSessionConfigurationCommands#PSSessionConfiguration" ;
/// <summary>
/// Run script to restart the WinRM service. The script will write
/// output and error into the cmdlets streams.
/// </summary>
/// <param name="cmdlet">
/// Cmdlet's context in which the restart-service script is run.
/// </param>
/// <param name="isErrorReported">
/// if true, then this method is a no-op.
/// </param>
/// <param name="force">
/// if true, then the user will not be prompted.
/// </param>
/// <param name="noServiceRestart">
/// if true, we dont attempt to restart winrm service ie. this will be a no-op.
/// </param>
internal static void RestartWinRMService ( PSCmdlet cmdlet , bool isErrorReported , bool force , bool noServiceRestart )
{
// restart the service only if there is no error running WSMan config command
if (!( isErrorReported || noServiceRestart ))
{
// restart-service winrm to make the changes effective.
string restartServiceAction = RemotingErrorIdStrings . RestartWSManServiceAction ;
string restartServiceTarget = StringUtil . Format ( RemotingErrorIdStrings . RestartWSManServiceTarget , "WinRM" );
if ( force || cmdlet . ShouldProcess ( restartServiceTarget , restartServiceAction ))
{
cmdlet . WriteVerbose ( StringUtil . Format ( RemotingErrorIdStrings . RestartWSManServiceMessageV ));
ScriptBlock restartServiceScript = cmdlet . InvokeCommand . NewScriptBlock ( restartWSManFormat );
var emptyArray = Utils . EmptyArray < object >();
restartServiceScript . InvokeUsingCmdlet (
contextCmdlet : cmdlet ,
useLocalScope : true ,
errorHandlingBehavior : ScriptBlock . ErrorHandlingBehavior . WriteToCurrentErrorPipe ,
dollarUnder : AutomationNull . Value ,
input : emptyArray ,
scriptThis : AutomationNull . Value ,
args : emptyArray );
}
}
}
internal static void MoveWinRmToIsolatedServiceHost ( bool forVirtualAccount )
{
string moveScript = "sc.exe config winrm type= own" ;
if ( forVirtualAccount )
{
moveScript += @"
$requiredPrivileges = Get-ItemPropertyValue -Path HKLM:\SYSTEM\CurrentControlSet\Services\winrm -Name RequiredPrivileges
if($requiredPrivileges -notcontains 'SeTcbPrivilege')
{
$requiredPrivileges += @('SeTcbPrivilege')
}
Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\winrm -Name RequiredPrivileges -Value $requiredPrivileges
Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\winrm -Name ObjectName -Value 'LocalSystem'" ;
}
using ( System . Management . Automation . PowerShell invoker = System . Management . Automation . PowerShell . Create ( RunspaceMode . CurrentRunspace ))
{
invoker . AddScript ( moveScript ). Invoke ();
}
}
/// <summary>
/// Gathers WhatIf, Confirm parameter values from the cmdlet.
/// </summary>
/// <param name="cmdlet"></param>
/// <param name="whatIf"></param>
/// <param name="confirm"></param>
internal static void CollectShouldProcessParameters ( PSCmdlet cmdlet , out bool whatIf , out bool confirm )
{
// gather -WhatIf, -Confirm parameter data and pass it to the script block
whatIf = false ;
// confirm is always true to start with
confirm = false ;
MshCommandRuntime cmdRuntime = cmdlet . CommandRuntime as MshCommandRuntime ;
if ( null != cmdRuntime )
{
whatIf = cmdRuntime . WhatIf ;
// take the value of confirm only if it is explicitly set by the user
if ( cmdRuntime . IsConfirmFlagSet )
{
confirm = cmdRuntime . Confirm ;
}
}
}
/// <summary>
/// Checks if the current thread is running elevated. If not, throws an error.
/// </summary>
/// <exception cref="InvalidOperationException">
/// 1. Acess is denied. You need to run this cmdlet from an elevated process.
/// </exception>
internal static void ThrowIfNotAdministrator ()
{
System . Security . Principal . WindowsIdentity currentIdentity = System . Security . Principal . WindowsIdentity . GetCurrent ();
System . Security . Principal . WindowsPrincipal principal = new System . Security . Principal . WindowsPrincipal ( currentIdentity );
if (! principal . IsInRole ( System . Security . Principal . WindowsBuiltInRole . Administrator ))
{
string message = StringUtil . Format ( RemotingErrorIdStrings . EDcsRequiresElevation );
throw new InvalidOperationException ( message );
}
}
/// <summary>
/// Creates a Grouped Managed Service Account credential based on the passed in account name
/// </summary>
/// <param name="gmsaAccount">Group Managed Service Account name</param>
/// <returns>PSCredential for GMS account</returns>
/// <exception cref="InvalidOperationException">
/// Invalid account name. Must be of form 'Domain\UserName'.
/// </exception>
internal static PSCredential CreateGMSAAccountCredentials ( string gmsaAccount )
{
Dbg . Assert (! string . IsNullOrEmpty ( gmsaAccount ), "Should not be null or empty string." );
// Validate account name form (must be DomainName\UserName)
var parts = gmsaAccount . Split ( Utils . Separators . Backslash );
if (( parts . Length != 2 ) ||
( string . IsNullOrEmpty ( parts [ 0 ])) ||
( string . IsNullOrEmpty ( parts [ 1 ]))
)
{
throw new InvalidOperationException ( RemotingErrorIdStrings . InvalidGMSAName );
}
// Use the provided GMSA account name (Domain\UserName$) with empty password.
string userName = gmsaAccount + "$" ;
SecureString password = new SecureString ();
return new PSCredential ( userName , password );
}
/// <summary>
/// Calculates the MaxPSVersion in the config xml from psVersion
/// </summary>
/// <param name="psVersion"></param>
internal static Version CalculateMaxPSVersion ( Version psVersion )
{
Version maxPSVersion = null ;
if ( psVersion != null && psVersion . Major == 2 )
{
maxPSVersion = new Version ( 2 , 0 );
}
return maxPSVersion ;
}
/// <summary>
/// Converts the module path represented in the string[] into a comma separated string.
/// </summary>
internal static string GetModulePathAsString ( object [] modulePath )
{
if ( modulePath != null && modulePath . Length > 0 )
{
StringBuilder sb = new StringBuilder ();
foreach ( object s in modulePath )
{
var module = s as string ;
if ( module != null )
{
sb . Append ( s );
sb . Append ( ',' );
}
else
{
var moduleSpec = s as ModuleSpecification ;
if ( moduleSpec != null )
{
// Double escaping on ModuleSpecification string is required to treat it as a single value along with module names/paths in SessionConfigurationData
sb . Append ( SecurityElement . Escape ( SecurityElement . Escape ( moduleSpec . ToString ())));
sb . Append ( ',' );
}
}
}
sb . Remove ( sb . Length - 1 , 1 );
return sb . ToString ();
}
return string . Empty ;
}
/// <summary>
/// Converts the version number to the format "Major.Minor" which needs to be persisted in the config xml
/// </summary>
/// <param name="psVersion"></param>
internal static Version ConstructVersionFormatForConfigXml ( Version psVersion )
{
Version result = null ;
if ( psVersion != null )
{
result = new Version ( psVersion . Major , psVersion . Minor );
}
return result ;
}
/// <summary>
/// Checks if the specified version of PowerShell is installed
/// </summary>
/// <param name="version"></param>
internal static void CheckIfPowerShellVersionIsInstalled ( Version version )
{
// Check if PowerShell 2.0 is installed
if ( version != null && version . Major == 2 )
{
2016-04-28 13:41:25 -07:00
#if CORECLR
// PowerShell 2.0 is not available for CoreCLR
throw new ArgumentException (
PSRemotingErrorInvariants . FormatResourceString (
RemotingErrorIdStrings . PowerShellNotInstalled ,
version , "PSVersion" ));
#else
// Because of app-compat issues, in Win8, we will have PS 2.0 installed by default but not .NET 2.0
// In such a case, it is not enough if we check just PowerShell registry keys. We also need to check if .NET 2.0 is installed.
2016-03-30 14:20:52 -07:00
try
{
RegistryKey engineKey = PSSnapInReader . GetPSEngineKey ( PSVersionInfo . RegistryVersion1Key );
// Also check for .NET 2.0 installation
if (! PsUtils . FrameworkRegistryInstallation . IsFrameworkInstalled ( 2 , 0 , 0 ))
{
throw new ArgumentException (
PSRemotingErrorInvariants . FormatResourceString (
RemotingErrorIdStrings . NetFrameWorkV2NotInstalled ));
}
}
catch ( PSArgumentException )
{
throw new ArgumentException (
PSRemotingErrorInvariants . FormatResourceString (
RemotingErrorIdStrings . PowerShellNotInstalled ,
version , "PSVersion" ));
}
2016-04-28 13:41:25 -07:00
#endif
2016-03-30 14:20:52 -07:00
}
}
/// <summary>
/// Takes array of group name string objects and returns a semicolon delimited string.
/// </summary>
/// <param name="groups"></param>
/// <returns></returns>
internal static string GetRunAsVirtualAccountGroupsString ( string [] groups )
{
if ( groups == null ) { return string . Empty ; }
return string . Join ( ";" , groups );
}
#endregion
#region Group Conditional SDDL
/// <summary>
/// Builds a session SDDL based on the provided configuration hashtable.
/// Retrieves RequiredGroups information to add conditional group membership restrictions to SDDL.
/// Retrieves RoleDefinitions information to include role user accounts.
/// </summary>
/// <param name="configTable"></param>
/// <param name="accessMode"></param>
/// <param name="error"></param>
/// <returns>SDDL</returns>
internal static string ComputeSDDLFromConfiguration (
Hashtable configTable ,
PSSessionConfigurationAccessMode accessMode ,
out ErrorRecord error )
{
Dbg . Assert ( configTable != null , "configTable input parameter cannot be null." );
string sddl = string . Empty ;
error = null ;
// RoleDefinitions
if ( configTable . ContainsKey ( ConfigFileConstants . RoleDefinitions ))
{
// Start with known good security descriptor.
if ( accessMode == PSSessionConfigurationAccessMode . Local )
{
sddl = PSSessionConfigurationCommandBase . GetLocalSddl ();
}
else if ( accessMode == PSSessionConfigurationAccessMode . Remote )
{
sddl = PSSessionConfigurationCommandBase . GetRemoteSddl ();
}
CommonSecurityDescriptor descriptor = new CommonSecurityDescriptor ( false , false , sddl );
// Purge all existing access rules so that only role definition principals are granted access.
List < SecurityIdentifier > sidsToRemove = new List < SecurityIdentifier >();
foreach ( CommonAce ace in descriptor . DiscretionaryAcl )
{
sidsToRemove . Add ( ace . SecurityIdentifier );
}
foreach ( var sidToRemove in sidsToRemove )
{
descriptor . PurgeAccessControl ( sidToRemove );
}
Hashtable roleNamesHash = configTable [ ConfigFileConstants . RoleDefinitions ] as Hashtable ;
foreach ( object roleName in roleNamesHash . Keys )
{
string roleNameValue = roleName . ToString ();
try
{
NTAccount ntAccount = new NTAccount ( roleNameValue );
SecurityIdentifier accountSid = ( SecurityIdentifier ) ntAccount . Translate ( typeof ( SecurityIdentifier ));
// AccessMask = 268435456 == 0x10000000 == GR == Generic Read
descriptor . DiscretionaryAcl . AddAccess ( AccessControlType . Allow , accountSid , 268435456 , InheritanceFlags . None , PropagationFlags . None );
}
catch ( IdentityNotMappedException e )
{
string message = StringUtil . Format ( RemotingErrorIdStrings . CouldNotResolveRoleDefinitionPrincipal , roleNameValue , e . Message );
InvalidOperationException ioe = new InvalidOperationException ( message , e );
error = new ErrorRecord ( ioe , "CouldNotResolveRoleDefinitionPrincipal" , ErrorCategory . ObjectNotFound , roleNameValue );
}
}
if ( descriptor . DiscretionaryAcl . Count > 0 )
{
sddl = descriptor . GetSddlForm ( AccessControlSections . All );
// RequiredGroups
string conditionalGroupACE = CreateConditionalACEFromConfig ( configTable );
if ( conditionalGroupACE != null )
{
sddl = UpdateSDDLUsersWithGroupConditional ( sddl , conditionalGroupACE );
}
}
}
return sddl ;
}
#region SDDL Update
private const char OpenParenChar = '(' ;
private const char CloseParenChar = ')' ;
private const char ACESeparatorChar = ';' ;
private const string ACESeparator = ";" ;
private const string ConditionalACEPrefix = "X" ;
/// <summary>
/// Update SDDL user ACEs with provided conditional group ACE fragment.
/// SDDL: O:NSG:BAD:P(A;;GA;;;BA)(A;;GA;;;RM)(A;;GA;;;IU)(A;;GA;;;S-1-5-21-2127438184-1604012920-1882527527)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)
/// Cond ACE: (Member_of {SID(2FA_GROUP_1)})
/// Cond SDDL: O:NSG:BAD:P(XA;;GA;;;BA)(A;;GA;;;RM)(A;;GA;;;IU)(XA;;GA;;;S-1-5-21-2127438184-1604012920-1882527527;(Member_of {SID(2FA_GROUP_1)}))S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)
/// </summary>
/// <param name="sddl"></param>
/// <param name="conditionalGroupACE"></param>
/// <returns></returns>
internal static string UpdateSDDLUsersWithGroupConditional (
string sddl ,
string conditionalGroupACE )
{
// Parse sddl for DACL user ACEs.
// prologue string contains the beginning owner and primary group components
// O:NSG:BAD:P
// epilogue string contains the ending (and optional) SACL components
// S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)
// (https://msdn.microsoft.com/en-us/library/windows/desktop/aa379570(v=vs.85).aspx)
string prologue ;
string epilogue ;
Collection < string > aces = ParseDACLACEs ( sddl , out prologue , out epilogue );
if ( aces . Count == 0 ) { return sddl ; }
StringBuilder sb = new StringBuilder ();
sb . Append ( prologue );
//
// Convert to condtional ACE
// Beginning (Regular) ACE has exactly 6 required fields and one (optional) field.
// We only manipulate ACEs that we create and we currently do not use the optional resource field,
// so we always expect a beginning ACE with exactly 6 fields.
// ace_type;ace_flags;rights;object_guid;inherit_object_guid;account_sid;(resource_attribute)
// (https://msdn.microsoft.com/en-us/library/windows/desktop/aa374928(v=vs.85).aspx)
//
// Converted (Conditional) ACE has exactly 7 required fields. In addition the ACE type
// is prepended with 'X' character.
// AceType;AceFlags;Rights;ObjectGuid;InheritObjectGuid;AccountSid;(ConditionalExpression)
// (https://msdn.microsoft.com/en-us/library/windows/desktop/dd981030(v=vs.85).aspx)
//
// e.g.
// Beginning ACE: (A;;GA;;;BA)
// Converted ACE: (XA;;GA;;;BA;(Member_of {SID(S-1-5-32-547)}))
//
foreach ( var ace in aces )
{
// Parse ACE components.
var components = ace . Split ( ACESeparatorChar );
if ( components . Length != 6 )
{
throw new PSInvalidOperationException (
StringUtil . Format ( RemotingErrorIdStrings . RequiredGroupsMalformedACE , ace ));
}
// Trim open and close parentheses from ACE string.
components [ 0 ] = components [ 0 ]. TrimStart ( OpenParenChar );
components [ 5 ] = components [ 5 ]. TrimEnd ( CloseParenChar );
// Building new conditional ACE
sb . Append ( OpenParenChar );
// Prepend the 'X' character
var accessType = ConditionalACEPrefix + components [ 0 ];
sb . Append ( accessType + ACESeparator );
for ( int i = 1 ; i < 6 ; i ++)
{
sb . Append ( components [ i ] + ACESeparator );
}
sb . Append ( conditionalGroupACE );
sb . Append ( CloseParenChar );
}
sb . Append ( epilogue );
return sb . ToString ();
}
private const string DACLPrefix = "D:" ;
private static Collection < string > ParseDACLACEs (
string sddl ,
out string prologue ,
out string epilogue )
{
//
// The format of the sddl is expected to be:
// owner (O:), primary group (G:), DACL (D:), and SACL (S:).
// (https://msdn.microsoft.com/en-us/library/windows/desktop/aa379570(v=vs.85).aspx)
// e.g.
// O:NSG:BAD:P(A;;GA;;;BA)(XA;;GA;;;RM)(XA;;GA;;;IU)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)
// prologue = "O:NSG:BAD:P"
// DACL ACEs = "(A;;GA;;;BA)(XA;;GA;;;RM)(XA;;GA;;;IU)"
// epilogue = "S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)"
//
Collection < string > sddlACEs = new Collection < string >();
// Find beginning of DACL ACEs
int index = sddl . IndexOf ( DACLPrefix , StringComparison . OrdinalIgnoreCase );
if ( index < 0 )
{
prologue = string . Empty ;
epilogue = string . Empty ;
return sddlACEs ;
}
// Advance index to beginning of DACL ACEs and save prologue
index = sddl . IndexOf ( OpenParenChar , index );
if ( index < 0 )
{
prologue = string . Empty ;
epilogue = string . Empty ;
return sddlACEs ;
}
prologue = sddl . Substring ( 0 , index );
int sddlLength = sddl . Length ;
while ( true )
{
// Find ending of ACE
int endIndex = sddl . IndexOf ( CloseParenChar , index );
if ( endIndex < 0 )
{
throw new PSInvalidOperationException (
StringUtil . Format ( RemotingErrorIdStrings . BadSDDLMismatchedParens , sddl ));
}
string ace = sddl . Substring ( index , ( endIndex - index + 1 ));
sddlACEs . Add ( ace );
// Next ACE is indicated by an immediate open parenthesis character.
index = endIndex + 1 ;
if (( index >= sddlLength ) || ( sddl [ index ] != OpenParenChar )) { break ; }
}
// SACLs will be at the end of the sddl string.
epilogue = ( index < sddlLength ) ? sddl . Substring ( index , ( sddlLength - index )) : string . Empty ;
return sddlACEs ;
}
#endregion
#region Conditional ACE
private const string AndOperator = "And" ;
private const string OrOperator = "Or" ;
private const string AndCondition = " && " ;
private const string OrCondition = " || " ;
private const string MemberOfFormat = "Member_of {{SID({0})}}" ;
/// <summary>
/// Parse RequiredGroups configruation and build conditional ACE string.
/// </summary>
/// <param name="configTable"></param>
/// <returns></returns>
// RequiredGroups: @{ And = @{ Or = '2FA_GROUP_1', '2FA_GROUP_2' }, @{ Or = 'TRUSTEDHOSTS_1', 'TRUSTEDHOSTS_2' } }
// User ACE: (XA;;GA;;;S-1-5-21-2127438184-1604012920-1882527527;CondionalPart)
// CondionalPart: ((Member_of {SID(2FA_GROUP_1)} || Member_of {SID(2FA_GROUP_2)}) && (Member_of {SID(TRUSTEDHOSTS_1)} || Member_of {TRUSTEDHOSTS_2}))
// where: 2FA_GROUP_1, 2FA_GROUP_2, TRUSTEDHOSTS_1, TRUSTEDHOSTS_2 are resolved SIDs of the group names.
// (https://msdn.microsoft.com/en-us/library/windows/desktop/dd981030(v=vs.85).aspx)
internal static string CreateConditionalACEFromConfig (
Hashtable configTable )
{
if (! configTable . ContainsKey ( ConfigFileConstants . RequiredGroups ))
{
return null ;
}
StringBuilder conditionalACE = new StringBuilder ();
Hashtable requiredGroupsHash = configTable [ ConfigFileConstants . RequiredGroups ] as Hashtable ;
if ( requiredGroupsHash == null )
{
throw new PSInvalidOperationException ( RemotingErrorIdStrings . RequiredGroupsNotHashTable );
}
if ( requiredGroupsHash . Count == 0 )
{
return null ;
}
// Recursively create group membership conditional ACE for each hash table key.
AddCondition ( conditionalACE , requiredGroupsHash );
return conditionalACE . ToString ();
}
private static void AddCondition (
StringBuilder sb ,
Hashtable condition )
{
// We currently support only 'And' and 'Or' logical operations for group membership
// combinations.
if ( condition . ContainsKey ( AndOperator ))
{
object keyValue = condition [ AndOperator ];
ParseKeyValue ( sb , keyValue , AndCondition );
}
else if ( condition . ContainsKey ( OrOperator ))
{
object keyValue = condition [ OrOperator ];
ParseKeyValue ( sb , keyValue , OrCondition );
}
else
{
throw new PSInvalidOperationException ( RemotingErrorIdStrings . UnknownGroupMembershipKey );
}
}
private static void ParseKeyValue (
StringBuilder sb ,
object keyValue ,
string logicalOperator )
{
// Start of condition
sb . Append ( OpenParenChar );
// Logical keyname value can contain:
// Single group name
// @{ Or = 'Group1' }
// (Member_of {SID(Group1)})
// Multiple group names
// @{ Or = 'Group1','Group2' }
// (Member_of {SID(Group2)} || Member_of {SID(Group2)})
// Single hash table
// @{ Or = @{ And = 'Group1' } }
// (Member_of {SID(Group1)})
// Multiple hash table
// @{ Or = @{ And = 'Group1','Group2' }, @{ And = 'Group3','Group4' } }
// ((Member_of {SID(Group1)} && Member_of {SID(Group2)}) || (Member_of {SID(Group3)} && Member_of {SID(Group4)}))
// Mixed
// @{ Or = 'Group1', @{ And = 'Group2','Group3' } }
// (Member_of {SID(Group1)} || (Member_of {SID(Group2)} && Member_of {SID(Group3)}))
object [] values = keyValue as object [];
if ( values != null )
{
int count = values . Length ;
2016-07-29 13:02:49 -07:00
for ( int i = 0 ; i < count ;)
2016-03-30 14:20:52 -07:00
{
ParseValues ( sb , values [ i ++]);
if ( i < count )
{
// Combine sub conditional ACEs with logical operator
sb . Append ( logicalOperator );
}
}
}
else
{
ParseValues ( sb , keyValue );
}
// End of condition
sb . Append ( CloseParenChar );
}
private static void ParseValues (
StringBuilder sb ,
object inValue )
{
// Value to parse can be a single object or an array of objects
object [] values = inValue as object [];
if ( values != null )
{
foreach ( object value in values )
{
ParseValue ( sb , value );
}
}
else
{
ParseValue ( sb , inValue );
}
}
private static void ParseValue (
StringBuilder sb ,
object value )
{
// Single value objects can be either a group name or a new logical hash table
string groupName = value as string ;
if ( groupName != null )
{
// Resolve group name to SID
NTAccount ntAccount = new NTAccount ( groupName );
SecurityIdentifier accountSid = ( SecurityIdentifier ) ntAccount . Translate ( typeof ( SecurityIdentifier ));
sb . Append ( StringUtil . Format ( MemberOfFormat , accountSid . ToString ()));
}
else
{
Hashtable recurseCondition = value as Hashtable ;
if ( recurseCondition != null )
{
// Recurse to handle logical hash table
AddCondition ( sb , recurseCondition );
}
else
{
throw new PSInvalidOperationException ( RemotingErrorIdStrings . UnknownGroupMembershipValue );
}
}
}
#endregion
#endregion
}
#endregion
#region PSCustomShellCommandBase
/// <summary>
/// Base class for PSCustomShell commands Register-PSSessionConfiguration, Set-PSSessionConfiguration
/// </summary>
public class PSSessionConfigurationCommandBase : PSCmdlet
{
#region Private Data
internal const string NameParameterSetName = "NameParameterSet" ;
internal const string AssemblyNameParameterSetName = "AssemblyNameParameterSet" ;
internal const string SessionConfigurationFileParameterSetName = "SessionConfigurationFile" ;
// Deny network access but allow local access
private const string localSDDL = "O:NSG:BAD:P(D;;GA;;;NU)(A;;GA;;;BA)(A;;GA;;;IU)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)" ;
private const string localSDDL_Win8 = "O:NSG:BAD:P(D;;GA;;;NU)(A;;GA;;;BA)(A;;GA;;;RM)(A;;GA;;;IU)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)" ; // Win8+ only
private const string remoteSDDL = "O:NSG:BAD:P(A;;GA;;;BA)(A;;GA;;;IU)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)" ;
private const string remoteSDDL_Win8 = "O:NSG:BAD:P(A;;GA;;;BA)(A;;GA;;;RM)(A;;GA;;;IU)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)" ; // Win8 only
// Win8 only SID for Remote Management Users group.
internal const string RemoteManagementUsersSID = "S-1-5-32-580" ;
// SID for Interactive Users group.
internal const string InteractiveUsersSID = "S-1-5-4" ;
internal Version MaxPSVersion = null ;
#endregion
#region Static methods
internal static string GetLocalSddl ()
{
return ( Environment . OSVersion . Version >= new Version ( 6 , 2 )) ? localSDDL_Win8 : localSDDL ;
}
internal static string GetRemoteSddl ()
{
return ( Environment . OSVersion . Version >= new Version ( 6 , 2 )) ? remoteSDDL_Win8 : remoteSDDL ;
}
internal static void CheckPSVersion ( Version version )
{
// PSVersion value can only be 2.0, 3.0, 4.0, 5.0, or 5.1
if ( version != null )
{
// PSVersion value can only be 2.0, 3.0, 4.0, 5.0, or 5.1
if (!(( version . Major >= 2 ) && ( version . Major <= 4 ) && ( version . Minor == 0 )) &&
!(( version . Major == 5 ) && ( version . Minor <= 1 ))
)
{
throw new ArgumentException (
PSRemotingErrorInvariants . FormatResourceString ( RemotingErrorIdStrings . PSVersionParameterOutOfRange ,
version , "PSVersion" )
);
}
}
}
#endregion
#region Parameters
/// <summary>
/// This parameter enables the user to specify a shell name for the
/// created custom shell.
/// </summary>
[Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true, ParameterSetName = NameParameterSetName)]
[Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true, ParameterSetName = AssemblyNameParameterSetName)]
[Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true, ParameterSetName = SessionConfigurationFileParameterSetName)]
[ValidateNotNullOrEmpty]
public string Name
{
get { return shellName ; }
set { shellName = value ; }
}
internal string shellName ;
/// <summary>
/// This parameter enables the user to load an Assembly and supply
/// InitialSessionstate for each user connecting to the shell.
/// </summary>
[Parameter(Position = 1, Mandatory = true, ParameterSetName = PSSessionConfigurationCommandBase.AssemblyNameParameterSetName)]
public string AssemblyName
{
get { return assemblyName ; }
set
{
assemblyName = value ;
isAssemblyNameSpecified = true ;
}
}
internal string assemblyName ;
internal bool isAssemblyNameSpecified ;
/// <summary>
/// This parameter can accompany with AssemblyName. This supplies
/// the directory to search while loading the assembly specified
/// with parameter AssemblyName. Environment variables are accepted
/// in the string value.
/// </summary>
[Parameter(ParameterSetName = NameParameterSetName)]
[Parameter(ParameterSetName = AssemblyNameParameterSetName)]
public string ApplicationBase
{
get { return applicationBase ; }
set
{
applicationBase = value ;
isApplicationBaseSpecified = true ;
}
}
internal string applicationBase ;
internal bool isApplicationBaseSpecified ;
/// <summary>
/// This parameter should be specified with AssemblyName. This supplies
/// the type to load to get the InitialSessionState. The type should
/// be derivided from <see cref="PSSessionConfiguration"/>.
/// </summary>
[Parameter(Position = 2, Mandatory = true, ParameterSetName = PSSessionConfigurationCommandBase.AssemblyNameParameterSetName)]
public string ConfigurationTypeName
{
get { return configurationTypeName ; }
set
{
configurationTypeName = value ;
isConfigurationTypeNameSpecified = true ;
}
}
internal string configurationTypeName ;
internal bool isConfigurationTypeNameSpecified ;
/// <summary>
/// Parameter used to specify the RunAs credentials.
/// </summary>
[Parameter, Credential]
public PSCredential RunAsCredential
{
get
{
return runAsCredential ;
}
set
{
runAsCredential = value ;
isRunAsCredentialSpecified = true ;
}
}
internal PSCredential runAsCredential ;
internal bool isRunAsCredentialSpecified ;
#if ! CORECLR // No ApartmentState In CoreCLR
/// <summary>
/// ApartmentState of the Runspace created for the shell.
/// </summary>
[Parameter()]
public ApartmentState ThreadApartmentState
{
get
{
if ( threadAptState . HasValue )
{
return threadAptState . Value ;
}
return ApartmentState . Unknown ;
}
set { threadAptState = value ; }
}
internal Nullable < ApartmentState > threadAptState ;
#endif
/// <summary>
/// ThreadOptions of the Runspace created for the shell.
/// </summary>
[Parameter()]
public PSThreadOptions ThreadOptions
{
get
{
if ( threadOptions . HasValue )
{
return threadOptions . Value ;
}
return PSThreadOptions . UseCurrentThread ;
}
set { threadOptions = value ; }
}
internal Nullable < PSThreadOptions > threadOptions ;
/// <summary>
/// Set access mode
/// </summary>
[Parameter]
public PSSessionConfigurationAccessMode AccessMode
{
2016-07-29 13:02:49 -07:00
get { return _accessMode ; }
2016-03-30 14:20:52 -07:00
set
{
2016-07-29 13:02:49 -07:00
_accessMode = value ;
2016-03-30 14:20:52 -07:00
accessModeSpecified = true ;
}
}
2016-07-29 13:02:49 -07:00
private PSSessionConfigurationAccessMode _accessMode = PSSessionConfigurationAccessMode . Remote ;
2016-03-30 14:20:52 -07:00
internal bool accessModeSpecified = false ;
/// <summary>
/// Host mode
/// </summary>
[Parameter]
public SwitchParameter UseSharedProcess
{
get
{
2016-07-29 13:02:49 -07:00
return _useSharedProcess ;
2016-03-30 14:20:52 -07:00
}
set
{
2016-07-29 13:02:49 -07:00
_useSharedProcess = value ;
2016-03-30 14:20:52 -07:00
isUseSharedProcessSpecified = true ;
}
}
2016-07-29 13:02:49 -07:00
private bool _useSharedProcess ;
2016-03-30 14:20:52 -07:00
internal bool isUseSharedProcessSpecified ;
/// <summary>
/// Initialization script to run upon Runspace creation for this shell.
/// </summary>
[Parameter()]
public string StartupScript
{
get { return configurationScript ; }
set
{
configurationScript = value ;
isConfigurationScriptSpecified = true ;
}
}
internal string configurationScript ;
internal bool isConfigurationScriptSpecified ;
/// <summary>
/// Total data (in MB) that can be received from a remote machine
/// targeted towards a command.
/// </summary>
[Parameter()]
[AllowNull]
public Nullable < double > MaximumReceivedDataSizePerCommandMB
{
get { return maxCommandSizeMB ; }
set
{
if (( value . HasValue ) && ( value . Value < 0 ))
{
throw new ArgumentException (
PSRemotingErrorInvariants . FormatResourceString ( RemotingErrorIdStrings . CSCDoubleParameterOutOfRange ,
value . Value , "MaximumReceivedDataSizePerCommandMB" )
);
}
maxCommandSizeMB = value ;
isMaxCommandSizeMBSpecified = true ;
}
}
internal Nullable < double > maxCommandSizeMB ;
internal bool isMaxCommandSizeMBSpecified ;
/// <summary>
/// Maximum size (in MB) of a deserialized object received from a remote machine.
/// </summary>
[Parameter()]
[AllowNull]
public Nullable < double > MaximumReceivedObjectSizeMB
{
get { return maxObjectSizeMB ; }
set
{
if (( value . HasValue ) && ( value . Value < 0 ))
{
throw new ArgumentException (
PSRemotingErrorInvariants . FormatResourceString ( RemotingErrorIdStrings . CSCDoubleParameterOutOfRange ,
value . Value , "MaximumReceivedObjectSizeMB" )
);
}
maxObjectSizeMB = value ;
isMaxObjectSizeMBSpecified = true ;
}
}
internal Nullable < double > maxObjectSizeMB ;
internal bool isMaxObjectSizeMBSpecified ;
/// <summary>
/// This enables the user to specify an SDDL on the shell.
/// The default SDDL is the default used by Wsman.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sddl")]
[Parameter()]
public string SecurityDescriptorSddl
{
get { return sddl ; }
set
{
if (! string . IsNullOrEmpty ( value ))
{
// this will validate if the sddl is valid.
CommonSecurityDescriptor c = new CommonSecurityDescriptor ( false , false , value );
// this will never be the case..as constructor either constructs or throws.
// this is used here to avoid FxCop violation.
if ( null == c )
{
throw new NotSupportedException ();
}
}
sddl = value ;
isSddlSpecified = true ;
}
}
internal string sddl ;
internal bool isSddlSpecified ;
/// <summary>
/// Shows a UI to choose permissions/access rights for this session configuration.
/// </summary>
[Parameter()]
public SwitchParameter ShowSecurityDescriptorUI
{
2016-07-29 13:02:49 -07:00
get { return _showUI ; }
2016-03-30 14:20:52 -07:00
set
{
2016-07-29 13:02:49 -07:00
_showUI = value ;
2016-03-30 14:20:52 -07:00
showUISpecified = true ;
}
}
2016-07-29 13:02:49 -07:00
private bool _showUI ;
2016-03-30 14:20:52 -07:00
internal bool showUISpecified ;
/// <summary>
/// Property that sets force parameter. This will allow
/// restarting the WinRM service after the changes were
/// made.
/// </summary>
[Parameter()]
public SwitchParameter Force
{
get { return force ; }
set { force = value ; }
}
internal bool force ;
/// <summary>
/// If true, then the cmdlet will not attempt to restart
/// WinRM service after completion. Typically WinRM service
/// need to be restarted for changes to take place.
/// </summary>
[Parameter()]
public SwitchParameter NoServiceRestart
{
get { return noRestart ; }
set { noRestart = value ; }
}
internal bool noRestart ;
/// <summary>
/// Property corresponding to PSVersion parameter in the ConfigXML. This is treated as the minimum PowerShell version to load.
/// This will allow existing endpoints creating during migration or upgrade that have PSVersion=2.0 to roll forward to PowerShell 3.0 automatically without changing the ConfigXML.
/// </summary>
[Parameter(ParameterSetName = NameParameterSetName)]
[Parameter(ParameterSetName = AssemblyNameParameterSetName)]
[Alias("PowerShellVersion")]
[ValidateNotNullOrEmpty]
public Version PSVersion
{
get { return psVersion ; }
set
{
CheckPSVersion ( value );
// Check if specified version of PowerShell is installed
PSSessionConfigurationCommandUtilities . CheckIfPowerShellVersionIsInstalled ( value );
psVersion = value ;
isPSVersionSpecified = true ;
}
}
internal Version psVersion ;
internal bool isPSVersionSpecified ;
/// <summary>
/// SessionTypeOption
/// </summary>
[Parameter(ParameterSetName = NameParameterSetName)]
[Parameter(ParameterSetName = AssemblyNameParameterSetName)]
public PSSessionTypeOption SessionTypeOption
{
get
{
return sessionTypeOption ;
}
set
{
sessionTypeOption = value ;
}
}
internal PSSessionTypeOption sessionTypeOption ;
/// <summary>
/// TransportOption
/// </summary>
[Parameter]
public PSTransportOption TransportOption
{
get
{
return transportOption ;
}
set
{
transportOption = value ;
}
}
internal PSTransportOption transportOption ;
/// <summary>
/// ModulesToImport
/// </summary>
[Parameter(ParameterSetName = NameParameterSetName)]
[Parameter(ParameterSetName = AssemblyNameParameterSetName)]
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
public object [] ModulesToImport
{
get
{
return modulesToImport ;
}
set
{
List < object > modulesToImportList = new List < object >();
if ( value != null )
{
foreach ( var s in value )
{
var hashtable = s as Hashtable ;
if ( hashtable != null )
{
var moduleSpec = new ModuleSpecification ( hashtable );
if ( moduleSpec != null )
{
modulesToImportList . Add ( moduleSpec );
}
continue ;
}
// Getting the string value of the object if it is not a ModuleSpecification, this is required for the cases like PSObject is specified as ModulesToImport value.
string modulepath = s . ToString ();
// Add this check after checking if it a path
if (! String . IsNullOrEmpty ( modulepath . Trim ()))
{
if (( modulepath . Contains ( "\\" ) || modulepath . Contains ( ":" )) &&
!( Directory . Exists ( modulepath ) || File . Exists ( modulepath )))
{
throw new ArgumentException (
StringUtil . Format (
RemotingErrorIdStrings . InvalidRegisterPSSessionConfigurationModulePath ,
modulepath ));
}
modulesToImportList . Add ( modulepath );
}
}
}
modulesToImport = modulesToImportList . ToArray ();
modulePathSpecified = true ;
}
}
internal object [] modulesToImport ;
internal bool modulePathSpecified = false ;
/// <summary>
/// Declaration initial session config file path
/// </summary>
[Parameter(Mandatory = true, ParameterSetName = SessionConfigurationFileParameterSetName)]
[ValidateNotNullOrEmpty]
2016-08-02 13:29:09 -07:00
public string Path { get ; set ; }
2016-03-30 14:20:52 -07:00
// Other helper variables that come along with the path
/// <summary>
/// </summary>
2016-08-02 13:29:09 -07:00
protected bool RunAsVirtualAccount { get ; set ; } = false ;
2016-03-30 14:20:52 -07:00
/// <summary>
/// </summary>
2016-08-02 13:29:09 -07:00
protected bool RunAsVirtualAccountSpecified { get ; set ; } = false ;
2016-03-30 14:20:52 -07:00
/// <summary>
/// Comma delimited string specifying groups a virtual account is associated with
/// </summary>
2016-08-02 13:29:09 -07:00
protected string RunAsVirtualAccountGroups { get ; set ; }
2016-03-30 14:20:52 -07:00
#endregion
#region Constructor
/// <summary>
/// This is internal to make 3rd parties not derive from this cmdlet.
/// </summary>
internal PSSessionConfigurationCommandBase ()
{
}
#endregion
}
#endregion
#region Unregister - PSSessionConfiguration cmdlet
/// <summary>
/// Class implemeting Unregister-PSSessionConfiguration
/// </summary>
[Cmdlet(VerbsLifecycle.Unregister, RemotingConstants.PSSessionConfigurationNoun,
SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.Low, HelpUri = "http://go.microsoft.com/fwlink/?LinkID=144308")]
public sealed class UnregisterPSSessionConfigurationCommand : PSCmdlet
{
#region Private Data
// To Escape " -- ""
private const string removePluginSbFormat = @"
function Unregister-PSSessionConfiguration
{{
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact=""Low"")]
param(
$filter,
$action,
$targetTemplate,
$shellNotErrMsgFormat,
[bool]$force)
begin
{{
}}
process
{{
$shellsFound = 0
Get-ChildItem 'WSMan:\localhost\Plugin\' -Force:$force | ? {{ $_.Name -like ""$filter"" }} | % {{
$pluginFileNamePath = join-path ""$($_.pspath)"" 'FileName'
if (!(test-path ""$pluginFileNamePath""))
{{
return
}}
$pluginFileName = get-item -literalpath ""$pluginFileNamePath""
if ((!$pluginFileName) -or ($pluginFileName.Value -notmatch '{0}'))
{{
return
}}
$shellsFound++
$shouldProcessTargetString = $targetTemplate -f $_.Name
$DISCConfigFilePath = [System.IO.Path]::Combine($_.PSPath, ""InitializationParameters"")
$DISCConfigFile = get-childitem -literalpath ""$DISCConfigFilePath"" | ? {{$_.Name -like ""configFilePath""}}
if($DISCConfigFile -ne $null)
{{
if(test-path -LiteralPath ""$($DISCConfigFile.Value)"") {{
remove-item -literalpath ""$($DISCConfigFile.Value)"" -recurse -force -confirm:$false
}}
}}
if($force -or $pscmdlet.ShouldProcess($shouldProcessTargetString, $action))
{{
remove-item -literalpath ""$($_.pspath)"" -recurse -force -confirm:$false
}}
}}
if (!$shellsFound)
{{
$errMsg = $shellNotErrMsgFormat -f $filter
Write-Error $errMsg
}}
}} # end of Process block
}}
if ($args[7] -eq $null)
{{
Unregister-PSSessionConfiguration -filter $args[0] -whatif:$args[1] -confirm:$args[2] -action $args[3] -targetTemplate $args[4] -shellNotErrMsgFormat $args[5] -force $args[6]
}}
else
{{
Unregister-PSSessionConfiguration -filter $args[0] -whatif:$args[1] -confirm:$args[2] -action $args[3] -targetTemplate $args[4] -shellNotErrMsgFormat $args[5] -force $args[6] -erroraction $args[7]
}}
" ;
2016-07-29 13:02:49 -07:00
private static readonly ScriptBlock s_removePluginSb ;
private bool _isErrorReported ;
2016-03-30 14:20:52 -07:00
#endregion
#region Parameters
/// <summary>
/// This parameter enables the user to specify a shell name to
/// remove.
/// </summary>
[Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true)]
[ValidateNotNullOrEmpty]
2016-08-02 13:29:09 -07:00
public string Name { get ; set ; }
2016-03-30 14:20:52 -07:00
/// <summary>
/// Property that sets force parameter. This will allow
/// restarting the WinRM service after the changes were
/// made.
/// </summary>
[Parameter()]
public SwitchParameter Force
{
get
{
2016-07-29 13:02:49 -07:00
return _force ;
2016-03-30 14:20:52 -07:00
}
set
{
2016-07-29 13:02:49 -07:00
_force = value ;
2016-03-30 14:20:52 -07:00
}
}
2016-07-29 13:02:49 -07:00
private bool _force ;
2016-03-30 14:20:52 -07:00
/// <summary>
/// If true, then the cmdlet will not attempt to restart
/// WinRM service after completion. Typically WinRM service
/// need to be restarted for changes to take place.
/// </summary>
[Parameter()]
public SwitchParameter NoServiceRestart
{
get
{
2016-07-29 13:02:49 -07:00
return _noRestart ;
2016-03-30 14:20:52 -07:00
}
set
{
2016-07-29 13:02:49 -07:00
_noRestart = value ;
2016-03-30 14:20:52 -07:00
}
}
2016-07-29 13:02:49 -07:00
private bool _noRestart ;
2016-03-30 14:20:52 -07:00
#endregion
#region Constructors
static UnregisterPSSessionConfigurationCommand ()
{
string removePluginScript = string . Format ( CultureInfo . InvariantCulture ,
removePluginSbFormat ,
RemotingConstants . PSPluginDLLName );
// compile the script block statically and reuse the same instance
// everytime the command is run..This will save on parsing time.
2016-07-29 13:02:49 -07:00
s_removePluginSb = ScriptBlock . Create ( removePluginScript );
s_removePluginSb . LanguageMode = PSLanguageMode . FullLanguage ;
2016-03-30 14:20:52 -07:00
}
#endregion
#region Cmdlet Overrides
/// <summary>
/// Verifies if remoting cmdlets can be used
/// </summary>
protected override void BeginProcessing ()
{
RemotingCommandUtil . CheckRemotingCmdletPrerequisites ();
PSSessionConfigurationCommandUtilities . ThrowIfNotAdministrator ();
}
/// <summary>
/// </summary>
protected override void ProcessRecord ()
{
WriteVerbose ( StringUtil . Format ( RemotingErrorIdStrings . RcsScriptMessageV , removePluginSbFormat ));
string action = StringUtil . Format ( RemotingErrorIdStrings . CSShouldProcessAction ,
this . CommandInfo . Name );
string targetTemplate = RemotingErrorIdStrings . CSShouldProcessTarget ;
string csNotFoundMessageFormat = RemotingErrorIdStrings . CustomShellNotFound ;
// gather -WhatIf, -Confirm parameter data and pass it to the script block
bool whatIf = false ;
// confirm is always true to start with
bool confirm = true ;
PSSessionConfigurationCommandUtilities . CollectShouldProcessParameters ( this , out whatIf , out confirm );
// gather -ErrorAction parameter data and pass it to the script block. if -ErrorAction is not set, pass $null in
object errorAction = null ;
if ( Context . CurrentCommandProcessor . CommandRuntime . IsErrorActionSet )
{
errorAction = Context . CurrentCommandProcessor . CommandRuntime . ErrorAction ;
}
ArrayList errorList = ( ArrayList ) Context . DollarErrorVariable ;
int errorCountBefore = errorList . Count ;
2016-07-29 13:02:49 -07:00
s_removePluginSb . InvokeUsingCmdlet (
2016-03-30 14:20:52 -07:00
contextCmdlet : this ,
useLocalScope : true ,
errorHandlingBehavior : ScriptBlock . ErrorHandlingBehavior . WriteToCurrentErrorPipe ,
dollarUnder : AutomationNull . Value ,
input : Utils . EmptyArray < object >(),
scriptThis : AutomationNull . Value ,
args : new object [] {
2016-08-02 13:29:09 -07:00
Name ,
2016-03-30 14:20:52 -07:00
whatIf ,
confirm ,
action ,
targetTemplate ,
csNotFoundMessageFormat ,
2016-07-29 13:02:49 -07:00
_force ,
2016-03-30 14:20:52 -07:00
errorAction
});
errorList = ( ArrayList ) Context . DollarErrorVariable ;
2016-07-29 13:02:49 -07:00
_isErrorReported = errorList . Count > errorCountBefore ;
2016-03-30 14:20:52 -07:00
}
/// <summary>
/// </summary>
protected override void EndProcessing ()
{
System . Management . Automation . Tracing . Tracer tracer = new System . Management . Automation . Tracing . Tracer ();
tracer . EndpointUnregistered ( this . Name , WindowsIdentity . GetCurrent (). Name );
}
#endregion
}
#endregion
#region Get - PSSessionConfiguration cmdlet
/// <summary>
/// Class implemeting Get-PSSessionConfiguration
/// </summary>
[Cmdlet(VerbsCommon.Get, RemotingConstants.PSSessionConfigurationNoun, HelpUri = "http://go.microsoft.com/fwlink/?LinkID=144304")]
[OutputType("Microsoft.PowerShell.Commands.PSSessionConfigurationCommands#PSSessionConfiguration")]
public sealed class GetPSSessionConfigurationCommand : PSCmdlet
{
#region Private Data
// To Escape { -- {{
// To Escape } -- }}
// To Escape " -- ""
private const string getPluginSbFormat = @"
function ExtractPluginProperties([string]$pluginDir, $objectToWriteTo)
{{
function Unescape-Xml($s) {{
if ($s) {{
$s = $s.Replace(""<"", ""<"");
$s = $s.Replace("">"", "">"");
$s = $s.Replace(""""", '""');
$s = $s.Replace(""'"", ""'"");
$s = $s.Replace(""'"", ""'"");
$s = $s.Replace(""&"", ""&"");
}}
return $s;
}}
# The default comparer is case insensitive and it is supported on Core CLR.
$h = new-object system.collections.hashtable
function Get-Details([string]$path, [hashtable]$h) {{
foreach ($o in (get-childitem -LiteralPath $path)) {{
if ($o.PSIsContainer) {{
Get-Details $o.PSPath $h
}} else {{
$h[$o.Name] = $o.Value
}}
}}
}}
Get-Details $pluginDir $h
if ($h[""AssemblyName""] -eq ""Microsoft.PowerShell.Workflow.ServiceCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"") {{
$serviceCore = [Reflection.Assembly]::Load(""Microsoft.Powershell.Workflow.ServiceCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"")
if ($serviceCore -ne $null) {{
$ci = new-Object system.management.automation.cmdletinfo ""New-PSWorkflowExecutionOptions"", ([Microsoft.PowerShell.Commands.NewPSWorkflowExecutionOptionCommand])
$wf = [powershell]::Create(""currentrunspace"").AddCommand($ci).Invoke()
if($wf -ne $null -and $wf.Count -ne 0) {{
$wf = $wf[0]
foreach ($o in $wf.GetType().GetProperties()) {{
$h[$o.Name] = $o.GetValue($wf, $null)
}}
}}
}}
}}
if (test-path -LiteralPath $pluginDir\InitializationParameters\SessionConfigurationData) {{
$xscd = [xml](Unescape-xml (Unescape-xml (get-item -LiteralPath $pluginDir\InitializationParameters\SessionConfigurationData).Value))
foreach ($o in $xscd.SessionConfigurationData.Param) {{
if ($o.Name -eq ""PrivateData"") {{
foreach($wf in $o.PrivateData.Param) {{
$h[$wf.Name] = $wf.Value
}}
}} else {{
$h[$o.Name] = $o.Value
}}
}}
}}
## Extract DISC related information
if(test-path -LiteralPath $pluginDir\InitializationParameters\ConfigFilePath) {{
$DISCFilePath = (get-item -LiteralPath $pluginDir\InitializationParameters\ConfigFilePath).Value
if(test-path -LiteralPath $DISCFilePath) {{
$DISCFileContent = get-content $DISCFilePath | out-string
$DISCHash = invoke-expression $DISCFileContent
foreach ($o in $DISCHash.Keys) {{
if ($o -ne ""PowerShellVersion"") {{
$objectToWriteTo = $objectToWriteTo | add-member -membertype noteproperty -name $o -value $DISCHash[$o] -force -passthru
}}
}}
}}
}}
if ($h[""SessionConfigurationData""]) {{
$h[""SessionConfigurationData""] = Unescape-Xml (Unescape-Xml $h[""SessionConfigurationData""])
}}
foreach ($o in $h.Keys) {{
if ($o -eq 'sddl') {{
$objectToWriteTo = $objectToWriteTo | add-member -membertype noteproperty -name 'SecurityDescriptorSddl' -value $h[$o] -force -passthru
}} else {{
$objectToWriteTo = $objectToWriteTo | add-member -membertype noteproperty -name $o -value $h[$o] -force -passthru
}}
}}
}}
$shellNotErrMsgFormat = $args[1]
$force = $args[2]
$args[0] | foreach {{
$shellsFound = 0;
$filter = $_
Get-ChildItem 'WSMan:\localhost\Plugin\' -Force:$force | ? {{ $_.name -like ""$filter"" }} | foreach {{
$customPluginObject = new-object object
$customPluginObject.pstypenames.Insert(0, '{0}')
ExtractPluginProperties ""$($_.PSPath)"" $customPluginObject
# this is powershell based custom shell only if its plugin dll is pwrshplugin.dll
if (($customPluginObject.FileName) -and ($customPluginObject.FileName -match '{1}'))
{{
$shellsFound++
$customPluginObject
}}
}} # end of foreach
if (!$shellsFound -and !([System.Management.Automation.WildcardPattern]::ContainsWildcardCharacters($_)))
{{
$errMsg = $shellNotErrMsgFormat -f $_
Write-Error $errMsg
}}
}}
" ;
private const string MODULEPATH = "ModulesToImport" ;
2016-07-29 13:02:49 -07:00
private static readonly ScriptBlock s_getPluginSb ;
2016-03-30 14:20:52 -07:00
#endregion
#region Constructors
static GetPSSessionConfigurationCommand ()
{
string scriptToRun = string . Format ( CultureInfo . InvariantCulture ,
getPluginSbFormat ,
PSSessionConfigurationCommandUtilities . PSCustomShellTypeName ,
RemotingConstants . PSPluginDLLName );
// compile the script block statically and reuse the same instance
// everytime the command is run..This will save on parsing time.
2016-07-29 13:02:49 -07:00
s_getPluginSb = ScriptBlock . Create ( scriptToRun );
s_getPluginSb . LanguageMode = PSLanguageMode . FullLanguage ;
2016-03-30 14:20:52 -07:00
}
#endregion
#region Parameters
/// <summary>
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
[Parameter(Position = 0, Mandatory = false)]
2016-08-02 13:29:09 -07:00
public string [] Name { get ; set ; }
2016-03-30 14:20:52 -07:00
/// <summary>
/// Force parameter
/// </summary>
[Parameter()]
public SwitchParameter Force
{
get
{
2016-07-29 13:02:49 -07:00
return _force ;
2016-03-30 14:20:52 -07:00
}
set
{
2016-07-29 13:02:49 -07:00
_force = value ;
2016-03-30 14:20:52 -07:00
}
}
2016-07-29 13:02:49 -07:00
private bool _force ;
2016-03-30 14:20:52 -07:00
#endregion
#region Cmdlet Overrides
/// <summary>
/// Verifies if remoting cmdlets can be used
/// </summary>
protected override void BeginProcessing ()
{
RemotingCommandUtil . CheckRemotingCmdletPrerequisites ();
PSSessionConfigurationCommandUtilities . ThrowIfNotAdministrator ();
}
/// <summary>
/// </summary>
protected override void ProcessRecord ()
{
WriteVerbose ( StringUtil . Format ( RemotingErrorIdStrings . GcsScriptMessageV , getPluginSbFormat ));
string csNotFoundMessageFormat = RemotingErrorIdStrings . CustomShellNotFound ;
object arguments = "*" ;
2016-08-02 13:29:09 -07:00
if ( null != Name )
2016-03-30 14:20:52 -07:00
{
2016-08-02 13:29:09 -07:00
arguments = Name ;
2016-03-30 14:20:52 -07:00
}
ActionPreference backupPreference = Context . ErrorActionPreferenceVariable ;
try
{
if ( Context . CurrentCommandProcessor . CommandRuntime . IsErrorActionSet )
{
Context . ErrorActionPreferenceVariable = Context . CurrentCommandProcessor . CommandRuntime . ErrorAction ;
}
2016-07-29 13:02:49 -07:00
s_getPluginSb . InvokeUsingCmdlet (
2016-03-30 14:20:52 -07:00
contextCmdlet : this ,
useLocalScope : true ,
errorHandlingBehavior : ScriptBlock . ErrorHandlingBehavior . WriteToCurrentErrorPipe ,
dollarUnder : AutomationNull . Value ,
input : Utils . EmptyArray < object >(),
scriptThis : AutomationNull . Value ,
args : new object [] {
arguments ,
csNotFoundMessageFormat ,
2016-07-29 13:02:49 -07:00
_force }
2016-03-30 14:20:52 -07:00
);
}
finally
{
Context . ErrorActionPreferenceVariable = backupPreference ;
}
}
#endregion
}
#endregion
#region Set - PSSessionConfiguration cmdlet
/// <summary>
/// Class implemeting Set-PSSessionConfiguration
/// </summary>
[Cmdlet(VerbsCommon.Set, RemotingConstants.PSSessionConfigurationNoun,
DefaultParameterSetName = PSSessionConfigurationCommandBase.NameParameterSetName,
SupportsShouldProcess = true,
ConfirmImpact = ConfirmImpact.Medium, HelpUri = "http://go.microsoft.com/fwlink/?LinkID=144307")]
public sealed class SetPSSessionConfigurationCommand : PSSessionConfigurationCommandBase
{
#region Private Data
// To Escape { -- {{
// To Escape } -- }}
// To Escape " -- ""
private const string getSessionTypeFormat = @"(get-item 'WSMan::localhost\Plugin\{0}\InitializationParameters\sessiontype' -ErrorAction SilentlyContinue).Value" ;
private const string getCurrentIdleTimeoutmsFormat = @"(Get-Item 'WSMan:\localhost\Plugin\{0}\Quotas\IdleTimeoutms').Value" ;
private const string getAssemblyNameDataFormat = @"(Get-Item 'WSMan:\localhost\Plugin\{0}\InitializationParameters\assemblyname').Value" ;
private const string getSessionConfigurationDataSbFormat = @"(Get-Item 'WSMan:\localhost\Plugin\{0}\InitializationParameters\SessionConfigurationData').Value" ;
private const string setSessionConfigurationDataSbFormat = @"
function Set-SessionConfigurationData([string] $scd) {{
if (test-path 'WSMan:\localhost\Plugin\{0}\InitializationParameters\" + ConfigurationDataFromXML . SESSIONCONFIGTOKEN + @"')
{{
set-item -WarningAction SilentlyContinue -Force 'WSMan:\localhost\Plugin\{0}\InitializationParameters\" + ConfigurationDataFromXML . SESSIONCONFIGTOKEN + @"' -Value $scd
}}
else
{{
new-item -WarningAction SilentlyContinue -path 'WSMan:\localhost\Plugin\{0}\InitializationParameters' -paramname " + ConfigurationDataFromXML . SESSIONCONFIGTOKEN + @" -paramValue $scd -Force
}}
}}
Set-SessionConfigurationData $args[0]
" ;
private const string setSessionConfigurationQuotaSbFormat = @"
function Set-SessionPluginQuota([hashtable] $quotas) {{
foreach($v in $quotas.GetEnumerator()) {{
$name = $v.Name;
$value = $v.Value;
if (!$value) {{
$value = [string]::empty;
}}
set-item -WarningAction SilentlyContinue ('WSMan:\localhost\Plugin\{0}\Quotas\' + $name) -Value $value -confirm:$false
}}
}}
Set-SessionPluginQuota $args[0]
" ;
private const string setSessionConfigurationTimeoutQuotasSbFormat = @"
function Set-SessionPluginIdleTimeoutQuotas([int] $maxIdleTimeoutms, [int] $idleTimeoutms, [bool] $setMaxIdleTimoutFirst) {{
if ($setMaxIdleTimoutFirst) {{
set-item -WarningAction SilentlyContinue 'WSMan:\localhost\Plugin\{0}\Quotas\MaxIdleTimeoutms' -Value $maxIdleTimeoutms -confirm:$false
set-item -WarningAction SilentlyContinue 'WSMan:\localhost\Plugin\{0}\Quotas\IdleTimeoutms' -Value $idleTimeoutms -confirm:$false
}}
else {{
set-item -WarningAction SilentlyContinue 'WSMan:\localhost\Plugin\{0}\Quotas\IdleTimeoutms' -Value $idleTimeoutms -confirm:$false
set-item -WarningAction SilentlyContinue 'WSMan:\localhost\Plugin\{0}\Quotas\MaxIdleTimeoutms' -Value $maxIdleTimeoutms -confirm:$false
}}
}}
Set-SessionPluginIdleTimeoutQuotas $args[0] $args[1] $args[2]
" ;
private const string setSessionConfigurationOptionsSbFormat = @"
function Set-SessionPluginOptions([hashtable] $options) {{
if ($options[""UsedSharedProcess""]) {{
$value = $options[""UseSharedProcess""];
set-item -WarningAction SilentlyContinue 'WSMan:\localhost\Plugin\{0}\UseSharedProcess' -Value $value -confirm:$false
$options.Remove(""UseSharedProcess"");
}}
foreach($v in $options.GetEnumerator()) {{
$name = $v.Name;
$value = $v.Value
if (!$value) {{
$value = 0;
}}
set-item -WarningAction SilentlyContinue ('WSMan:\localhost\Plugin\{0}\' + $name) -Value $value -confirm:$false
}}
}}
Set-SessionPluginOptions $args[0]
" ;
private const string setRunAsSbFormat = @"
function Set-RunAsCredential{{
param (
[string]$runAsUserName,
[system.security.securestring]$runAsPassword
)
$cred = new-object System.Management.Automation.PSCredential($runAsUserName, $runAsPassword)
set-item -WarningAction SilentlyContinue 'WSMan:\localhost\Plugin\{0}\RunAsUser' $cred -confirm:$false
}}
Set-RunAsCredential $args[0] $args[1]
" ;
private const string setPluginSbFormat = @"
function Set-PSSessionConfiguration([PSObject]$customShellObject,
[Array]$initParametersMap,
[bool]$force,
[string]$sddl,
[bool]$isSddlSpecified,
[bool]$shouldShowUI,
[string]$resourceUri,
[string]$pluginNotFoundErrorMsg,
[string]$pluginNotPowerShellMsg,
[System.Management.Automation.Runspaces.PSSessionConfigurationAccessMode]$accessMode
)
{{
$wsmanPluginDir = 'WSMan:\localhost\Plugin'
$pluginName = $customShellObject.Name;
$pluginDir = Join-Path ""$wsmanPluginDir"" ""$pluginName""
if ((!$pluginName) -or !(test-path ""$pluginDir""))
{{
Write-Error $pluginNotFoundErrorMsg
return
}}
# check if the plugin is a PowerShell plugin
$pluginFileNamePath = Join-Path ""$pluginDir"" 'FileName'
if (!(test-path ""$pluginFileNamePath""))
{{
Write-Error $pluginNotPowerShellMsg
return
}}
$pluginFileName = get-item -literalpath ""$pluginFileNamePath""
if ((!$pluginFileName) -or ($pluginFileName.Value -notmatch '{0}'))
{{
Write-Error $pluginNotPowerShellMsg
return
}}
# set Initialization Parameters
$initParametersPath = Join-Path ""$pluginDir"" 'InitializationParameters'
foreach($initParameterName in $initParametersMap)
{{
if ($customShellObject | get-member $initParameterName)
{{
$parampath = Join-Path ""$initParametersPath"" $initParameterName
if (test-path $parampath)
{{
remove-item -path ""$parampath""
}}
# 0 is an accepted value for MaximumReceivedDataSizePerCommandMB and MaximumReceivedObjectSizeMB
if (($customShellObject.$initParameterName) -or ($customShellObject.$initParameterName -eq 0))
{{
new-item -path ""$initParametersPath"" -paramname $initParameterName -paramValue ""$($customShellObject.$initParameterName)"" -Force
}}
}}
}}
# sddl processing
if ($isSddlSpecified)
{{
$resourcesPath = Join-Path ""$pluginDir"" 'Resources'
Get-ChildItem -literalpath ""$resourcesPath"" | % {{
$securityPath = Join-Path ""$($_.pspath)"" 'Security'
if ((@(Get-ChildItem -literalpath ""$securityPath"")).count -gt 0)
{{
Get-ChildItem -literalpath ""$securityPath"" | % {{
$securityIDPath = ""$($_.pspath)""
remove-item -path ""$securityIDPath"" -recurse -force
}} #end of securityPath
if ($sddl)
{{
new-item -path ""$securityPath"" -Sddl $sddl -force
}}
}}
else
{{
if ($sddl)
{{
new-item -path ""$securityPath"" -Sddl $sddl -force
}}
}}
}} # end of resources
return
}} #end of sddl processing
elseif ($shouldShowUI)
{{
$null = winrm configsddl $resourceUri
}}
# If accessmode is 'Disabled', we don't bother to check the sddl
if ([System.Management.Automation.Runspaces.PSSessionConfigurationAccessMode]::Disabled.Equals($accessMode))
{{
return
}}
# Construct SID for network users
[system.security.principal.wellknownsidtype]$evst = ""NetworkSid""
$networkSID = new-object system.security.principal.securityidentifier $evst,$null
$resPath = Join-Path ""$pluginDir"" 'Resources'
Get-ChildItem -literalpath ""$resPath"" | % {{
$securityPath = Join-Path ""$($_.pspath)"" 'Security'
if ((@(Get-ChildItem -literalpath ""$securityPath"")).count -gt 0)
{{
Get-ChildItem -literalpath ""$securityPath"" | % {{
$sddlPath = Join-Path ""$($_.pspath)"" 'Sddl'
$curSDDL = (get-item -path $sddlPath).value
$sd = new-object system.security.accesscontrol.commonsecuritydescriptor $false,$false,$curSDDL
$newSDDL = $null
$disableNetworkExists = $false
$securityIdentifierToPurge = $null
$sd.DiscretionaryAcl | % {{
if (($_.acequalifier -eq ""accessdenied"") -and ($_.securityidentifier -match $networkSID) -and ($_.AccessMask -eq 268435456))
{{
$disableNetworkExists = $true
$securityIdentifierToPurge = $_.securityidentifier
}}
}}
if ([System.Management.Automation.Runspaces.PSSessionConfigurationAccessMode]::Local.Equals($accessMode) -and !$disableNetworkExists)
{{
$sd.DiscretionaryAcl.AddAccess(""deny"", $networkSID, 268435456, ""None"", ""None"")
$newSDDL = $sd.GetSddlForm(""all"")
}}
if ([System.Management.Automation.Runspaces.PSSessionConfigurationAccessMode]::Remote.Equals($accessMode) -and $disableNetworkExists)
{{
# Remove the specific ACE
$sd.discretionaryacl.RemoveAccessSpecific('Deny', $securityIdentifierToPurge, 268435456, 'none', 'none')
# if there is no discretionaryacl..add Builtin Administrators and Remote Management Users
# to the DACL group as this is the default WSMan behavior
if ($sd.discretionaryacl.count -eq 0)
{{
# Built-in administrators
[system.security.principal.wellknownsidtype]$bast = ""BuiltinAdministratorsSid""
$basid = new-object system.security.principal.securityidentifier $bast,$null
$sd.DiscretionaryAcl.AddAccess('Allow',$basid, 268435456, 'none', 'none')
# Remote Management Users, Win8+ only
if ([System.Environment]::OSVersion.Version -ge ""6.2.0.0"")
{{
$rmSidId = new-object system.security.principal.securityidentifier ""{2}""
$sd.DiscretionaryAcl.AddAccess('Allow', $rmSidId, 268435456, 'none', 'none')
}}
# Interactive Users
$iuSidId = new-object system.security.principal.securityidentifier ""{3}""
$sd.DiscretionaryAcl.AddAccess('Allow', $iuSidId, 268435456, 'none', 'none')
}}
$newSDDL = $sd.GetSddlForm(""all"")
}}
if ($newSDDL)
{{
set-item -WarningAction SilentlyContinue -path $sddlPath -value $newSDDL -force
}}
}}
}}
else
{{
if (([System.Management.Automation.Runspaces.PSSessionConfigurationAccessMode]::Local.Equals($accessMode)))
{{
new-item -path ""$securityPath"" -Sddl ""{1}"" -force
}}
}}
}}
}}
Set-PSSessionConfiguration $args[0] $args[1] $args[2] $args[3] $args[4] $args[5] $args[6] $args[7] $args[8] $args[9]
" ;
private const string initParamFormat = @"<Param Name='{0}' Value='{1}' />" ;
private const string privateDataFormat = @"<Param Name='PrivateData'>{0}</Param>" ;
private const string SessionConfigDataFormat = @"<SessionConfigurationData>{0}</SessionConfigurationData>" ;
private const string UseSharedProcessToken = "UseSharedProcess" ;
private const string AllowRemoteAccessToken = "Enabled" ;
2016-07-29 13:02:49 -07:00
private static readonly ScriptBlock s_setPluginSb ;
2016-03-30 14:20:52 -07:00
// property names used by the script to update InitParameters.
2016-07-29 13:02:49 -07:00
private static readonly string [] s_initParametersMap = new string [] {
2016-03-30 14:20:52 -07:00
ConfigurationDataFromXML . APPBASETOKEN ,
ConfigurationDataFromXML . ASSEMBLYTOKEN ,
ConfigurationDataFromXML . SHELLCONFIGTYPETOKEN ,
ConfigurationDataFromXML . STARTUPSCRIPTTOKEN ,
ConfigurationDataFromXML . MAXRCVDOBJSIZETOKEN ,
ConfigurationDataFromXML . MAXRCVDCMDSIZETOKEN ,
ConfigurationDataFromXML . THREADOPTIONSTOKEN ,
#if ! CORECLR // No ApartmentState In CoreCLR
ConfigurationDataFromXML . THREADAPTSTATETOKEN ,
#endif
ConfigurationDataFromXML . PSVERSIONTOKEN ,
ConfigurationDataFromXML . MAXPSVERSIONTOKEN ,
ConfigurationDataFromXML . SESSIONCONFIGTOKEN ,
};
2016-07-29 13:02:49 -07:00
private bool _isErrorReported ;
2016-03-30 14:20:52 -07:00
2016-07-29 13:02:49 -07:00
private Hashtable _configTable ;
private string _configFilePath ;
private string _gmsaAccount ;
private string _configSddl ;
2016-03-30 14:20:52 -07:00
#endregion
#region Constructors
static SetPSSessionConfigurationCommand ()
{
string localSDDL = GetLocalSddl ();
string setPluginScript = string . Format ( CultureInfo . InvariantCulture ,
setPluginSbFormat ,
RemotingConstants . PSPluginDLLName , localSDDL , RemoteManagementUsersSID , InteractiveUsersSID );
// compile the script block statically and reuse the same instance
// everytime the command is run..This will save on parsing time.
2016-07-29 13:02:49 -07:00
s_setPluginSb = ScriptBlock . Create ( setPluginScript );
s_setPluginSb . LanguageMode = PSLanguageMode . FullLanguage ;
2016-03-30 14:20:52 -07:00
}
#endregion
#region Cmdlet overrides
/// <summary>
///
/// </summary>
/// <exception cref="InvalidOperationException">
/// 1. Either both "AssemblyName" and "ConfigurationTypeName" must be specified
/// or both must not be specified.
/// </exception>
protected override void BeginProcessing ()
{
if ( isSddlSpecified && showUISpecified )
{
string message = StringUtil . Format ( RemotingErrorIdStrings . ShowUIAndSDDLCannotExist ,
"SecurityDescriptorSddl" ,
"ShowSecurityDescriptorUI" );
throw new PSInvalidOperationException ( message );
}
if ( isRunAsCredentialSpecified )
{
WriteWarning ( RemotingErrorIdStrings . RunAsSessionConfigurationSecurityWarning );
}
// Populate the configTable hash, and get its SDDL if needed
2016-07-29 13:02:49 -07:00
if ( Path != null )
2016-03-30 14:20:52 -07:00
{
ProviderInfo provider = null ;
PSDriveInfo drive ;
2016-07-29 13:02:49 -07:00
_configFilePath = SessionState . Path . GetUnresolvedProviderPathFromPSPath ( Path , out provider , out drive );
2016-03-30 14:20:52 -07:00
string scriptName ;
2016-07-29 13:02:49 -07:00
ExternalScriptInfo scriptInfo = DISCUtils . GetScriptInfoForFile ( this . Context , _configFilePath , out scriptName );
_configTable = DISCUtils . LoadConfigFile ( this . Context , scriptInfo );
2016-03-30 14:20:52 -07:00
if (! isSddlSpecified )
{
ErrorRecord error ;
2016-07-29 13:02:49 -07:00
_configSddl = PSSessionConfigurationCommandUtilities . ComputeSDDLFromConfiguration (
_configTable ,
2016-03-30 14:20:52 -07:00
AccessMode ,
out error );
if ( error != null )
{
WriteError ( error );
}
2016-07-29 13:02:49 -07:00
if (! string . IsNullOrEmpty ( _configSddl ))
2016-03-30 14:20:52 -07:00
{
// Use Sddl from configuration.
isSddlSpecified = true ;
2016-07-29 13:02:49 -07:00
SecurityDescriptorSddl = _configSddl ;
2016-03-30 14:20:52 -07:00
}
}
2016-07-29 13:02:49 -07:00
if ( _configTable . ContainsKey ( ConfigFileConstants . RunAsVirtualAccount ))
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
this . RunAsVirtualAccount = LanguagePrimitives . ConvertTo < bool >( _configTable [ ConfigFileConstants . RunAsVirtualAccount ]);
2016-03-30 14:20:52 -07:00
this . RunAsVirtualAccountSpecified = true ;
}
2016-07-29 13:02:49 -07:00
if ( _configTable . ContainsKey ( ConfigFileConstants . RunAsVirtualAccountGroups ))
2016-03-30 14:20:52 -07:00
{
this . RunAsVirtualAccountGroups = PSSessionConfigurationCommandUtilities . GetRunAsVirtualAccountGroupsString (
2016-07-29 13:02:49 -07:00
DISCPowerShellConfiguration . TryGetStringArray ( _configTable [ ConfigFileConstants . RunAsVirtualAccountGroups ]));
2016-03-30 14:20:52 -07:00
}
}
if ( isSddlSpecified && accessModeSpecified )
{
// Constructor call should succeed. The sddl is check in the property setter
CommonSecurityDescriptor descriptor = new CommonSecurityDescriptor ( false , false , sddl );
SecurityIdentifier networkSidIdentifier = new SecurityIdentifier ( WellKnownSidType . NetworkSid , null );
bool networkDenyAllExists = false ;
foreach ( CommonAce ace in descriptor . DiscretionaryAcl )
{
if ( ace . AceQualifier . Equals ( AceQualifier . AccessDenied ) && ace . SecurityIdentifier . Equals ( networkSidIdentifier ) && ace . AccessMask == 268435456 )
{
networkDenyAllExists = true ;
break ;
}
}
switch ( AccessMode )
{
case PSSessionConfigurationAccessMode . Local :
if (! networkDenyAllExists )
{
descriptor . DiscretionaryAcl . AddAccess ( AccessControlType . Deny , networkSidIdentifier , 268435456 , InheritanceFlags . None , PropagationFlags . None );
sddl = descriptor . GetSddlForm ( AccessControlSections . All );
}
break ;
case PSSessionConfigurationAccessMode . Remote :
if ( networkDenyAllExists )
{
// Remove the specific ACE
descriptor . DiscretionaryAcl . RemoveAccessSpecific ( AccessControlType . Deny , networkSidIdentifier , 268435456 , InheritanceFlags . None , PropagationFlags . None );
// If the discretionaryAcl becomes empty, add the BA and RM which is the default WinRM behavior
if ( descriptor . DiscretionaryAcl . Count == 0 )
{
sddl = GetRemoteSddl ();
}
else
{
sddl = descriptor . GetSddlForm ( AccessControlSections . All );
}
}
break ;
case PSSessionConfigurationAccessMode . Disabled :
break ;
}
}
// check if we have compatible WSMan
RemotingCommandUtil . CheckRemotingCmdletPrerequisites ();
PSSessionConfigurationCommandUtilities . ThrowIfNotAdministrator ();
}
/// <summary>
///
/// </summary>
protected override void ProcessRecord ()
{
WriteVerbose ( StringUtil . Format ( RemotingErrorIdStrings . ScsScriptMessageV , setPluginSbFormat ));
string shouldProcessAction = StringUtil . Format ( RemotingErrorIdStrings . CSShouldProcessAction ,
this . CommandInfo . Name );
string shouldProcessTarget ;
if (! isSddlSpecified )
{
shouldProcessTarget = StringUtil . Format ( RemotingErrorIdStrings . CSShouldProcessTarget ,
this . Name );
}
else
{
shouldProcessTarget = StringUtil . Format ( RemotingErrorIdStrings . ScsShouldProcessTargetSDDL ,
this . Name ,
sddl );
}
if (! noRestart && ! force )
{
string action = StringUtil . Format ( RemotingErrorIdStrings . CSShouldProcessAction ,
this . CommandInfo . Name );
WriteWarning ( StringUtil . Format ( RemotingErrorIdStrings . WinRMRestartWarning , action ));
}
if (! force && ! ShouldProcess ( shouldProcessTarget , shouldProcessAction ))
{
return ;
}
// Update the configuration to use a shared process
if ( isUseSharedProcessSpecified )
{
using ( System . Management . Automation . PowerShell ps = System . Management . Automation . PowerShell . Create ())
{
ps . AddScript ( string . Format ( CultureInfo . InvariantCulture , getSessionTypeFormat , CodeGeneration . EscapeSingleQuotedStringContent ( Name )));
Collection < PSObject > psObjectCollection = ps . Invoke ( new object [] { Name }) as Collection < PSObject >;
if ( psObjectCollection == null || psObjectCollection . Count != 1 )
{
Dbg . Assert ( false , "This should never happen. ps.Invoke always return a Collection<PSObject>" );
}
// UseSharedProcess Can not be False for Workflow session type configuration
//
if ( UseSharedProcess == false &&
psObjectCollection [ 0 ] != null &&
String . Compare ( psObjectCollection [ 0 ]. ToString (), "Workflow" , StringComparison . OrdinalIgnoreCase ) == 0 )
{
throw new PSInvalidOperationException ( RemotingErrorIdStrings . UseSharedProcessCannotBeFalseForWorkflowSessionType );
}
}
}
// Update the PSSC file if there was one
2016-07-29 13:02:49 -07:00
if ( _configTable != null )
2016-03-30 14:20:52 -07:00
{
Guid sessionGuid = Guid . Empty ;
// Extract the GUID for the configuration
2016-07-29 13:02:49 -07:00
if ( _configTable . ContainsKey ( ConfigFileConstants . Guid ))
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
sessionGuid = Guid . Parse ( _configTable [ ConfigFileConstants . Guid ]. ToString ());
2016-03-30 14:20:52 -07:00
}
// Extract the GMSA account name if available
2016-07-29 13:02:49 -07:00
if ( _configTable . ContainsKey ( ConfigFileConstants . GMSAAccount ))
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
_gmsaAccount = _configTable [ ConfigFileConstants . GMSAAccount ] as string ;
2016-03-30 14:20:52 -07:00
}
string destPath = System . IO . Path . Combine ( Utils . GetApplicationBase ( Utils . DefaultPowerShellShellID ), "SessionConfig" ,
shellName + "_" + sessionGuid . ToString () + StringLiterals . PowerShellDISCFileExtension );
// If the config file with the same guid name already exists then it would be overwritten.
2016-07-29 13:02:49 -07:00
File . Copy ( _configFilePath , destPath , true );
2016-03-30 14:20:52 -07:00
}
string shellNotFoundErrorMsg = StringUtil . Format ( RemotingErrorIdStrings . CSCmdsShellNotFound , shellName );
string shellNotPowerShellMsg = StringUtil . Format ( RemotingErrorIdStrings . CSCmdsShellNotPowerShellBased , shellName );
// construct object to update the properties
PSObject propertiesToUpdate = ConstructPropertiesForUpdate ();
// hack to see if there is any error reported running the script.
ArrayList errorList = ( ArrayList ) Context . DollarErrorVariable ;
int errorCountBefore = errorList . Count ;
2016-07-29 13:02:49 -07:00
s_setPluginSb . InvokeUsingCmdlet (
2016-03-30 14:20:52 -07:00
contextCmdlet : this ,
useLocalScope : true ,
errorHandlingBehavior : ScriptBlock . ErrorHandlingBehavior . WriteToCurrentErrorPipe ,
dollarUnder : AutomationNull . Value ,
input : Utils . EmptyArray < object >(),
scriptThis : AutomationNull . Value ,
args : new object [] {
propertiesToUpdate ,
2016-07-29 13:02:49 -07:00
s_initParametersMap ,
2016-03-30 14:20:52 -07:00
force ,
sddl ,
isSddlSpecified ,
ShowSecurityDescriptorUI . ToBool (),
WSManNativeApi . ResourceURIPrefix + shellName ,
shellNotFoundErrorMsg ,
shellNotPowerShellMsg ,
accessModeSpecified ? AccessMode : PSSessionConfigurationAccessMode . Disabled ,
});
2016-07-29 13:02:49 -07:00
errorList = ( ArrayList ) Context . DollarErrorVariable ;
2016-03-30 14:20:52 -07:00
if ( errorList . Count > errorCountBefore )
{
2016-07-29 13:02:49 -07:00
_isErrorReported = true ;
2016-03-30 14:20:52 -07:00
return ;
}
SetSessionConfigurationTypeOptions ();
if ( errorList . Count > errorCountBefore )
{
2016-07-29 13:02:49 -07:00
_isErrorReported = true ;
2016-03-30 14:20:52 -07:00
return ;
}
SetQuotas ();
if ( errorList . Count > errorCountBefore )
{
2016-07-29 13:02:49 -07:00
_isErrorReported = true ;
2016-03-30 14:20:52 -07:00
return ;
}
SetRunAs ();
if ( errorList . Count > errorCountBefore )
{
2016-07-29 13:02:49 -07:00
_isErrorReported = true ;
2016-03-30 14:20:52 -07:00
return ;
}
SetVirtualAccount ();
if ( errorList . Count > errorCountBefore )
{
2016-07-29 13:02:49 -07:00
_isErrorReported = true ;
2016-03-30 14:20:52 -07:00
return ;
}
// Move the WinRM service to its own service host if the endpoint is given elevated credentials
if ( isRunAsCredentialSpecified || RunAsVirtualAccountSpecified )
{
PSSessionConfigurationCommandUtilities . MoveWinRmToIsolatedServiceHost ( RunAsVirtualAccountSpecified );
}
if ( errorList . Count > errorCountBefore )
{
2016-07-29 13:02:49 -07:00
_isErrorReported = true ;
2016-03-30 14:20:52 -07:00
return ;
}
SetOptions ();
if ( errorList . Count > errorCountBefore )
{
2016-07-29 13:02:49 -07:00
_isErrorReported = true ;
2016-03-30 14:20:52 -07:00
return ;
}
}
private void SetRunAs ()
{
if ( this . runAsCredential == null )
{
2016-07-29 13:02:49 -07:00
if ( string . IsNullOrEmpty ( _gmsaAccount )) { return ; }
runAsCredential = PSSessionConfigurationCommandUtilities . CreateGMSAAccountCredentials ( _gmsaAccount );
2016-03-30 14:20:52 -07:00
}
ScriptBlock setRunAsSbFormatSb = ScriptBlock . Create (
string . Format ( CultureInfo . InvariantCulture , setRunAsSbFormat , CodeGeneration . EscapeSingleQuotedStringContent ( Name )));
setRunAsSbFormatSb . LanguageMode = PSLanguageMode . FullLanguage ;
setRunAsSbFormatSb . InvokeUsingCmdlet (
contextCmdlet : this ,
useLocalScope : true ,
errorHandlingBehavior : ScriptBlock . ErrorHandlingBehavior . WriteToCurrentErrorPipe ,
dollarUnder : AutomationNull . Value ,
input : Utils . EmptyArray < object >(),
scriptThis : AutomationNull . Value ,
args : new object [] {
2016-07-29 13:02:49 -07:00
runAsCredential . UserName ,
2016-03-30 14:20:52 -07:00
runAsCredential . Password ,
2016-07-29 13:02:49 -07:00
});
2016-03-30 14:20:52 -07:00
}
private void SetVirtualAccount ()
{
2016-07-29 13:02:49 -07:00
if (! this . RunAsVirtualAccountSpecified )
2016-03-30 14:20:52 -07:00
return ;
using ( System . Management . Automation . PowerShell invoker = System . Management . Automation . PowerShell . Create ( RunspaceMode . CurrentRunspace ))
{
string pluginLocation = StringUtil . Format ( @"WSMAN:\localhost\plugin\{0}\RunAsVirtualAccount" , Name );
invoker . AddCommand ( "Set-Item" ). AddParameter ( "Path" , pluginLocation ). AddParameter ( "Value" , this . RunAsVirtualAccount );
invoker . Invoke ();
}
}
private void SetQuotas ()
{
if ( this . transportOption != null )
{
ScriptBlock setQuotasSb = ScriptBlock . Create (
string . Format ( CultureInfo . InvariantCulture , setSessionConfigurationQuotaSbFormat , CodeGeneration . EscapeSingleQuotedStringContent ( Name )));
setQuotasSb . LanguageMode = PSLanguageMode . FullLanguage ;
Hashtable quotas = transportOption . ConstructQuotasAsHashtable ();
int idleTimeOut = 0 ;
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
if ( idleTimeOut != 0 && quotas . ContainsKey ( WSManConfigurationOption . AttribMaxIdleTimeout ))
{
bool setMaxIdleTimoutFirst = true ;
int maxIdleTimeOut ;
if ( LanguagePrimitives . TryConvertTo < int >( quotas [ WSManConfigurationOption . AttribMaxIdleTimeout ], out maxIdleTimeOut ))
{
int? currentIdleTimeoutms = WSManConfigurationOption . DefaultIdleTimeout ;
// Get the current IdleTimeOut quota value
//
using ( System . Management . Automation . PowerShell ps = System . Management . Automation . PowerShell . Create ())
{
ps . AddScript ( string . Format ( CultureInfo . InvariantCulture , getCurrentIdleTimeoutmsFormat , CodeGeneration . EscapeSingleQuotedStringContent ( Name )));
Collection < PSObject > psObjectCollection = ps . Invoke ( new object [] { Name }) as Collection < PSObject >;
if ( psObjectCollection == null || psObjectCollection . Count != 1 )
{
Dbg . Assert ( false , "This should never happen. ps.Invoke always return a Collection<PSObject>" );
}
currentIdleTimeoutms = Convert . ToInt32 ( psObjectCollection [ 0 ]. ToString (), CultureInfo . InvariantCulture );
}
if ( currentIdleTimeoutms >= maxIdleTimeOut && currentIdleTimeoutms >= idleTimeOut )
{
setMaxIdleTimoutFirst = false ;
}
}
ScriptBlock setTimeoutQuotasSb = ScriptBlock . Create (
string . Format ( CultureInfo . InvariantCulture , setSessionConfigurationTimeoutQuotasSbFormat , CodeGeneration . EscapeSingleQuotedStringContent ( Name )));
setTimeoutQuotasSb . LanguageMode = PSLanguageMode . FullLanguage ;
setTimeoutQuotasSb . InvokeUsingCmdlet (
contextCmdlet : this ,
useLocalScope : true ,
errorHandlingBehavior : ScriptBlock . ErrorHandlingBehavior . WriteToCurrentErrorPipe ,
dollarUnder : AutomationNull . Value ,
input : Utils . EmptyArray < object >(),
scriptThis : AutomationNull . Value ,
args : new object [] { maxIdleTimeOut , idleTimeOut , setMaxIdleTimoutFirst });
// Remove Idle timeout values as we have set them above
//
quotas . Remove ( WSManConfigurationOption . AttribMaxIdleTimeout );
quotas . Remove ( WSManConfigurationOption . AttribIdleTimeout );
}
setQuotasSb . InvokeUsingCmdlet (
contextCmdlet : this ,
useLocalScope : true ,
errorHandlingBehavior : ScriptBlock . ErrorHandlingBehavior . WriteToCurrentErrorPipe ,
dollarUnder : AutomationNull . Value ,
input : Utils . EmptyArray < object >(),
scriptThis : AutomationNull . Value ,
args : new object [] { quotas , });
}
}
private void SetOptions ()
{
ScriptBlock setOptionsSb = ScriptBlock . Create (
string . Format ( CultureInfo . InvariantCulture , setSessionConfigurationOptionsSbFormat , CodeGeneration . EscapeSingleQuotedStringContent ( Name )));
setOptionsSb . LanguageMode = PSLanguageMode . FullLanguage ;
Hashtable optionsTable
= transportOption != null
? transportOption . ConstructOptionsAsHashtable ()
: new Hashtable ();
if ( accessModeSpecified )
{
switch ( AccessMode )
{
case PSSessionConfigurationAccessMode . Disabled :
optionsTable [ AllowRemoteAccessToken ] = false . ToString ();
break ;
case PSSessionConfigurationAccessMode . Local :
case PSSessionConfigurationAccessMode . Remote :
optionsTable [ AllowRemoteAccessToken ] = true . ToString ();
break ;
}
}
if ( isUseSharedProcessSpecified )
{
optionsTable [ UseSharedProcessToken ] = UseSharedProcess . ToBool (). ToString ();
}
setOptionsSb . InvokeUsingCmdlet (
contextCmdlet : this ,
useLocalScope : true ,
errorHandlingBehavior : ScriptBlock . ErrorHandlingBehavior . WriteToCurrentErrorPipe ,
dollarUnder : AutomationNull . Value ,
input : Utils . EmptyArray < object >(),
scriptThis : AutomationNull . Value ,
args : new object [] {
2016-07-29 13:02:49 -07:00
optionsTable ,
});
2016-03-30 14:20:52 -07:00
}
private void SetSessionConfigurationTypeOptions ()
{
if ( sessionTypeOption != null )
{
using ( System . Management . Automation . PowerShell ps = System . Management . Automation . PowerShell . Create ())
{
ps . AddScript ( string . Format ( CultureInfo . InvariantCulture , getSessionConfigurationDataSbFormat , CodeGeneration . EscapeSingleQuotedStringContent ( Name )));
Collection < PSObject > psObjectCollection = ps . Invoke ( new object [] { Name }) as Collection < PSObject >;
if ( psObjectCollection == null || psObjectCollection . Count != 1 )
{
Dbg . Assert ( false , "This should never happen. Plugin must exist because caller code has already checked this." );
}
PSSessionConfigurationData scd = PSSessionConfigurationData . Create ( psObjectCollection [ 0 ] == null ? string . Empty : PSSessionConfigurationData . Unescape ( psObjectCollection [ 0 ]. BaseObject . ToString ()));
PSSessionTypeOption original = sessionTypeOption . ConstructObjectFromPrivateData ( scd . PrivateData );
original . CopyUpdatedValuesFrom ( sessionTypeOption );
StringBuilder sessionConfigurationData = new StringBuilder ();
string modulePathParameter = null ;
string unsetModulePathStr = string . Empty ;
bool unsetModulePath = false ;
if ( modulePathSpecified )
{
bool isWorkflowConfiguration = IsWorkflowConfigurationType ( ps );
2016-07-29 13:02:49 -07:00
if ( modulesToImport == null ||
modulesToImport . Length == 0 ||
2016-03-30 14:20:52 -07:00
( modulesToImport . Length == 1 && modulesToImport [ 0 ] is string && (( string ) modulesToImport [ 0 ]). Equals ( string . Empty , StringComparison . OrdinalIgnoreCase )))
{
unsetModulePath = true ;
unsetModulePathStr = isWorkflowConfiguration ? ConfigurationDataFromXML . PSWORKFLOWMODULE : string . Empty ;
}
else
{
modulePathParameter = PSSessionConfigurationCommandUtilities . GetModulePathAsString ( this . modulesToImport ). Trim ();
// Add the built-in module path if it's a workflow config
if (! string . IsNullOrEmpty ( modulePathParameter ) && isWorkflowConfiguration )
{
List < object > modifiedModulePath = new List < object >( modulesToImport );
modifiedModulePath . Insert ( 0 , ConfigurationDataFromXML . PSWORKFLOWMODULE );
modulePathParameter = PSSessionConfigurationCommandUtilities . GetModulePathAsString ( modifiedModulePath . ToArray ()). Trim ();
}
}
}
// If the ModulesToImport parameter is not specified, or it is specified, but modulePathParameter turns out to be an empty string,
// we use the original module path
if (! unsetModulePath && string . IsNullOrEmpty ( modulePathParameter ))
{
modulePathParameter = ( scd . ModulesToImportInternal == null ) ? null : PSSessionConfigurationCommandUtilities . GetModulePathAsString ( scd . ModulesToImportInternal . ToArray ()). Trim ();
}
// 1. unsetModulePath is true. In this case, modulePathParameter is definitely null
// 2. unsetModulePath is false.
// a. modulePathSpecified is false. In this case, modulePathParameter will be the original module path,
// and it's null or empty when the original module path is null or empty
// b. modulePathSpecified is true. In this case, the user specified modulePathParameter is empty after trim(),
// and it will be the original module path. It's null or empty when the original module path is null or empty.
if ( unsetModulePath || string . IsNullOrEmpty ( modulePathParameter ))
{
sessionConfigurationData . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
PSSessionConfigurationData . ModulesToImportToken , unsetModulePathStr ));
}
// unsetModulePath is false AND modulePathParameter is not empty.
// 1. modulePathSpecified is false. In this case, modulePathParameter will be the original module path.
// 2. modulePathSpecified is true. In this case, the user is not unsetting the module path.
// a. the user specified module path is not empty.
// b. the user specified module path is empty after trim(), and modulePathParameter will be the original module path.
else
{
sessionConfigurationData . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
PSSessionConfigurationData . ModulesToImportToken ,
modulePathParameter ));
}
string privateData = original . ConstructPrivateData ();
if (! string . IsNullOrEmpty ( privateData ))
{
sessionConfigurationData . Append ( string . Format ( CultureInfo . InvariantCulture , privateDataFormat , privateData ));
}
if ( sessionConfigurationData . Length > 0 )
{
string sessionConfigData = string . Format ( CultureInfo . InvariantCulture ,
SessionConfigDataFormat ,
sessionConfigurationData );
string encodedSessionConfigData = SecurityElement . Escape ( sessionConfigData );
ScriptBlock setSessionConfigurationSb = ScriptBlock . Create (
string . Format ( CultureInfo . InvariantCulture , setSessionConfigurationDataSbFormat , CodeGeneration . EscapeSingleQuotedStringContent ( Name ))
);
setSessionConfigurationSb . LanguageMode = PSLanguageMode . FullLanguage ;
setSessionConfigurationSb . InvokeUsingCmdlet (
contextCmdlet : this ,
useLocalScope : true ,
errorHandlingBehavior : ScriptBlock . ErrorHandlingBehavior . WriteToCurrentErrorPipe ,
dollarUnder : AutomationNull . Value ,
input : Utils . EmptyArray < object >(),
scriptThis : AutomationNull . Value ,
args : new object [] {
2016-07-29 13:02:49 -07:00
encodedSessionConfigData ,
});
2016-03-30 14:20:52 -07:00
}
}
}
}
/// <summary>
/// </summary>
protected override void EndProcessing ()
{
2016-07-29 13:02:49 -07:00
PSSessionConfigurationCommandUtilities . RestartWinRMService ( this , _isErrorReported , Force , noRestart );
if (! _isErrorReported && noRestart )
2016-03-30 14:20:52 -07:00
{
string action = StringUtil . Format ( RemotingErrorIdStrings . CSShouldProcessAction , this . CommandInfo . Name );
WriteWarning ( StringUtil . Format ( RemotingErrorIdStrings . WinRMRequiresRestart , action ));
}
System . Management . Automation . Tracing . Tracer tracer = new System . Management . Automation . Tracing . Tracer ();
tracer . EndpointModified ( this . Name , WindowsIdentity . GetCurrent (). Name );
}
#endregion
#region Private Methods
/// <summary>
/// Check if the current configuration is a workflow endpoint
/// </summary>
/// <returns></returns>
private bool IsWorkflowConfigurationType ( System . Management . Automation . PowerShell ps )
{
// Get the AssemblyName
ps . AddScript ( string . Format ( CultureInfo . InvariantCulture , getAssemblyNameDataFormat , CodeGeneration . EscapeSingleQuotedStringContent ( Name )));
Collection < PSObject > psObjectCollection = ps . Invoke ( new object [] { Name }) as Collection < PSObject >;
if ( psObjectCollection == null || psObjectCollection . Count != 1 )
{
Dbg . Assert ( false , "This should never happen. ps.Invoke always return a Collection<PSObject>" );
}
if ( psObjectCollection [ 0 ] == null )
{
// Not workflow endpoint, no assembly name
return false ;
}
string assemblyNameOfCurrentConfiguration = psObjectCollection [ 0 ]. BaseObject . ToString ();
return assemblyNameOfCurrentConfiguration . Equals ( ConfigurationDataFromXML . WORKFLOWCOREASSEMBLY , StringComparison . OrdinalIgnoreCase );
}
private PSObject ConstructPropertiesForUpdate ()
{
PSObject result = new PSObject ();
result . Properties . Add ( new PSNoteProperty ( "Name" , shellName ));
if ( isAssemblyNameSpecified )
{
result . Properties . Add ( new PSNoteProperty ( ConfigurationDataFromXML . ASSEMBLYTOKEN , assemblyName ));
}
if ( isApplicationBaseSpecified )
{
result . Properties . Add ( new PSNoteProperty ( ConfigurationDataFromXML . APPBASETOKEN , applicationBase ));
}
if ( isConfigurationTypeNameSpecified )
{
result . Properties . Add ( new PSNoteProperty ( ConfigurationDataFromXML . SHELLCONFIGTYPETOKEN , configurationTypeName ));
}
if ( isConfigurationScriptSpecified )
{
result . Properties . Add ( new PSNoteProperty ( ConfigurationDataFromXML . STARTUPSCRIPTTOKEN , configurationScript ));
}
if ( isMaxCommandSizeMBSpecified )
{
object input = maxCommandSizeMB . HasValue ? ( object ) maxCommandSizeMB . Value : null ;
result . Properties . Add ( new PSNoteProperty ( ConfigurationDataFromXML . MAXRCVDCMDSIZETOKEN , input ));
}
if ( isMaxObjectSizeMBSpecified )
{
object input = maxObjectSizeMB . HasValue ? ( object ) maxObjectSizeMB . Value : null ;
result . Properties . Add ( new PSNoteProperty ( ConfigurationDataFromXML . MAXRCVDOBJSIZETOKEN , input ));
}
#if ! CORECLR // No ApartmentState In CoreCLR
if ( threadAptState . HasValue )
{
result . Properties . Add ( new PSNoteProperty ( ConfigurationDataFromXML . THREADAPTSTATETOKEN , threadAptState . Value ));
}
#endif
if ( threadOptions . HasValue )
{
result . Properties . Add ( new PSNoteProperty ( ConfigurationDataFromXML . THREADOPTIONSTOKEN , threadOptions . Value ));
}
if ( isPSVersionSpecified )
{
result . Properties . Add ( new PSNoteProperty ( ConfigurationDataFromXML . PSVERSIONTOKEN , PSSessionConfigurationCommandUtilities . ConstructVersionFormatForConfigXml ( psVersion )));
MaxPSVersion = PSSessionConfigurationCommandUtilities . CalculateMaxPSVersion ( psVersion );
// We add MaxPSVersion to the result irrespective of whether the string is empty or not.
// This is done to cover the following scenario
// Register-PSSessionConfiguration -Name "blah" -PSVersion 2
// followed by a
// Set-PSSessionConfiguration -Name "blah" -PSVersion 3
// If you create an end point with version 2 and then update it to 3, then the MaxPsVersion parameter should be removed from config xml
// So, we create a MaxPSVersion property with no value.
// In function Set-PSSessionConfiguration, the registry item MaxPSVersion will be removed.
// But it will not be created since the value is empty.
result . Properties . Add ( new PSNoteProperty ( ConfigurationDataFromXML . MAXPSVERSIONTOKEN , PSSessionConfigurationCommandUtilities . ConstructVersionFormatForConfigXml ( MaxPSVersion )));
}
if ( modulePathSpecified && sessionTypeOption == null )
{
bool unsetModulePath = false ;
string modulePathParameter = null ;
if ( modulesToImport == null || modulesToImport . Length == 0 ||
( modulesToImport . Length == 1 && modulesToImport [ 0 ] is string && (( string ) modulesToImport [ 0 ]). Equals ( string . Empty , StringComparison . OrdinalIgnoreCase )))
{
unsetModulePath = true ;
}
else
{
modulePathParameter = PSSessionConfigurationCommandUtilities . GetModulePathAsString ( this . modulesToImport ). Trim ();
}
if ( unsetModulePath || ! string . IsNullOrEmpty ( modulePathParameter ))
{
using ( System . Management . Automation . PowerShell ps = System . Management . Automation . PowerShell . Create ())
{
bool isWorkflowConfiguration = IsWorkflowConfigurationType ( ps );
if (! string . IsNullOrEmpty ( modulePathParameter ) && isWorkflowConfiguration )
{
List < object > modifiedModulePath = new List < object >( modulesToImport );
modifiedModulePath . Insert ( 0 , ConfigurationDataFromXML . PSWORKFLOWMODULE );
modulePathParameter = PSSessionConfigurationCommandUtilities . GetModulePathAsString ( modifiedModulePath . ToArray ()). Trim ();
}
// Get the SessionConfigurationDataFormat
ps . AddScript ( string . Format ( CultureInfo . InvariantCulture , getSessionConfigurationDataSbFormat , CodeGeneration . EscapeSingleQuotedStringContent ( Name )));
Collection < PSObject > psObjectCollection = ps . Invoke ( new object [] { Name }) as Collection < PSObject >;
if ( psObjectCollection == null || psObjectCollection . Count != 1 )
{
Dbg . Assert ( false , "This should never happen. ps.Invoke always return a Collection<PSObject>" );
}
StringBuilder sessionConfigurationData = new StringBuilder ();
// SessionConfigurationData doesn't exist in InitializationParameters
if ( psObjectCollection [ 0 ] == null )
{
// if unsetModulePath is false, we set the new sessionConfigurationData to contain the specified module path
if (! unsetModulePath )
{
sessionConfigurationData . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
PSSessionConfigurationData . ModulesToImportToken ,
modulePathParameter ));
}
// if unsetModulePath is true, we don't need to do anything because ModulesToImport doesn't exist
// in the original configuration. If it's a workflow config, it's not a valid one.
}
// SessionConfigurationData exists in InitializationParameters
else
{
PSSessionConfigurationData scd = PSSessionConfigurationData . Create ( psObjectCollection [ 0 ]. BaseObject . ToString ());
string privateData = string . IsNullOrEmpty ( scd . PrivateData )
? null
: scd . PrivateData . Replace ( '"' , '\'' );
// The user tries to unset the module path)
if ( unsetModulePath )
{
// ModulesToImport exist in the pssessionConfigurationData
if ( scd . ModulesToImportInternal != null && scd . ModulesToImportInternal . Count != 0 )
{
string unsetModulePathStr = isWorkflowConfiguration ? ConfigurationDataFromXML . PSWORKFLOWMODULE : string . Empty ;
sessionConfigurationData . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
PSSessionConfigurationData . ModulesToImportToken , unsetModulePathStr ));
if (! string . IsNullOrEmpty ( privateData ))
{
sessionConfigurationData . Append ( string . Format ( CultureInfo . InvariantCulture , privateDataFormat , privateData ));
}
}
// if ModulesToImport doesn't exist in the pssessionConfigurationData, we don't need to do anything.
// in this case, if the current config is of type workfolw, it's not a valid config.
}
else
{
// Replace the old module path
sessionConfigurationData . Append ( string . Format ( CultureInfo . InvariantCulture ,
initParamFormat ,
PSSessionConfigurationData . ModulesToImportToken ,
modulePathParameter ));
if (! string . IsNullOrEmpty ( privateData ))
{
sessionConfigurationData . Append ( string . Format ( CultureInfo . InvariantCulture , privateDataFormat , privateData ));
}
}
}
if ( sessionConfigurationData . Length > 0 )
{
string sessionConfigData = string . Format ( CultureInfo . InvariantCulture ,
SessionConfigDataFormat ,
sessionConfigurationData );
string encodedSessionConfigData = SecurityElement . Escape ( sessionConfigData );
result . Properties . Add ( new PSNoteProperty ( ConfigurationDataFromXML . SESSIONCONFIGTOKEN , encodedSessionConfigData ));
}
} // end of using (...)
} // end of if (unsetModulePath || !string.IsNullOrEmpty(modulePathParameter))
} // end of if (modulePathSpecified)
// DISC endpoint
if ( Path != null )
{
ProviderInfo provider = null ;
PSDriveInfo drive ;
string filePath = SessionState . Path . GetUnresolvedProviderPathFromPSPath ( Path , out provider , out drive );
if (! provider . NameEquals ( Context . ProviderNames . FileSystem ) || ! filePath . EndsWith ( StringLiterals . PowerShellDISCFileExtension , StringComparison . OrdinalIgnoreCase ))
{
string message = StringUtil . Format ( RemotingErrorIdStrings . InvalidPSSessionConfigurationFilePath , Path );
InvalidOperationException ioe = new InvalidOperationException ( message );
ErrorRecord er = new ErrorRecord ( ioe , "InvalidPSSessionConfigurationFilePath" ,
ErrorCategory . InvalidArgument , Path );
ThrowTerminatingError ( er );
}
Guid sessionGuid = Guid . Empty ;
// Load session GUID from config file
string scriptName ;
ExternalScriptInfo scriptInfo = DISCUtils . GetScriptInfoForFile ( this . Context , filePath , out scriptName );
Hashtable configTable = DISCUtils . LoadConfigFile ( this . Context , scriptInfo );
foreach ( object currentKey in configTable . Keys )
{
if ( result . Properties [ currentKey . ToString ()] == null )
{
result . Properties . Add ( new PSNoteProperty ( currentKey . ToString (), configTable [ currentKey ]));
}
else
{
result . Properties [ currentKey . ToString ()]. Value = configTable [ currentKey ];
}
}
}
return result ;
}
#endregion
}
#endregion
#region Enable / Disable - PSSessionConfiguration
/// <summary>
/// Class implementing Enable-PSSessionConfiguration cmdlet
/// </summary>
[Cmdlet(VerbsLifecycle.Enable, RemotingConstants.PSSessionConfigurationNoun,
SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.Medium, HelpUri = "http://go.microsoft.com/fwlink/?LinkID=144301")]
public sealed class EnablePSSessionConfigurationCommand : PSCmdlet
{
#region Private Data
private const string setWSManConfigCommand = "Set-WSManQuickConfig" ;
// To Escape { -- {{
// To Escape } -- }}
// To Escape " -- ""
private const string enablePluginSbFormat = @"
function Test-WinRMQuickConfigNeeded
{{
# Checking the following items
#1. Starting or restarting (if already started) the WinRM service
#2. Setting the WinRM service startup type to Automatic
#3. Creating a listener to accept requests on any IP address
#4. Enabling Windows Firewall inbound rule exceptions for WS-Management traffic (for http only).
$winrmQuickConfigNeeded = $false
# check if WinRM service is running
if ((Get-Service winrm).Status -ne 'Running'){{
$winrmQuickConfigNeeded = $true
}}
# check if WinRM service startup is Auto
elseif ((Get-CimInstance -Query ""Select StartMode From Win32_Service Where Name='winmgmt'"").StartMode -ne 'Auto'){{
$winrmQuickConfigNeeded = $true
}}
# check if a winrm listener is present
elseif (!(Test-Path WSMan:\localhost\Listener) -or ((Get-ChildItem WSMan:\localhost\Listener) -eq $null)){{
$winrmQuickConfigNeeded = $true
}}
# check if WinRM firewall is enabled for HTTP
else{{
if (Get-Command Get-NetFirewallRule -ErrorAction SilentlyContinue){{
$winrmFirewall = Get-NetFirewallRule -Name 'WINRM-HTTP-In-TCP' -ErrorAction SilentlyContinue
if (!$winrmFirewall -or $winrmFirewall.Enabled -ne $true){{
$winrmQuickConfigNeeded = $true
}}
}}
else{{
$winrmQuickConfigNeeded = $true
}}
}}
$winrmQuickConfigNeeded
}}
function Enable-PSSessionConfiguration
{{
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact=""Medium"")]
param(
[Parameter(Position=0, ValueFromPipeline=$true)]
[System.String]
$Name,
[Parameter()]
[bool]
$Force,
[Parameter()]
[string]
$sddl,
[Parameter()]
[bool]
$isSDDLSpecified,
[Parameter()]
[string]
$queryForSet,
[Parameter()]
[string]
$captionForSet,
[Parameter()]
[string]
$queryForQC,
[Parameter()]
[string]
$captionForQC,
[Parameter()]
[string]
$shouldProcessDescForQC,
[Parameter()]
[string]
$setEnabledTarget,
[Parameter()]
[string]
$setEnabledAction,
[Parameter()]
[bool]
$skipNetworkProfileCheck,
[Parameter()]
[bool]
$noServiceRestart
)
begin
{{
$winrmQuickConfigNeeded = Test-WinRMQuickConfigNeeded
if ($winrmQuickConfigNeeded -and ($force -or $pscmdlet.ShouldProcess($shouldProcessDescForQC, $queryForQC, $captionForQC)))
{{
# get the status of winrm before Quick Config. if it is already
# running..restart the service after Quick Config.
$svc = get-service winrm
if ($skipNetworkProfileCheck)
{{
{0} -force -SkipNetworkProfileCheck
}}
else
{{
{0} -force
}}
if ($svc.Status -match ""Running"")
{{
Restart-Service winrm -force -confirm:$false
}}
}}
}} #end of Begin block
process
{{
Get-PSSessionConfiguration $name -Force:$Force | % {{
if ($_.Enabled -eq $false -and ($force -or $pscmdlet.ShouldProcess($setEnabledTarget, $setEnabledAction)))
{{
Set-Item -WarningAction SilentlyContinue -Path ""WSMan:\localhost\Plugin\$name\Enabled"" -Value $true -confirm:$false
}}
if (!$isSDDLSpecified)
{{
$sddlTemp = $null
if ($_.psobject.members[""SecurityDescriptorSddl""])
{{
$sddlTemp = $_.psobject.members[""SecurityDescriptorSddl""].Value
}}
$securityIdentifierToPurge = $null
# strip out Disable-Everyone DACL from the SDDL
if ($sddlTemp)
{{
# construct SID for ""EveryOne""
[system.security.principal.wellknownsidtype]$evst = ""worldsid""
$everyOneSID = new-object system.security.principal.securityidentifier $evst,$null
$sd = new-object system.security.accesscontrol.commonsecuritydescriptor $false,$false,$sddlTemp
$sd.DiscretionaryAcl | % {{
if (($_.acequalifier -eq ""accessdenied"") -and ($_.securityidentifier -match $everyOneSID))
{{
$securityIdentifierToPurge = $_.securityidentifier
}}
}}
if ($securityIdentifierToPurge)
{{
$sd.discretionaryacl.purge($securityIdentifierToPurge)
# if there is no discretionaryacl..add Builtin Administrators and Remote Management Users
# to the DACL group as this is the default WSMan behavior
if ($sd.discretionaryacl.count -eq 0)
{{
# Built-in administrators
[system.security.principal.wellknownsidtype]$bast = ""BuiltinAdministratorsSid""
$basid = new-object system.security.principal.securityidentifier $bast,$null
$sd.DiscretionaryAcl.AddAccess('Allow',$basid, 268435456, 'none', 'none')
# Remote Management Users, Win8+ only
if ([System.Environment]::OSVersion.Version -ge ""6.2.0.0"")
{{
$rmSidId = new-object system.security.principal.securityidentifier ""{1}""
$sd.DiscretionaryAcl.AddAccess('Allow', $rmSidId, 268435456, 'none', 'none')
}}
# Interactive Users
$iuSidId = new-object system.security.principal.securityidentifier ""{2}""
$sd.DiscretionaryAcl.AddAccess('Allow', $iuSidId, 268435456, 'none', 'none')
}}
$sddl = $sd.GetSddlForm(""all"")
}}
}} # if ($sddlTemp)
}} # if (!$isSDDLSpecified)
$qMessage = $queryForSet -f $_.name,$sddl
if (($sddl -or $isSDDLSpecified) -and ($force -or $pscmdlet.ShouldProcess($qMessage, $captionForSet)))
{{
$null = Set-PSSessionConfiguration -Name $_.Name -SecurityDescriptorSddl $sddl -NoServiceRestart -force -WarningAction 0
}}
}} #end of Get-PSSessionConfiguration | foreach
}} # end of Process block
End
{{
}}
}}
$_ | Enable-PSSessionConfiguration -force $args[0] -sddl $args[1] -isSDDLSpecified $args[2] -queryForSet $args[3] -captionForSet $args[4] -queryForQC $args[5] -captionForQC $args[6] -whatif:$args[7] -confirm:$args[8] -shouldProcessDescForQC $args[9] -setEnabledTarget $args[10] -setEnabledAction $args[11] -skipNetworkProfileCheck $args[12] -noServiceRestart $args[13]
" ;
2016-07-29 13:02:49 -07:00
private static ScriptBlock s_enablePluginSb ;
2016-03-30 14:20:52 -07:00
#endregion
#region Constructors
static EnablePSSessionConfigurationCommand ()
{
string enablePluginScript = string . Format ( CultureInfo . InvariantCulture ,
enablePluginSbFormat , setWSManConfigCommand , PSSessionConfigurationCommandBase . RemoteManagementUsersSID , PSSessionConfigurationCommandBase . InteractiveUsersSID );
// compile the script block statically and reuse the same instance
// everytime the command is run..This will save on parsing time.
2016-07-29 13:02:49 -07:00
s_enablePluginSb = ScriptBlock . Create ( enablePluginScript );
s_enablePluginSb . LanguageMode = PSLanguageMode . FullLanguage ;
2016-03-30 14:20:52 -07:00
}
#endregion
#region Parameters
/// <summary>
/// Configurations to Enable
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
[Parameter(Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]
[ValidateNotNullOrEmpty]
2016-08-02 13:29:09 -07:00
public string [] Name { get ; set ; }
2016-07-29 13:02:49 -07:00
private Collection < string > _shellsToEnable = new Collection < string >();
2016-03-30 14:20:52 -07:00
/// <summary>
/// Property that sets force parameter. This will allow
/// configuring the WinRM and enabling the session configurations
/// without prompting the user.
/// </summary>
[Parameter()]
public SwitchParameter Force
{
get
{
2016-07-29 13:02:49 -07:00
return _force ;
2016-03-30 14:20:52 -07:00
}
set
{
2016-07-29 13:02:49 -07:00
_force = value ;
2016-03-30 14:20:52 -07:00
}
}
2016-07-29 13:02:49 -07:00
private bool _force ;
2016-03-30 14:20:52 -07:00
/// <summary>
/// This enables the user to specify an SDDL for whom the session
/// configuration is enabled.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sddl")]
[Parameter()]
public string SecurityDescriptorSddl
{
get
{
return sddl ;
}
set
{
if (! string . IsNullOrEmpty ( value ))
{
// this will validate if the sddl is valid.
CommonSecurityDescriptor c = new CommonSecurityDescriptor ( false , false , value );
// this will never be the case..as constructor either constructs or throws.
// this is used here to avoid FxCop violation.
if ( null == c )
{
throw new NotSupportedException ();
}
}
sddl = value ;
isSddlSpecified = true ;
}
}
internal string sddl ;
internal bool isSddlSpecified ;
/// <summary>
/// Property that will allow configuring WinRM with Public
/// profile exception enabled.
/// </summary>
[Parameter()]
public SwitchParameter SkipNetworkProfileCheck
{
2016-07-29 13:02:49 -07:00
get { return _skipNetworkProfileCheck ; }
set { _skipNetworkProfileCheck = value ; }
2016-03-30 14:20:52 -07:00
}
2016-07-29 13:02:49 -07:00
private bool _skipNetworkProfileCheck ;
2016-03-30 14:20:52 -07:00
/// <summary>
/// If true, then the cmdlet will not attempt to restart
/// WinRM service after completion. Typically WinRM service
/// needs to be restarted for changes to take place.
/// </summary>
[Parameter()]
public SwitchParameter NoServiceRestart
{
2016-07-29 13:02:49 -07:00
get { return _noRestart ; }
set { _noRestart = value ; }
2016-03-30 14:20:52 -07:00
}
2016-07-29 13:02:49 -07:00
private bool _noRestart ;
2016-03-30 14:20:52 -07:00
#endregion
#region Cmdlet Overrides
/// <summary>
///
/// </summary>
/// <exception cref="InvalidOperationException">
/// 1. Either both "AssemblyName" and "ConfigurationTypeName" must be specified
/// or both must not be specified.
/// </exception>
protected override void BeginProcessing ()
{
// check if we have compatible WSMan
RemotingCommandUtil . CheckRemotingCmdletPrerequisites ();
PSSessionConfigurationCommandUtilities . ThrowIfNotAdministrator ();
}
/// <summary>
///
/// </summary>
protected override void ProcessRecord ()
{
2016-08-02 13:29:09 -07:00
if ( Name != null )
2016-03-30 14:20:52 -07:00
{
2016-08-02 13:29:09 -07:00
foreach ( string shell in Name )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
_shellsToEnable . Add ( shell );
2016-03-30 14:20:52 -07:00
}
}
}
/// <summary>
///
/// </summary>
protected override void EndProcessing ()
{
// if user did not specify any shell, act on the default shell.
2016-07-29 13:02:49 -07:00
if ( _shellsToEnable . Count == 0 )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
_shellsToEnable . Add ( RemotingConstants . DefaultShellName );
2016-03-30 14:20:52 -07:00
}
WriteVerbose ( StringUtil . Format ( RemotingErrorIdStrings . EcsScriptMessageV , enablePluginSbFormat ));
// gather -WhatIf, -Confirm parameter data and pass it to the script block
bool whatIf = false ;
// confirm is always true to start with
bool confirm = true ;
PSSessionConfigurationCommandUtilities . CollectShouldProcessParameters ( this , out whatIf , out confirm );
string qcCaptionMessage = StringUtil . Format ( RemotingErrorIdStrings . EcsWSManQCCaption );
string qcQueryMessage = StringUtil . Format ( RemotingErrorIdStrings . EcsWSManQCQuery , setWSManConfigCommand );
string qcShoudProcessDesc = StringUtil . Format ( RemotingErrorIdStrings . EcsWSManShouldProcessDesc , setWSManConfigCommand );
string setCaptionMessage = StringUtil . Format ( RemotingErrorIdStrings . CSShouldProcessAction ,
"Set-PSSessionConfiguration" );
string setQueryMessage = RemotingErrorIdStrings . EcsShouldProcessTarget ;
string setEnabledAction = StringUtil . Format ( RemotingErrorIdStrings . CSShouldProcessAction , "Set-Item" );
string setEnabledTarget = RemotingErrorIdStrings . SetEnabledTrueTarget ;
2016-07-29 13:02:49 -07:00
s_enablePluginSb . InvokeUsingCmdlet (
2016-03-30 14:20:52 -07:00
contextCmdlet : this ,
useLocalScope : true ,
errorHandlingBehavior : ScriptBlock . ErrorHandlingBehavior . WriteToCurrentErrorPipe ,
2016-07-29 13:02:49 -07:00
dollarUnder : _shellsToEnable ,
2016-03-30 14:20:52 -07:00
input : Utils . EmptyArray < object >(),
scriptThis : AutomationNull . Value ,
args : new object [] {
2016-07-29 13:02:49 -07:00
_force ,
2016-03-30 14:20:52 -07:00
sddl ,
isSddlSpecified ,
setQueryMessage ,
setCaptionMessage ,
qcQueryMessage ,
qcCaptionMessage ,
whatIf ,
confirm ,
qcShoudProcessDesc ,
setEnabledTarget ,
setEnabledAction ,
2016-07-29 13:02:49 -07:00
_skipNetworkProfileCheck ,
_noRestart });
2016-03-30 14:20:52 -07:00
System . Management . Automation . Tracing . Tracer tracer = new System . Management . Automation . Tracing . Tracer ();
StringBuilder sb = new StringBuilder ();
foreach ( string endPointName in Name ?? Utils . EmptyArray < string >())
{
sb . Append ( endPointName );
sb . Append ( ", " );
}
if ( sb . Length > 0 )
{
sb . Remove ( sb . Length - 2 , 2 );
}
tracer . EndpointEnabled ( sb . ToString (), WindowsIdentity . GetCurrent (). Name );
}
#endregion
}
/// <summary>
///
/// </summary>
[Cmdlet(VerbsLifecycle.Disable, RemotingConstants.PSSessionConfigurationNoun,
SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.Low, HelpUri = "http://go.microsoft.com/fwlink/?LinkID=144299")]
public sealed class DisablePSSessionConfigurationCommand : PSCmdlet
{
#region Private Data
// To Escape { -- {{
// To Escape } -- }}
// To Escape " -- ""
private const string disablePluginSbFormat = @"
function Disable-PSSessionConfiguration
{{
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact=""Low"")]
param(
[Parameter(Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[System.String]
$Name,
[Parameter()]
[bool]
$Force,
[Parameter()]
[string]
$restartWinRMMessage,
[Parameter()]
[string]
$setEnabledTarget,
[Parameter()]
[string]
$setEnabledAction,
[Parameter()]
[bool]
$noServiceRestart
)
begin
{{
if ($force -or $pscmdlet.ShouldProcess($restartWinRMMessage))
{{
$svc = get-service winrm
if ($svc.Status -match ""Stopped"")
{{
Restart-Service winrm -force -confirm:$false
}}
}}
}} #end of Begin block
process
{{
Get-PSSessionConfiguration $name -Force:$Force | % {{
if ($_.Enabled -and ($force -or $pscmdlet.ShouldProcess($setEnabledTarget, $setEnabledAction)))
{{
Set-Item -WarningAction SilentlyContinue -Path ""WSMan:\localhost\Plugin\$name\Enabled"" -Value $false -Force -Confirm:$false
}}
}} # end of foreach block
}} #end of process block
# no longer necessary to restart the winrm to apply the config change
End
{{
}}
}}
$_ | Disable-PSSessionConfiguration -force $args[0] -whatif:$args[1] -confirm:$args[2] -restartWinRMMessage $args[3] -setEnabledTarget $args[4] -setEnabledAction $args[5] -noServiceRestart $args[6]
" ;
2016-07-29 13:02:49 -07:00
private static ScriptBlock s_disablePluginSb ;
2016-03-30 14:20:52 -07:00
#endregion
#region Constructors
static DisablePSSessionConfigurationCommand ()
{
string disablePluginScript = string . Format ( CultureInfo . InvariantCulture ,
disablePluginSbFormat );
// compile the script block statically and reuse the same instance
// everytime the command is run..This will save on parsing time.
2016-07-29 13:02:49 -07:00
s_disablePluginSb = ScriptBlock . Create ( disablePluginScript );
s_disablePluginSb . LanguageMode = PSLanguageMode . FullLanguage ;
2016-03-30 14:20:52 -07:00
}
#endregion
#region Parameters
/// <summary>
/// Configurations to Enable
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
[Parameter(Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]
[ValidateNotNullOrEmpty]
2016-08-02 13:29:09 -07:00
public string [] Name { get ; set ; }
2016-07-29 13:02:49 -07:00
private Collection < string > _shellsToDisable = new Collection < string >();
2016-03-30 14:20:52 -07:00
/// <summary>
/// Property that sets force parameter. This will allow
/// configuring the WinRM and enabling the session configurations
/// without prompting the user.
/// </summary>
[Parameter()]
public SwitchParameter Force
{
get
{
2016-07-29 13:02:49 -07:00
return _force ;
2016-03-30 14:20:52 -07:00
}
set
{
2016-07-29 13:02:49 -07:00
_force = value ;
2016-03-30 14:20:52 -07:00
}
}
2016-07-29 13:02:49 -07:00
private bool _force ;
2016-03-30 14:20:52 -07:00
/// <summary>
/// If true, then the cmdlet will not attempt to restart
/// WinRM service after completion. Typically WinRM service
/// needs to be restarted for changes to take place.
/// </summary>
[Parameter()]
public SwitchParameter NoServiceRestart
{
2016-07-29 13:02:49 -07:00
get { return _noRestart ; }
set { _noRestart = value ; }
2016-03-30 14:20:52 -07:00
}
2016-07-29 13:02:49 -07:00
private bool _noRestart ;
2016-03-30 14:20:52 -07:00
#endregion
#region Cmdlet Overrides
/// <summary>
///
/// </summary>
/// <exception cref="InvalidOperationException">
/// 1. Either both "AssemblyName" and "ConfigurationTypeName" must be specified
/// or both must not be specified.
/// </exception>
protected override void BeginProcessing ()
{
// check if we have compatible WSMan
RemotingCommandUtil . CheckRemotingCmdletPrerequisites ();
PSSessionConfigurationCommandUtilities . ThrowIfNotAdministrator ();
}
/// <summary>
///
/// </summary>
protected override void ProcessRecord ()
{
2016-08-02 13:29:09 -07:00
if ( Name != null )
2016-03-30 14:20:52 -07:00
{
2016-08-02 13:29:09 -07:00
foreach ( string shell in Name )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
_shellsToDisable . Add ( shell );
2016-03-30 14:20:52 -07:00
}
}
}
/// <summary>
///
/// </summary>
protected override void EndProcessing ()
{
// if user did not specify any shell, act on the default shell.
2016-07-29 13:02:49 -07:00
if ( _shellsToDisable . Count == 0 )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
_shellsToDisable . Add ( RemotingConstants . DefaultShellName );
2016-03-30 14:20:52 -07:00
}
//WriteWarning(StringUtil.Format(RemotingErrorIdStrings.DcsWarningMessage));
WriteVerbose ( StringUtil . Format ( RemotingErrorIdStrings . EcsScriptMessageV , disablePluginSbFormat ));
// gather -WhatIf, -Confirm parameter data and pass it to the script block
bool whatIf = false ;
// confirm is always true to start with
bool confirm = true ;
PSSessionConfigurationCommandUtilities . CollectShouldProcessParameters ( this , out whatIf , out confirm );
string restartWinRMMessage = RemotingErrorIdStrings . RestartWinRMMessage ;
string setEnabledTarget = RemotingErrorIdStrings . SetEnabledFalseTarget ;
string setEnabledAction = StringUtil . Format ( RemotingErrorIdStrings . CSShouldProcessAction , "Set-Item" );
2016-07-29 13:02:49 -07:00
s_disablePluginSb . InvokeUsingCmdlet (
2016-03-30 14:20:52 -07:00
contextCmdlet : this ,
useLocalScope : true ,
errorHandlingBehavior : ScriptBlock . ErrorHandlingBehavior . WriteToCurrentErrorPipe ,
2016-07-29 13:02:49 -07:00
dollarUnder : _shellsToDisable ,
2016-03-30 14:20:52 -07:00
input : Utils . EmptyArray < object >(),
scriptThis : AutomationNull . Value ,
args : new object [] {
2016-07-29 13:02:49 -07:00
_force ,
2016-03-30 14:20:52 -07:00
whatIf ,
confirm ,
restartWinRMMessage ,
setEnabledTarget ,
setEnabledAction ,
2016-07-29 13:02:49 -07:00
_noRestart });
2016-03-30 14:20:52 -07:00
System . Management . Automation . Tracing . Tracer tracer = new System . Management . Automation . Tracing . Tracer ();
StringBuilder sb = new StringBuilder ();
foreach ( string endPointName in Name ?? Utils . EmptyArray < string >())
{
sb . Append ( endPointName );
sb . Append ( ", " );
}
if ( sb . Length > 0 )
{
sb . Remove ( sb . Length - 2 , 2 );
}
tracer . EndpointDisabled ( sb . ToString (), WindowsIdentity . GetCurrent (). Name );
}
#endregion
}
#endregion
#region Enable - PSRemoting
/// <summary>
///
/// </summary>
#if ! CORECLR
[Cmdlet(VerbsLifecycle.Enable, RemotingConstants.PSRemotingNoun,
SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.Medium, HelpUri = "http://go.microsoft.com/fwlink/?LinkID=144300")]
#endif
public sealed class EnablePSRemotingCommand : PSCmdlet
{
#region Private Data
// To Escape { -- {{
// To Escape } -- }}
// To Escape " -- ""
//TODO: CLR4: Remove the logic for setting the MaxMemoryPerShellMB to 200 MB once IPMO->Get-Command->Get-Help memory usage issue is fixed.
private const string enableRemotingSbFormat = @"
function Enable-PSRemoting
{{
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact=""Medium"")]
param(
[Parameter()] [bool] $Force,
[Parameter()] [string] $queryForRegisterDefault,
[Parameter()] [string] $captionForRegisterDefault,
[Parameter()] [string] $queryForSet,
[Parameter()] [string] $captionForSet,
[Parameter()] [bool] $skipNetworkProfileCheck
)
end
{{
# Enable all Session Configurations
try {{
$null = $PSBoundParameters.Remove(""queryForRegisterDefault"")
$null = $PSBoundParameters.Remove(""captionForRegisterDefault"")
$null = $PSBoundParameters.Remove(""queryForSet"")
$null = $PSBoundParameters.Remove(""captionForSet"")
$PSBoundParameters.Add(""Name"",""*"")
# first try to enable all the sessions
Enable-PSSessionConfiguration @PSBoundParameters
# make sure default powershell end points exist
# ie., Microsoft.PowerShell
# and Microsoft.PowerShell32 (wow64)
$errorCount = $error.Count
$endPoint = Get-PSSessionConfiguration {0} -Force:$Force -ErrorAction silentlycontinue 2>&1
$newErrorCount = $error.Count
# remove the 'No Session Configuration matches criteria' errors
for ($index = 0; $index -lt ($newErrorCount - $errorCount); $index ++)
{{
$error.RemoveAt(0)
}}
$qMessage = $queryForRegisterDefault -f ""{0}"",""Register-PSSessionConfiguration {0} -force""
if ((!$endpoint) -and
($force -or $pscmdlet.ShouldProcess($qMessage, $captionForRegisterDefault)))
{{
$null = Register-PSSessionConfiguration {0} -force
set-item -WarningAction SilentlyContinue wsman:\localhost\plugin\{0}\Quotas\MaxShellsPerUser -value ""25"" -confirm:$false
set-item -WarningAction SilentlyContinue wsman:\localhost\plugin\{0}\Quotas\MaxIdleTimeoutms -value {4} -confirm:$false
restart-service winrm -confirm:$false
}}
# Check Microsoft.PowerShell.Workflow endpoint
$errorCount = $error.Count
$endPoint = Get-PSSessionConfiguration {0}.workflow -Force:$Force -ErrorAction silentlycontinue 2>&1
$newErrorCount = $error.Count
# remove the 'No Session Configuration matches criteria' errors
for ($index = 0; $index -lt ($newErrorCount - $errorCount); $index ++)
{{
$error.RemoveAt(0)
}}
if (!$endpoint)
{{
$qMessage = $queryForRegisterDefault -f ""Microsoft.PowerShell.Workflow"",""Register-PSSessionConfiguration Microsoft.PowerShell.Workflow -force""
if ($force -or $pscmdlet.ShouldProcess($qMessage, $captionForRegisterDefault)) {{
$tempxmlfile = [io.path]::Gettempfilename()
""{1}"" | out-file -force -filepath $tempxmlfile -confirm:$false
$null = winrm create winrm/config/plugin?Name=Microsoft.PowerShell.Workflow -file:$tempxmlfile
remove-item -path $tempxmlfile -force -confirm:$false
restart-service winrm -confirm:$false
}}
}}
$pa = $env:PROCESSOR_ARCHITECTURE
if ($pa -eq ""x86"")
{{
# on 64-bit platforms, wow64 bit process has the correct architecture
# available in processor_architew6432 varialbe
$pa = $env:PROCESSOR_ARCHITEW6432
}}
if ((($pa -eq ""amd64"")) -and (test-path $env:windir\syswow64\pwrshplugin.dll))
{{
# Check availability of WOW64 endpoint. Register if not available.
$errorCount = $error.Count
$endPoint = Get-PSSessionConfiguration {0}32 -Force:$Force -ErrorAction silentlycontinue 2>&1
$newErrorCount = $error.Count
# remove the 'No Session Configuration matches criteria' errors
for ($index = 0; $index -lt ($newErrorCount - $errorCount); $index ++)
{{
$error.RemoveAt(0)
}}
$qMessage = $queryForRegisterDefault -f ""{0}32"",""Register-PSSessionConfiguration {0}32 -processorarchitecture x86 -force""
if ((!$endpoint) -and
($force -or $pscmdlet.ShouldProcess($qMessage, $captionForRegisterDefault)))
{{
$null = Register-PSSessionConfiguration {0}32 -processorarchitecture x86 -force
set-item -WarningAction SilentlyContinue wsman:\localhost\plugin\{0}32\Quotas\MaxShellsPerUser -value ""25"" -confirm:$false
set-item -WarningAction SilentlyContinue wsman:\localhost\plugin\{0}32\Quotas\MaxIdleTimeoutms -value {4} -confirm:$false
restart-service winrm -confirm:$false
}}
}}
# remove the 'network deny all' tag
Get-PSSessionConfiguration -Force:$Force | % {{
$sddl = $null
if ($_.psobject.members[""SecurityDescriptorSddl""])
{{
$sddl = $_.psobject.members[""SecurityDescriptorSddl""].Value
}}
if ($sddl)
{{
# Construct SID for network users
[system.security.principal.wellknownsidtype]$evst = ""NetworkSid""
$networkSID = new-object system.security.principal.securityidentifier $evst,$null
$securityIdentifierToPurge = $null
$sd = new-object system.security.accesscontrol.commonsecuritydescriptor $false,$false,$sddl
$sd.DiscretionaryAcl | % {{
if (($_.acequalifier -eq ""accessdenied"") -and ($_.securityidentifier -match $networkSID) -and ($_.AccessMask -eq 268435456))
{{
$securityIdentifierToPurge = $_.securityidentifier
}}
}}
if ($securityIdentifierToPurge)
{{
# Remove the specific ACE
$sd.discretionaryacl.RemoveAccessSpecific('Deny', $securityIdentifierToPurge, 268435456, 'none', 'none')
# if there is no discretionaryacl..add Builtin Administrators and Remote Management Users
# to the DACL group as this is the default WSMan behavior
if ($sd.discretionaryacl.count -eq 0)
{{
# Built-in administrators.
[system.security.principal.wellknownsidtype]$bast = ""BuiltinAdministratorsSid""
$basid = new-object system.security.principal.securityidentifier $bast,$null
$sd.DiscretionaryAcl.AddAccess('Allow',$basid, 268435456, 'none', 'none')
# Remote Management Users, Win8+ only
if ([System.Environment]::OSVersion.Version -ge ""6.2.0.0"")
{{
$rmSidId = new-object system.security.principal.securityidentifier ""{2}""
$sd.DiscretionaryAcl.AddAccess('Allow', $rmSidId, 268435456, 'none', 'none')
}}
# Interactive Users
$iaSidId = new-object system.security.principal.securityidentifier ""{3}""
$sd.DiscretionaryAcl.AddAccess('Allow', $iaSidId, 268435456, 'none', 'none')
}}
$sddl = $sd.GetSddlForm(""all"")
}}
}} ## end of if($sddl)
$qMessage = $queryForSet -f $_.name,$sddl
if (($sddl) -and ($force -or $pscmdlet.ShouldProcess($qMessage, $captionForSet)))
{{
$null = Set-PSSessionConfiguration -Name $_.Name -SecurityDescriptorSddl $sddl -NoServiceRestart -force -WarningAction 0
}}
}} ## end of foreach-object
}}
catch {{
throw
}} # end of catch
}} # end of end block
}} # end of Enable-PSRemoting
Enable-PSRemoting -force $args[0] -queryForRegisterDefault $args[1] -captionForRegisterDefault $args[2] -queryForSet $args[3] -captionForSet $args[4] -whatif:$args[5] -confirm:$args[6] -skipNetworkProfileCheck $args[7]
" ;
private const string _workflowConfigXml = @"
<PlugInConfiguration xmlns='http://schemas.microsoft.com/wbem/wsman/1/config/PluginConfiguration'
Name='Microsoft.Powershell.Workflow'
Filename='%windir%\system32\pwrshplugin.dll'
SDKVersion='2'
XmlRenderingType='text'
UseSharedProcess='true'
ProcessIdleTimeoutSec='1209600'
OutputBufferingMode='Block'
Enabled='True'
>
<InitializationParameters>
<Param Name='PSVersion' Value='{0}' />
<Param Name='AssemblyName' Value='Microsoft.PowerShell.Workflow.ServiceCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL'/>
<Param Name='PSSessionConfigurationTypeName' Value='Microsoft.PowerShell.Workflow.PSWorkflowSessionConfiguration'/>
<Param Name='SessionConfigurationData'
Value ='
<SessionConfigurationData>
<Param Name="ModulesToImport" Value="%windir%\system32\windowspowershell\v1.0\Modules\PSWorkflow" />
<Param Name="PrivateData">
<PrivateData>
<Param Name="enablevalidation" Value="true" />
</PrivateData>
</Param>
</SessionConfigurationData>
'
/>
</InitializationParameters>
<Resources>
<Resource ResourceUri='http://schemas.microsoft.com/powershell/Microsoft.Powershell.Workflow' SupportsOptions='true' ExactMatch='true'>
<Security Uri='http://schemas.microsoft.com/powershell/Microsoft.PowerShell.Workflow' Sddl='{1}' ExactMatch='False'/>
<Capability Type='Shell' />
</Resource>
</Resources>
<Quotas MaxMemoryPerShellMB='1024' MaxIdleTimeoutms='2147483647' MaxConcurrentUsers='5' IdleTimeoutms='7200000' MaxProcessesPerShell='15' MaxConcurrentCommandsPerShell='1000' MaxShells='25' MaxShellsPerUser='25' />
</PlugInConfiguration>
" ;
2016-07-29 13:02:49 -07:00
private static ScriptBlock s_enableRemotingSb ;
2016-03-30 14:20:52 -07:00
#endregion
#region Constructors
static EnablePSRemotingCommand ()
{
string workflowConfigXml = string . Format ( CultureInfo . InvariantCulture , _workflowConfigXml ,
string . Format ( CultureInfo . InvariantCulture , "{0}.{1}" , PSVersionInfo . PSVersion . Major , PSVersionInfo . PSVersion . Minor ),
PSSessionConfigurationCommandBase . GetLocalSddl ());
string enableRemotingScript = string . Format ( CultureInfo . InvariantCulture ,
enableRemotingSbFormat , RemotingConstants . DefaultShellName ,
// Workflow endpoint configuration will be done through Register-PSSessionConfiguration
// when the new features are available.
workflowConfigXml , PSSessionConfigurationCommandBase . RemoteManagementUsersSID , PSSessionConfigurationCommandBase . InteractiveUsersSID ,
RemotingConstants . MaxIdleTimeoutMS );
// compile the script block statically and reuse the same instance
// everytime the command is run..This will save on parsing time.
2016-07-29 13:02:49 -07:00
s_enableRemotingSb = ScriptBlock . Create ( enableRemotingScript );
s_enableRemotingSb . LanguageMode = PSLanguageMode . FullLanguage ;
2016-03-30 14:20:52 -07:00
}
#endregion
#region Parameters
/// <summary>
/// Property that sets force parameter. This will allow
/// configuring the WinRM and enabling the session configurations
/// without prompting the user.
/// </summary>
[Parameter()]
public SwitchParameter Force
{
get
{
2016-07-29 13:02:49 -07:00
return _force ;
2016-03-30 14:20:52 -07:00
}
set
{
2016-07-29 13:02:49 -07:00
_force = value ;
2016-03-30 14:20:52 -07:00
}
}
2016-07-29 13:02:49 -07:00
private bool _force ;
2016-03-30 14:20:52 -07:00
/// <summary>
/// Property that will allow configuring WinRM with Public
/// profile exception enabled.
/// </summary>
[Parameter()]
public SwitchParameter SkipNetworkProfileCheck
{
2016-07-29 13:02:49 -07:00
get { return _skipNetworkProfileCheck ; }
set { _skipNetworkProfileCheck = value ; }
2016-03-30 14:20:52 -07:00
}
2016-07-29 13:02:49 -07:00
private bool _skipNetworkProfileCheck ;
2016-03-30 14:20:52 -07:00
#endregion
#region Cmdlet Overrides
/// <summary>
///
/// </summary>
/// <exception cref="InvalidOperationException">
/// 1. Either both "AssemblyName" and "ConfigurationTypeName" must be specified
/// or both must not be specified.
/// </exception>
protected override void BeginProcessing ()
{
// check if we have compatible WSMan
RemotingCommandUtil . CheckRemotingCmdletPrerequisites ();
PSSessionConfigurationCommandUtilities . ThrowIfNotAdministrator ();
}
/// <summary>
///
/// </summary>
protected override void EndProcessing ()
{
// gather -WhatIf, -Confirm parameter data and pass it to the script block
bool whatIf = false ;
// confirm is always true to start with
bool confirm = true ;
PSSessionConfigurationCommandUtilities . CollectShouldProcessParameters ( this , out whatIf , out confirm );
string captionMessage = RemotingErrorIdStrings . ERemotingCaption ;
string queryMessage = RemotingErrorIdStrings . ERemotingQuery ;
string setCaptionMessage = StringUtil . Format ( RemotingErrorIdStrings . CSShouldProcessAction , "Set-PSSessionConfiguration" );
string setQueryMessage = RemotingErrorIdStrings . EcsShouldProcessTarget ;
2016-07-29 13:02:49 -07:00
s_enableRemotingSb . InvokeUsingCmdlet (
2016-03-30 14:20:52 -07:00
contextCmdlet : this ,
useLocalScope : true ,
errorHandlingBehavior : ScriptBlock . ErrorHandlingBehavior . WriteToCurrentErrorPipe ,
dollarUnder : AutomationNull . Value ,
input : Utils . EmptyArray < object >(),
scriptThis : AutomationNull . Value ,
args : new object [] {
2016-07-29 13:02:49 -07:00
_force ,
2016-03-30 14:20:52 -07:00
queryMessage ,
captionMessage ,
setQueryMessage ,
setCaptionMessage ,
whatIf ,
confirm ,
2016-07-29 13:02:49 -07:00
_skipNetworkProfileCheck });
2016-03-30 14:20:52 -07:00
}
#endregion
}
#endregion
#region Disable - PSRemoting
/// <summary>
/// Disable-PSRemoting cmdlet
/// Only disable the network access to the Session Configuration. The
/// local access is still enabled
/// </summary>
#if ! CORECLR
[Cmdlet(VerbsLifecycle.Disable, RemotingConstants.PSRemotingNoun,
SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.Medium, HelpUri = "http://go.microsoft.com/fwlink/?LinkID=144298")]
#endif
public sealed class DisablePSRemotingCommand : PSCmdlet
{
# region Private Data
// To Escape { -- {{
// To Escape } -- }}
// To Escape " -- ""
private const string disablePSRemotingFormat = @"
function Disable-PSRemoting
{{
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact=""Medium"")]
param(
[Parameter()]
[switch]
$force,
[Parameter()]
[string]
$queryForSet,
[Parameter()]
[string]
$captionForSet,
[Parameter()]
[string]
$restartWinRMMessage
)
begin
{{
if ($pscmdlet.ShouldProcess($restartWinRMMessage))
{{
$svc = get-service winrm
if ($svc.Status -match ""Stopped"")
{{
Restart-Service winrm -force -confirm:$false
}}
}}
}} # end of begin block
end
{{
# Disable the network for all Session Configurations
Get-PSSessionConfiguration -Force:$force | % {{
if ($_.Enabled)
{{
$sddl = $null
if ($_.psobject.members[""SecurityDescriptorSddl""])
{{
$sddl = $_.psobject.members[""SecurityDescriptorSddl""].Value
}}
if (!$sddl)
{{
# Disable network users from accessing this configuration
$sddl = ""{0}""
}}
else
{{
# Construct SID for network users
[system.security.principal.wellknownsidtype]$evst = ""NetworkSid""
$networkSID = new-object system.security.principal.securityidentifier $evst,$null
# Add disable network to the existing sddl
$sd = new-object system.security.accesscontrol.commonsecuritydescriptor $false,$false,$sddl
$disableNetworkExists = $false
$sd.DiscretionaryAcl | % {{
if (($_.acequalifier -eq ""accessdenied"") -and ($_.securityidentifier -match $networkSID) -and ($_.AccessMask -eq 268435456))
{{
$disableNetworkExists = $true
}}
}}
if (!$disableNetworkExists)
{{
$sd.DiscretionaryAcl.AddAccess(""deny"", $networkSID, 268435456, ""None"", ""None"")
$sddl = $sd.GetSddlForm(""all"")
}}
else
{{
# since disable network GA already exists, we dont need to change anything.
$sddl = $null
}}
}} ## end of if(!$sddl)
$qMessage = $queryForSet -f $_.name,$sddl
if (($sddl) -and ($force -or $pscmdlet.ShouldProcess($qMessage, $captionForSet)))
{{
$null = Set-PSSessionConfiguration -Name $_.Name -SecurityDescriptorSddl $sddl -NoServiceRestart -force -WarningAction 0
}}
}} ## end of if($_.Enabled)
}} ## end of %
}} ## end of Process block
}}
Disable-PSRemoting -force:$args[0] -queryForSet $args[1] -captionForSet $args[2] -restartWinRMMessage $args[3] -whatif:$args[4] -confirm:$args[5]
" ;
2016-07-29 13:02:49 -07:00
private static ScriptBlock s_disableRemotingSb ;
2016-03-30 14:20:52 -07:00
#endregion Private Data
#region Constructors
static DisablePSRemotingCommand ()
{
string localSDDL = PSSessionConfigurationCommandBase . GetLocalSddl ();
string disableRemotingScript = string . Format ( CultureInfo . InvariantCulture , disablePSRemotingFormat , localSDDL );
// compile the script block statically and reuse the same instance
// everytime the command is run..This will save on parsing time.
2016-07-29 13:02:49 -07:00
s_disableRemotingSb = ScriptBlock . Create ( disableRemotingScript );
s_disableRemotingSb . LanguageMode = PSLanguageMode . FullLanguage ;
2016-03-30 14:20:52 -07:00
}
#endregion Constructors
#region Parameters
/// <summary>
/// Force parameter
/// </summary>
[Parameter()]
public SwitchParameter Force
{
get
{
2016-07-29 13:02:49 -07:00
return _force ;
2016-03-30 14:20:52 -07:00
}
set
{
2016-07-29 13:02:49 -07:00
_force = value ;
2016-03-30 14:20:52 -07:00
}
}
2016-07-29 13:02:49 -07:00
private bool _force ;
2016-03-30 14:20:52 -07:00
#endregion Parameters
#region Cmdlet Override
/// <summary>
/// Check for prerequisites and eleveation mode
/// </summary>
protected override void BeginProcessing ()
{
// check if we have compatible WSMan
RemotingCommandUtil . CheckRemotingCmdletPrerequisites ();
PSSessionConfigurationCommandUtilities . ThrowIfNotAdministrator ();
}
/// <summary>
/// Invoke Disable-PSRemoting
/// </summary>
protected override void EndProcessing ()
{
WriteWarning ( StringUtil . Format ( RemotingErrorIdStrings . DcsWarningMessage ));
WriteVerbose ( StringUtil . Format ( RemotingErrorIdStrings . EcsScriptMessageV , disablePSRemotingFormat ));
// gather -WhatIf, -Confirm parameter data and pass it to the script block
bool whatIf = false ;
// confirm is always true to start with
bool confirm = true ;
PSSessionConfigurationCommandUtilities . CollectShouldProcessParameters ( this , out whatIf , out confirm );
string captionMessage = StringUtil . Format ( RemotingErrorIdStrings . CSShouldProcessAction , "Set-PSSessionConfiguration" );
string queryMessage = RemotingErrorIdStrings . DisableRemotingShouldProcessTarget ;
string restartWinRMMessage = RemotingErrorIdStrings . RestartWinRMMessage ;
2016-07-29 13:02:49 -07:00
s_disableRemotingSb . InvokeUsingCmdlet (
2016-03-30 14:20:52 -07:00
contextCmdlet : this ,
useLocalScope : true ,
errorHandlingBehavior : ScriptBlock . ErrorHandlingBehavior . WriteToCurrentErrorPipe ,
dollarUnder : AutomationNull . Value ,
input : Utils . EmptyArray < object >(),
scriptThis : AutomationNull . Value ,
args : new object [] {
2016-07-29 13:02:49 -07:00
_force ,
2016-03-30 14:20:52 -07:00
queryMessage ,
captionMessage ,
restartWinRMMessage ,
whatIf ,
confirm });
}
#endregion Cmdlet Override
}
#endregion
#region Get - PSSessionCapability
/// <summary>
/// Gets the capabilities of a constrained endpoint on the local machine for a specific user.
/// </summary>
[Cmdlet(VerbsCommon.Get, "PSSessionCapability", HelpUri = "http://go.microsoft.com/fwlink/?LinkId=623709")]
[OutputType(new Type[] { typeof ( System . Management . Automation . CommandInfo ), typeof ( System . Management . Automation . Runspaces . InitialSessionState ) })]
public sealed class GetPSSessionCapabilityCommand : PSCmdlet
{
/// <summary>
/// Gets or sets the session name that should be queried for its session capabilities
/// </summary>
[Parameter(Mandatory = true, Position = 0)]
public string ConfigurationName { get ; set ; }
/// <summary>
/// Gets or sets the user name that should be applied to the session
/// </summary>
[Parameter(Mandatory = true, Position = 1)]
public string Username { get ; set ; }
/// <summary>
/// Gets or sets the switch that determines whether just the commands should be returned,
/// or the entire Initial Session State.
/// </summary>
[Parameter()]
public SwitchParameter Full { get ; set ; }
/// <summary>
/// </summary>
protected override void BeginProcessing ()
{
// Validate that WinRM is OK and that the user can query system state
RemotingCommandUtil . CheckRemotingCmdletPrerequisites ();
PSSessionConfigurationCommandUtilities . ThrowIfNotAdministrator ();
// Get the configuration for the given endpoint
Collection < PSObject > configurations = null ;
using ( PowerShellApi invoker = PowerShellApi . Create ( RunspaceMode . CurrentRunspace ))
{
invoker . AddCommand ( "Get-PSSessionConfiguration" ). AddParameter ( "Name" , this . ConfigurationName ). AddParameter ( "ErrorAction" , "Stop" );
try
{
// If the session name doesn't exist, this Invoke() throws
configurations = invoker . Invoke ();
}
catch ( ActionPreferenceStopException e )
{
ThrowTerminatingError ( new ErrorRecord ( e . ErrorRecord . Exception , "CouldNotFindSessionConfiguration" , ErrorCategory . ObjectNotFound , this . ConfigurationName ));
}
}
// The validator that will be applied to the role lookup
Func < string , bool > validator = ( role ) => true ;
if (! String . IsNullOrEmpty ( this . Username ))
{
if ( this . Username . IndexOf ( "\\" , StringComparison . OrdinalIgnoreCase ) >= 0 )
{
validator = null ;
// Convert DOMAIN\user to the upn (user@DOMAIN)
string [] upnComponents = this . Username . Split ( Utils . Separators . Backslash );
if ( upnComponents . Length == 2 )
{
this . Username = upnComponents [ 1 ] + "@" + upnComponents [ 0 ];
}
}
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
try
{
System . Security . Principal . WindowsPrincipal windowsPrincipal = new System . Security . Principal . WindowsPrincipal (
new System . Security . Principal . WindowsIdentity ( this . Username ));
validator = ( role ) => windowsPrincipal . IsInRole ( role );
}
2016-07-29 13:02:49 -07:00
catch ( SecurityException e )
2016-03-30 14:20:52 -07:00
{
// Identity could not be mapped
string message = StringUtil . Format ( RemotingErrorIdStrings . CouldNotResolveUsername , this . Username );
ArgumentException ioe = new ArgumentException ( message , e );
ErrorRecord er = new ErrorRecord ( ioe , "CouldNotResolveUsername" , ErrorCategory . InvalidArgument , this . Username );
ThrowTerminatingError ( er );
return ;
}
}
2016-07-29 13:02:49 -07:00
foreach ( PSObject foundConfiguration in configurations )
2016-03-30 14:20:52 -07:00
{
string configFilePath = null ;
PSPropertyInfo configFilePathProperty = foundConfiguration . Properties [ "ConfigFilePath" ];
2016-07-29 13:02:49 -07:00
if ( configFilePathProperty != null )
2016-03-30 14:20:52 -07:00
{
configFilePath = configFilePathProperty . Value as string ;
}
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
// If we could not get the config file, throw an error that it's not a configuration created with
// config file-based session configurations.
2016-07-29 13:02:49 -07:00
if ( configFilePath == null )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
string configurationName = ( string ) foundConfiguration . Properties [ "Name" ]. Value ;
2016-03-30 14:20:52 -07:00
string message = StringUtil . Format ( RemotingErrorIdStrings . SessionConfigurationMustBeFileBased , configurationName );
ArgumentException ioe = new ArgumentException ( message );
ErrorRecord er = new ErrorRecord ( ioe , "SessionConfigurationMustBeFileBased" , ErrorCategory . InvalidArgument , foundConfiguration );
WriteError ( er );
continue ;
}
InitialSessionState iss = InitialSessionState . CreateFromSessionConfigurationFile ( configFilePath , validator );
2016-07-29 13:02:49 -07:00
if ( this . Full )
2016-03-30 14:20:52 -07:00
{
WriteObject ( iss );
}
else
{
2016-07-29 13:02:49 -07:00
using ( PowerShellApi analyzer = PowerShellApi . Create ( iss ))
2016-03-30 14:20:52 -07:00
{
analyzer . AddCommand ( "Get-Command" ). AddParameter ( "CommandType" , "All" );
2016-07-29 13:02:49 -07:00
foreach ( PSObject output in analyzer . Invoke ())
2016-03-30 14:20:52 -07:00
{
WriteObject ( output );
}
}
}
}
}
}
#endregion
}
namespace Microsoft.PowerShell.Commands.Internal
{
/// <summary>
/// This class is public for implementation reasons only and should not be used.
/// </summary>
[SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", Justification = "Needed for resources from scripts")]
public static class RemotingErrorResources
{
/// <summary>
/// </summary>
public static string WinRMRestartWarning { get { return RemotingErrorIdStrings . WinRMRestartWarning ; } }
/// <summary>
/// </summary>
public static string CouldNotResolveRoleDefinitionPrincipal { get { return RemotingErrorIdStrings . CouldNotResolveRoleDefinitionPrincipal ; } }
}
}