Running PowerShell scripts as a “service” (Events: Prologue)

I’ve been playing with PowerShell eventing quite a bit recently and I was looking for a better way to deploy scripts that contained event subrcribers. When you create (or register) a new event using PowerShell its only in effect for the life of that session (usually the console window the code is executing in). I wanted to be able to write scripts that utilized the power of events but also had some persistence, like what you would find from a traditional Windows service.

I achieved my PowerShell scripts as “services” using the Windows 7/2008 Task Scheduler. Basically I use the Task Scheduler to execute the script using the -noexit argument on powershell.exe. This effectively keeps the session alive and the event subscriptions inside able to catch events and execute my code. Using the various settings of Task Scheduler I can restart the exeuction if something should fail or if the system is restarted, not unlike the behavior you would get via a typical Windows service, kind of a “poor-man’s” Windows Service Control Manager.

Below is a breakdown of the setup I found that meets my goal. Additionally Task Scheduler is an excellent platform to execute your typical PowerShell scripts using regular traditional schedules and other triggers. My breakdown should be useful for both scenarios.

Here is the “General” tab of the task I created for my Events testing. I try to follow the general rule of “Principle of Least Privilege”, which basically means grant access only to what resources the task (or user) requires. As such I created my task to run as a regular user. I hate spending time working on something, only to find out it worked for me because I was an admin, but doesn’t work in my production environment. Additionally I also cleared the “Do not store password” checkbox so the script would run as the user when no one was logged in.

The “Triggers” tab allows for the creation of one or more cases in which your script will be executed. For my purposes I chose “On a schedule”, and Daily. Under the Advanced settings I choose “Repeat task every: 5 minutes” and “for duration of: Indefinitely”. This way I’m assured that if my script or PowerShell fails it will start a new instance of my script within 5 minutes. If you were concerned about memory leaks or resource utilization over time you could check “Stop task if it runs longer than:” and set it too a time period you were comfortable with. This would stop the running instance and create a new one in whatever repeat time you specified above. I’ve been running tasks like this for a few weeks now and I’ve not seen any memory leaks or crazy utilization. That will probably depend on your scripts but I see more “utilization creep” from my consoles that I regularly use and abuse.

Here is the drop down showing the other possible triggers you could utilize. There really are quite a few nowadays that could provide some excellent opportunities for script deployment in general.

The “Actions” tab is the heart of Task Scheduler. You can create multiple actions (i.e. multiple script executions are possible for a single task, great for the logon triggers), though I created just a single “Start a program” action for my purposes. For PowerShell you want to choose powershell.exe. If you are on a 64bit machine you actually have two powershell.exe files one for 32bit and one for 64bit. You will need to make sure to select the right one if it makes a difference for your script. The path C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe is the 64bit version on a 64bit OS. The 32bit version is at C:\Windows\syswow64\WindowsPowerShell\v1.0\powershell.exe on a 64bit OS.

For my purposes the most important argument to powershell.exe here is “-noexit” this will keep the PowerShell session open in the background and keep my event subscriptions active. If you were executing a traditional script on a schedule you would need to omit that argument. I’ve included the “-NoProfile” argument here, depending on what your script is doing you may or may not want to use this argument. The key item for this action is the “-File” argument which points to the ps1 script file containing my code. You might also use the “-command” argument in place of the file arugment if you wanted to execute a specific cmdlet or PowerShell one-liner. You can see all the possible arguments by typing powershell.exe /? at the PowerShell command prompt.

I didn’t utilize anything but the defaults on the “Conditions” tab, but you may want to change those settings if you are running your task from say a Laptop or a system that is not always on.

The “Settings” tab has a few options that I select to keep my task running trouble free and “always on”. I selected “Run task as soon as possible…” and “If the task fails, restart” to facilitate re-running my script as soon as possible after a restart or failure of some kind. As in the “Triggers” tab you could use the “Stop the task if it runs longer than…” to refresh your script if you were concern about resources. I un-selected it here so as to keep my script running indefinitely.  It was also important for my purposes that I made sure to select “Do not start a new instance” so as not to create a whole lot of executions of my script.

That is basically my strategy for using Task Scheduler to run my PowerShell scripts as quasi Windows services. As I said above I’ve been running several scripts this way for a few weeks now and not really had any problems with the setup. I’ve been monitoring it for resource utilization and it does not seem to be a problem. I’ve seen poorly written Windows services that do nothing and take more resources. Let me know in the comments what you think, am I missing something? Do you have any other stratagies for runnig scripts in a “service” like fashion?

I’m going to be doing a few more posts on my experiences with PowerShell and Events. This is how I’ve been doing my events testing but Task Scheduler is really secondary to that and is a fantastic platform for PowerShell scripting in general.


10 responses to “Running PowerShell scripts as a “service” (Events: Prologue)

  1. Pingback: PowerShell Events with .NET (Events: Part 1) « Start-Transcript·

  2. I have a few monitoring scripts that need to always be running. Thanks for taking the time to put this together,

  3. Hi, I have set something similiar up to keep my script running…
    But I wonder… what happens if the script exits with or without an error code? I use error codes in mine to “refresh” it when it looses contact with certain resources like exchange and lync… which tends to happen at times…

  4. Hi there,
    Good article…..but isn’t there something called as Permanent Event Subscriptions…There is a module called “PowerEvents” by Trevor Sullivan which eases the process of creating permanent WMI Event Consumers.

    • Yep. I’m familiar with the WMI events and the PowerEvents module. I’m certainly not saying this is the only method. This is just the one I preferred for my scenarios. I found the WMI method a little messy. This way its just a scheduled task. Someone in the future or without much PowerShell or WMI knowledge can see the setup of the task and know basically what it was doing and how to troubleshoot it. To each their own, there are dozens of ways to solve problems with PowerShell.

  5. Great article, thanks for sharing. I’m worried that the restart may not work because if the script quit unexpectedly it might not be able to send a $lastexicode to the task scheduler. That being said it’s worth testing, and it’s really neat regardless. Thanks again!

    • I’ve had a number of scripts that I setup this way fail and they’ve recovered as I expected. I’ve even killed a few with Task Manager and they’ve gone back to doing what they do. Since it’s the host process powershell.exe that’s reporting to the scheduler I would assume any non zero value would be considered and reported in Task Scheduler as failure. I’m not sure what scenarios Win32 process handling would report 0 or 1. However I’m usually pretty good with my exception handling setting ErrorAction appropriately on my cmdlet calls and if my catch misses I would certainly expect a non zero exit code from PowerShell.exe under those conditions. However that is a good point, PowerShell and Task Scheduler in this scenario are pretty divorced and its a lot like the old days of checking %ERRORLEVEL% on your batch files.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s