Benutzerdefinierte Sealing-Skripte¶
PVS Forge kann Ihre eigenen Skripte auf dem Master Target rund um den Imaging-Lauf ausführen — ein Pre-Sealing-Skript vor der Image-Aufnahme (Dienste stoppen, maschinenspezifische Rückstände bereinigen, ein Hersteller-Cleanup-Tool laufen lassen) und ein symmetrisches Post-Sealing-Skript danach (alles wieder aktivieren, was Sie deaktiviert haben). Diese Seite erklärt, wie die Skripte aufgebaut sein müssen, damit sie sauber laufen, und liefert fertige Vorlagen.
Wann und wie sie laufen¶
Die beiden Skripte rahmen den Imaging-Lauf:
- Das Pre-Sealing-Skript läuft nach der Pending-Reboot-Prüfung und bevor die Auto-Updates deaktiviert werden — also bevor das Image erstellt wird. Alles, was es ändert, landet in der vDisk.
- Das Post-Sealing-Skript läuft nach dem Imaging und bevor die Auto-Updates wieder aktiviert werden. Nutzen Sie es, um rückgängig zu machen, was das Pre-Sealing-Skript getan hat — z. B. die vom Pre-Sealing-Skript deaktivierten Dienste und Update-Tasks wieder aktivieren, damit das Master Target in einen lauffähigen Zustand zurückkehrt.
- Beide laufen erhöht unter dem Imaging-Konto, nicht-interaktiv (headless), ohne interaktives Benutzerprofil oder Desktop.
- Unterstützte Dateitypen (für beide Skripte identisch):
.ps1→powershell.exe -NonInteractive -ExecutionPolicy Bypass -File "<Pfad>".cmd→cmd.exe /c "<Pfad>"
- Jeder Lauf wird im Seal/Unseal-Log (
<vDisk>.seal-unseal.logim Store) mit Startzeit, Befehl,stdout,stderr, Exit-Code und Dauer protokolliert — sodass Sie genau sehen, was Ihr Skript getan hat.
Pfade, Timeouts und das Fehlerverhalten konfigurieren Sie unter Einstellungen → vDisk & Imaging.
Pfade und Umgebungsvariablen¶
Beide Skript-Pfade dürfen auf einen lokalen Pfad auf dem Master Target
oder eine vom Master Target erreichbare UNC-Freigabe zeigen — z. B. ein
zentrales Skript auf NETLOGON. PVS Forge holt ein UNC-Skript zunächst in eine
temporäre lokale Kopie (mit den Imaging-Anmeldedaten) und führt diese aus — so
funktioniert es, obwohl das Imaging-Konto keine interaktive Netzwerksitzung hat.
Umgebungsvariablen im Pfad werden auf dem Master Target aufgelöst:
| Unterstützt | Beispiele |
|---|---|
| Maschinen-Variablen | %SystemRoot%, %windir%, %SystemDrive%, %ProgramFiles%, %ProgramFiles(x86)%, %ProgramData%, %ALLUSERSPROFILE%, %ComputerName%, %Public% |
| Domäne / Anmeldung (von PVS Forge aufgelöst) | %LOGONSERVER%, %USERDNSDOMAIN%, %USERDOMAIN% |
| Nicht unterstützt — nicht verwenden | Warum |
|---|---|
%USERPROFILE%, %APPDATA%, %LOCALAPPDATA%, %HOMEPATH%, %USERNAME% |
Sie zeigen auf das Imaging-Dienstkonto, nicht auf einen echten Nutzer — nicht das, was Sie erwarten |
Zentrale Skripte auf NETLOGON
\\%LOGONSERVER%\NETLOGON\seal.cmd funktioniert, aber
\\<ihre-domaene>\NETLOGON\seal.cmd (z. B. \\contoso.com\NETLOGON\seal.cmd)
ist robuster — es trifft immer einen lebenden Domänencontroller. UNC-Zugriff
braucht Domänen-Anmeldedaten: das Imaging-Konto muss die Freigabe lesen können.
MT-spezifische Skripte mit %MT%¶
Bei mehreren Master Targets können Sie eine Skript-Konfiguration behalten und
trotzdem zielspezifische Skripte ausführen — über den Platzhalter %MT% im Pfad:
| Token | Gilt für | Beispiel (3 Targets) |
|---|---|---|
%MT% |
alle Master Targets (kein Positions-Filter) | läuft für 1, 2 und 3 |
%MT2% |
nur Position 2 | läuft für 2 |
%MT13% |
Position 1 und 3 — jede Ziffer ist eine Position (nicht „dreizehn"), max. Position 9 | läuft für 1 und 3 |
Die Position ist die laufende Nummer, die in der Master-Target-Liste vor jedem
Eintrag angezeigt wird (oberster = 1). %MT% wird durch den Kurznamen (NetBIOS)
des aktuellen Targets ersetzt und kann als Ordner oder als Dateiname-Bestandteil
dienen:
\\%LOGONSERVER%\NETLOGON\%MT%\pre.ps1→\\...\NETLOGON\SRV04\pre.ps1\\%LOGONSERVER%\NETLOGON\scripts\pre-%MT13%.ps1(Position 1) →...\pre-SRV04.ps1
Verhalten:
- Position nicht durch
%MT<ziffern>%ausgewählt → das Skript wird für dieses Target schlicht nicht ausgeführt (kein Fehler, kein Abbruch). - Skript für dieses Target gemeint (
%MT%für alle Targets oder eine getroffene%MT<ziffern>%-Position), aber nicht vorhanden → der Lauf wird vor dem Imaging abgebrochen. Ist ein Target für den Hook vorgesehen, muss sein Skript existieren — sonst gilt es als Konfigurationsfehler. - Ein fester Pfad (ohne
%MT%-Token), der nicht existiert → ebenfalls Abbruch vor dem Imaging wie bisher.
Positionen sind dauerhaft
Die Position gehört fest zum Master Target und ändert sich nie, solange
das Target in der Liste bleibt — weder durch Imaging-Läufe noch durch einen
Neustart von PVS Forge noch durch das Hinzufügen weiterer Targets (Neue
kommen ans Ende bzw. füllen eine Lücke, siehe unten). Ihre
%MT<ziffern>%-Filter bleiben damit stabil.
Ein Target, das zu einem laufenden Imaging-Lauf gehört, kann nicht gelöscht werden — brechen Sie den Lauf zuerst ab (Stopp-Symbol an der Zeile), und entfernen Sie das Target danach.
Beim Löschen eines Targets behalten alle übrigen ihre Nummer — die Liste
zeigt dann eine sichtbare Lücke (z. B. 1, 3). Das nächste neu hinzugefügte
Target füllt die kleinste Lücke (im Beispiel: Position 2). Prüfen Sie nach
Löschen/Neuanlegen, ob ein %MT<ziffern>%-Filter die neu besetzte Position
meint. Bei mehr als 9 Targets sind Positionen ab 10 per Ziffern-Filter nicht
adressierbar — dort den namensbasierten Weg (%MT% mit Hostnamen)
verwenden, der ohnehin der empfohlene Standard ist.
Was ein Exit-Code ungleich null bewirkt¶
Ein Skript-Fehler bricht den Imaging-Lauf nie ab
- Ein fehlerhaftes Skript stoppt das Imaging nicht. Ein Exit-Code
ungleich null, ein Timeout oder eine Exception wird ins Seal/Unseal-Log
geschrieben (inklusive
stdout/stderr) und im Live-Log angezeigt — der Lauf läuft aber weiter und die Reaktivierung erfolgt immer, sodass das Master Target nie im gesealten Zustand zurückbleibt. - Ein Skript-Fehler wird immer gemeldet. Ein fehlerhaftes Skript markiert den Lauf mit einem Hinweis in Status und Benachrichtigungs-E-Mail — so sehen Sie es immer und halten ein stillschweigend fehlgeschlagenes Skript nicht für „alles in Ordnung". Das Imaging wird trotzdem abgeschlossen und die vDisk importiert.
- Ein fehlendes oder nicht unterstütztes Skript bricht vor dem Imaging
ab. Ist ein Pfad konfiguriert, die Datei aber nicht vorhanden, oder ist
die Endung nicht
.ps1oder.cmd, stoppt die Vorabprüfung den Lauf vor dem Imaging — es wurde noch nichts gesealed. Beide Skripte werden gemeinsam geprüft.
| Situation | Ergebnis |
|---|---|
Exit-Code 0 |
Skript erfolgreich, kein Fehler protokolliert |
| Exit ungleich null / Timeout / Exception | In Seal/Unseal- + Live-Log protokolliert, Lauf läuft weiter, und der Job wird in Status und E-Mail markiert |
| Pfad fehlt oder nicht unterstützte Endung | Von der Vorabprüfung erkannt — Imaging bricht ab, bevor es startet |
Beenden Sie jedes erfolgreiche Skript explizit mit exit 0 (PowerShell) bzw.
exit /b 0 (CMD), damit das Log ein sauberes Ergebnis zeigt.
Die Regeln¶
Bauen Sie Ihre Skripte für den Dienst-Kontext
- Unbeaufsichtigt laufen. Kein
pause, keinRead-Host, keine GUI — alles, was auf Eingabe wartet, blockiert bis zum Timeout. - Timeout beachten. Läuft ein Skript länger als der konfigurierte Timeout (ein gemeinsamer Wert für beide Skripte, Standard 90 s), wird es beendet (und der Fehler protokolliert).
- Nur absolute Pfade. Das Arbeitsverzeichnis ist undefiniert.
- Kein Netzwerkzugriff zur Laufzeit. Das Imaging-Konto hat während der
Skriptausführung keine interaktive Netzwerksitzung. Der Skript-Pfad darf
eine UNC-Freigabe sein (PVS Forge holt ihn), aber im Skript selbst
net use, Netzlaufwerke und das Lesen anderer Freigaben vermeiden — benötigte Dateien vorab lokal bereitstellen.
Fallen im Dienst-Kontext¶
| Falle | Problem | Lösung |
|---|---|---|
pause / Read-Host |
Blockiert bis zum Timeout | Entfernen |
net stop auf bereits gestoppten Dienst |
Exit-Code 2 (als Fehler protokolliert) | sc stop verwenden (immer Exit 0) |
net use / Freigaben im Skript lesen |
Keine Netzwerksitzung zur Laufzeit | Vermeiden; Dateien vorab lokal bereitstellen |
$env:APPDATA / $env:LOCALAPPDATA |
Zeigen auf das Dienstkonto-Profil | Absolute Pfade verwenden |
GUI-Aufrufe (notepad, mmc, …) |
Blockieren ohne Desktop | Entfernen |
Start-Process ohne -Wait |
Skript endet, während das Kind bis zum Timeout weiterläuft | Immer -Wait |
| Relative Pfade | Arbeitsverzeichnis unbekannt | Absolute Pfade verwenden |
sc statt net stop / net start bevorzugen
sc stop <Dienst> liefert immer Exit-Code 0 — auch wenn der Dienst bereits
gestoppt ist oder nicht existiert. net stop gibt bei einem bereits
gestoppten Dienst Exit-Code 2 zurück, was als Skript-Fehler protokolliert wird.
Dienste deaktivieren — nicht nur stoppen
Ein gestoppter, aber weiterhin aktivierter Dienst startet beim Boot des Ziels wieder — Stoppen allein hält einen Updater also nicht von der laufenden Maschine fern. Setzen Sie im Pre-Sealing-Skript den Starttyp auf Disabled und im Post-Sealing-Skript zurück auf Automatic (oder Manual). Dasselbe gilt für Update-Scheduled-Tasks — viele Hersteller starten Updates über den Taskplaner: im Pre deaktivieren, im Post wieder aktivieren.
Vorlagen¶
Pre-Sealing (seal.ps1)¶
# seal.ps1 - Pre-Sealing-Vorlage fuer PVS Forge
# Laeuft headless unter dem Imaging-Konto, BEVOR das Image aufgenommen wird.
# Regeln: keine Eingabe-Prompts, absolute Pfade, bei Erfolg mit exit 0 enden.
$ErrorActionPreference = 'Stop'
try {
# --- Updater/Dienste DEAKTIVIEREN, damit sie auf dem gebooteten Ziel nicht starten ---
# Deaktivieren, nicht nur stoppen: ein gestoppter, aber aktivierter Dienst startet beim Boot.
foreach ($svc in 'MyUpdater', 'AnotherSvc') {
Set-Service -Name $svc -StartupType Disabled -ErrorAction SilentlyContinue
Stop-Service -Name $svc -Force -ErrorAction SilentlyContinue
}
# --- Hersteller-Update-Scheduled-Tasks deaktivieren (viele Updater laufen ueber den Taskplaner) ---
Disable-ScheduledTask -TaskName 'MyVendorUpdate' -ErrorAction SilentlyContinue | Out-Null
# --- Maschinenspezifische Rueckstaende entfernen ---
Remove-ItemProperty -Path 'HKLM:\SOFTWARE\MyApp' -Name 'LastUser' -ErrorAction SilentlyContinue
Remove-Item -Path 'C:\ProgramData\MyApp\cache\*' -Recurse -Force -ErrorAction SilentlyContinue
# --- Externes Cleanup-Tool starten und DARAUF WARTEN ---
# Ohne -Wait wuerde das Skript enden, bevor das Tool fertig ist.
Start-Process -FilePath 'C:\Tools\cleanup.exe' -ArgumentList '/silent' -Wait
Write-Output 'Pre-Sealing abgeschlossen.'
exit 0
}
catch {
# Die Meldung landet im Seal/Unseal-Log; der Lauf laeuft trotzdem weiter.
Write-Error "Sealing-Skript fehlgeschlagen: $($_.Exception.Message)"
exit 1
}
Post-Sealing (unseal.ps1)¶
# unseal.ps1 - Post-Sealing-Vorlage fuer PVS Forge
# Laeuft headless unter dem Imaging-Konto, NACH dem Imaging, VOR der Reaktivierung.
# Aktiviert GENAU das wieder, was seal.ps1 deaktiviert hat (lauffaehiger Boot-Zustand).
$ErrorActionPreference = 'Stop'
try {
foreach ($svc in 'MyUpdater', 'AnotherSvc') {
Set-Service -Name $svc -StartupType Automatic -ErrorAction SilentlyContinue
Start-Service -Name $svc -ErrorAction SilentlyContinue
}
Enable-ScheduledTask -TaskName 'MyVendorUpdate' -ErrorAction SilentlyContinue | Out-Null
Write-Output 'Post-Sealing abgeschlossen.'
exit 0
}
catch {
Write-Error "Post-Sealing-Skript fehlgeschlagen: $($_.Exception.Message)"
exit 1
}
CMD (seal.cmd)¶
@echo off
REM seal.cmd - Pre-Sealing-Vorlage fuer PVS Forge
REM Laeuft headless unter dem Imaging-Konto. sc verwenden (immer Exit 0), nie "net stop".
REM Deaktivieren (nicht nur stoppen), damit der Updater beim Boot des Ziels aus bleibt.
sc config MyUpdater start= disabled
sc stop MyUpdater
sc config AnotherSvc start= disabled
sc stop AnotherSvc
REM Hersteller-Update-Scheduled-Task deaktivieren
schtasks /Change /TN "MyVendorUpdate" /DISABLE
del /q "C:\ProgramData\MyApp\cache\*"
REM Bei Erfolg immer mit exit 0 beenden
exit /b 0
Vor dem Produktiveinsatz testen¶
Führen Sie das Skript manuell auf dem Master Target in einem nicht-interaktiven,
erhöhten Kontext aus, um die echte Umgebung anzunähern — z. B. mit
PsExec (-s läuft als
SYSTEM, nah genug, um Headless-/Profil-Probleme zu finden):
psexec -s -i powershell.exe -NonInteractive -ExecutionPolicy Bypass -File "C:\Scripts\seal.ps1"
echo $LASTEXITCODE # 0 = sauber; ungleich null wird protokolliert, stoppt das Imaging aber nicht
Läuft es ohne Eingabeaufforderung durch und liefert 0, läuft es auch während
des Sealings sauber.