Benutzerdefinierte Windows-Dienste erstellen und verwalten

Benutzerdefinierte Windows-Dienste erstellen und verwalten

Hintergrundwissen

Viele Windows-Nutzerinnen und -Nutzer – ob privat oder im Unternehmensumfeld – stoßen früher oder später auf die Notwendigkeit, Aufgaben automatisiert im Hintergrund ausführen zu lassen, ohne dass jemand konstant davor sitzt oder unmittelbar eingreift. Genau hier kommen die Windows-Dienste (Services) zum Einsatz: Sie ermöglichen es, Skripte oder Anwendungen „unsichtbar“ auszuführen, vom Systemstart an und unabhängig davon, ob ein Benutzer angemeldet ist.

Diese Funktionalität erweist sich als überaus nützlich für permanent laufende Überwachungsaufgaben, das Erfassen von Daten, wiederkehrende Backups, Protokollanalysen und viele weitere Szenarien, bei denen ein dauerhafter Hintergrundprozess wünschenswert ist. In diesem Artikel wird zunächst erläutert, was Windows-Dienste ausmacht, warum sie so wertvoll sind und welche Tools Microsoft (und die Community) zum Erstellen und Verwalten bereitstellen. Anschließend wird anhand einer Reihe konkreter, praxisnaher Beispiele aufgezeigt, wie man eigene Dienste mit PowerShell schreibt und produktiv betreibt.

Der Artikel richtet sich sowohl an ambitionierte Privatanwenderinnen und -anwender, die regelmäßig mit PowerShell arbeiten, als auch an erfahrene Administratorinnen und Administratoren, die Automatisierungsprozesse im Unternehmen einführen oder verbessern möchten. Viele der Hinweise basieren auf offiziellen Microsoft-Dokumenten, Community-Erfahrungen und jahrelanger Praxis in IT-Umgebungen verschiedenster Größenordnungen.

1. Grundlagen: Was sind Windows-Dienste?

Windows-Dienste sind spezielle Programme, die in einem eigenständigen Kontext ausgeführt werden und keinen direkten Benutzer benötigen. Sie starten oft bereits beim Hochfahren des Systems oder zu einem frei definierten Zeitpunkt, können ebenfalls angehalten und neu gestartet werden, ohne dass sich jemand anmeldet. Insbesondere in Server-Umgebungen, aber auch auf Workstations, ist das essenziell: So kann z. B. ein Dienst dazu dienen, regelmäßig bestimmte Dateien zu sichern, Netzwerkverbindungen zu testen, Systemupdates zu koordinieren oder mehrere Prozesse gleichzeitig zu steuern.

Das Betriebssystem bringt von Haus aus bereits zahlreiche Dienste mit (z. B. den Windows Update-Dienst, den Druckerwarteschlangendienst oder den Ereignisprotokolldienst). Der Clou: Man darf auch selbst Dienste erstellen, um die Automatisierung flexibler und leistungsfähiger zu gestalten. PowerShell bietet hierfür verschiedene Cmdlets – allen voran

New-Service,

Start-Service,

Stop-Service,

Set-Service und andere.


2. Vorteile benutzerdefinierter Dienste

1. Kontinuierliche Ausführung: Ein Dienst kann dauerhaft aktiv sein, selbst wenn niemand an der Maschine sitzt oder eingeloggt ist.
2. Früher Systemstart: Dienste können bereits unmittelbar nach Systemstart loslegen, auch noch vor einer Benutzeranmeldung.
3. Zuverlässigkeit: Sie lassen sich so konfigurieren, dass sie bei Fehlern automatisch neu starten oder andere Aktionen ausführen (z. B. Protokollierung).
4. Sicherheit: Wenn man mit einem dedizierten Dienstkonto (Service Account) arbeitet, sind die Rechte klar definiert. Das ist sicherer, als ein Skript jedes Mal mit hohen Privilegien von Hand auszuführen.
5. Zentralisierte Verwaltung: Über Dienste kann man in Netzwerken sehr viel automatisieren und orchestrieren.


3. Verfügbare Tools und Methoden zum Erstellen von Diensten

Windows selbst liefert verschiedene Werkzeuge mit, um Dienste anzulegen und zu konfigurieren:

PowerShell:

  • New-Service: Legt einen neuen Dienst an.
  • Remove-Service: Löscht einen existierenden Dienst.
  • Set-Service: Ändert Parameter eines Dienstes wie Starttyp oder Anmeldekonto.

sc.exe (Service Controller):

  • Ein Kommandozeilentool, das schon sehr lange unter Windows existiert und manuell oder in Batch-Skripten genutzt werden kann.
  • Beispiel: sc create MeinDienst binPath= "C:\path\to\programm.exe" start= auto

NSSM (Non-Sucking Service Manager):

  • Ein beliebter Community-Weg, um selbst geschriebene Skripte (PowerShell, Python, Java etc.) als Dienst einzurichten.
  • Vereinfacht das Handling und bietet u. a. eine übersichtliche GUI für die Konfiguration.

.NET und Visual Studio:

  • Wer eine eigene Anwendung (z. B. in C#) schreibt, kann direkt auf die ServiceBase-Klasse zurückgreifen und so professionelle Windows-Dienste entwickeln.

Im Fokus dieses Artikels steht PowerShell, weil es schnell und unkompliziert ist, vorhandene Skripte in Form eines Windows-Dienstes laufen zu lassen.


4. Erstes Beispiel: PowerShell-Dienst erstellen (Grundgerüst)

Ein klassisches PowerShell-Beispiel für die Erstellung eines neuen Dienstes lautet:

New-Service -Name "MeinDienst" -BinaryPathName "C:\Scripts\MeinScript.ps1" -DisplayName "Mein PowerShell Dienst" -StartupType Automatic

Dieses Fragment erstellt formal einen Dienst namens „MeinDienst“. Allerdings werden PowerShell-Skripte nicht immer problemlos als Dienstprozesse anerkannt, da üblicherweise ein eigenständiges ausführbares Programm (EXE) erwartet wird. Man muss daher ggf. den Aufruf über powershell.exe -File "C:\Scripts\MeinScript.ps1" als Parameter angeben. Alternativ setzt man auf Tools wie NSSM, die diesen Zwischenschritt automatisch für einen übernehmen.


5. Wichtige Befehle zur Dienstverwaltung (PowerShell)

  • Start-Service -Name "MeinDienst": Dienst starten.
  • Stop-Service -Name "MeinDienst": Dienst anhalten.
  • Restart-Service -Name "MeinDienst": Dienst neu starten (praktisch, wenn Konfigurationen geändert wurden).
  • Get-Service -Name "MeinDienst": Liefert Informationen zum Dienststatus.

Darüber hinaus kann man mit Set-Service den Starttyp ändern, z. B. Set-Service -Name "MeinDienst" -StartupType Manual. Über Windows lässt sich dasselbe in der Dienste-Verwaltungskonsole services.msc erledigen.


6. Neustartverhalten bei Fehlern konfigurieren

Gerade in produktiven Umgebungen ist es wichtig, das Wiederherstellungsverhalten einzustellen. Windows-Dienste können beispielsweise beim ersten Fehler, zweiten Fehler oder dritten und weiteren Fehlern automatisch neugestartet werden. Auch das Ausführen eines eigenen Programms oder das Erstellen eines Ereignisprotokolleintrags ist möglich.

Diese Optionen findet man entweder in services.msc unter den Eigenschaften des jeweiligen Dienstes (Registerkarte „Wiederherstellung“) oder konfiguriert sie über Gruppenrichtlinien. So stellt man sicher, dass ein Dienst, der für den Geschäftsbetrieb essenziell ist, bei einer Störung automatisiert wieder angelaufen wird.


7. Integration mit Aufgabenplanung

Die Windows-Aufgabenplanung (Task Scheduler) ermöglicht ebenfalls, bestimmte Aktionen zeit- oder ereignisgesteuert auszuführen. Sie kann zudem genutzt werden, um Dienste zu starten oder zu stoppen, beispielsweise nach definierten Arbeitszeiten oder bei bestimmten Events (z. B. Login/Logout).

Auch interessant:  E-Rechnungspflicht ab Januar 2025 – auch für Selbstständige und kleine Unternehmen

Mancherorts reicht es bereits aus, ein Skript über den Task Scheduler zu starten. Doch wenn ein dauerhafter, residenter Prozess benötigt wird, ist ein Dienst oft die stabilere Lösung.


8. Sicherheit: Dienstkonten und Berechtigungen

Ein wichtiger Aspekt bei Windows-Diensten ist das Anmeldekonto, unter dem sie laufen. Das Standardkonto „Lokales System“ hat weitreichende Privilegien. Für eigene Dienste empfiehlt es sich meist, ein spezielles Dienstkonto (bspw. einen eigenen lokalen Benutzer) mit minimal nötigen Rechten einzurichten. So minimiert man Risiken bei Kompromittierungen und hält das Berechtigungskonzept schlank.


9. Abhängigkeiten definieren

Muss ein eigener Dienst erst dann starten, wenn ein anderer Dienst (z. B. Netzwerk, Datenbank) bereits läuft, legt man eine Dienstabhängigkeit fest. Somit verhindert man Startfehler, die entstehen, wenn eine benötigte Ressource noch nicht bereitsteht.

In der Registry ist das unter HKLM\SYSTEM\CurrentControlSet\Services\<Dienstname>\DependOnService hinterlegt. Oder man nutzt Tools wie sc.exe config MeinDienst depend= "NetMan" usw.


10. Fehlerprotokollierung und Debugging

Eigene Skripte sollte man so schreiben, dass sie Fehlermeldungen protokollieren. Unter PowerShell kann man beispielsweise Write-EventLog verwenden, um Einträge ins Eventlog zu senden. Alternativ können Logs auch in eine Datei geschrieben werden, was bei umfangreichen Debug-Informationen hilfreich ist.

Besonders in Umgebungen mit vielen Diensten ist eine gute Logstrategie wertvoll. So sieht man sofort, wann und warum ein Dienst ausfällt oder seine Aufgabe nicht erfüllt.


11. Praxisnahe Einsatzbereiche

  • Monitoring (CPU, RAM, Netzwerk, Freigaben)
  • Dateiverarbeitung (Archivierung, Kompression, Migration)
  • Automatische Database-Jobs (Backup, Wartung)
  • Alarmierungen per E-Mail (Low-Disk-Space, Zugriffsfehler)
  • Konfigurationsanpassungen (Registry, Firewallregeln)
  • Sammeln von Eventlog-Einträgen (zentrales Logging)

Im Folgenden werden nun zehn konkrete Beispiele für PowerShell-Skripte vorgestellt, die sich als Dienste eignen. Jedes Beispiel skizziert ein potenzielles Szenario, von simpel bis anspruchsvoll, und zeigt das grundlegende Skriptgerüst samt Vorgehen zum Registrieren als Dienst.

10 sinnvolle, praxinahe Beispiele zum Schreiben eigener PowerShell-Dienste

(Ergänzend zum allgemeinen Teil; die Skripte selbst werden nicht in die Textlänge eingerechnet.)

Jedes dieser Beispiele kann direkt in eine .ps1-Datei kopiert und auf die eigene Umgebung angepasst werden. Anschließend wird der Dienst mit New-Service (oder einer alternativen Methode) angelegt und ist bereit für den Betrieb. Wichtig bleibt, ein geeignetes Anmeldekonto und ausreichende Rechte für das Skript sicherzustellen.

Beispiel 1: Einfache CPU-Überwachung

Szenario
Ein Dienst, der in regelmäßigen Abständen die CPU-Auslastung misst und einen Warnhinweis ins Ereignisprotokoll schreibt, wenn sie über 80 % liegt. Nützlich in Situationen, in denen man eine gewisse Last nicht dauerhaft überschreiten möchte oder Engpässe frühzeitig bemerken will.

Skript (C:\Scripts\CPUWatchdog.ps1):

# CPUWatchdog.ps1
# Überwacht die CPU-Auslastung und schreibt einen Log-Eintrag,
# wenn der Wert über 80% liegt.

$LogName  = "Application"
$EventID  = 3001
$EventSrc = "CPUWatchdog"

# Dienst-Schleife
while ($true) {
    # CPU ermitteln
    $cpu = Get-Counter -Counter "\Processor(_Total)\% Processor Time" -SampleInterval 1 -MaxSamples 1
    $cpuValue = [int]$cpu.CounterSamples.CookedValue

    if ($cpuValue -gt 80) {
        # Ereignis ins Event Log schreiben
        Write-EventLog -LogName $LogName -Source $EventSrc -EventId $EventID -EntryType Warning -Message "CPU-Auslastung über 80%: $cpuValue%"
    }

    Start-Sleep -Seconds 5
}

Dienst erstellen:

New-Service -Name "CPUWatchdog" `
    -BinaryPathName "powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\CPUWatchdog.ps1" `
    -DisplayName "CPU Watchdog Dienst" `
    -Description "Überwacht kontinuierlich die CPU-Auslastung und warnt bei Überschreitung." `
    -StartupType Automatic

Beispiel 2: Automatische SQL-Datenbank-Backups

Szenario
Tägliche Sicherung einer oder mehrerer Datenbanken auf einem Netzlaufwerk. Unternehmen profitieren, da sie Backups nicht mehr manuell anstoßen müssen. Das Skript läuft im Hintergrund als Dienst und kümmert sich um die Datensicherung.

Skript (C:\Scripts\DatabaseBackup.ps1):

# DatabaseBackup.ps1
# Führt ein tägliches Backup einer SQL-Datenbank durch und legt das Ergebnis
# in einem gewünschten Verzeichnis ab.

$SqlServer = "SQL01\INSTANCE"
$Database  = "WichtigeDB"
$BackupDir = "\\Fileserver\Backups\Databases"
$LogName   = "Application"
$Source    = "DBBackupService"

function Do-DatabaseBackup {
    param (
        [string]$ServerInstance,
        [string]$DbName,
        [string]$BackupPath
    )

    $backupFile = Join-Path $BackupPath ("$($DbName)_$(Get-Date -Format yyyyMMdd_HHmmss).bak")
    $sql = "BACKUP DATABASE [$DbName] TO DISK = N'$backupFile' WITH NOFORMAT, NOINIT, NAME = N'$($DbName) Full Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10;"

    try {
        Invoke-Sqlcmd -ServerInstance $ServerInstance -Query $sql
        Write-EventLog -LogName $LogName -Source $Source -EventId 4001 -EntryType Information -Message "Backup für $DbName erfolgreich abgeschlossen. Datei: $backupFile"
    }
    catch {
        Write-EventLog -LogName $LogName -Source $Source -EventId 4002 -EntryType Error -Message "Fehler beim Backup von $DbName: $_"
    }
}

while ($true) {
    $currentTime = Get-Date -Format "HH:mm"
    if ($currentTime -eq "01:00") {
        Do-DatabaseBackup -ServerInstance $SqlServer -DbName $Database -BackupPath $BackupDir
        Start-Sleep -Seconds 86400
    } else {
        Start-Sleep -Seconds 300
    }
}

Dienst erstellen:

New-Service -Name "DBBackupService" `
    -BinaryPathName "powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\DatabaseBackup.ps1" `
    -DisplayName "Datenbank-Backup-Dienst" `
    -Description "Sichert jede Nacht die SQL-Datenbank und legt die Sicherung auf dem Netzlaufwerk ab." `
    -StartupType Automatic

Beispiel 3: Log-Archivierung mit Kompression

Szenario
Stetig wachsende Logdateien in einem bestimmten Verzeichnis belasten die Übersicht oder den Speicherplatz. Dieses Skript sucht nach älteren Logs (z. B. älter als eine Woche), komprimiert sie und verschiebt sie ins Archiv.

Skript (C:\Scripts\LogArchiver.ps1):

# LogArchiver.ps1
# Durchsucht ein Verzeichnis nach alten Logdateien (älter als 7 Tage), komprimiert sie
# und verschiebt die Archiv-ZIP-Dateien in ein spezielles Archivverzeichnis.

$SourcePath   = "C:\Logs"
$ArchivePath  = "C:\Logs\Archiv"
$LogName      = "Application"
$EventSource  = "LogArchiver"
$ZipModule    = "C:\Scripts\ModulesZipZip.psm1"  # Beispiel für externes Modul

Import-Module $ZipModule -ErrorAction SilentlyContinue

function Compress-LogFile {
    param (
        [string]$FilePath
    )
    $fileName  = Split-Path $FilePath -Leaf
    $zipName   = $fileName.Replace(".log", ".zip")
    $zipTarget = Join-Path $ArchivePath $zipName

    # Beispielhafter Aufruf einer externen ZIP-Funktion:
    # Compress-7Zip -Source $FilePath -Destination $zipTarget 

    # Demonstration: nur Kopieren und alte Datei löschen
    [System.IO.File]::Copy($FilePath, $zipTarget)
    Remove-Item $FilePath -Force
}

while ($true) {
    try {
        $oldFiles = Get-ChildItem $SourcePath -Filter *.log -File | Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-7) }
        foreach ($file in $oldFiles) {
            Compress-LogFile -FilePath $file.FullName
        }
        Write-EventLog -LogName $LogName -Source $EventSource -EventId 5001 -EntryType Information -Message "Logarchivierung erfolgreich abgeschlossen."
    }
    catch {
        Write-EventLog -LogName $LogName -Source $EventSource -EventId 5002 -EntryType Error -Message "Fehler bei der Logarchivierung: $_"
    }

    Start-Sleep -Seconds 86400
}

Dienst erstellen:

New-Service -Name "LogArchiverService" `
    -BinaryPathName "powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\LogArchiver.ps1" `
    -DisplayName "Log-Archivierungsdienst" `
    -Description "Komprimiert alte Logdateien und verschiebt sie ins Archiv." `
    -StartupType Automatic

Beispiel 4: Netzwerkfreigabe-Überwachung (Ping-Monitor)

Szenario
Ein wichtiges Netzwerkshare wird sporadisch unerreichbar. Dieser PowerShell-Dienst pingt den Fileserver, um Ausfälle zu registrieren. Bei wiederholten Fehlschlägen wird ein Eintrag im Eventlog erzeugt und ggf. ein Dienstneustart (z. B. LanmanServer) ausgelöst.

Skript (C:\Scripts\ShareMonitor.ps1):

# ShareMonitor.ps1
# Überwacht in festen Abständen eine Netzwerkfreigabe per Ping und meldet Ausfälle.

$Server         = "FILESERVER01"
$LogName        = "System"
$EventSource    = "ShareMonitor"
$PingInterval   = 60   # in Sekunden
$FailureCount   = 0
$FailureLimit   = 3    # Ab wie vielen aufeinanderfolgenden Fehlern eine Aktion ausgelöst wird

while ($true) {
    $pingResult = Test-Connection -ComputerName $Server -Count 1 -Quiet
    if ($pingResult -eq $false) {
        $FailureCount++
        if ($FailureCount -ge $FailureLimit) {
            Write-EventLog -LogName $LogName -Source $EventSource -EventId 6001 -EntryType Error -Message "$Server nicht erreichbar. $FailureCount Pings fehlgeschlagen."
            # Beispielhafter Neustart eines Dienstes
            # Restart-Service -Name "LanmanServer" -ErrorAction SilentlyContinue
        }
    } else {
        $FailureCount = 0
    }
    Start-Sleep -Seconds $PingInterval
}

Dienst erstellen:

New-Service -Name "ShareMonitorService" `
    -BinaryPathName "powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\ShareMonitor.ps1" `
    -DisplayName "Netzwerkfreigabe-Überwachung" `
    -Description "Ping-Überwachung einer wichtigen Netzwerkfreigabe." `
    -StartupType Automatic

Beispiel 5: Automatische Firewallregel-Aktualisierung

Szenario
Bestimmte IP-Ranges ändern sich regelmäßig, sollen aber immer Zugriff auf Port 80/443 haben. Das Skript liest eine Konfigurationsdatei ein und passt eine Firewallregel an. Ideal, wenn sich externe Partner-Netzwerke öfter ändern.

Auch interessant:  Energie sparen: So passen Sie die Windows-Einstellungen an Ihren Bedarf an

Skript (C:\Scripts\FirewallUpdateService.ps1):

# FirewallUpdateService.ps1
# Liest eine Datei mit neuen IP-Bereichen und passt die Windows-Firewallregeln an.

$configFile    = "C:\Scripts\allowedIPs.txt"
$ruleName      = "AllowCustomIPs"
$LogName       = "Application"
$EventSource   = "FirewallUpdateService"

function Update-FirewallRule {
    param(
        [string]$RuleName,
        [string[]]$IPList
    )
    # Bestehende Regel löschen, falls vorhanden
    netsh advfirewall firewall delete rule name="$RuleName" dir=in
    # Neue Regel anlegen
    netsh advfirewall firewall add rule name="$RuleName" dir=in action=allow protocol=TCP localport=80,443 remoteip=($IPList -join ",")
}

while ($true) {
    try {
        if (Test-Path $configFile) {
            $ipList = Get-Content $configFile | Where-Object { $_ -match '^\d{1,3}\.' }
            if ($ipList) {
                Update-FirewallRule -RuleName $ruleName -IPList $ipList
                Write-EventLog -LogName $LogName -Source $EventSource -EventId 7001 -EntryType Information -Message "Firewallregel erfolgreich aktualisiert."
            }
        }
    }
    catch {
        Write-EventLog -LogName $LogName -Source $EventSource -EventId 7002 -EntryType Error -Message "Fehler bei Firewall-Aktualisierung: $_"
    }

    Start-Sleep -Seconds 14400
}

Dienst erstellen:

New-Service -Name "FirewallUpdateService" `
    -BinaryPathName "powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\FirewallUpdateService.ps1" `
    -DisplayName "Firewall Update Dienst" `
    -Description "Aktualisiert regelmäßig eine Windows-Firewallregel basierend auf externer Konfiguration." `
    -StartupType Automatic

Beispiel 6: Automatischer Shutdown bei Inaktivität

Szenario
Ein Dienst, der prüft, ob der Computer in einem bestimmten Zeitfenster (z. B. 22–06 Uhr) nicht genutzt wird. Ist keine Session aktiv, erfolgt ein Herunterfahren, um Energie zu sparen. Typisch für Bürorechner oder Terminalserver, die nachts nicht laufen müssen.

Skript (C:\Scripts\AutoShutdown.ps1):

# AutoShutdown.ps1
# Wenn das System in einem festgelegten Zeitfenster ohne aktive User ist,
# soll es heruntergefahren werden.

$startTime = [TimeSpan]"22:00"
$endTime   = [TimeSpan]"06:00"
$LogSource = "AutoShutdownService"

while ($true) {
    $nowTimeSpan = (Get-Date).TimeOfDay
    if (($nowTimeSpan -ge $startTime) -or ($nowTimeSpan -le $endTime)) {
        # Prüfen, ob ein Benutzer angemeldet ist
        $sessions = quser 2>$null | Select-Object -Skip 1
        if (-not $sessions) {
            Write-EventLog -LogName "System" -Source $LogSource -EventId 8001 -EntryType Information -Message "Leerlauf erkannt, initiiere Shutdown."
            Stop-Computer -Force
        }
    }
    Start-Sleep -Seconds 300
}

Dienst erstellen:

New-Service -Name "AutoShutdownService" `
    -BinaryPathName "powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\AutoShutdown.ps1" `
    -DisplayName "Automatischer Shutdown" `
    -Description "Fährt das System in definierten Zeitfenstern automatisch herunter, wenn keine Benutzer angemeldet sind." `
    -StartupType Automatic

Beispiel 7: E-Mail-Warnung bei wenig Festplattenspeicher

Szenario
Wird auf Laufwerk C: der freie Speicher zu knapp, soll eine Warn-E-Mail ans Administratorenteam gesendet werden. So vermeidet man Systemstillstände wegen volllaufender Partitionen.

Skript (C:\Scripts\DiskSpaceMonitor.ps1):

# DiskSpaceMonitorSSL_Plaintext.ps1
# ---------------------------------
# Überwacht den freien Speicherplatz auf Laufwerk C und verschickt eine Warn-E-Mail
# über SSL/TLS, sobald ein definierter Schwellwert (z. B. 5 GB) unterschritten wird.
# Die SMTP-Zugangsdaten liegen hier bewusst im Klartext vor. In echten Produktivumgebungen
# ist dies aus Sicherheitsgründen NICHT empfohlen.

# ---------------------------
# KONFIGURATION
# ---------------------------
$Drive         = "C:"
$ThresholdGB   = 5

# SMTP-Einstellungen
$SmtpServer    = "mail.example.com"
$SmtpPort      = 587      # 587 (TLS) oder 465 (SSL)
$UseSsl        = $true    # True für SSL/TLS-Verschlüsselung

# Klartext-Zugangsdaten
$SmtpUsername  = "monitor@example.com"
$SmtpPassword  = "P@ssw0rd123"  # Beispielpasswort (im Klartext!)

# Absender und Empfänger
$EmailFrom     = "monitor@example.com"
$EmailTo       = "admin@example.com"
$MailSubject   = "WARNUNG: Freier Speicherplatz knapp"

# Windows-Eventlog-Einstellungen
$LogName       = "Application"
$EventSource   = "DiskSpaceMonitorSSLPlaintext"

# ---------------------------
# PSCredential für Send-MailMessage erstellen
# (Trotz Klartext-Passwort erzeugen wir ein SecureString-Objekt,
#  damit Send-MailMessage ein Credential-Objekt nutzen kann.)
# ---------------------------
$SecurePassword = ConvertTo-SecureString $SmtpPassword -AsPlainText -Force
$SmtpCredential = New-Object System.Management.Automation.PSCredential($SmtpUsername, $SecurePassword)

# ---------------------------
# FUNKTION: SendWarningMail
# ---------------------------
function SendWarningMail {
    param (
        [string]$Subject,
        [string]$Body
    )
    Send-MailMessage `
        -SmtpServer  $SmtpServer `
        -Port        $SmtpPort `
        -UseSsl      $UseSsl `
        -Credential  $SmtpCredential `
        -From        $EmailFrom `
        -To          $EmailTo `
        -Subject     $Subject `
        -Body        $Body
}

# ---------------------------
# HAUPTSCHLEIFE
# ---------------------------
while ($true) {
    try {
        # Freien Speicherplatz ermitteln
        $FreeGB = [math]::Round((Get-PSDrive $Drive).Free / 1GB, 2)
        
        # Prüfen, ob der Schwellwert unterschritten ist
        if ($FreeGB -lt $ThresholdGB) {
            $Subject = "$MailSubject ($FreeGB GB)"
            $Body    = "ACHTUNG: Das Laufwerk $Drive hat nur noch $FreeGB GB freien Speicherplatz."
            
            # Warn-E-Mail versenden
            SendWarningMail -Subject $Subject -Body $Body
            
            # Ereignis ins Windows-Eventlog schreiben
            Write-EventLog -LogName $LogName `
                           -Source $EventSource `
                           -EventId 9001 `
                           -EntryType Warning `
                           -Message $Subject
        }
    }
    catch {
        # Bei Fehlern Eintrag ins Eventlog
        Write-EventLog -LogName $LogName `
                       -Source $EventSource `
                       -EventId 9002 `
                       -EntryType Error `
                       -Message "Fehler beim DiskSpace-Monitoring (SSL): $_"
    }

    # 30 Minuten warten, dann erneut prüfen
    Start-Sleep -Seconds 1800
}

Dienst erstellen:

NeNew-Service -Name "DiskSpaceMonitorSSLPlaintextService" `
    -BinaryPathName "powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\DiskSpaceMonitorSSL_Plaintext.ps1" `
    -DisplayName "Festplattenspeicher-Überwachung SSL (Plaintext Credentials)" `
    -Description "Versendet Warnmails bei knappem Speicher. SMTP-Credentials im Klartext." `
    -StartupType Automatic

Beispiel 8: Zentrales Eventlog-Forwarding (Syslog)

Szenario
Manche Organisationen leiten Windows-Ereignisse in Echtzeit an einen zentralen Syslog-Server weiter. Das folgende Skript beobachtet das „Application“-Log und leitet neu auftretende Events an einen Syslog-Endpunkt (beispielhaft) weiter.

Skript (C:\Scripts\EventLogCollector.ps1):

# EventLogCollector.ps1
# Liest neu auftretende Ereignisse aus der Windows-Ereignisanzeige (Application) und sendet sie an einen Syslog-Server.

$syslogServer  = "192.168.0.50"
$logname       = "Application"
$EventSource   = "EventLogCollector"

$lastCheck = Get-Date

function Send-Syslog {
    param(
        [string]$Message
    )
    # Beispiel: Syslog-Send via UDP oder externes Modul
    Write-Host "Sende an Syslog: $Message"
}

while ($true) {
    try {
        $newEvents = Get-EventLog -LogName $logname -After $lastCheck
        if ($newEvents) {
            foreach ($evt in $newEvents) {
                $msg = "EventID: $($evt.EventID), Source: $($evt.Source), Message: $($evt.Message)"
                Send-Syslog -Message $msg
            }
            $lastCheck = Get-Date
        }
    }
    catch {
        Write-EventLog -LogName $logname -Source $EventSource -EventId 10001 -EntryType Error -Message "Fehler beim Abfragen von EventLogs: $_"
    }
    Start-Sleep -Seconds 15
}

Dienst erstellen:

New-Service -Name "EventLogCollectorService" `
    -BinaryPathName "powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\EventLogCollector.ps1" `
    -DisplayName "Eventlog nach Syslog-Weiterleitung" `
    -Description "Sammelt Ereignisse aus der Windows-Ereignisanzeige und sendet sie an einen Syslog-Server." `
    -StartupType Automatic

Beispiel 9: Registry-Bereinigung

Szenario
In manchen Umgebungen entstehen nach dem Systemstart oder nach Software-Updates irrelevante Registry-Einträge, die regelmäßig entfernt werden sollen. Das Skript löscht diese Einträge periodisch.

Skript (C:\Scripts\RegistryCleaner.ps1):

# RegistryCleaner.ps1
# Beispielhaftes Skript, das bestimmte Registrypfade bereinigt.

$pathsToClean = @(
    "HKCU:\Software\SomeOldApp",
    "HKLM:\SOFTWARE\AnotherKey"
)
$LogName     = "System"
$EventSource = "RegistryCleaner"

while ($true) {
    try {
        foreach ($path in $pathsToClean) {
            if (Test-Path $path) {
                Remove-Item $path -Recurse -Force
                Write-EventLog -LogName $LogName -Source $EventSource -EventId 11001 -EntryType Information -Message "Registry-Schlüssel entfernt: $path"
            }
        }
    }
    catch {
        Write-EventLog -LogName $LogName -Source $EventSource -EventId 11002 -EntryType Error -Message "Fehler bei der Registry-Bereinigung: $_"
    }

    Start-Sleep -Seconds 43200
}

Dienst erstellen:

New-Service -Name "RegistryCleanerService" `
    -BinaryPathName "powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\RegistryCleaner.ps1" `
    -DisplayName "Registry-Bereinigungsdienst" `
    -Description "Entfernt ungenutzte Registry-Einträge in festen Intervallen." `
    -StartupType Automatic

Beispiel 10: Datenkollektion für ein Custom-Dashboard

Szenario
In vielen IT-Teams entsteht das Bedürfnis, ein eigenes Dashboard zu bauen, das System-Kennzahlen in Echtzeit anzeigt. Dieses Skript sammelt CPU-Last, Speicherauslastung und Prozessanzahl und schreibt sie alle paar Minuten in eine CSV-Datei, die man mit Power BI, Excel oder anderen Tools visualisieren kann.

Auch interessant:  Vergleich verschiedener Cloud-Speicherdienste (2024)

Skript (C:\Scripts\DashboardCollector.ps1):

# DashboardCollector.ps1
# Sammelt diverse Kennzahlen und schreibt sie in eine CSV-Datei.

$outputFile = "C:\Scripts\DashboardData.csv"

if (-not (Test-Path $outputFile)) {
    "Timestamp,CPU_Usage,Memory_MB,Processes_Count" | Out-File $outputFile
}

while ($true) {
    $timestamp   = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $cpuCounter  = Get-Counter "\Processor(_Total)\% Processor Time" -SampleInterval 1 -MaxSamples 1
    $cpuUsage    = [int]$cpuCounter.CounterSamples.CookedValue
    $memUsedMB   = (Get-WmiObject Win32_OperatingSystem).TotalVisibleMemorySize - (Get-WmiObject Win32_OperatingSystem).FreePhysicalMemory
    $procCount   = (Get-Process | Measure-Object).Count

    $line = "$timestamp,$cpuUsage,$memUsedMB,$procCount"
    Add-Content -Path $outputFile -Value $line

    Start-Sleep -Seconds 60
}

Dienst erstellen:

New-Service -Name "DashboardCollectorService" `
    -BinaryPathName "powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\DashboardCollector.ps1" `
    -DisplayName "Dashboard-Datenkollektor" `
    -Description "Erfasst Systemkennzahlen und schreibt sie in eine CSV-Datei." `
    -StartupType Automatic

Erweiterte Hinweise & Best Practices

All die oben vorgestellten Beispiele legen nahe, wie man PowerShell-Skripte als kontinuierliche Prozesse im Hintergrund betreibt. Damit sie reibungslos in verschiedensten Systemen laufen, sollten folgende Punkte beachtet werden:

  1. Rechtemanagement und Dienstkonten
    Dienste werden gerne im Kontext von „Lokales System“ gestartet, was jedoch sehr weitgehende Privilegien mit sich bringt. Sicherer ist es, ein eigenes Dienstkonto mit genau den Rechten anzulegen, die das Skript benötigt. Bei Datenbankzugriffen sollte man beispielsweise ein Konto wählen, das Zugriffsrechte auf die betreffende Datenbank besitzt, ansonsten aber keine unnötigen Admin-Privilegien.
  2. Logging und Eventlog
    Eine solide Protokollierung der Skriptaktivitäten ist essentiell. In vielen Beispielen werden Einträge ins Eventlog geschrieben, die im Fehlerfall wertvolle Hinweise liefern. Alternativ oder zusätzlich kann man Logfiles erstellen, insbesondere wenn sehr viele Detailinformationen anfallen.
  3. Fehler- und Ausfallstrategie
    Windows bietet die Option, einen Dienst bei Fehlern automatisch neu zu starten. Das ist besonders wichtig, wenn der Dienst geschäftskritische Aufgaben erfüllt (etwa Backups oder Monitoring). Ein Blick in services.msc unter „Wiederherstellung“ zeigt, wie sich das konfigurieren lässt: Neustart beim ersten Fehler, zweiten Fehler etc.
  4. Starttyp und Timing
    Nicht alle Skripte müssen rund um die Uhr laufen. Wer nur nächtliche Datensicherungen benötigt, kann sich überlegen, ob eine Windows-Aufgabe reicht. Ein Dienst, der ständig im Hintergrund aktiv ist, kann unnötige Ressourcen verbrauchen, wenn die Aufgabe lediglich einmal pro Tag relevant ist.
  5. Modularisierung
    Größere PowerShell-Skripte nutzt man oft in mehreren Kontexten. Es bietet sich an, Kernfunktionen in Module auszulagern und diese Module zu importieren. So bleibt das Hauptskript übersichtlich, und die Wartung fällt leichter.
  6. Versionskontrolle
    Wer regelmäßig Skripte anpasst, sollte ein Versionskontrollsystem (z. B. Git) verwenden. Ein Dienst wird in aller Regel nicht täglich umgeschrieben, aber es ist dennoch praktisch, bei Bedarf auf ältere Versionen zurückgreifen zu können oder Änderungen transparent nachzuvollziehen.
  7. Zentrale Verwaltung und Deployment
    In großen Umgebungen verteilen Administratorinnen und Administratoren Dienste in automatisierten Prozessen. Das kann via SCCM, über Gruppenrichtlinien oder PowerShell Remoting (Invoke-Command) geschehen. Wichtig ist, die Dienste konsistent zu konfigurieren, etwa was Starttyp, Dienstkonto und Protokollierung angeht.
  8. Troubleshooting
    • Überprüfung, ob das Skript interaktiv läuft: Lässt sich das Skript als normale PowerShell-Session erfolgreich ausführen? Falls nein, findet man so erste Fehlerursachen.
    • UAC und ExecutionPolicy: Standardmäßig verhindert die Windows-PowerShell möglicherweise Skript-Ausführungen (Policy). Daher bei Bedarf -ExecutionPolicy Bypass angeben.
    • Zeitüberschreitung beim Start: Dienste haben ein Startzeitlimit. Dauert die Initialisierung zu lange, interpretiert Windows das als Fehlschlag. Entweder das Skript optimieren oder den Timeout-Wert hochsetzen.
  9. Performance vs. Ressourcenverbrauch
    Dienste wie das CPU-Monitoring könnten sehr häufig in kurzen Intervallen aktiv sein. Wenn das System stabil ist und die CPU nicht zu stark belastet wird, ist das unkritisch. Andernfalls sollte man Intervalle vergrößern (z. B. alle 15 Sekunden statt alle 5 Sekunden), um Ressourcen zu schonen.
  10. Größere Anwendungen in .NET realisieren
    Wer mehr Kontrolle oder komplexere Anforderungen hat, entwickelt eigene Windows-Dienstanwendungen in C# oder VB.NET. PowerShell-Skripte sind großartig für rasche Automatisierungen und moderate Komplexität. Bei Großprojekten bietet das .NET-Framework (oder .NET Core) jedoch umfassendere Tools, Debugmöglichkeiten und Integrationen.

Weitere Quellen

Windows-Dienste sind ein bewährter Mechanismus, um automatisierte Aufgaben ohne Benutzerinteraktion dauerhaft, sicher und stabil abzuwickeln. PowerShell ermöglicht es, diese Dienste unkompliziert zu erstellen, speziell wenn man bereits Skripte im Einsatz hat, die man nicht immer manuell starten möchte. Mit ein wenig Sorgfalt bei der Einrichtung und ausreichend Tests läuft ein Dienst nach seiner Installation oft jahrelang ohne nennenswerte Eingriffe.

Wer tiefer in das Thema einsteigen möchte, findet bei Microsoft reichlich Dokumentation:

Auch in der PowerShell-Community (z. B. in Foren, auf GitHub oder in einschlägigen Blogs) existieren zahlreiche erprobte Lösungen, Tutorials und Diskussionsbeiträge, die praktische Tipps liefern – gerade dann, wenn ein Dienst nicht so rund läuft wie erwartet oder besondere Anforderungen (etwa in Multi-Domänen-Netzwerken) bestehen.

Zudem lohnt es sich, die Weiterentwicklung von PowerShell im Auge zu behalten. In den aktuellen Versionen (PowerShell 7+) gibt es regelmäßig neue Cmdlets, Performance-Optimierungen und Sicherheitspatches. Für den professionellen Einsatz ist daher eine gewisse Pflege der Umgebung ratsam.

Zusammenfassend lässt sich festhalten, dass Windows-Dienste in Kombination mit PowerShell eine mächtige, stabile und dennoch relativ einfach zu beherrschende Grundlage für Automatisierungen aller Art darstellen. Ob man kleine Einzeiler zur CPU-Überwachung realisiert oder große Projekte mit Datenbank-Backups, Monitoring und Alarmierungen aufbaut – die vorgestellten Beispiele demonstrieren, wie flexibel und doch überschaubar die Umsetzung sein kann.

Viele Aufgaben, die sonst manuelle Eingriffe erfordern, lassen sich so dauerhaft ohne Aufsicht erledigen. Das entlastet das IT-Personal, reduziert menschliche Fehlerquellen und sorgt für eine lückenlose Erfüllung essenzieller Prozesse. Und ist der Dienst einmal eingerichtet, kann man sich darauf verlassen, dass er still und zuverlässig im Hintergrund seine Arbeit verrichtet.

(Alle Skripte sind Beispielcode und müssen ggf. an die individuelle Umgebung, Rechte- und Sicherheitsvorgaben sowie Pfade angepasst werden. Vor dem Einsatz in Produktionsumgebungen empfiehlt sich stets ein Testlauf in einer isolierten Umgebung.)

Wie hilfreich war dieser Beitrag?

Klicke auf die Sterne um zu bewerten!

Es tut uns leid, dass der Beitrag für dich nicht hilfreich war!

Lasse uns diesen Beitrag verbessern!

Wie können wir diesen Beitrag verbessern?

Abonnieren
Benachrichtige mich bei
0 Kommentare
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben scrollen