# Dienste und Aufgabenplanung

> Starttypen, Dienstidentitäten und Wiederherstellungsaktionen richtig setzen, Anmeldetypen geplanter Aufgaben nach ihrer Reichweite wählen und Statuscodes lesen, ohne sie für Ergebnisse zu halten.

Track: [Windows Server Administration](https://physar.tech/learn/windows-server-administration)  
Kanonische Fassung: https://physar.tech/learn/windows-server-administration/services-scheduling  
Stand: 2026-09-03  
Interaktiver Teil: 9 Checks (nur im Browser)

## Dienste und Aufgabenplanung: was ohne angemeldeten Menschen startet

### Zwei Mechanismen, dieselben drei Fragen

Auf einem Server läuft der meiste Code, ohne dass jemand davorsitzt. Windows hat dafür zwei Mechanismen: **Dienste** und **geplante Aufgaben**. Sie sehen verschieden aus, werfen aber dieselben drei Fragen auf — wann startet das, unter welcher Identität, und was passiert, wenn es scheitert. Wer beide zusammen betrachtet, spart sich, dieselben Antworten zweimal zu lernen.

**Dienst:** Ein Prozess, den der Dienststeuerungs-Manager (SCM) startet, überwacht und beendet. Er meldet dem SCM seinen Zustand zurück und läuft in der Regel dauerhaft.

**Geplante Aufgabe:** Ein Programmaufruf, den die Aufgabenplanung bei einem **Auslöser** startet — zu einer Zeit, beim Start, bei einer Anmeldung oder bei einem Ereignis. Sie läuft, tut etwas und endet.

Die Faustregel: Was dauerhaft auf etwas warten soll, ist ein Dienst. Was in Abständen etwas erledigt und danach fertig ist, ist eine Aufgabe. Ein Skript in einer Endlosschleife als Dienst zu verkleiden, ist eine Konstruktion, die auf beiden Seiten Nachteile einsammelt.

### Ein Dienst ist ein Registry-Schlüssel

Jeder Dienst hat einen Schlüssel unter `HKLM\SYSTEM\CurrentControlSet\Services\<Name>`. Dort steht alles, was ihn ausmacht: `ImagePath` für die Programmdatei mit Argumenten, `ObjectName` für das Konto, `Start` für den Starttyp, `DependOnService` für Abhängigkeiten. Die Werkzeuge sind Oberflächen über diesen Werten, keine eigene Datenbank.

|  |  |
| --- | --- |
| `Get-Service` | Name, Anzeigename, Status. Sagt **nichts** über Konto und Pfad. |
| `Get-CimInstance Win32_Service` | Zusätzlich `StartName` (das Konto) und `PathName` (die Befehlszeile). |
| `sc.exe qc <Name>` | Starttyp, Pfad, Konto und Abhängigkeiten in einer Ausgabe. |
| `sc.exe config <Name> …` | Ändert dieselben Werte, ohne die Registry direkt anzufassen. |

> **Warum das die erste Diagnose ist:** Bei einem Dienst, der „läuft, aber nicht funktioniert“, klärt eine einzige Ausgabe die Hälfte der möglichen Ursachen: Steht der Pfad noch auf dem alten Verzeichnis? Läuft er unter dem Konto, das ihr letztes Jahr umgestellt habt? Hängt er an einem Dienst, den jemand deaktiviert hat? `Get-Service` beantwortet keine dieser Fragen — es zeigt nur den Zustand.

`sc.exe qc <Name>` — Starttyp, Programmpfad, Konto und Abhängigkeiten in einem Blick. → `sc.exe qfailure <Name>` — die eingerichteten Wiederherstellungsaktionen samt Rücksetzfenster. → `Get-CimInstance Win32_Service -Filter "Name='<Name>'"` — dieselben Angaben als Objekte, für den Vergleich über viele Server hinweg. → Erst danach `Get-Service` für den aktuellen Zustand.

### Starttypen: was sie zusagen und was nicht

|  |  |
| --- | --- |
| `auto` | Startet bei jedem Systemstart, auch ohne Anmeldung. |
| `delayed-auto` | Startet automatisch, aber **kurz nachdem** die übrigen `auto`-Dienste gestartet sind. |
| `demand` | Startet nur auf Anforderung. Das ist der Standard, wenn nichts angegeben wird. |
| `disabled` | Kann nicht gestartet werden, solange der Typ nicht geändert wird. |
| `boot` / `system` | Treiber, die vom Startladeprogramm bzw. während der Kernelinitialisierung geladen werden. |

`delayed-auto` ist der Typ, der am häufigsten falsch verstanden wird. Er ist kein Zeitplan und keine Verzögerung um eine bestimmte Zahl von Sekunden, sondern eine Aussage über die **Reihenfolge**: Der Dienst kommt an die Reihe, wenn die anderen automatischen Dienste durch sind. Wer damit ein Abhängigkeitsproblem umgeht, hat es nicht gelöst, sondern nur unwahrscheinlicher gemacht — auf einem langsameren oder stärker belasteten Server kommt es zurück.

> **Der Unterschied zwischen laufend und zugesagt:** `Running` und der Starttyp sind zwei getrennte Aussagen. Ein von Hand gestarteter Dienst mit dem Typ `demand` läuft — und ist nach dem nächsten Neustart weg. Umgekehrt bedeutet `auto` nicht, dass der Dienst gerade läuft. Bei jeder Übergabe gehören deshalb beide Werte in den Nachweis, nicht nur der Status.

### Unter welcher Identität ein Dienst läuft

|  |  |
| --- | --- |
| `LocalSystem` | Höchste lokale Rechte. Im Netz tritt der Dienst als **Computerkonto** auf. |
| `NETWORK SERVICE` | Eingeschränkt lokal, im Netz ebenfalls als Computerkonto. |
| `LOCAL SERVICE` | Eingeschränkt lokal, im Netz **anonym** — für Netzzugriffe unbrauchbar. |
| Domänenkonto / gMSA | Eigene Identität im Netz, eigene Berechtigungen, eigene Nachvollziehbarkeit. |

Die Zeile, die im Betrieb am meisten Zeit kostet, ist die dritte. Ein Dienst unter `LOCAL SERVICE`, der auf eine Freigabe zugreifen soll, scheitert — und die Fehlermeldung spricht von fehlenden Berechtigungen, was zu der falschen Reaktion einlädt, auf der Freigabe Rechte zu vergeben. Es gibt aber niemanden, dem man sie geben könnte.

> **Das Recht, ohne das nichts startet:** Ein Konto, unter dem ein Dienst laufen soll, braucht das Benutzerrecht **Anmelden als Dienst** (`SeServiceLogonRight`). Fehlt es, startet der Dienst nicht — mit einer Meldung über einen fehlgeschlagenen Anmeldevorgang, die nach einem falschen Kennwort aussieht. Wer das Kennwort über die Dienste-Konsole setzt, bekommt das Recht automatisch mitgesetzt; wer das Konto per Skript oder Gruppenrichtlinie einträgt, in der Regel nicht. Das erklärt, warum derselbe Dienst von Hand eingerichtet läuft und automatisiert eingerichtet nicht.

Bleibt die Frage nach dem Kennwort. Ein Domänenkonto als Dienstkonto bedeutet, dass irgendwo ein Kennwort steht, das jemand drehen muss — und dass beim Drehen jeder Dienst mitgezogen werden will, der es benutzt. Genau deshalb gibt es gruppenverwaltete Dienstkonten (gMSA), bei denen das Verzeichnis das Kennwort selbst rotiert und die berechtigten Hosts es abholen. Für dieses Modul reicht die Merkregel: Wenn ein Dienstkonto auf mehr als einem Server gebraucht wird, ist ein statisches Kennwort die teuerste der möglichen Antworten. Die Einzelheiten dazu stehen im Modul über Dienstidentitäten.

### Wiederherstellungsaktionen greifen seltener, als man denkt

In den Eigenschaften eines Dienstes lässt sich unter **Wiederherstellung** festlegen, was beim ersten, zweiten und jedem weiteren Fehler passieren soll: Dienst neu starten, Programm ausführen, Computer neu starten. Das ist der Teil, den jeder kennt. Der Teil, den fast niemand kennt, steht daneben.

|  |  |
| --- | --- |
| Dienst neu starten | Der SCM startet den Prozess erneut. Ohne Beseitigung der Ursache entsteht daraus ein Muster, das im Status nicht auffällt. |
| Programm ausführen | Ein Befehl, den der SCM startet. Er läuft laut Dokumentation **unter demselben Konto wie der Dienst**. |
| Computer neu starten | Der drastischste Fall. Bei einem Dienst mit mehreren Verbrauchern selten die richtige Antwort. |
| Keine Aktion | Der Standard. Er ist eine Entscheidung, auch wenn er wie eine fehlende aussieht. |

> **Der dokumentierte Haken:** In der Voreinstellung werden die Wiederherstellungsaktionen **nur** ausgelöst, wenn der Prozess endet, **ohne** dem SCM einen ordentlichen Stopp zu melden — also beim Absturz. Beendet sich der Dienst geordnet und meldet dabei einen Fehlercode ungleich null, passiert nichts. Erst das zusätzliche Kennzeichen — in der Oberfläche das Kästchen für Beendigungen mit Fehlern — erweitert die Auslösung auf diesen Fall. Ein Dienst, der sich bei einer unerreichbaren Datenbank sauber selbst beendet, ist damit genau der Fall, in dem die eingerichtete Wiederherstellung nicht greift.

Die Konsequenz für eine Übergabe: „Wiederherstellung ist konfiguriert“ ist keine prüfbare Aussage. Prüfbar ist, ob die Aktionen für den Fehlerfall gelten, der bei diesem Dienst tatsächlich auftritt — und den kennt man erst, wenn man einmal hingesehen hat, wie er ausfällt.

### Der Fehlerzähler und sein Rücksetzfenster

Die Wiederherstellung kennt drei Stufen: erster Fehler, zweiter Fehler, alle weiteren. Welche Stufe gilt, entscheidet ein Zähler — und der wird nach einer einstellbaren Zeit ohne Fehler auf null zurückgesetzt. Diese Zeit heißt Rücksetzfenster und steht üblicherweise auf einen Tag.

### Der Dienst, der nie die zweite Stufe erreicht

Ein Dienst stürzt etwa alle 30 Stunden ab. Konfiguriert ist: erster Fehler Neustart, zweiter Fehler Neustart, weitere Fehler Programm ausführen — ein Skript, das ein Ticket öffnet. Das Ticket ist in vier Monaten nie entstanden.

**Anforderungen**

- Der Absturz soll auffallen, nicht nur überlebt werden.

**Schritte**

- Rücksetzfenster ablesen: ein Tag. Zwischen zwei Abstürzen liegen 30 Stunden.
- Daraus folgt: Der Zähler ist bei jedem Absturz längst wieder null. Es ist immer der erste Fehler.
- Das Ticket-Skript hängt an „weitere Fehler“ und wird deshalb nie erreicht.
- Korrektur: entweder das Rücksetzfenster über den Abstand zwischen zwei Abstürzen hinaus verlängern, oder die Meldung an den ersten Fehler hängen statt an den dritten.

**Merksatz:** Die Konfiguration war nicht falsch abgetippt — sie war für ein Fehlerbild gedacht, das dieser Dienst nicht zeigt. Eine Wiederherstellungseinstellung ist nur zusammen mit der Fehlerfrequenz lesbar.

### Abhängigkeit ist Reihenfolge, nicht Bereitschaft

Trägt ein Dienst in `DependOnService` einen anderen ein, startet der SCM den anderen zuerst und verweigert den eigenen Start, wenn das misslingt. Das ist eine Aussage über die **Reihenfolge** und über den vom SCM gemeldeten Zustand — mehr nicht.

„Gestartet“ heißt: Der Prozess läuft und hat sich beim SCM gemeldet. Es heißt nicht, dass er seinen Port bedient, seine Datenbank geöffnet oder seinen Zwischenspeicher aufgebaut hat. Ein Dienst, der zehn Sekunden zur Bereitschaft braucht, ist für den SCM schon nach einer Sekunde gestartet — und der abhängige Dienst läuft in genau dieses Fenster.

Umgekehrt gilt dasselbe beim Anhalten: Wer einen Dienst stoppt, von dem andere abhängen, nimmt diese mit. Die Dienste-Konsole fragt vorher; ein Skript in der Regel nicht. Bevor ein Dienst im laufenden Betrieb angehalten wird, gehört deshalb die Frage dazu, wer an ihm hängt — sichtbar in `sc.exe enumdepend <Name>`. Die Liste ist oft länger, als das Änderungsvorhaben annimmt, und sie ist der Unterschied zwischen einem Neustart eines Dienstes und einem Ausfall mehrerer.

> **Warum eine Wartezeit die falsche Antwort ist:** Der übliche Reflex ist eine Verzögerung: `delayed-auto`, oder ein Skript, das erst einmal wartet. Beides verschiebt die Fehlerwahrscheinlichkeit, statt sie zu beseitigen, und beides bricht wieder, sobald der Server langsamer bootet als am Tag des Tests. Tragfähig ist, dass der abhängige Dienst die Bereitschaft **prüft** und es erneut versucht — eine Eigenschaft der Anwendung, keine der Dienstkonfiguration. Wo die Anwendung das nicht kann, ist die Wartezeit ein bewusst gewählter Kompromiss und gehört als solcher dokumentiert.

### `Running` beweist nur die eigene Schicht

Der Status eines Dienstes ist ein Signal über einen Prozess. Zwischen diesem Signal und der Aussage „der Dienst erfüllt seinen Zweck“ liegen mehrere Schichten, von denen jede einzeln ausfallen kann.

_[Abbildung: Jede Schicht belegt nur sich selbst. Abgenommen ist erst, was auf dem Weg des Clients ankommt.]_

Praktisch heißt das: Nach einem Neustart oder einer Änderung ist die Prüfung nicht `Get-Service`, sondern der kürzeste Weg, der dem echten Zugriff entspricht — eine Anfrage auf dem Port, ein Aufruf über den Namen, den die Clients benutzen. Der Statusabruf bleibt nützlich, aber als erster Schritt einer Kette, nicht als deren Ergebnis.

### Die geplante Aufgabe: Auslöser, Aktion, Principal

Eine Aufgabe besteht aus drei Teilen, die man getrennt betrachten muss: dem **Auslöser**, der bestimmt wann, der **Aktion**, die bestimmt was, und dem **Principal**, der bestimmt wer. Der dritte Teil ist der, an dem die meisten Aufgaben scheitern.

|  |  |
| --- | --- |
| `Password` | Anmeldung mit hinterlegtem Kennwort. Läuft unabhängig von einer Anmeldung, **mit** Netzzugriff. |
| `S4U` | Anmeldung ohne hinterlegtes Kennwort. Läuft unabhängig von einer Anmeldung, **ohne** Netzzugriff. |
| `Interactive` | Läuft nur, wenn der Benutzer bereits angemeldet ist. |
| `ServiceAccount` | Läuft unter `LocalSystem`, `LOCAL SERVICE` oder `NETWORK SERVICE`. |

Angelegt und geprüft wird das mit `Register-ScheduledTask` und `New-ScheduledTaskPrincipal`, oder mit `schtasks`. `Get-ScheduledTask <Name> | Select-Object -ExpandProperty Principal` zeigt bei einer bestehenden Aufgabe, welcher Anmeldetyp tatsächlich gesetzt ist.

Neben Auslöser, Aktion und Principal gibt es einen vierten Bereich, der Aufgaben stillschweigend aussetzt: die **Bedingungen**. Dort steht etwa, dass die Aufgabe nur bei Netzbetrieb starten soll, nur wenn der Computer im Leerlauf ist, oder nur wenn eine bestimmte Netzwerkverbindung besteht. Ist eine Bedingung nicht erfüllt, startet die Aufgabe nicht — und im Verlauf steht kein Fehler, weil nichts fehlgeschlagen ist. Dazu kommen die **Einstellungen** mit der Höchstlaufzeit: Wird sie erreicht, beendet die Aufgabenplanung den Vorgang. Beide Bereiche gehören in die Prüfung einer Aufgabe, die „einfach nicht läuft“.

### S4U: kein Kennwort, kein Netz

In der Oberfläche gibt es die Auswahl **Unabhängig von der Benutzeranmeldung ausführen** und darunter das Kästchen **Kennwort nicht speichern**. Wer das Kästchen setzt, wählt damit `S4U`. Die Dokumentation ist an dieser Stelle deutlich: Bei einer S4U-Anmeldung speichert das System kein Kennwort — und es gibt **weder Zugriff auf das Netzwerk noch auf verschlüsselte Dateien**.

**Kurzcheck:** Ein Skript sichert eine Datei nach `\\fs01\backup`. Von Hand gestartet funktioniert es. Als Aufgabe mit „Unabhängig von der Benutzeranmeldung“ und „Kennwort nicht speichern“ meldet es Zugriff verweigert. Was ist die Ursache?

- [x] Die Aufgabe läuft als S4U und hat damit keinen Netzzugriff
- [ ] Dem Konto fehlen Rechte auf der Freigabe `\\fs01\backup`
- [ ] Der Aufgabe fehlt das Recht, als Batch-Auftrag angemeldet zu werden

> Richtig. Ohne hinterlegtes Kennwort gibt es keine Anmeldeinformationen, die im Netz vorgezeigt werden könnten — unabhängig davon, welche Rechte das Konto auf der Freigabe hat.

> **Die Wahl, die dahintersteckt:** Damit ist die Entscheidung offengelegt: Entweder ein Kennwort wird auf dem Server hinterlegt, oder die Aufgabe erreicht kein Netzlaufwerk. Wer beides will, braucht eine dritte Antwort — ein verwaltetes Dienstkonto, dessen Kennwort das Verzeichnis selbst rotiert. Was von Hand funktioniert, ist deshalb kein Beweis dafür, dass es geplant funktioniert: Es sind zwei verschiedene Anmeldungen.

### Die zwei Anmelderechte

Dienst und Aufgabe brauchen verschiedene Benutzerrechte, und beide sind unabhängig von den Berechtigungen auf Dateien oder Freigaben. Ein Konto kann auf ein Verzeichnis vollen Zugriff haben und trotzdem nichts starten dürfen.

|  |  |
| --- | --- |
| Anmelden als Dienst | `SeServiceLogonRight`. Ohne dieses Recht startet der Dienst nicht. |
| Anmelden als Batch-Auftrag | `SeBatchLogonRight`. Die Aufgabenplanung meldet das Konto als Batch-Auftrag an, nicht interaktiv. |
| Standard laut Doku | Dienst: `NETWORK SERVICE`. Batch: Administratoren, Sicherungs-Operatoren, Benutzer der Leistungsprotokolle. |

> **Wo das Recht wieder verschwindet:** Beim Anlegen einer Aufgabe über den Assistenten mit Benutzername und Kennwort wird das Batch-Recht automatisch mit vergeben. Steuert eine Gruppenrichtlinie dieselbe Richtlinieneinstellung zentral, überschreibt sie beim nächsten Anwenden die lokale Liste — und nimmt das automatisch vergebene Recht wieder mit. Die Aufgabe lief dann wochenlang und hört ohne erkennbaren Anlass auf. Der Anlass ist die Richtlinie, nicht die Aufgabe.

Prüfen lässt sich der Ist-Zustand mit `secedit /export /areas USER_RIGHTS /cfg rechte.inf` und einem Blick in die erzeugte Datei: Dort stehen `SeServiceLogonRight` und `SeBatchLogonRight` mit den Sicherheitskennungen der berechtigten Konten. Das ist umständlicher als ein Klick in der lokalen Sicherheitsrichtlinie, hat aber zwei Vorteile — es funktioniert auf Server Core, und das Ergebnis ist eine Datei, die sich zwischen zwei Servern vergleichen lässt.

### Was die Aufgabenplanung meldet — und was nicht

In der Spalte **Letztes Ausführungsergebnis** steht ein Code. Er wird häufig als Erfolgsmeldung der Aufgabe gelesen und ist etwas anderes.

|  |  |
| --- | --- |
| `0x0` | Der Vorgang wurde gestartet und endete ohne Fehler. Über das fachliche Ergebnis sagt das nichts. |
| `0x00041301` | Die Aufgabe läuft noch. Ein Wert, der stehen bleibt, deutet auf einen hängenden Vorgang. |
| `0x00041303` | Die Aufgabe wurde noch nie ausgeführt — der Auslöser hat nie gegriffen. |
| `0x00041306` | Der letzte Lauf wurde beendet, etwa weil die eingestellte Höchstlaufzeit erreicht war. |

> **Die Lücke zwischen 0x0 und erledigt:** `0x0` bedeutet: Der Prozess wurde gestartet und hat sich mit Rückgabewert null beendet. Ein Skript, das seinen Fehler abfängt und trotzdem sauber endet, meldet ebenfalls `0x0`. Ein Skript, dessen Pfad nicht existiert, meldet dagegen einen Startfehler — der Unterschied ist wichtig: Die Aufgabenplanung überwacht den **Start** eines Vorgangs, nicht sein Ergebnis. Wer wissen will, ob die Sicherung gelaufen ist, braucht einen Nachweis aus der Sicherung, nicht aus der Aufgabenplanung.

Wo man tatsächlich nachsieht: Der Ereigniskanal `Microsoft-Windows-TaskScheduler/Operational` protokolliert je Aufgabe den registrierten Start, den gestarteten Vorgang und dessen Ende mit Rückgabewert. Er ist die einzige Quelle, die zwischen „der Auslöser hat nicht gegriffen“, „der Vorgang wurde gestartet und ist gescheitert“ und „der Vorgang läuft noch“ unterscheidet — die Spalte in der Übersicht kann das nicht. Prüfe bei der Übernahme eines Servers, ob dieser Kanal aktiviert ist; ohne ihn bleibt für die Diagnose nur die letzte Zeile eines Ergebnisses.

## Quellen

- sc.exe config — https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/sc-config
- SERVICE_FAILURE_ACTIONS_FLAG structure — https://learn.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_failure_actions_flag
- SERVICE_FAILURE_ACTIONS structure — https://learn.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_failure_actionsa
- TASK_LOGON_TYPE enumeration — https://learn.microsoft.com/en-us/windows/win32/api/taskschd/ne-taskschd-task_logon_type
- Principal.LogonType — https://learn.microsoft.com/en-us/windows/win32/taskschd/principal-logontype
- Task Scheduler Error and Success Constants — https://learn.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-error-and-success-constants
- Log on as a service — https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/log-on-as-a-service
- Log on as a batch job — https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/log-on-as-a-batch-job
- New-ScheduledTaskPrincipal — https://learn.microsoft.com/en-us/powershell/module/scheduledtasks/new-scheduledtaskprincipal
- Registry Hives — https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-hives
