PowerShell-Skript als Dienst starten
Es mag ungewöhnlich klingen, aber warum sollte man ein PowerShell-Script als Dienst starten wollen. Ist das denn stabil genug?
Task oder Dienst?
Sie glauben gar nicht, wie oft ich vor der Aufgabe stehe, Automatisierungen mit PowerShell auch zum Dauerläufer zu machen. Wenn es darum geht, bestimmte Dinge immer wieder auszuführen, dann muss es noch kein Dienst sein. Dann reicht auch ein geplanter Task, welcher das PowerShell-Skript nach einem Zeitplan oder auch basierend auf einem Event startet. In den meisten Fällen reicht das völlig aus und wir haben weitere Vorteile:
- Anmeldung per Taskplaner
Ich muss im Skript keine Anmeldedaten im "Klartext" o.ä. hinterlegen, wenn der Windows Taskplaner das Skript einfach mit einem dort hinterlegten Benutzerkonto startet - Recovery
Sollte das Skript einmal "hängen" bleiben, kann kann der Taskplaner es nach zu langer Laufzeit beenden und neu starten. Es kann so also ein Recovery stattfinden. - Monitoring
Quasi jede Überwachungslösung kann den Taskplaner abfragen, ob ein Skript läuft, wann es zuletzt gelaufen ist und welchen Exit-Code es gemeldet hat. - Cleanup
Es kann durchaus passieren, dass ein Skript "Speiciher frisst", weil man als Entwickler nicht sauber mit Variablen umgeht. Ein regelmäßiges Ende und neuer Start erlaubt Windows die Ressourcen wegzuräumen.
Es gibt aber auch Nachteile, wenn ein Skript nicht durchlaufen kann.
- Initialisierung
Das Beenden und Neustarten terminiert natürlich auch alle Verbindungen. Wenn ich im Skript jedes mal wieder z.B. eine Anmeldung an verschiedenen Servern oder eine Exchange PowerShell neu aufbauen muss, dann kostet das einfach Zeit und Ressourcen. - Throttling
Viele Produkte haben eine "Drossel", d.h. erlauben nur eine gewisse Anzahl von Verbindungen. Ein User kann normalerweise nur fünf Exchange PowerShell Sessions parallel starten. Sollte das Skript diese nicht sauber abbauen, blockiert dies nachfolgende Läufe - Keine Interaktion
Das Skript kann nicht auf eingehende Verbindungen o.ä. warten. einen Webserver wird man nicht als "geplanten Task" immer wieder starten. Meine Aktion müsste dann schon das Skript selbst anstarten.
Es kann also schon Gründe geben, dass ein PowerShell Script mit einer "While ($true) {}" - Schleife startet und permanent aktiv bleibt
Grundlagen Automatisierung
Ob mein Skript nun dauerhaft läuft oder immer wieder gestartet macht keinen großen Unterschied, wenn wir über im Hintergrund ablaufende Prozesse sprechen, zu denen es keine GUI und keinen Menschen zur Überwachung gibt.
- Pause/Shutdown/Restart
Ich muss mir Gedanken machen, wie ein bereits laufendes Skript von außen gesteuert werden kann. Kann ich es einfach anhalten oder abbrechen oder muss ich mir überlegen, wie ich dem Skript ein "Pause" oder einen geregelten "Shutdown" signalisiere, so dass es offene Aktionen noch abschließen kann. Unter Linux kann ich ein "Signal" an den Prozess senden. Bei Windows Diensten gibt es entsprechende Schnittstellen. Ein PowerShell-Skript kennt dies erst einmal nicht - Recovery
Sollte ihr Skript - Eventlogging
Jeder unsichtbare Prozess sollte seine Aktivitäten und insbesondere Fehler loggen. Eine Ausgabe auf eine nicht vorhandene Konsole bringt sie nicht weiter. Also überlegen Sie sich eine Protokollierung in einem eigenen Eventlog oder z.B.Textdateien, die sie aber auch rotieren und später wegräumen sollten. - Perfmon
Nett ist auch die Bereitstellung von Kennzahlen über die Performance, z.B. Aktionen pro Zeit - ExitCode
Wenn das Skript aufgrund eines Fehlers nicht weitermachen kann und sich beendet, dann sollten Sie einen EXIT-Code übermitteln, damit Sie zwischen einem fehlerhaften oder geplanten Ende unterscheiden können
Es klingt einfach aber wenn Sie all das umsetzen, dann wird aus einem kleinen
Wrapper
Ein PowerShell-Skript kann ich nicht nur innerhalb einer PowerShell-Session mit "powershell.exe" oder "pwsh.exe" starten, sondern auch diese EXE-Programme instanzieren eigentlich einen PowerShell-Host, in dem der Code ausgeführt wird. Das kann ich auch mit anderen Programmen machen aber ich kann definitiv nicht einfach eine "ps1"-Datei durch den "Service Control Manager" (SCM https://learn.microsoft.com/en-us/windows/win32/services/service-control-manager ) von Windows starten lassen. Der SCM startet meines Wissens nur Console-Progamme (EXE), die zudem die Klasse System.ServiceProcess.ServiceBase (https://learn.microsoft.com/en-us/dotnet/api/system.serviceprocess.servicebase) bereitstellen müssen.
Eine Lösung ist ein Wrapper, der einfach PowerShell.exe mit den passenden Parametern startet. Solche Programme gibt es schon lange von Microsoft und anderen Herstellern.
- srvany.exe oder instsrv.exe
Waren Bestandteil des Windows Server Resource 2003 Kit
Create a user-defined service
https://learn.microsoft.com/en-us/troubleshoot/windows-client/setup-upgrade-and-drivers/create-user-defined-service - NSSM (Non-Sucking Service Manager)
https://nssm.cc/ - Windows Service Wrapper
less restrictive license https://github.com/winsw/winsw/tree/master
permissive license https://github.com/winsw/winsw
Damit bekommen Sie sehr schnell eine PS1-Datei über die PowerShell.exe gestartet. Allerdings enden die Aufrufe des SCM zur Steuerung von Diensten im Wrapper, der den Dienst nur starten oder hart beenden kann.
Taskplaner
Aber warum müssen sie einen Windows Service nutzen, wenn er eh nur eine PowerShell.exe/pwsh.exe startet und den Prozess hart beendet? Das kann auch der Windows Taskmanager und dafür gibt es sogar eine GUI mit umfangreichen Einstellmöglichkeiten. Es ist kein Problem per Taskmanager einen Task beim Start des Computers automatisch anlaufen zu lassen. Wir können auch einstellen, dass ein beendeter Prozess automatisch neu gestartet wird. Die kompletten Aktionen zu Start/Ende landen sowieso schon im Eventlog, womit ein Teil der Überwachung schon erledigt ist.
Wenn ich ein PowerShell-Script automatisch starten lassen will, dann nutze ich den Taskmanager.
WebServer als Wrapper
HTTP ist ein sehr flexibles und universelles Protokoll. Früher habe ich mit ASP-Seiten, quasi "VBScript in Webseiten" mit dem IIS einfache Lösungen gebaut. Solche Skripte hatten natürlich einige Einschränkungen hinsichtlich maximaler Laufzeit, Speicherbedarf und Initialisierung aber einfache LDAP-Abfragen zur Anzeige von Benutzerinformationen waren recht einfach möglich und selbst mit Notepad editierbar. Einige ASPX-Versuche habe ich auch gestartet.
Heute ist aber PowerShell meine primäre "Programmiersprache" und ich habe mir schon überlegt, warum ich nicht einfach PowerShell in HTML-Seiten einbinden kann oder ein IIS einfach PS1-Code startet und den HTTP-Request über eine Pipeline übergibt und die Rückgabe verarbeitet. Ob das nun der IIS ist oder ein anderer Webserver auf Windows oder bei PowerShell Core wäre es sogar auf Linux denkbar, wäre mir egal. Der Vorteil wäre, dass sich der Webserver um den Start als "Service" und optional auch Authentifizierung, TLS-Verschlüsselung etc kümmern würde und ich mich auf die Businesslogik konzentrieren könnte.
Mittlerweile gib es schon entsprechende Hosts, die "Powershell im HTML" erlauben, z.B.
- PowerShell ASP: Create Dyanmic Web
Content With PowerShell ($)
https://www.nsoftware.com/kb/articles/psasp-create-dynamic-web-content - PowerShell Universal ($)
https://ironmansoftware.com/powershell-universal
Auf der anderen Seite gibt es noch Webserver, die selbst in PowerShell geschrieben sind und entsprechend einfach andere PS1-Datei einbinden können, z.B.
- PowerShell als HTTPServer
- PowerShell / Polaris
https://GitHub.com/PowerShell/Polaris - How to Create a PowerShell Powered Web Server Using Pode
https://badgerati.GitHub.io/Pode/
https://petri.com/how-to-create-a-powershell-powered-web-server-using-pode -
PowerShell Web Server
http://www.poshserver.net/index.html
WebServer in PowerShell geschrieben
Einige davon lassen sich auch als Dienst installiert oder natürlich per Taskplaner starten.
Weitere Links
- PowerShell als HTTPServer
- Writing Windows Services in PowerShell
https://learn.microsoft.com/en-us/archive/msdn-magazine/2016/may/windows-powershell-writing-windows-services-in-powershell - Write a Windows Service in PowerShell
https://www.sapien.com/blog/2017/07/12/write-a-windows-service-in-powershell/ - PSService.ps1
https://github.com/JFLarvoire/SysToolsLib/blob/master/PowerShell/PSService.ps1
http://jf.larvoire.free.fr/progs/PSService.ps1 - New-PSService
https://github.com/matthewoestreich/New-PSService - Sorlov.PowerShell
https://github.com/DSorlov/Sorlov.PowerShell - Running PowerShell Script (*.PS1) as a
Windows Service
https://woshub.com/run-powershell-script-as-windows-service/ - How to run a PowerShell script as a
Windows service
https://4sysops.com/archives/how-to-run-a-powershell-script-as-a-windows-service/ - How to Run any PowerShell Script as a
Windows Service with AlwaysUp ($$)
https://www.coretechnologies.com/products/AlwaysUp/Apps/RunPowerShellScriptAsAService.html - Service control manager
https://learn.microsoft.com/en-us/windows/win32/services/service-control-manager - .NET ServiceBase Class
https://learn.microsoft.com/en-us/dotnet/api/system.serviceprocess.servicebase - Linux Signals
https://man7.org/linux/man-pages/man7/signal.7.html
https://faculty.cs.niu.edu/~hutchins/csci480/signals.htm - Windows Signal
https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/signal?view=msvc-170 - Create a user-defined service
https://learn.microsoft.com/en-us/troubleshoot/windows-client/setup-upgrade-and-drivers/create-user-defined-service - NSSM (Non-Sucking Service Manager)
https://nssm.cc/