MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections.Generic;6using System.Collections.ObjectModel;7using System.Diagnostics.CodeAnalysis;8using System.Management.Automation;9using System.Management.Automation.Host;10using System.Management.Automation.Internal;11using System.Management.Automation.Remoting;12using System.Management.Automation.Remoting.Client;13using System.Management.Automation.Runspaces;14using System.Threading;15 16using Dbg = System.Management.Automation.Diagnostics;17 18namespace Microsoft.PowerShell.Commands19{20 /// <summary>21 /// This cmdlet connects PS sessions (RemoteRunspaces) that are in the Disconnected22 /// state and returns those PS session objects in the Opened state. One or more23 /// session objects can be specified for connecting, or a remote computer name can24 /// be specified and in this case all disconnected remote runspaces found on the25 /// remote computer will be connected and PSSession objects created on the local26 /// machine.27 ///28 /// The cmdlet can be used in the following ways:29 ///30 /// Connect a PS session object:31 /// > $session = New-PSSession serverName32 /// > Disconnect-PSSession $session33 /// > Connect-PSSession $session34 ///35 /// Connect a PS session by name:36 /// > Connect-PSSession $session.Name37 ///38 /// Connect a PS session by Id:39 /// > Connect-PSSession $session.Id40 ///41 /// Connect a collection of PS session:42 /// > Get-PSSession | Connect-PSSession43 ///44 /// Connect all disconnected PS sessions on a remote computer45 /// > Connect-PSSession serverName.46 /// </summary>47 [SuppressMessage("Microsoft.PowerShell", "PS1012:CallShouldProcessOnlyIfDeclaringSupport")]48 [Cmdlet(VerbsCommunications.Connect, "PSSession", SupportsShouldProcess = true, DefaultParameterSetName = ConnectPSSessionCommand.NameParameterSet,49 HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096694", RemotingCapability = RemotingCapability.OwnedByCommand)]50 [OutputType(typeof(PSSession))]51 public class ConnectPSSessionCommand : PSRunspaceCmdlet, IDisposable52 {53 #region Parameters54 55 private const string ComputerNameGuidParameterSet = "ComputerNameGuid";56 private const string ConnectionUriParameterSet = "ConnectionUri";57 private const string ConnectionUriGuidParameterSet = "ConnectionUriGuid";58 59 /// <summary>60 /// The PSSession object or objects to be connected.61 /// </summary>62 [Parameter(Position = 0,63 Mandatory = true,64 ValueFromPipelineByPropertyName = true,65 ValueFromPipeline = true,66 ParameterSetName = ConnectPSSessionCommand.SessionParameterSet)]67 [ValidateNotNullOrEmpty]68 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]69 public PSSession[] Session { get; set; }70 71 /// <summary>72 /// Computer names to connect to.73 /// </summary>74 [Parameter(Position = 0,75 ParameterSetName = ConnectPSSessionCommand.ComputerNameParameterSet,76 Mandatory = true)]77 [Parameter(ParameterSetName = ConnectPSSessionCommand.ComputerNameGuidParameterSet,78 Mandatory = true)]79 [ValidateNotNullOrEmpty]80 [Alias("Cn")]81 public override string[] ComputerName { get; set; }82 83 /// <summary>84 /// This parameters specifies the appname which identifies the connection85 /// end point on the remote machine. If this parameter is not specified86 /// then the value specified in DEFAULTREMOTEAPPNAME will be used. If that's87 /// not specified as well, then "WSMAN" will be used.88 /// </summary>89 [Parameter(ValueFromPipelineByPropertyName = true,90 ParameterSetName = ConnectPSSessionCommand.ComputerNameParameterSet)]91 [Parameter(ValueFromPipelineByPropertyName = true,92 ParameterSetName = ConnectPSSessionCommand.ComputerNameGuidParameterSet)]93 public string ApplicationName94 {95 get96 {97 return _appName;98 }99 100 set101 {102 _appName = ResolveAppName(value);103 }104 }105 106 private string _appName;107 108 /// <summary>109 /// If this parameter is not specified then the value specified in110 /// the environment variable DEFAULTREMOTESHELLNAME will be used. If111 /// this is not set as well, then Microsoft.PowerShell is used.112 /// </summary>113 [Parameter(ValueFromPipelineByPropertyName = true,114 ParameterSetName = ConnectPSSessionCommand.ComputerNameParameterSet)]115 [Parameter(ValueFromPipelineByPropertyName = true,116 ParameterSetName = ConnectPSSessionCommand.ComputerNameGuidParameterSet)]117 [Parameter(ValueFromPipelineByPropertyName = true,118 ParameterSetName = ConnectPSSessionCommand.ConnectionUriParameterSet)]119 [Parameter(ValueFromPipelineByPropertyName = true,120 ParameterSetName = ConnectPSSessionCommand.ConnectionUriGuidParameterSet)]121 public string ConfigurationName122 {123 get124 {125 return _shell;126 }127 128 set129 {130 _shell = ResolveShell(value);131 }132 }133 134 private string _shell;135 136 /// <summary>137 /// A complete URI(s) specified for the remote computer and shell to138 /// connect to and create a runspace for.139 /// </summary>140 [Parameter(Position = 0, Mandatory = true,141 ValueFromPipelineByPropertyName = true,142 ParameterSetName = ConnectPSSessionCommand.ConnectionUriParameterSet)]143 [Parameter(Position = 0, Mandatory = true,144 ValueFromPipelineByPropertyName = true,145 ParameterSetName = ConnectPSSessionCommand.ConnectionUriGuidParameterSet)]146 [ValidateNotNullOrEmpty]147 [Alias("URI", "CU")]148 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]149 public Uri[] ConnectionUri { get; set; }150 151 /// <summary>152 /// The AllowRedirection parameter enables the implicit redirection functionality.153 /// </summary>154 [Parameter(ParameterSetName = ConnectPSSessionCommand.ConnectionUriParameterSet)]155 [Parameter(ParameterSetName = ConnectPSSessionCommand.ConnectionUriGuidParameterSet)]156 public SwitchParameter AllowRedirection157 {158 get { return _allowRedirection; }159 160 set { _allowRedirection = value; }161 }162 163 private bool _allowRedirection = false;164 165 /// <summary>166 /// RemoteRunspaceId to retrieve corresponding PSSession167 /// object.168 /// </summary>169 [Parameter(ParameterSetName = ConnectPSSessionCommand.ComputerNameGuidParameterSet,170 Mandatory = true)]171 [Parameter(ParameterSetName = ConnectPSSessionCommand.ConnectionUriGuidParameterSet,172 Mandatory = true)]173 [Parameter(ParameterSetName = ConnectPSSessionCommand.InstanceIdParameterSet,174 Mandatory = true)]175 [ValidateNotNull]176 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]177 public override Guid[] InstanceId178 {179 get { return base.InstanceId; }180 181 set { base.InstanceId = value; }182 }183 184 /// <summary>185 /// Name of the remote runspaceinfo object.186 /// </summary>187 [Parameter(ParameterSetName = ConnectPSSessionCommand.NameParameterSet,188 Mandatory = true)]189 [Parameter(ParameterSetName = ConnectPSSessionCommand.ComputerNameParameterSet)]190 [Parameter(ParameterSetName = ConnectPSSessionCommand.ConnectionUriParameterSet)]191 [ValidateNotNullOrEmpty]192 public override string[] Name193 {194 get { return base.Name; }195 196 set { base.Name = value; }197 }198 199 /// <summary>200 /// Specifies the credentials of the user to impersonate in the201 /// remote machine. If this parameter is not specified then the202 /// credentials of the current user process will be assumed.203 /// </summary>204 [Parameter(ParameterSetName = ConnectPSSessionCommand.ComputerNameParameterSet)]205 [Parameter(ParameterSetName = ConnectPSSessionCommand.ComputerNameGuidParameterSet)]206 [Parameter(ParameterSetName = ConnectPSSessionCommand.ConnectionUriParameterSet)]207 [Parameter(ParameterSetName = ConnectPSSessionCommand.ConnectionUriGuidParameterSet)]208 [Credential]209 public PSCredential Credential210 {211 get212 {213 return _psCredential;214 }215 216 set217 {218 _psCredential = value;219 220 PSRemotingBaseCmdlet.ValidateSpecifiedAuthentication(Credential, CertificateThumbprint, Authentication);221 }222 }223 224 private PSCredential _psCredential;225 226 /// <summary>227 /// Use basic authentication to authenticate the user.228 /// </summary>229 [Parameter(ParameterSetName = ConnectPSSessionCommand.ComputerNameParameterSet)]230 [Parameter(ParameterSetName = ConnectPSSessionCommand.ComputerNameGuidParameterSet)]231 [Parameter(ParameterSetName = ConnectPSSessionCommand.ConnectionUriParameterSet)]232 [Parameter(ParameterSetName = ConnectPSSessionCommand.ConnectionUriGuidParameterSet)]233 public AuthenticationMechanism Authentication234 {235 get236 {237 return _authentication;238 }239 240 set241 {242 _authentication = value;243 244 PSRemotingBaseCmdlet.ValidateSpecifiedAuthentication(Credential, CertificateThumbprint, Authentication);245 }246 }247 248 private AuthenticationMechanism _authentication;249 250 /// <summary>251 /// Specifies the certificate thumbprint to be used to impersonate the user on the252 /// remote machine.253 /// </summary>254 [Parameter(ParameterSetName = ConnectPSSessionCommand.ComputerNameParameterSet)]255 [Parameter(ParameterSetName = ConnectPSSessionCommand.ComputerNameGuidParameterSet)]256 [Parameter(ParameterSetName = ConnectPSSessionCommand.ConnectionUriParameterSet)]257 [Parameter(ParameterSetName = ConnectPSSessionCommand.ConnectionUriGuidParameterSet)]258 public string CertificateThumbprint259 {260 get261 {262 return _thumbprint;263 }264 265 set266 {267 _thumbprint = value;268 269 PSRemotingBaseCmdlet.ValidateSpecifiedAuthentication(Credential, CertificateThumbprint, Authentication);270 }271 }272 273 private string _thumbprint;274 275 /// <summary>276 /// Port specifies the alternate port to be used in case the277 /// default ports are not used for the transport mechanism278 /// (port 80 for http and port 443 for useSSL)279 /// </summary>280 /// <remarks>281 /// Currently this is being accepted as a parameter. But in future282 /// support will be added to make this a part of a policy setting.283 /// When a policy setting is in place this parameter can be used284 /// to override the policy setting285 /// </remarks>286 [Parameter(ParameterSetName = ConnectPSSessionCommand.ComputerNameParameterSet)]287 [Parameter(ParameterSetName = ConnectPSSessionCommand.ComputerNameGuidParameterSet)]288 [ValidateRange((int)1, (int)UInt16.MaxValue)]289 public int Port { get; set; }290 291 /// <summary>292 /// This parameter suggests that the transport scheme to be used for293 /// remote connections is useSSL instead of the default http.Since294 /// there are only two possible transport schemes that are possible295 /// at this point, a SwitchParameter is being used to switch between296 /// the two.297 /// </summary>298 [Parameter(ParameterSetName = ConnectPSSessionCommand.ComputerNameParameterSet)]299 [Parameter(ParameterSetName = ConnectPSSessionCommand.ComputerNameGuidParameterSet)]300 [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "SSL")]301 public SwitchParameter UseSSL { get; set; }302 303 /// <summary>304 /// Extended session options. Used in this cmdlet to set server disconnect options.305 /// </summary>306 [Parameter(ParameterSetName = ConnectPSSessionCommand.ComputerNameParameterSet)]307 [Parameter(ParameterSetName = ConnectPSSessionCommand.ComputerNameGuidParameterSet)]308 [Parameter(ParameterSetName = ConnectPSSessionCommand.ConnectionUriParameterSet)]309 [Parameter(ParameterSetName = ConnectPSSessionCommand.ConnectionUriGuidParameterSet)]310 public PSSessionOption SessionOption { get; set; }311 312 /// <summary>313 /// Allows the user of the cmdlet to specify a throttling value314 /// for throttling the number of remote operations that can315 /// be executed simultaneously.316 /// </summary>317 [Parameter(ParameterSetName = ConnectPSSessionCommand.SessionParameterSet)]318 [Parameter(ParameterSetName = ConnectPSSessionCommand.ComputerNameParameterSet)]319 [Parameter(ParameterSetName = ConnectPSSessionCommand.IdParameterSet)]320 [Parameter(ParameterSetName = ConnectPSSessionCommand.ComputerNameGuidParameterSet)]321 [Parameter(ParameterSetName = ConnectPSSessionCommand.ConnectionUriParameterSet)]322 [Parameter(ParameterSetName = ConnectPSSessionCommand.ConnectionUriGuidParameterSet)]323 [Parameter(ParameterSetName = ConnectPSSessionCommand.NameParameterSet)]324 [Parameter(ParameterSetName = ConnectPSSessionCommand.InstanceIdParameterSet)]325 public int ThrottleLimit { get; set; } = 0;326 327 /// <summary>328 /// Overriding to suppress this parameter.329 /// </summary>330 public override string[] ContainerId331 {332 get333 {334 return null;335 }336 }337 338 /// <summary>339 /// Overriding to suppress this parameter.340 /// </summary>341 public override Guid[] VMId342 {343 get344 {345 return null;346 }347 }348 349 /// <summary>350 /// Overriding to suppress this parameter.351 /// </summary>352 public override string[] VMName353 {354 get355 {356 return null;357 }358 }359 360 #endregion361 362 #region Cmdlet Overrides363 364 /// <summary>365 /// Set up the ThrottleManager for runspace connect processing.366 /// </summary>367 protected override void BeginProcessing()368 {369 base.BeginProcessing();370 371 _throttleManager.ThrottleLimit = ThrottleLimit;372 _throttleManager.ThrottleComplete += HandleThrottleConnectComplete;373 }374 375 /// <summary>376 /// Perform runspace connect processing on all input.377 /// </summary>378 protected override void ProcessRecord()379 {380 Collection<PSSession> psSessions;381 382 try383 {384 if (ParameterSetName == ConnectPSSessionCommand.ComputerNameParameterSet ||385 ParameterSetName == ConnectPSSessionCommand.ComputerNameGuidParameterSet ||386 ParameterSetName == ConnectPSSessionCommand.ConnectionUriParameterSet ||387 ParameterSetName == ConnectPSSessionCommand.ConnectionUriGuidParameterSet)388 {389 // Query remote computers for disconnected sessions.390 psSessions = QueryForDisconnectedSessions();391 }392 else393 {394 // Collect provided disconnected sessions.395 psSessions = CollectDisconnectedSessions();396 }397 }398 catch (PSRemotingDataStructureException)399 {400 // Allow cmdlet to end and then re-throw exception.401 _operationsComplete.Set();402 throw;403 }404 catch (PSRemotingTransportException)405 {406 // Allow cmdlet to end and then re-throw exception.407 _operationsComplete.Set();408 throw;409 }410 catch (RemoteException)411 {412 // Allow cmdlet to end and then re-throw exception.413 _operationsComplete.Set();414 throw;415 }416 catch (InvalidRunspaceStateException)417 {418 // Allow cmdlet to end and then re-throw exception.419 _operationsComplete.Set();420 throw;421 }422 423 ConnectSessions(psSessions);424 }425 426 /// <summary>427 /// End processing clean up.428 /// </summary>429 protected override void EndProcessing()430 {431 _throttleManager.EndSubmitOperations();432 433 // Wait for all connect operations to complete.434 _operationsComplete.WaitOne();435 436 // If there are failed connect operations due to stale437 // session state then perform the query retry here.438 if (_failedSessions.Count > 0)439 {440 RetryFailedSessions();441 }442 443 // Read all objects in the stream pipeline.444 while (_stream.ObjectReader.Count > 0)445 {446 object streamObject = _stream.ObjectReader.Read();447 WriteStreamObject((Action<Cmdlet>)streamObject);448 }449 450 _stream.ObjectWriter.Close();451 452 // Add all successfully connected sessions to local repository.453 foreach (PSSession psSession in _allSessions)454 {455 if (psSession.Runspace.RunspaceStateInfo.State == RunspaceState.Opened)456 {457 // Make sure that this session is included in the PSSession repository.458 // If it already exists then replace it because we want the latest/connected session in the repository.459 this.RunspaceRepository.AddOrReplace(psSession);460 }461 }462 }463 464 /// <summary>465 /// User has signaled a stop for this cmdlet.466 /// </summary>467 protected override void StopProcessing()468 {469 // Close the output stream for any further writes.470 _stream.ObjectWriter.Close();471 472 // Stop any remote server queries that may be running.473 _queryRunspaces.StopAllOperations();474 475 // Signal the ThrottleManager to stop any further476 // PSSession connect processing.477 _throttleManager.StopAllOperations();478 479 // Signal the Retry throttle manager in case it is running.480 _retryThrottleManager.StopAllOperations();481 }482 483 #endregion484 485 #region ConnectRunspaceOperation Class486 487 /// <summary>488 /// Throttle class to perform a remoterunspace connect operation.489 /// </summary>490 private sealed class ConnectRunspaceOperation : IThrottleOperation491 {492 private PSSession _session;493 private PSSession _oldSession;494 private readonly ObjectStream _writeStream;495 private readonly Collection<PSSession> _retryList;496 private readonly PSHost _host;497 private readonly QueryRunspaces _queryRunspaces;498 private static readonly object s_LockObject = new object();499 500 internal ConnectRunspaceOperation(501 PSSession session,502 ObjectStream stream,503 PSHost host,504 QueryRunspaces queryRunspaces,505 Collection<PSSession> retryList)506 {507 _session = session;508 _writeStream = stream;509 _host = host;510 _queryRunspaces = queryRunspaces;511 _retryList = retryList;512 _session.Runspace.StateChanged += StateCallBackHandler;513 }514 515 internal override void StartOperation()516 {517 bool startedSuccessfully = true;518 519 Exception ex = null;520 try521 {522 if (_queryRunspaces != null)523 {524 PSSession newSession = QueryForSession(_session);525 if (newSession != null)526 {527 _session.Runspace.StateChanged -= StateCallBackHandler;528 _oldSession = _session;529 _session = newSession;530 _session.Runspace.StateChanged += StateCallBackHandler;531 _session.Runspace.ConnectAsync();532 }533 else534 {535 startedSuccessfully = false;536 }537 }538 else539 {540 _session.Runspace.ConnectAsync();541 }542 }543 catch (PSInvalidOperationException e)544 {545 ex = e;546 }547 catch (InvalidRunspacePoolStateException e)548 {549 ex = e;550 }551 catch (RuntimeException e)552 {553 ex = e;554 }555 556 if (ex != null)557 {558 startedSuccessfully = false;559 WriteConnectFailed(ex, _session);560 }561 562 if (!startedSuccessfully)563 {564 // We are done at this point. Notify throttle manager.565 _session.Runspace.StateChanged -= StateCallBackHandler;566 SendStartComplete();567 }568 }569 570 internal override void StopOperation()571 {572 _queryRunspaces?.StopAllOperations();573 574 _session.Runspace.StateChanged -= StateCallBackHandler;575 SendStopComplete();576 }577 578 internal override event EventHandler<OperationStateEventArgs> OperationComplete;579 580 internal PSSession QueryForSession(PSSession session)581 {582 Collection<WSManConnectionInfo> wsManConnectionInfos = new Collection<WSManConnectionInfo>();583 wsManConnectionInfos.Add(session.Runspace.ConnectionInfo as WSManConnectionInfo);584 585 Exception ex = null;586 Collection<PSSession> sessions = null;587 try588 {589 sessions = _queryRunspaces.GetDisconnectedSessions(wsManConnectionInfos, _host, _writeStream, null,590 0, SessionFilterState.Disconnected, new Guid[] { session.InstanceId }, null, null);591 }592 catch (RuntimeException e)593 {594 ex = e;595 }596 597 if (ex != null)598 {599 WriteConnectFailed(ex, session);600 return null;601 }602 603 if (sessions.Count != 1)604 {605 ex = new RuntimeException(StringUtil.Format(RemotingErrorIdStrings.CannotFindSessionForConnect,606 session.Name, session.ComputerName));607 608 WriteConnectFailed(ex, session);609 return null;610 }611 612 return sessions[0];613 }614 615 private void StateCallBackHandler(object sender, RunspaceStateEventArgs eArgs)616 {617 if (eArgs.RunspaceStateInfo.State == RunspaceState.Connecting ||618 eArgs.RunspaceStateInfo.State == RunspaceState.Disconnecting ||619 eArgs.RunspaceStateInfo.State == RunspaceState.Disconnected)620 {621 return;622 }623 624 Dbg.Assert(eArgs.RunspaceStateInfo.State != RunspaceState.BeforeOpen, "Can't reconnect a session that hasn't been previously Opened");625 Dbg.Assert(eArgs.RunspaceStateInfo.State != RunspaceState.Opening, "Can't reconnect a session that hasn't been previously Opened");626 627 if (eArgs.RunspaceStateInfo.State == RunspaceState.Opened)628 {629 // Connect operation succeeded, write the PSSession object.630 WriteConnectedPSSession();631 }632 else633 {634 // Check to see if failure is due to stale PSSession error and635 // add to retry list if this is the case.636 bool writeError = true;637 if (_queryRunspaces == null)638 {639 PSRemotingTransportException transportException = eArgs.RunspaceStateInfo.Reason as PSRemotingTransportException;640 if (transportException != null &&641 transportException.ErrorCode == WSManNativeApi.ERROR_WSMAN_INUSE_CANNOT_RECONNECT)642 {643 lock (s_LockObject)644 {645 _retryList.Add(_session);646 }647 648 writeError = false;649 }650 }651 652 if (writeError)653 {654 // Connect operation failed, write error.655 WriteConnectFailed(eArgs.RunspaceStateInfo.Reason, _session);656 }657 }658 659 _session.Runspace.StateChanged -= StateCallBackHandler;660 SendStartComplete();661 }662 663 private void SendStartComplete()664 {665 OperationStateEventArgs operationStateEventArgs = new OperationStateEventArgs();666 operationStateEventArgs.OperationState = OperationState.StartComplete;667 OperationComplete.SafeInvoke(this, operationStateEventArgs);668 }669 670 private void SendStopComplete()671 {672 OperationStateEventArgs operationStateEventArgs = new OperationStateEventArgs();673 operationStateEventArgs.OperationState = OperationState.StopComplete;674 OperationComplete.SafeInvoke(this, operationStateEventArgs);675 }676 677 private void WriteConnectedPSSession()678 {679 // Use temporary variable because we need to preserve _session class variable680 // for later clean up.681 PSSession outSession = _session;682 if (_queryRunspaces != null)683 {684 lock (s_LockObject)685 {686 // Pass back the original session if possible.687 if (_oldSession != null &&688 _oldSession.InsertRunspace(_session.Runspace as RemoteRunspace))689 {690 outSession = _oldSession;691 _retryList.Add(_oldSession);692 }693 else694 {695 _retryList.Add(_session);696 }697 }698 }699 700 if (_writeStream.ObjectWriter.IsOpen)701 {702 // This code is based on ThrottleManager infrastructure703 // and this particular method may be called on a thread that704 // is different from Pipeline Execution Thread. Hence using705 // a delegate to perform the WriteObject.706 Action<Cmdlet> outputWriter = (Cmdlet cmdlet) => cmdlet.WriteObject(outSession);707 _writeStream.ObjectWriter.Write(outputWriter);708 }709 }710 711 private void WriteConnectFailed(712 Exception e,713 PSSession session)714 {715 if (_writeStream.ObjectWriter.IsOpen)716 {717 string FQEID = "PSSessionConnectFailed";718 Exception reason;719 if (e != null && !string.IsNullOrEmpty(e.Message))720 {721 // Update fully qualified error Id if we have a transport error.722 PSRemotingTransportException transportException = e as PSRemotingTransportException;723 if (transportException != null)724 {725 FQEID = WSManTransportManagerUtils.GetFQEIDFromTransportError(transportException.ErrorCode, FQEID);726 }727 728 reason = new RuntimeException(729 StringUtil.Format(RemotingErrorIdStrings.RunspaceConnectFailedWithMessage, session.Name, e.Message),730 e);731 }732 else733 {734 reason = new RuntimeException(735 StringUtil.Format(RemotingErrorIdStrings.RunspaceConnectFailed, session.Name,736 session.Runspace.RunspaceStateInfo.State.ToString()), null);737 }738 739 ErrorRecord errorRecord = new ErrorRecord(reason, FQEID, ErrorCategory.InvalidOperation, null);740 Action<Cmdlet> errorWriter = (Cmdlet cmdlet) => cmdlet.WriteError(errorRecord);741 _writeStream.ObjectWriter.Write(errorWriter);742 }743 }744 }745 746 #endregion747 748 #region Private Methods749 750 /// <summary>751 /// Enum indicating an override on which parameter is used to filter752 /// local sessions.753 /// </summary>754 private enum OverrideParameter755 {756 /// <summary>757 /// No override.758 /// </summary>759 None = 0,760 761 /// <summary>762 /// Use the Name parameter as a filter.763 /// </summary>764 Name = 1,765 766 /// <summary>767 /// Use the InstanceId parameter as a filter.768 /// </summary>769 InstanceId = 2770 }771 772 /// <summary>773 /// Retrieves a collection of disconnected PSSession objects queried from774 /// remote computers.775 /// </summary>776 /// <returns>Collection of disconnected PSSession objects.</returns>777 private Collection<PSSession> QueryForDisconnectedSessions()778 {779 Collection<WSManConnectionInfo> connectionInfos = GetConnectionObjects();780 Collection<PSSession> psSessions = _queryRunspaces.GetDisconnectedSessions(connectionInfos, this.Host, _stream,781 this.RunspaceRepository, ThrottleLimit,782 SessionFilterState.Disconnected, this.InstanceId, this.Name, ConfigurationName);783 784 // Write any error output from stream object.785 Collection<object> streamObjects = _stream.ObjectReader.NonBlockingRead();786 foreach (object streamObject in streamObjects)787 {788 WriteStreamObject((Action<Cmdlet>)streamObject);789 }790 791 return psSessions;792 }793 794 /// <summary>795 /// Creates a collection of PSSession objects based on cmdlet parameters.796 /// </summary>797 /// <param name="overrideParam">OverrideParameter.</param>798 /// <returns>Collection of PSSession objects in disconnected state.</returns>799 private Collection<PSSession> CollectDisconnectedSessions(OverrideParameter overrideParam = OverrideParameter.None)800 {801 Collection<PSSession> psSessions = new Collection<PSSession>();802 803 // Get all remote runspaces to disconnect.804 if (ParameterSetName == DisconnectPSSessionCommand.SessionParameterSet)805 {806 if (Session != null)807 {808 foreach (PSSession psSession in Session)809 {810 psSessions.Add(psSession);811 }812 }813 }814 else815 {816 Dictionary<Guid, PSSession> entries = null;817 818 switch (overrideParam)819 {820 case OverrideParameter.None:821 entries = GetMatchingRunspaces(false, true);822 break;823 824 case OverrideParameter.Name:825 entries = GetMatchingRunspacesByName(false, true);826 break;827 828 case OverrideParameter.InstanceId:829 entries = GetMatchingRunspacesByRunspaceId(false, true);830 break;831 }832 833 if (entries != null)834 {835 foreach (PSSession psSession in entries.Values)836 {837 psSessions.Add(psSession);838 }839 }840 }841 842 return psSessions;843 }844 845 /// <summary>846 /// Connect all disconnected sessions.847 /// </summary>848 private void ConnectSessions(Collection<PSSession> psSessions)849 {850 List<IThrottleOperation> connectOperations = new List<IThrottleOperation>();851 852 // Create a disconnect operation for each runspace to disconnect.853 foreach (PSSession psSession in psSessions)854 {855 if (ShouldProcess(psSession.Name, VerbsCommunications.Connect))856 {857 if (psSession.ComputerType != TargetMachineType.RemoteMachine)858 {859 // PS session disconnection is not supported for VM/Container sessions.860 string msg = StringUtil.Format(RemotingErrorIdStrings.RunspaceCannotBeConnectedForVMContainerSession,861 psSession.Name, psSession.ComputerName, psSession.ComputerType);862 Exception reason = new PSNotSupportedException(msg);863 ErrorRecord errorRecord = new ErrorRecord(reason, "CannotConnectVMContainerSession", ErrorCategory.InvalidOperation, psSession);864 WriteError(errorRecord);865 }866 else if (psSession.Runspace.RunspaceStateInfo.State == RunspaceState.Disconnected &&867 psSession.Runspace.RunspaceAvailability == RunspaceAvailability.None)868 {869 // Can only connect sessions that are in Disconnected state.870 // Update session connection information based on cmdlet parameters.871 UpdateConnectionInfo(psSession.Runspace.ConnectionInfo as WSManConnectionInfo);872 873 ConnectRunspaceOperation connectOperation = new ConnectRunspaceOperation(874 psSession,875 _stream,876 this.Host,877 null,878 _failedSessions);879 connectOperations.Add(connectOperation);880 }881 else if (psSession.Runspace.RunspaceStateInfo.State != RunspaceState.Opened)882 {883 // Write error record if runspace is not already in the Opened state.884 string msg = StringUtil.Format(RemotingErrorIdStrings.RunspaceCannotBeConnected, psSession.Name);885 Exception reason = new RuntimeException(msg);886 ErrorRecord errorRecord = new ErrorRecord(reason, "PSSessionConnectFailed", ErrorCategory.InvalidOperation, psSession);887 WriteError(errorRecord);888 }889 else890 {891 // Session is already connected. Write to output.892 WriteObject(psSession);893 }894 }895 896 _allSessions.Add(psSession);897 }898 899 if (connectOperations.Count > 0)900 {901 // Make sure operations are not set as complete while processing input.902 _operationsComplete.Reset();903 904 // Submit list of connect operations.905 _throttleManager.SubmitOperations(connectOperations);906 907 // Write any output now.908 Collection<object> streamObjects = _stream.ObjectReader.NonBlockingRead();909 foreach (object streamObject in streamObjects)910 {911 WriteStreamObject((Action<Cmdlet>)streamObject);912 }913 }914 }915 916 /// <summary>917 /// Handles the connect throttling complete event from the ThrottleManager.918 /// </summary>919 /// <param name="sender">Sender.</param>920 /// <param name="eventArgs">EventArgs.</param>921 private void HandleThrottleConnectComplete(object sender, EventArgs eventArgs)922 {923 _operationsComplete.Set();924 }925 926 private Collection<WSManConnectionInfo> GetConnectionObjects()927 {928 Collection<WSManConnectionInfo> connectionInfos = new Collection<WSManConnectionInfo>();929 930 if (ParameterSetName == ConnectPSSessionCommand.ComputerNameParameterSet ||931 ParameterSetName == ConnectPSSessionCommand.ComputerNameGuidParameterSet)932 {933 string scheme = UseSSL.IsPresent ? WSManConnectionInfo.HttpsScheme : WSManConnectionInfo.HttpScheme;934 935 foreach (string computerName in ComputerName)936 {937 WSManConnectionInfo connectionInfo = new WSManConnectionInfo();938 connectionInfo.Scheme = scheme;939 connectionInfo.ComputerName = ResolveComputerName(computerName);940 connectionInfo.AppName = ApplicationName;941 connectionInfo.ShellUri = ConfigurationName;942 connectionInfo.Port = Port;943 if (CertificateThumbprint != null)944 {945 connectionInfo.CertificateThumbprint = CertificateThumbprint;946 }947 else948 {949 connectionInfo.Credential = Credential;950 }951 952 connectionInfo.AuthenticationMechanism = Authentication;953 UpdateConnectionInfo(connectionInfo);954 955 connectionInfos.Add(connectionInfo);956 }957 }958 else if (ParameterSetName == ConnectPSSessionCommand.ConnectionUriParameterSet ||959 ParameterSetName == ConnectPSSessionCommand.ConnectionUriGuidParameterSet)960 {961 foreach (var connectionUri in ConnectionUri)962 {963 WSManConnectionInfo connectionInfo = new WSManConnectionInfo();964 connectionInfo.ConnectionUri = connectionUri;965 connectionInfo.ShellUri = ConfigurationName;966 if (CertificateThumbprint != null)967 {968 connectionInfo.CertificateThumbprint = CertificateThumbprint;969 }970 else971 {972 connectionInfo.Credential = Credential;973 }974 975 connectionInfo.AuthenticationMechanism = Authentication;976 UpdateConnectionInfo(connectionInfo);977 978 connectionInfos.Add(connectionInfo);979 }980 }981 982 return connectionInfos;983 }984 985 /// <summary>986 /// Updates connection info with the data read from cmdlet's parameters.987 /// </summary>988 /// <param name="connectionInfo"></param>989 private void UpdateConnectionInfo(WSManConnectionInfo connectionInfo)990 {991 if (ParameterSetName != ConnectPSSessionCommand.ConnectionUriParameterSet &&992 ParameterSetName != ConnectPSSessionCommand.ConnectionUriGuidParameterSet)993 {994 // uri redirection is supported only with URI parameter set995 connectionInfo.MaximumConnectionRedirectionCount = 0;996 }997 998 if (!_allowRedirection)999 {1000 // uri redirection required explicit user consent1001 connectionInfo.MaximumConnectionRedirectionCount = 0;1002 }1003 1004 // Update the connectionInfo object with passed in session options.1005 if (SessionOption != null)1006 {1007 connectionInfo.SetSessionOptions(SessionOption);1008 }1009 }1010 1011 private void RetryFailedSessions()1012 {1013 using (ManualResetEvent retrysComplete = new ManualResetEvent(false))1014 {1015 Collection<PSSession> connectedSessions = new Collection<PSSession>();1016 List<IThrottleOperation> retryConnectionOperations = new List<IThrottleOperation>();1017 _retryThrottleManager.ThrottleLimit = ThrottleLimit;1018 _retryThrottleManager.ThrottleComplete += (sender, eventArgs) =>1019 {1020 try1021 {1022 retrysComplete.Set();1023 }1024 catch (ObjectDisposedException) { }1025 };1026 1027 foreach (var session in _failedSessions)1028 {1029 retryConnectionOperations.Add(new ConnectRunspaceOperation(1030 session,1031 _stream,1032 this.Host,1033 new QueryRunspaces(),1034 connectedSessions));1035 }1036 1037 _retryThrottleManager.SubmitOperations(retryConnectionOperations);1038 _retryThrottleManager.EndSubmitOperations();1039 1040 retrysComplete.WaitOne();1041 1042 // Add or replace all successfully connected sessions to the local repository.1043 foreach (var session in connectedSessions)1044 {1045 this.RunspaceRepository.AddOrReplace(session);1046 }1047 }1048 }1049 1050 #endregion1051 1052 #region IDisposable1053 1054 /// <summary>1055 /// Dispose method of IDisposable. Gets called in the following cases:1056 /// 1. Pipeline explicitly calls dispose on cmdlets1057 /// 2. Called by the garbage collector.1058 /// </summary>1059 public void Dispose()1060 {1061 Dispose(true);1062 1063 GC.SuppressFinalize(this);1064 }1065 1066 /// <summary>1067 /// Internal dispose method which does the actual1068 /// dispose operations and finalize suppressions.1069 /// </summary>1070 /// <param name="disposing">Whether method is called1071 /// from Dispose or destructor</param>1072 private void Dispose(bool disposing)1073 {1074 if (disposing)1075 {1076 _throttleManager.Dispose();1077 1078 _operationsComplete.WaitOne();1079 _operationsComplete.Dispose();1080 1081 _throttleManager.ThrottleComplete -= HandleThrottleConnectComplete;1082 _retryThrottleManager.Dispose();1083 1084 _stream.Dispose();1085 }1086 }1087 1088 #endregion1089 1090 #region Private Members1091 1092 // Collection of PSSessions to be connected.1093 private readonly Collection<PSSession> _allSessions = new Collection<PSSession>();1094 1095 // Object used to perform network disconnect operations in a limited manner.1096 private readonly ThrottleManager _throttleManager = new ThrottleManager();1097 1098 // Event indicating that all disconnect operations through the ThrottleManager1099 // are complete.1100 private readonly ManualResetEvent _operationsComplete = new ManualResetEvent(true);1101 1102 // Object used for querying remote runspaces.1103 private readonly QueryRunspaces _queryRunspaces = new QueryRunspaces();1104 1105 // Object to collect output data from multiple threads.1106 private readonly ObjectStream _stream = new ObjectStream();1107 1108 // Support for connection retry on failure.1109 private readonly ThrottleManager _retryThrottleManager = new ThrottleManager();1110 private readonly Collection<PSSession> _failedSessions = new Collection<PSSession>();1111 1112 #endregion1113 }1114}1115 