@@ -71,168 +71,175 @@ function Update-ServiceStatus {
7171 $svcControlBlock = {
7272 $group = $_.Group
7373 $computerName = $_.Name
74- # Create a CIM session preferring DCOM to avoid requiring WinRM on the target machine.
75- # CIM instances become deserialized when crossing runspace boundaries and lose their
76- # session context, causing Invoke-CimMethod to attempt a new WinRM connection by default.
77- # Using DCOM avoids this WinRM dependency for machines where WinRM is not configured.
78- $splatCimDcomOption = @ {
79- Protocol = " Dcom"
74+ $cimSession = $null
75+ $splatCimSession = @ {
76+ ComputerName = $computerName
77+ ErrorAction = " Stop"
8078 }
81- $cimDcomOption = New-CimSessionOption @splatCimDcomOption
82- $splatCimSessionDcom = @ {
83- ComputerName = $computerName
84- SessionOption = $cimDcomOption
85- ErrorAction = " Stop"
79+ if ($Credential ) {
80+ $splatCimSession.Credential = $Credential
8681 }
82+
8783 try {
88- $cimSession = New-CimSession @splatCimSessionDcom
89- } catch {
90- # Fall back to default protocol (WinRM) if DCOM is unavailable
84+ # Create a CIM session preferring DCOM to avoid requiring WinRM on the target machine.
85+ # CIM instances become deserialized when crossing runspace boundaries and lose their
86+ # session context, causing Invoke-CimMethod to attempt a new WinRM connection by default.
87+ # Using DCOM avoids this WinRM dependency for machines where WinRM is not configured.
88+ $splatCimDcomOption = @ {
89+ Protocol = " Dcom"
90+ }
91+ $cimDcomOption = New-CimSessionOption @splatCimDcomOption
92+ $splatCimSession.SessionOption = $cimDcomOption
9193 try {
92- $cimSession = New-CimSession - ComputerName $computerName - ErrorAction " Stop "
94+ $cimSession = New-CimSession @splatCimSession
9395 } catch {
94- $cimSession = $null
95- }
96- }
97- $servicePriorityCollection = $group.ServicePriority | Select-Object - unique | Sort-Object - Property @ { Expression = { [int ]$_ }; Descending = $action -ne ' stop' }
98- foreach ($priority in $servicePriorityCollection ) {
99- $services = $group | Where-Object { $_.ServicePriority -eq $priority }
100- $servicesToRestart = @ ()
101- foreach ($service in $services ) {
102- if (' dbatools.DbaSqlService' -in $service.PSObject.TypeNames ) {
103- $cimObject = $service._CimObject
104- if (($cimObject.State -eq ' Running' -and $action -eq ' start' ) -or ($cimObject.State -eq ' Stopped' -and $action -eq ' stop' )) {
105- $service | Add-Member - Force - NotePropertyName Status - NotePropertyValue ' Successful' - PassThru |
106- Add-Member - Force - NotePropertyName Message - NotePropertyValue " The service is already $actionText , no action required" - PassThru
107- } elseif ($cimObject.StartMode -eq ' Disabled' -and $action -in ' start' , ' restart' ) {
108- $service | Add-Member - Force - NotePropertyName Status - NotePropertyValue ' Failed' - PassThru |
109- Add-Member - Force - NotePropertyName Message - NotePropertyValue " The service is disabled and cannot be $actionText " - PassThru
110- } else {
111- $servicesToRestart += $service
112- }
113- } else {
114- throw " Unknown object in pipeline - make sure to use Get-DbaService cmdlet"
115- }
116- }
117- # Set desired $action
118- if ($action -in ' start' , ' restart' ) {
119- $methodName = ' StartService'
120- $desiredState = ' Running'
121- $undesiredState = ' Stopped'
122- } elseif ($action -eq ' stop' ) {
123- $methodName = ' StopService'
124- $desiredState = ' Stopped'
125- $undesiredState = ' Running'
126- }
127- $invokeResults = @ ()
128- foreach ($service in $servicesToRestart ) {
129- if ($Pscmdlet.ShouldProcess (" Sending $action request to service $ ( $service.ServiceName ) on $ ( $service.ComputerName ) " )) {
130- # Get a fresh CIM instance via the DCOM session to avoid issues with deserialized
131- # CIM objects crossing runspace boundaries without their session context.
132- if ($cimSession ) {
96+ # Fall back to default protocol (WinRM) if DCOM is unavailable
97+ $null = $splatCimSession.Remove (" SessionOption" )
13398 try {
134- $splatGetFreshCim = @ {
135- CimSession = $cimSession
136- Namespace = " root\cimv2"
137- Query = " SELECT * FROM Win32_Service WHERE Name = '$ ( $service.ServiceName ) '"
138- }
139- $freshCimObj = Get-CimInstance @splatGetFreshCim
140- if ($freshCimObj ) {
141- $service._CimObject = $freshCimObj
142- }
99+ $cimSession = New-CimSession @splatCimSession
143100 } catch {
144- # Fall back to using the existing deserialized CIM object if session refresh fails
101+ $cimSession = $null
145102 }
146103 }
147- # Invoke corresponding CIM method
148- $invokeResult = Invoke-CimMethod - InputObject $service._CimObject - MethodName $methodName
149- $invokeResults += [psobject ]@ {
150- InvokeResult = $invokeResult
151- ServiceState = $invokeResult.State
152- ServiceExitCode = $invokeResult.ReturnValue
153- CheckPending = $true
154- Service = $service
104+ $servicePriorityCollection = $group.ServicePriority | Select-Object - unique | Sort-Object - Property @ { Expression = { [int ]$_ }; Descending = $action -ne " stop" }
105+ foreach ($priority in $servicePriorityCollection ) {
106+ $services = $group | Where-Object { $_.ServicePriority -eq $priority }
107+ $servicesToRestart = @ ()
108+ foreach ($service in $services ) {
109+ if (" dbatools.DbaSqlService" -in $service.PSObject.TypeNames ) {
110+ $cimObject = $service._CimObject
111+ if (($cimObject.State -eq " Running" -and $action -eq " start" ) -or ($cimObject.State -eq " Stopped" -and $action -eq " stop" )) {
112+ $service | Add-Member - Force - NotePropertyName Status - NotePropertyValue " Successful" - PassThru |
113+ Add-Member - Force - NotePropertyName Message - NotePropertyValue " The service is already $actionText , no action required" - PassThru
114+ } elseif ($cimObject.StartMode -eq " Disabled" -and $action -in " start" , " restart" ) {
115+ $service | Add-Member - Force - NotePropertyName Status - NotePropertyValue " Failed" - PassThru |
116+ Add-Member - Force - NotePropertyName Message - NotePropertyValue " The service is disabled and cannot be $actionText " - PassThru
117+ } else {
118+ $servicesToRestart += $service
119+ }
120+ } else {
121+ throw " Unknown object in pipeline - make sure to use Get-DbaService cmdlet"
155122 }
156123 }
157- }
158-
159- $startTime = Get-Date
160- if ($Pscmdlet.ShouldProcess (" Waiting the services to $action on $computerName " )) {
161- # Wait for the service to complete the action until timeout
162- while ($invokeResults.CheckPending -contains $true ) {
163- foreach ($result in ($invokeResults | Where-Object CheckPending -eq $true )) {
164- try {
165- # Refresh Cim instance - not using Get-DbaCmObject because module is not loaded here, but it only refreshes existing object
166- if ($cimSession ) {
167- $splatRefreshCim = @ {
124+ # Set desired $action
125+ if ($action -in " start" , " restart" ) {
126+ $methodName = " StartService"
127+ $desiredState = " Running"
128+ $undesiredState = " Stopped"
129+ } elseif ($action -eq " stop" ) {
130+ $methodName = " StopService"
131+ $desiredState = " Stopped"
132+ $undesiredState = " Running"
133+ }
134+ $invokeResults = @ ()
135+ foreach ($service in $servicesToRestart ) {
136+ if ($Pscmdlet.ShouldProcess (" Sending $action request to service $ ( $service.ServiceName ) on $ ( $service.ComputerName ) " )) {
137+ # Get a fresh CIM instance via the DCOM session to avoid issues with deserialized
138+ # CIM objects crossing runspace boundaries without their session context.
139+ if ($cimSession ) {
140+ try {
141+ $splatGetFreshCim = @ {
168142 CimSession = $cimSession
169143 Namespace = " root\cimv2"
170- Query = " SELECT State FROM Win32_Service WHERE Name = '$ ( $result .Service .ServiceName ) '"
144+ Query = " SELECT * FROM Win32_Service WHERE Name = '$ ( $service .ServiceName ) '"
171145 }
172- $refreshedCimObj = Get-CimInstance @splatRefreshCim
173- if ($refreshedCimObj ) {
174- $result .Service. _CimObject = $refreshedCimObj
146+ $freshCimObj = Get-CimInstance @splatGetFreshCim
147+ if ($freshCimObj ) {
148+ $service . _CimObject = $freshCimObj
175149 }
176- } else {
177- $result .Service._CimObject = $result .Service._CimObject | Get-CimInstance
150+ } catch {
151+ # Fall back to using the existing deserialized CIM object if session refresh fails
178152 }
179- } catch {
180- $result.ServiceExitCode = -3
181- $result.ServiceState = ' Unknown'
182- $result.CheckPending = $false
183- continue
184- }
185- $result.ServiceState = $result.Service._CimObject.State
186- # Failed or succeeded
187- if ($result.ServiceExitCode -ne 0 -or $result.ServiceState -eq $desiredState ) {
188- $result.CheckPending = $false
189- continue
190153 }
191- # Failed after being in the Pending state
192- if ($result.CheckPending -and $result.ServiceState -eq $undesiredState ) {
193- $result.ServiceExitCode = -2
194- $result.CheckPending = $false
195- continue
154+ # Invoke corresponding CIM method
155+ $invokeResult = Invoke-CimMethod - InputObject $service._CimObject - MethodName $methodName
156+ $invokeResults += [psobject ]@ {
157+ InvokeResult = $invokeResult
158+ ServiceState = $invokeResult.State
159+ ServiceExitCode = $invokeResult.ReturnValue
160+ CheckPending = $true
161+ Service = $service
196162 }
197- # Timed out
198- if ($timeout -gt 0 -and ((Get-Date ) - $startTime ).TotalSeconds -gt $timeout ) {
199- $result.ServiceExitCode = -1
200- $result.CheckPending = $false
201- continue
202- }
203- # Still pending - leave CheckPending as is and run again
204163 }
205- Start-Sleep - Milliseconds 200
206164 }
207- }
208- foreach ($result in $invokeResults ) {
209- # Add status
210- $status = switch ($result.ServiceExitCode ) {
211- 0 { ' Successful' }
212- 10 { ' Successful ' } # Already running - FullText service is started automatically
213- default { ' Failed' }
165+
166+ $startTime = Get-Date
167+ if ($Pscmdlet.ShouldProcess (" Waiting the services to $action on $computerName " )) {
168+ # Wait for the service to complete the action until timeout
169+ while ($invokeResults.CheckPending -contains $true ) {
170+ foreach ($result in ($invokeResults | Where-Object CheckPending -eq $true )) {
171+ try {
172+ # Refresh Cim instance - not using Get-DbaCmObject because module is not loaded here, but it only refreshes existing object
173+ if ($cimSession ) {
174+ $splatRefreshCim = @ {
175+ CimSession = $cimSession
176+ Namespace = " root\cimv2"
177+ Query = " SELECT State FROM Win32_Service WHERE Name = '$ ( $result.Service.ServiceName ) '"
178+ }
179+ $refreshedCimObj = Get-CimInstance @splatRefreshCim
180+ if ($refreshedCimObj ) {
181+ $result.Service._CimObject = $refreshedCimObj
182+ }
183+ } else {
184+ $result.Service._CimObject = $result.Service._CimObject | Get-CimInstance
185+ }
186+ } catch {
187+ $result.ServiceExitCode = -3
188+ $result.ServiceState = " Unknown"
189+ $result.CheckPending = $false
190+ continue
191+ }
192+ $result.ServiceState = $result.Service._CimObject.State
193+ # Failed or succeeded
194+ if ($result.ServiceExitCode -ne 0 -or $result.ServiceState -eq $desiredState ) {
195+ $result.CheckPending = $false
196+ continue
197+ }
198+ # Failed after being in the Pending state
199+ if ($result.CheckPending -and $result.ServiceState -eq $undesiredState ) {
200+ $result.ServiceExitCode = -2
201+ $result.CheckPending = $false
202+ continue
203+ }
204+ # Timed out
205+ if ($timeout -gt 0 -and ((Get-Date ) - $startTime ).TotalSeconds -gt $timeout ) {
206+ $result.ServiceExitCode = -1
207+ $result.CheckPending = $false
208+ continue
209+ }
210+ # Still pending - leave CheckPending as is and run again
211+ }
212+ Start-Sleep - Milliseconds 200
213+ }
214214 }
215- Add-Member - Force - InputObject $result.Service - NotePropertyName Status - NotePropertyValue $status
216- # Add error message
217- $errorMessageFromReturnValue = if ($result.ServiceExitCode -in 0 .. ($errorCodes.Length - 1 )) {
218- $errorCodes [$result.ServiceExitCode ]
219- } else { " Unknown error." }
220- $message = switch ($result.ServiceExitCode ) {
221- -2 { " The service failed to $action ." }
222- -1 { " The attempt to $action the service has timed out." }
223- 0 { " Service was successfully $actionText ." }
224- default { " The attempt to $action the service returned the following error: $errorMessageFromReturnValue " }
215+ foreach ($result in $invokeResults ) {
216+ # Add status
217+ $status = switch ($result.ServiceExitCode ) {
218+ 0 { " Successful" }
219+ 10 { " Successful " } # Already running - FullText service is started automatically
220+ default { " Failed" }
221+ }
222+ Add-Member - Force - InputObject $result.Service - NotePropertyName Status - NotePropertyValue $status
223+ # Add error message
224+ $errorMessageFromReturnValue = if ($result.ServiceExitCode -in 0 .. ($errorCodes.Length - 1 )) {
225+ $errorCodes [$result.ServiceExitCode ]
226+ } else { " Unknown error." }
227+ $message = switch ($result.ServiceExitCode ) {
228+ -2 { " The service failed to $action ." }
229+ -1 { " The attempt to $action the service has timed out." }
230+ 0 { " Service was successfully $actionText ." }
231+ default { " The attempt to $action the service returned the following error: $errorMessageFromReturnValue " }
232+ }
233+ Add-Member - Force - InputObject $result.Service - NotePropertyName Message - NotePropertyValue $message
234+ # Refresh service state for the object
235+ if ($result.ServiceState ) { $result.Service.State = $result.ServiceState }
236+ $result
225237 }
226- Add-Member - Force - InputObject $result.Service - NotePropertyName Message - NotePropertyValue $message
227- # Refresh service state for the object
228- if ($result.ServiceState ) { $result.Service.State = $result.ServiceState }
229- $result
230238 }
231- }
232- # Clean up the CIM session created for DCOM connections
233- if ($cimSession ) {
234- Remove-CimSession - CimSession $cimSession - ErrorAction " SilentlyContinue"
235- $cimSession = $null
239+ } finally {
240+ if ($cimSession ) {
241+ Remove-CimSession - CimSession $cimSession - ErrorAction " SilentlyContinue"
242+ }
236243 }
237244}
238245
@@ -286,4 +293,4 @@ process {
286293}
287294end {
288295}
289- }
296+ }
0 commit comments