Stop remote service using PowerShell - powershell

I've a script that stops remote services through WMI:
(get-service -ComputerName $server_ip -Name $service).Stop()
I want to force a service after five tries. I have build a counter, but what is the command to force a stop?

If you have winRM enabled you can use the following:
Invoke-Command -ComputerName server1 -ScriptBlock {
Stop-Service $args[0] -Force } -ArgumentList $service

If by "force a stop" you mean you want to forcibly terminate (i.e. kill) the service process you could try killing the process via its PID:
$server = '1.2.3.4'
$service = 'name'
Invoke-Command -Computer $server -ScriptBlock {
param($svc)
$Error.Clear()
1..5 | % {
Stop-Service -Name $svc
if ($?) { break } # exit from loop if previous command succeeded
}
if ($Error.Count -eq 5) {
$pid = (Get-WmiObject Win32_Service -Filter "Name='$svc'").ProcessId
(Get-Process -Id $pid).Kill()
}
} -ArgumentList $service
Running the code via Invoke-Command is to avoid multiple remote connections.
This should work even for services that have the NOT_STOPPABLE flag set.

Related

Disable Print Spooler Script [duplicate]

I came across this one liner that appears to work:
stop-service -inputobject $(get-service -ComputerName remotePC -Name Spooler)
Can anyone explain why, because I thought stop-service didn't work unless you either used remoting or it occurred on the local host.
The output of Get-Service is a System.ServiceProcess.ServiceController .NET class that can operate on remote computers. How it accomplishes that, I don't know - probably DCOM or WMI. Once you've gotten one of these from Get-Service, it can be passed into Stop-Service which most likely just calls the Stop() method on this object. That stops the service on the remote machine. In fact, you could probably do this as well:
(get-service -ComputerName remotePC -Name Spooler).Stop()
Thanks to everyone's contributions to this question, I've come up with the following script. Change the values for $SvcName and $SvrName to suit your needs. This script will start the remote service if it is stopped, or stop it if it is started. And it uses the cool .WaitForStatus method to wait while the service responds.
#Change this values to suit your needs:
$SvcName = 'Spooler'
$SvrName = 'remotePC'
#Initialize variables:
[string]$WaitForIt = ""
[string]$Verb = ""
[string]$Result = "FAILED"
$svc = (get-service -computername $SvrName -name $SvcName)
Write-host "$SvcName on $SvrName is $($svc.status)"
Switch ($svc.status) {
'Stopped' {
Write-host "Starting $SvcName..."
$Verb = "start"
$WaitForIt = 'Running'
$svc.Start()}
'Running' {
Write-host "Stopping $SvcName..."
$Verb = "stop"
$WaitForIt = 'Stopped'
$svc.Stop()}
Default {
Write-host "$SvcName is $($svc.status). Taking no action."}
}
if ($WaitForIt -ne "") {
Try { # For some reason, we cannot use -ErrorAction after the next statement:
$svc.WaitForStatus($WaitForIt,'00:02:00')
} Catch {
Write-host "After waiting for 2 minutes, $SvcName failed to $Verb."
}
$svc = (get-service -computername $SvrName -name $SvcName)
if ($svc.status -eq $WaitForIt) {$Result = 'SUCCESS'}
Write-host "$Result`: $SvcName on $SvrName is $($svc.status)"
}
Of course, the account you run this under will need the proper privileges to access the remote computer and start and stop services. And when executing this against older remote machines, you might first have to install WinRM 3.0 on the older machine.
Based on the built-in Powershell examples, this is what Microsoft suggests. Tested and verified:
To stop:
(Get-WmiObject Win32_Service -filter "name='IPEventWatcher'" -ComputerName Server01).StopService()
To start:
(Get-WmiObject Win32_Service -filter "name='IPEventWatcher'" -ComputerName Server01).StartService()
This worked for me, but I used it as start. powershell outputs,
waiting for service to finshing starting a few times then finishes and then a get-service on the remote server shows the service started.
**start**-service -inputobject $(get-service -ComputerName remotePC -Name Spooler)
Another option; use invoke-command:
cls
$cred = Get-Credential
$server = 'MyRemoteComputer'
$service = 'My Service Name'
invoke-command -Credential $cred -ComputerName $server -ScriptBlock {
param(
[Parameter(Mandatory=$True,Position=0)]
[string]$service
)
stop-service $service
} -ArgumentList $service
NB: to use this option you'll need PowerShell to be installed on the remote machine and for the firewall to allow requests through, and for the Windows Remote Management service to be running on the target machine. You can configure the firewall by running the following script directly on the target machine (one off task): Enable-PSRemoting -force.
You can also do (Get-Service -Name "what ever" - ComputerName RemoteHost).Status = "Stopped"
You could just run a foreach and have logging enabled.
The console will show if something goes wrong and you can look in the log.
That way, you can then handle the errors individually.
I think it works better this way than running a Test-Netconnection for the verification part because firewall rules can create the value false.
For this example you ned a csv file with column ServerName, Populate the column with servername.contoso.com
$ServerList = "$PSScriptRoot\Serverlist.csv"
$Transcriptlog = "$PSScriptRoot\Transcipt.txt"
Start-Transcript -Path $Transcriptlog -Force
Get-Date -Format "yyyy/MM/dd HH:mm"
Try
{ # Start Try
$ImportServerList = Import-Csv $ServerList -Encoding UTF8 | ForEach-Object { # Start Foreach
New-Object PsObject -Prop #{ # Start New-Object
ServerName = $_.ServerName } # End NewObject
Invoke-Command -ComputerName $_.ServerName -ErrorAction Continue -ScriptBlock { # Start ScriptBlock
# Disable Service PrintSpooler
Get-Service -Name Spooler | Stop-Service -Force
} # End ScriptBlock
} # End Foreach
} # End Try
Catch
{ # Start Catch
Write-Warning -Message "## ERROR## "
Write-Warning -Message "## Script could not start ## "
Write-Warning $Error[0]
} # End Catch
Stop-Transcript
stop-service -inputobject $(get-service -ComputerName remotePC -Name Spooler)
This fails because of your variables
-ComputerName remotePC needs to be a variable $remotePC or a string "remotePC"
-Name Spooler(same thing for spooler)
As far as I know, and I cant verify it now, you cannot stop remote services with the Stop-Service cmdlet or with .Net, it is not supported.
Yes it works, but it stopes the service on your local machine, not on the remote computer.
Now, if the above is correct, without remoting or wmi enabled, you could set a scheduled job on the remote system, using AT, that runs Stop-Service locally.

How to change startup type and task to start/stop services from csv file?

I have the following csv file:
Server,Service,Startup Type,Task
server1,SQL Server Analysis Services (MSSQLSERVER),automatic,start
server2,"SQL Server Analysis Services (MSSQLSERVER), SQL Server Analysis Services (MSSQLSERVER) CEIP",Manual,stop
I have the following script but it uses built-in Stop-Service or Start-Service for now.
I want to allow the flexibility to define all parameters in the csv file and the service is started/stopped based on what the csv file has, as well as setting the startup type to a different startup type if its changed/different from csv file and current state on the server(s).
$csvFile = Import-CSV .\SSAS_services.csv
ForEach ($Server in $csvFile)
{ Invoke-Command -ScriptBlock { Stop-Service $args[0] } -ComputerName $Server.Server -ArgumentList $Server.Service.Split(",")
}
Start-Sleep -Seconds 60
ForEach ($service in (($csvFile.Count - 1)..0))
{ Invoke-Command -ScriptBlock { Stop-Service $args[0] } -ComputerName $csvFile[$service].Server -ArgumentList $csvFile[$service].Service.Split(",")
}
You can use the $using to read a local variable in a remote session, see the about_remote_variables section.
I'm assuming you want to do the following on the remote macines:
Stop the service
Adapt the startup type
Start the service
So the code should approp. look like:
$rows = Import-CSV .\SSAS_services.csv
ForEach ($row in $rows)
{
# Stop the service on the remote machine
Invoke-Command -ScriptBlock { Stop-Service $using:row.Service } -ComputerName $row.Server -ArgumentList $row
Start-Sleep 60
Invoke-Command -ScriptBlock {
# Set the startup type on the remote machine
Set-Service $using:row.Service -StartupType $using:row."Startup Type"
Start-Service $using:row.Service
} -ComputerName $row.Server -ArgumentList $row
}
Hope that helps.

Check the AppPool status through power-shell and start that on remote machine

The requirement is to extract the server name one by one and check the AppPool status, if that is found to be stopped, make it running. below code is not helping out.
$ErrorActionPreference = "Continue"
$status = gc -path "D:\Servers\server.txt"|ForEach-Object (invoke-command -ComputerName $_ -ScriptBlock {Import-Module Webadministration Get-WebAppPoolState -name (gc "D:\AppPool.txt")})
if ($status.value -eq "Started")
{
Write-Host ("ApppPool already running")
}
else
{
Invoke-Command -ScriptBlock {Start-WebAppPool}
Write-host ("AppPool has started successfully")
}
There were multiple problems with your code, I've gone through them individually so you can see what was stopping it from working correctly.
The syntax for foreach was wrong, you needed to use {} not () in this case. Normal brackets are only used like this ForEach ($number in $numArray ) {CODE} which you aren't.
You were checking $status outside the foreach loop - so it so was evaluating $status only once (with the final computers AppPool status) rather than for each computer.
Your second Invoke-Command didn't have a ComputerName parameter specified so was only running the command locally not against the remote computer, meaning the AppPool would never be started.
As you were specifying the AppPool name using gc "D:\AppPool.txt" this file would have to be present on every remote computer for it to work. I've changed this to be passed into the command as an argument so the file only needs to be on the computer running the script.
$Credentials = Get-Credential
$AppPools = Get-Content "D:\AppPool.txt"
$Servers = Get-Content -Path "D:\Servers\server.txt"
ForEach ($Server in $Servers) {
ForEach ($AppPool in $AppPools) {
$AppPoolState = Invoke-Command -ComputerName $Server -ScriptBlock {Import-Module WebAdministration; Get-WebAppPoolState -Name $args[0] } -ArgumentList $AppPool -Credential $Credentials
if ($AppPoolState.Value -eq "Started")
{
Write-Host "$AppPool AppPool already running on $Server"
}
else
{
Invoke-Command -ComputerName $Server -ScriptBlock {Start-WebAppPool -Name $args[0] } -ArgumentList $AppPool -Credential $Credentials
Write-Host "$AppPool AppPool started on $Server"
}
}
}
Note: I run a non-privileged account so have to supply Credentials. If the account you're running the script as has appropriate permissions to all the remote computers you can remove the three Credentials references.

Restart a service in the background using Powershell

I'm trying to restart a service in the background, then monitor the if the service has been restarted. If the service has not started as a while, then the service is to be restarted.
I'm having issues with restarting the service in the background the start-job command does run so thats where i'm most probably going wrong. the restart-service and do command work correctly, just cant get it to run in the background.
$a = "Restart-Service -InputObject $(get-service -ComputerName $Server -Name Service)"
start-job -scriptblock {$a}
do {
($Check = (Get-Service -ComputerName $RadSvr -Name IAS).status -eq "Running") }
Until ($check -eq $true)
After playing around, I noticed the script ran in the background if i didnt use variables in the Start-Job. so if i changed the start-job to include the IP address of a server
start-job -scriptblock { Restart-Service -InputObject $(get-service -ComputerName 10.10.10.10 -Name Service) }
anyone know how to make variable work?
Use Wait-Job to wait for the job to complete.
$a = "Restart-Service -InputObject $(get-service -ComputerName $Server -Name Service)"
$job = start-job -scriptblock {$a}
Wait-Job $job
Maybe you can simplify your code a little bit by using process functions
$service = Get-Service -Name $servicename -ComputerName $targetcomputer
$service.Stop();
$maxTimeout = "00:03:00"
$service.WaitForStatus('Stopped', $maxTimeout);
# check again the status and eventually perform another action
I got the script working. I needed to add param and -argumentlist in the start-job
start-job -scriptblock {param($Server) Restart-Service -InputObject $(get-service -ComputerName $Server -Name Service)} -Argumentlist $Server

Using Powershell to stop a service remotely without WMI or remoting

I came across this one liner that appears to work:
stop-service -inputobject $(get-service -ComputerName remotePC -Name Spooler)
Can anyone explain why, because I thought stop-service didn't work unless you either used remoting or it occurred on the local host.
The output of Get-Service is a System.ServiceProcess.ServiceController .NET class that can operate on remote computers. How it accomplishes that, I don't know - probably DCOM or WMI. Once you've gotten one of these from Get-Service, it can be passed into Stop-Service which most likely just calls the Stop() method on this object. That stops the service on the remote machine. In fact, you could probably do this as well:
(get-service -ComputerName remotePC -Name Spooler).Stop()
Thanks to everyone's contributions to this question, I've come up with the following script. Change the values for $SvcName and $SvrName to suit your needs. This script will start the remote service if it is stopped, or stop it if it is started. And it uses the cool .WaitForStatus method to wait while the service responds.
#Change this values to suit your needs:
$SvcName = 'Spooler'
$SvrName = 'remotePC'
#Initialize variables:
[string]$WaitForIt = ""
[string]$Verb = ""
[string]$Result = "FAILED"
$svc = (get-service -computername $SvrName -name $SvcName)
Write-host "$SvcName on $SvrName is $($svc.status)"
Switch ($svc.status) {
'Stopped' {
Write-host "Starting $SvcName..."
$Verb = "start"
$WaitForIt = 'Running'
$svc.Start()}
'Running' {
Write-host "Stopping $SvcName..."
$Verb = "stop"
$WaitForIt = 'Stopped'
$svc.Stop()}
Default {
Write-host "$SvcName is $($svc.status). Taking no action."}
}
if ($WaitForIt -ne "") {
Try { # For some reason, we cannot use -ErrorAction after the next statement:
$svc.WaitForStatus($WaitForIt,'00:02:00')
} Catch {
Write-host "After waiting for 2 minutes, $SvcName failed to $Verb."
}
$svc = (get-service -computername $SvrName -name $SvcName)
if ($svc.status -eq $WaitForIt) {$Result = 'SUCCESS'}
Write-host "$Result`: $SvcName on $SvrName is $($svc.status)"
}
Of course, the account you run this under will need the proper privileges to access the remote computer and start and stop services. And when executing this against older remote machines, you might first have to install WinRM 3.0 on the older machine.
Based on the built-in Powershell examples, this is what Microsoft suggests. Tested and verified:
To stop:
(Get-WmiObject Win32_Service -filter "name='IPEventWatcher'" -ComputerName Server01).StopService()
To start:
(Get-WmiObject Win32_Service -filter "name='IPEventWatcher'" -ComputerName Server01).StartService()
This worked for me, but I used it as start. powershell outputs,
waiting for service to finshing starting a few times then finishes and then a get-service on the remote server shows the service started.
**start**-service -inputobject $(get-service -ComputerName remotePC -Name Spooler)
Another option; use invoke-command:
cls
$cred = Get-Credential
$server = 'MyRemoteComputer'
$service = 'My Service Name'
invoke-command -Credential $cred -ComputerName $server -ScriptBlock {
param(
[Parameter(Mandatory=$True,Position=0)]
[string]$service
)
stop-service $service
} -ArgumentList $service
NB: to use this option you'll need PowerShell to be installed on the remote machine and for the firewall to allow requests through, and for the Windows Remote Management service to be running on the target machine. You can configure the firewall by running the following script directly on the target machine (one off task): Enable-PSRemoting -force.
You can also do (Get-Service -Name "what ever" - ComputerName RemoteHost).Status = "Stopped"
You could just run a foreach and have logging enabled.
The console will show if something goes wrong and you can look in the log.
That way, you can then handle the errors individually.
I think it works better this way than running a Test-Netconnection for the verification part because firewall rules can create the value false.
For this example you ned a csv file with column ServerName, Populate the column with servername.contoso.com
$ServerList = "$PSScriptRoot\Serverlist.csv"
$Transcriptlog = "$PSScriptRoot\Transcipt.txt"
Start-Transcript -Path $Transcriptlog -Force
Get-Date -Format "yyyy/MM/dd HH:mm"
Try
{ # Start Try
$ImportServerList = Import-Csv $ServerList -Encoding UTF8 | ForEach-Object { # Start Foreach
New-Object PsObject -Prop #{ # Start New-Object
ServerName = $_.ServerName } # End NewObject
Invoke-Command -ComputerName $_.ServerName -ErrorAction Continue -ScriptBlock { # Start ScriptBlock
# Disable Service PrintSpooler
Get-Service -Name Spooler | Stop-Service -Force
} # End ScriptBlock
} # End Foreach
} # End Try
Catch
{ # Start Catch
Write-Warning -Message "## ERROR## "
Write-Warning -Message "## Script could not start ## "
Write-Warning $Error[0]
} # End Catch
Stop-Transcript
stop-service -inputobject $(get-service -ComputerName remotePC -Name Spooler)
This fails because of your variables
-ComputerName remotePC needs to be a variable $remotePC or a string "remotePC"
-Name Spooler(same thing for spooler)
As far as I know, and I cant verify it now, you cannot stop remote services with the Stop-Service cmdlet or with .Net, it is not supported.
Yes it works, but it stopes the service on your local machine, not on the remote computer.
Now, if the above is correct, without remoting or wmi enabled, you could set a scheduled job on the remote system, using AT, that runs Stop-Service locally.