PowerShell: Ensure services are always on

PowerShell: Ensure services are always on


...to the best of our ability given the limitation that sometimes the issue is a problem code can't fix.




High-Level Overview:

  1. Define the service you want to monitor.
  2. If the service isn't running, restart it and check again to see if it is now.
  3. If it still isn't, try to kill the service, restart it again, and wait a bit to see if that fixes the issue.
  4. Problem persisting? Destroy the computer. Only kidding, tell the user what's going on, how they can likely fix it, and create an event log stating the same.



cls ?
?
# Call program desired frequency using task scheduler
?
# Service you'd like to monitor...
$ServiceName = "spooler"
$ServiceObj = Get-Service -Name $ServiceName
$StatusMessage = "Service status:", $ServiceObj.status ?
?
?

?
if ($ServiceObj.Status -ne 'Running')
{ ???????????
???Restart-Service $ServiceName
???for ($i = 70; $i -le 100; $i++ ) {
???????Write-Progress -Activity "$StatusMessage. Attempting to restart.." -Status "$i% Complete:" -PercentComplete $i
???????Start-Sleep -Seconds 1
???}
??Write-Progress -Completed -Activity "Completed"
??Start-Sleep -Seconds 1
?
???$ServiceObj.Refresh() ?
?????????
???if ($ServiceObj.Status -eq 'Running') ???
???{
???????Clear-Host
???????Write-Host "The $ServiceName service is running."
???} ??
???else {
???????Clear-Host
???????for ($i = 70; $i -le 100; $i++ ) {
???????Write-Progress -Activity "$StatusMessage. Waiting.." -Status "$i% Complete:" -PercentComplete $i
???????Start-Sleep -Seconds 1
???}
??Write-Progress -Completed -Activity "Completed"
??Start-Sleep -Seconds 1
?
???$ServiceObj.Refresh()
?
???if ($ServiceObj.Status -eq 'Running') ???
???????{
???????????Clear-Host
???????????Write-Host "The $ServiceName service is running."
???????} ??
???????else {
??????????for ($i = 70; $i -le 100; $i++ ) {
??????? ???????Write-Progress -Activity "Service still not running. Attempting to kill and restart it.." -Status "$i% Complete:" -PercentComplete $i
??????? ???????Start-Sleep -Seconds 1
??? ????????}
??????????Write-Progress -Completed -Activity "Completed"
??????????Start-Sleep -Seconds 1
?
???????????$service = Get-CimInstance -class win32_service | Where-Object name -eq $ServiceName | select name, processid
???????????$process = Get-Process | Where-Object ID -EQ $service.processid
?
???????????taskkill /f /pid $process.Id
???????????Restart-Service $ServiceName
???????????for ($i = 70; $i -le 100; $i++ ) {
??????? ???????Write-Progress -Activity "Waiting for results.." -Status "$i% Complete:" -PercentComplete $i
??????? ???????Start-Sleep -Seconds 1
??? ????????}
??????????Write-Progress -Completed -Activity "Completed"
??????????Start-Sleep -Seconds 1
?
???????????????if ($ServiceObj.Status -eq 'Running') ???
???????????????{
???????????????????Clear-Host
???????????????????Write-Host "The $ServiceName service is running."
???????????????}
???????????????else {
??????????????????$StatusMessage = "Service status:", $ServiceObj.status
?
???????????????????Clear-Host
??????????????????Write-Host ""
??????????????????Write-Host "$StatusMessage"
??????????????????Write-Host ""
???????????????????Write-Host 'Recommended actions:
????- Run "sfc /scannow"
????- Run "chkdsk /r"
????- Restart the computer
?
???????????????????'
??????????????????Write-Host "Event Log entry made."
??????????????????Write-EventLog -LogName "Application" -Source "EnsureServicesOn" -EventID 3001 -EntryType Error -Message 'Despite killing the $ServiceName service, restarting it, and giving it some time to start running again, it would not enter a running state. Recommended next steps: Try "sfc /scannow", "chkdsk /r" and then restarting the computer. '
?
?
???????????????}
?
???????????}
???????}
?
???}        




Deep-dive:


First, we're clearing the console with "cls" because I like to be neat. Then I'm defining variables we'll use later on throughout the code (text that starts with a $). $ServiceObj is used for us to get information about the service we specified earlier. $StatusMessage is so that I can write less later on.



cls ?
?
# Call program desired frequency using task scheduler
?
# Service you'd like to monitor...
$ServiceName = "spooler"
$ServiceObj = Get-Service -Name $ServiceName
$StatusMessage = "Service status:", $ServiceObj.status ?        



The if statement checks if the service is not equal to "Running". If so, we restart the service. The next bit of logic after "for" is to create a pretty GUI progress bar that appears to the user. I have it set to run for thirty seconds and display a message informing the user of what the service state was and that we're attempting to restart it. After those thirty seconds the "Write-Progress -Completed" line makes the progress bar go away. Otherwise you'll have a completed progress bar doing nothing other than taking up space on your console. "Start-Sleep" is used to make the program pause for a second. I put it there because otherwise it'll go immediately to the next visual element which I found to make the user experience choppy.



if ($ServiceObj.Status -ne 'Running')
{ ???????????
???Restart-Service $ServiceName
???for ($i = 70; $i -le 100; $i++ ) {
???????Write-Progress -Activity "$StatusMessage. Attempting to restart.." -Status "$i% Complete:" -PercentComplete $i
???????Start-Sleep -Seconds 1
???}
??Write-Progress -Completed -Activity "Completed"
??Start-Sleep -Seconds 1        



"$ServiceObj.Refresh()"; "$ServiceObj" is our variable from before and ".Refresh()" is called a method which in this case allows us to retrieve the service's state again to ensure we're dealing with current data. The rest of the code you should be familiar with. We're waiting another thirty seconds to see if the service has started running.



$ServiceObj.Refresh() ?
?????????
???if ($ServiceObj.Status -eq 'Running') ???
???{
???????Clear-Host
???????Write-Host "The $ServiceName service is running."
???} ??
???else {
???????Clear-Host
???????for ($i = 70; $i -le 100; $i++ ) {
???????Write-Progress -Activity "$StatusMessage. Waiting.." -Status "$i% Complete:" -PercentComplete $i
???????Start-Sleep -Seconds 1
???}
??Write-Progress -Completed -Activity "Completed"
??Start-Sleep -Seconds 1
?
???$ServiceObj.Refresh()        



Nothing is new here. Since it has been found that the specified service wasn't running the two times we checked it earlier, we're going to try and kill it instead of just restart it. Sometimes the service will respond to a kill command as opposed to a "Restart-Service" or "Stop-Service" command. The reason for this is because when you stop a service, the operating system is told to pause the service's main functions, allow for back-end things to occur like the application to write log files or save data etc. and then terminate the service. With a kill command, the OS immediately terminates everything to do with that service. This bit of code prepares us for actually doing what I've described in the next section.



if ($ServiceObj.Status -eq 'Running') ???
???????{
???????????Clear-Host
???????????Write-Host "The $ServiceName service is running."
???????} ??
???????else {
??????????for ($i = 70; $i -le 100; $i++ ) {
??????? ???????Write-Progress -Activity "Service still not running. Attempting to kill and restart it.." -Status "$i% Complete:" -PercentComplete $i
??????? ???????Start-Sleep -Seconds 1
??? ????????}
??????????Write-Progress -Completed -Activity "Completed"
??????????Start-Sleep -Seconds 1        



In the very beginning we wrote "Get-Process" to get access to some information that came along with the service. Unfortunately it doesn't have the information we need here- the process ID. We need the process ID because later on there's a command we'll use that requires it. "$service" and "$process" in conjunction get us that information (in reality they get us more than just that information that's why I used pipes ["|"] to parse through the array of data found). "taskkill" is the kill command we spoke about earlier with "/f" relaying that we want to "forcefully end" the service.



           $service = Get-CimInstance -class win32_service | Where-Object name -eq $ServiceName | select name, processid
???????????$process = Get-Process | Where-Object ID -EQ $service.processid
?
???????????taskkill /f /pid $process.Id
???????????Restart-Service $ServiceName
???????????for ($i = 70; $i -le 100; $i++ ) {
??????? ???????Write-Progress -Activity "Waiting for results.." -Status "$i% Complete:" -PercentComplete $i
??????? ???????Start-Sleep -Seconds 1
??? ????????}
??????????Write-Progress -Completed -Activity "Completed"
??????????Start-Sleep -Seconds 1        



You've seen the beginning portion a dozen times now. Although, the group of "Write-Host" commands is noteworthy. I'm writing a message to the user that the service in this case has still not been successful and recommending them to run those commands into a PowerShell or cmd prompt. "sfc /scannow" replaces corrupted system files (can happen overtime without foul play- hackers etc.) with a cached copy. "Chkdsk /r" attempts to repair corrupted portions of your hard drive. Lastly, "Write-EventLog". I'm going to assume that you know what event logs are. I'm creating one with that line that contains custom information, including the event ID number. All of it there was chosen because I thought it was logical and aesthetically pleasing. You can ask me about it if you're curious.



}
??????????Write-Progress -Completed -Activity "Completed"
??????????Start-Sleep -Seconds 1
?
???????????????if ($ServiceObj.Status -eq 'Running') ???
???????????????{
???????????????????Clear-Host
???????????????????Write-Host "The $ServiceName service is running."
???????????????}
???????????????else {
??????????????????$StatusMessage = "Service status:", $ServiceObj.status
?
???????????????????Clear-Host
??????????????????Write-Host ""
??????????????????Write-Host "$StatusMessage"
??????????????????Write-Host ""
???????????????????Write-Host 'Recommended actions:
????- Run "sfc /scannow"
????- Run "chkdsk /r"
????- Restart the computer
?
???????????????????'
??????????????????Write-Host "Event Log entry made."
??????????????????Write-EventLog -LogName "Application" -Source "EnsureServicesOn" -EventID 3001 -EntryType Error -Message 'Despite killing the $ServiceName service, restarting it, and giving it some time to start running again, it would not enter a running state. Recommended next steps: Try "sfc /scannow", "chkdsk /r" and then restarting the computer. '
?
?
???????????????}
?
???????????}
???????}
?
???}        


That's it! Thanks for reading!! Let me know if you have any issues and I'd be happy to help or I'm always open to feedback. Take care!




Improvements that could be made:

  • You may want to have this run for multiple services. An easy way I can see to do that would be to have the $ServiceName Var refer to an excel sheet you define in the code and then have a column composed of the service names you wanted to monitor. Then you'd have to edit some of the logic I wrote so that it refers to the correct service during Write-Outputs.
  • If you're able to restart the computer, you could rewrite the code at the end so instead of informing you that running those commands and restarting will probably fix the issue, it would just do that for you.
  • Depending on the environment, you could run this on one computer and have it remotely monitor the services on other machines.





要查看或添加评论,请登录

社区洞察

其他会员也浏览了