Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
ConnectPSSession.cs1115 linesDownload Raw Back to commands
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