Einführung
Ein Raumänderungsbefehl sollte das Vertrauen der Organisation verdienen: Eine Vorschau darf keine Arbeit hinterlassen, eine Ablehnung darf nichts tun, und eine Zustimmung muss genau den vollständigen zulässigen Plan anwenden. Die bloße Deklaration von WhatIf-Unterstützung schützt nicht jede Methode zum Schreiben von Dateien.
Dieses unabhängige Lab beginnt mit einem eigenen Ereignis und einem unvollständigen Skript. Nutze die ID-basierte Validierung sowie JSON-, Parameter- und Fehlerbehandlungskenntnisse aus dem vorherigen geführten Lab; Dateien aus einer früheren VM werden nicht benötigt. PowerShell und synthetische Ressourcen sind vorbereitet. Du verwendest das Standard-Terminal, führst echte native Bestätigungen aus und öffnest das tatsächlich gespeicherte Ereignis erneut. Die Dauer ist eine Autorenschätzung einschließlich Lesen und Fehlerbehebung.
Jeden Schreibzugriff hinter eine Entscheidung stellen
In diesem Schritt korrigierst du eine irreführende WhatIf-Implementierung, damit sowohl die Vorschau als auch eine abgelehnte Bestätigung den Arbeitsbereich unverändert lassen.
Erkennen, warum das Startskript unsicher ist
Öffne Terminal, zunächst eine Linux-Shell. Starte eine saubere PowerShell-Sitzung mit UTF-8-Einstellungen nur für diese Rollensitzung. env übergibt die Einstellungen nur diesem Programm; -NoLogo blendet das Banner aus und -NoProfile überspringt persönliche Profile:
env LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 pwsh -NoLogo -NoProfile
Warte auf die PS-Eingabeaufforderung. Alle weiteren Befehle sind PowerShell-Befehle. Dieses Lab besitzt seine eigene vorbereitete Szene, unabhängig von früheren VMs:
Set-Location /home/labex/project/room-changes
Quellereignis und Anfragen verwenden dasselbe Format wie im vorherigen Lab. Lies sie vor der Skriptänderung:
Get-Content ./requests.json -Raw
./Show-RoomSchedule.ps1 -Path ./event.json
Das anfängliche apply-room-changes.ps1 ist absichtlich unvollständig. Es deklariert WhatIf-Unterstützung, schreibt aber ohne Erlaubnis über .NET und hat die vollständigen Raumänderungen nicht implementiert. Probiere es nur mit dieser neuen, entbehrlichen Demonstrationsausgabe aus:
pwsh -NoLogo -NoProfile -File ./apply-room-changes.ps1 -EventPath ./event.json -PlanPath ./requests.json -OutputPath ./unsafe-preview.json -WhatIf
Es gibt Plan applied. aus. Diese Meldung beweist weder die Anwendung des richtigen Plans noch die Beachtung der Vorschau. Prüfe die tatsächliche Datei:
Test-Path ./unsafe-preview.json
True
Die Deklaration eines allgemeinen Parameters schützt nicht automatisch beliebige .NET-Schreibzugriffe. Das ursprüngliche Ereignis ist weiterhin intakt, aber diese Vorschau hätte keine Ausgabe anlegen dürfen. Entferne mit Remove-Item -LiteralPath nur diese entbehrliche Datei und lasse Eingaben und Notizen unberührt:
Remove-Item -LiteralPath ./unsafe-preview.json
Das vollständige Ereignis erhalten
Die Eingaben event.json, requests.json, small-request.json, bad-request.json und die unabhängige notes.txt müssen ihre Bytes, ihren Dateimodus, Eigentümer und mtime behalten. Ändere den vorbereiteten Leser nicht. Du darfst dein apply-room-changes.ps1 ersetzen und das benannte neue Ergebnis erstellen. Ereignis und Anfragen liegen als wohlgeformtes UTF-8-JSON vor. Das Ereignis enthält eindeutige exakte IDs, nicht negative ganzzahlige Teilnahmezahlen und Kapazitäten sowie eine Genehmigungsliste. Anfragen sind ein nicht leeres Array eindeutiger Sitzungs-IDs und Zielraum-IDs. Namen dienen der Anzeige; IDs beachten die Groß-/Kleinschreibung.
Unbekannte Werte dürfen verschachtelte Objekte, geordnete Arrays, null, boolesche Werte, Zeichenfolgen und begrenzte endliche JSON-Zahlen innerhalb von 20 Ebenen enthalten. Exakt doppelte Objektschlüssel und beliebige fehlerhafte Schemas liegen außerhalb des Umfangs. Verwende ConvertFrom-Json -AsHashtable -DateKind String, um unbekannte Schlüssel mit verschiedener Groß-/Kleinschreibung und datumsähnliche Zeichenfolgen mit ihren exakten ursprünglichen Zeitversätzen zu erhalten. Diese Optionen wurden im vorherigen Lab eingeführt. Die Quelle enthält sowohl When als auch when; fasse sie nicht zusammen. Verwende für das vollständige Ereignis ConvertTo-Json -Depth 100, damit unbekannte verschachtelte Objekte erhalten bleiben. Die Standardtiefe zwei reicht nicht. Gleiche endliche Zahlenschreibweisen wie 1 und 1.0 sind zulässig; boolesche Werte müssen boolesch bleiben, die Arrayreihenfolge muss gleich bleiben, und die Reihenfolge der Objekteigenschaften ist kosmetisch.
Die Ausgabe muss eine neue Datei unter einem bereits vorhandenen echten Elternverzeichnis sein und sich von jeder Eingabe unterscheiden. Die Verzeichniskette des Kurses ist echt; kein anderer Prozess schreibt gleichzeitig in diese Dateien. Lehne bestehende Dateien, Verzeichnisse und Links einschließlich ins Leere zeigender Links unverändert ab. Ein ungültiger Plan muss den gesamten Aufruf vor dem Schreiben ablehnen: Eine frühere gültige Zeile rechtfertigt keine spätere unbekannte ID, nicht genehmigte Sitzung, doppelte Anfrage oder Kapazitätsüberschreitung. Kapazitätsgleichheit ist gültig. Die Zielräume sind zu den betreffenden Zeiten bereits frei.
Natives ShouldProcess verwenden
Dein wiederverwendbarer Aufruf erhält wie der Validator EventPath, PlanPath und OutputPath. Er muss mit diesen Argumenten und installierten Befehlen eigenständig funktionieren, ohne benachbarte Hilfsdateien. Lasse die vollständige Prüfung vor der Autorisierungsentscheidung und speichere anschließend das vollständige Ereignis mit ausschließlich den angeforderten RoomId-Änderungen.
Lies vor dem Speichern des Codes diese Ergänzungen:
[CmdletBinding(SupportsShouldProcess)]ergänzt die nativen allgemeinen Parameter-WhatIfund-Confirm. Erfinde keinen booleschen Parameter, der nur eine Vorschau ausgibt.$PSCmdletist PowerShells automatisches Befehlsobjekt.$PSCmdlet.ShouldProcess(target, action)gibt zurück, ob die Operation ausgeführt werden soll. Mit WhatIf zeigt es die vorgeschlagene Aktion und liefert false; mit Confirm fragt es nach und liefert nur bei Zustimmung true.- Rufe es einmal für den gesamten geprüften Plan auf. Das Ziel kennzeichnet die Ausgabe, und die Aktion nennt jede exakte Sitzungs-ID sowie alte und neue Raum-ID.
ForEach-Objecterstellt die Beschreibung;-join '; 'verbindet die Zeichenfolgen. Nur der true-Zweig darf Objekte ändern oder Dateien schreiben. Eine Ablehnung darf keine Ausgabeverzeichnisse, Dateien oder temporären Arbeiten erzeugen und keine bestehende Arbeit ändern. - Dies ist eine Gesamtbestätigung, keine Rückfrage je Sitzung. Eine .NET-Methode erzeugt hier keine zweite native Rückfrage. Falls du später ein inneres Cmdlet mit Bestätigungsunterstützung verwendest, unterdrücke dessen zusätzliche Rückfrage nur im bereits autorisierten Zweig; umgehe niemals die äußere Entscheidung.
- Nach der Zustimmung verweist jede gespeicherte
Sessionauf das passende Objekt im vollständigen Ereignis. Die Änderung ihrerRoomIderhält alle anderen Felder. Der .NET-AufrufWriteAllTextund die explizite UTF-8-Kodierung entsprechen dem im vorherigen Lab eingeführten Mechanismus, jetzt durch ShouldProcess geschützt.
Pfadauflösung, Einlesen des Originalobjekts, Trefferarrays, Vergleiche mit Groß-/Kleinschreibung, Fehlerbehandlung und das Sammeln aller Änderungen vor der Nutzung folgen dem vorherigen Lab. Speichere den vollständigen Ersatz mit dem wörtlichen Here-String; er ersetzt nur dein Skript:
Set-Content -Path ./apply-room-changes.ps1 -Encoding utf8 -Value @'
[CmdletBinding(SupportsShouldProcess)]
param(
[Parameter(Mandatory)][string]$EventPath,
[Parameter(Mandatory)][string]$PlanPath,
[Parameter(Mandatory)][string]$OutputPath
)
try {
$ErrorActionPreference = 'Stop'
$fullOutput = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutputPath)
if (Get-Item -LiteralPath $fullOutput -Force -ErrorAction SilentlyContinue) {
throw 'Output already exists; choose a new path.'
}
$parent = Split-Path -LiteralPath $fullOutput
if (-not (Test-Path -LiteralPath $parent -PathType Container)) {
throw 'Output parent must already exist.'
}
$event = Get-Content -LiteralPath $EventPath -Raw | ConvertFrom-Json -AsHashtable -DateKind String
$requests = Get-Content -LiteralPath $PlanPath -Raw | ConvertFrom-Json -AsHashtable -DateKind String -NoEnumerate
if ($requests -isnot [array] -or $requests.Count -eq 0) { throw 'Plan must be a nonempty array.' }
$changes = @()
$seen = @()
foreach ($request in $requests) {
if ($seen -ccontains $request.SessionId) { throw "Duplicate session ID: $($request.SessionId)" }
$seen += $request.SessionId
$sessions = @($event.Sessions | Where-Object { $_.SessionId -ceq $request.SessionId })
$rooms = @($event.Rooms | Where-Object { $_.RoomId -ceq $request.RoomId })
if ($sessions.Count -ne 1) { throw "Unknown or ambiguous session ID: $($request.SessionId)" }
if ($rooms.Count -ne 1) { throw "Unknown or ambiguous room ID: $($request.RoomId)" }
if ($event.ApprovedSessionIds -cnotcontains $request.SessionId) { throw "Unapproved session ID: $($request.SessionId)" }
if ($sessions[0].Attendees -gt $rooms[0].Capacity) { throw "Capacity exceeded: $($request.SessionId) -> $($request.RoomId)" }
$changes += [pscustomobject]@{
Session = $sessions[0]
SessionId = $request.SessionId
OldRoomId = $sessions[0].RoomId
NewRoomId = $request.RoomId
}
}
$description = ($changes | ForEach-Object { "$($_.SessionId): $($_.OldRoomId) -> $($_.NewRoomId)" }) -join '; '
if ($PSCmdlet.ShouldProcess($fullOutput, "Apply complete room plan [$description]")) {
foreach ($change in $changes) { $change.Session.RoomId = $change.NewRoomId }
$json = ConvertTo-Json -InputObject $event -Depth 100
[System.IO.File]::WriteAllText($fullOutput, $json + [Environment]::NewLine, [System.Text.UTF8Encoding]::new($false))
Write-Output "Saved: $fullOutput"
}
}
catch {
Write-Error ("Room change failed: {0}" -f $_.Exception.Message) -ErrorAction Continue
exit 1
}
'@
Vorschau anzeigen und die gesamte Operation ablehnen
Der nächste Befehl muss eine native WhatIf-Meldung ausgeben, die sowohl s-a: r-old -> r-equal als auch s-c: r-old -> r-large beschreibt und den neuen Ausgabepfad als Ziel nennt:
pwsh -NoLogo -NoProfile -File ./apply-room-changes.ps1 -EventPath ./event.json -PlanPath ./requests.json -OutputPath ./event-updated.json -WhatIf
Lies den nativen Status des Kindprozesses sofort:
$LASTEXITCODE
0
Fordere nun eine native Bestätigung an. Warte auf die tatsächliche Rückfrage, lies die vollständige Aktion, gib N ein und drücke Enter. Füge N nicht schon vor Erscheinen der Rückfrage ein:
pwsh -NoLogo -NoProfile -File ./apply-room-changes.ps1 -EventPath ./event.json -PlanPath ./requests.json -OutputPath ./event-updated.json -Confirm
Du solltest Confirm, die vollständige Aktion und Optionen wie [Y] Yes und [N] No sehen. Für beide Änderungen gibt es eine Frage. Mit No wird die Operation übersprungen; dies ist kein Validierungsfehler, daher beendet sich der Kindprozess erfolgreich, ohne Arbeit auszuführen:
$LASTEXITCODE
0
Test-Path ./event-updated.json
False
Lies das Original erneut, statt einer Speichervariablen zu vertrauen:
Get-Content ./event.json -Raw
Alle RoomId-Werte der Sitzungen bleiben r-old, und die verschachtelten Notizen bleiben vorhanden. Weder WhatIf noch No dürfen eine Datei, ein Verzeichnis oder einen temporären Kandidaten hinterlassen. Die Funktionsprüfung liest deine Originaldateien und testet das exakte Skript in entbehrlichen PowerShell-PTYs, einschließlich echter N/Y-Eingaben. Diese Proben testen das Programmverhalten; sie erzeugen oder behaupten keine Historie deiner Bestätigungen in der echten Szene.
Prüfe bei einem falschen Pfad Get-Location und Get-ChildItem und kehre mit Set-Location /home/labex/project/room-changes zurück. Einen Skript-Tippfehler behebst du durch erneutes Speichern des vollständigen korrigierten Here-Strings. Falls du versehentlich zu früh zustimmst, erhalte die Quelle und entferne vor dem erneuten Üben der Ablehnung nur dein eigenes event-updated.json mit Remove-Item -LiteralPath ./event-updated.json. Lösche oder bearbeite niemals das ursprüngliche Ereignis, die Anfragen oder Notizen, um einen Fehler zu verbergen.
Zustimmen und den tatsächlichen Zeitplan erneut öffnen
In diesem Schritt genehmigst du den vollständigen geprüften Plan, liest das gespeicherte Ereignis und prüfst die Ablehnung einer späteren ungültigen Anfrage.
Beginne in derselben PowerShell-Sitzung von Terminal unter /home/labex/project/room-changes. Dein korrigiertes Skript ist aus dem vorherigen Schritt gespeichert; die endgültige Ausgabe fehlt weiterhin. Wenn du ein neues Terminal geöffnet hast, verwende denselben expliziten UTF-8-PowerShell-Aufruf und Set-Location aus jenem Schritt. Dieses Skript benötigt keine Sitzungsvariablen, da jeder Pfad ein Argument ist.
Einen vollständigen genehmigten Plan anwenden
Führe den Bestätigungsaufruf erneut aus. Warte auf die native Rückfrage, lies beide ID-Zuordnungen, gib Y ein und drücke Enter:
pwsh -NoLogo -NoProfile -File ./apply-room-changes.ps1 -EventPath ./event.json -PlanPath ./requests.json -OutputPath ./event-updated.json -Confirm
Erwarte eine Frage, danach Saved: und den Ausgabepfad. Lies sofort den nativen Status:
$LASTEXITCODE
0
Damit bestätigst du den gesamten geprüften Plan einmal. Dies garantiert keine Transaktion bei Absturz, Stromausfall oder gleichzeitigen Schreibzugriffen. Die Eingabe bleibt das Original; das Ergebnis ist ein separates vollständiges Ereignis. Ein Schreibfehler ist ein Fehler und keine Erlaubnis, die Anwendung des Zeitplans zu behaupten.
Der vorbereitete Leser liest genau die an -Path übergebene Datei. Er erzeugt kein fehlendes Ergebnis neu und greift nicht ersatzweise auf event.json zurück:
./Show-RoomSchedule.ps1 -Path ./event-updated.json
Die tatsächlich gespeicherten Zeilen zeigen nun s-a in r-equal, s-b weiterhin in r-old und s-c in r-large. Zwei Sitzungen heißen weiterhin Shared maps und zwei Räume weiterhin Studio; die IDs unterscheiden sie.
Öffne die vollständige Ausgabe erneut als Wörterbücher und behalte zeitstempelähnliche Texte als Zeichenfolgen:
$saved = Get-Content ./event-updated.json -Raw | ConvertFrom-Json -AsHashtable -DateKind String
Ein Punkt führt durch verschachtelte Eigenschaften. Die ursprünglichen Metadaten der ersten Sitzung enthalten einen datumsähnlichen Text und einen anders geschriebenen Schlüssel. Der Zugriff in eckigen Klammern verwendet den jeweiligen exakten Wörterbuchschlüssel. Lies beide getrennt:
$saved.Sessions[0].Metadata.contact['When']
2026-11-07T09:30:00+05:30
$saved.Sessions[0].Metadata.contact['when']
Keep this distinct key
Lies einen weiteren unbekannten Wert und die unabhängige Originalnotiz:
$saved.Unknown.offset
2027-01-02T12:34:56-07:00
Get-Content ./notes.txt
Venue manager: preserve this personal access note.
Diese Beobachtungen ergänzen den vollständigen semantischen Vergleich; zwei erhaltene Felder allein beweisen nicht, dass alle unbekannten Objekte überlebt haben. Baue das Ereignis nicht aus einer Projektion auf, die nur angezeigte Spalten enthält.
Verhindern, dass eine spätere ungültige Anfrage eine Teiländerung veröffentlicht
Die vorbereitete bad-request.json enthält eine gültige erste Anfrage und einen unbekannten Raum in der zweiten. Verwende eine andere neue Ausgabe, damit das fertige Ergebnis erhalten bleibt. Der nächste Aufruf schlägt absichtlich vor jeder Bestätigung fehl:
pwsh -NoLogo -NoProfile -File ./apply-room-changes.ps1 -EventPath ./event.json -PlanPath ./bad-request.json -OutputPath ./rejected-event.json -Confirm
Erwarte Room change failed: und eine Erklärung zum unbekannten Raum, ohne Bestätigungsfrage. Lies sofort das Ergebnis:
$LASTEXITCODE
1
Test-Path ./rejected-event.json
False
Die frühere gültige Anfrage wurde nicht allein angewendet oder veröffentlicht. Ändere für spätere legitime Arbeit das Argument zurück zu requests.json; bearbeite das bereitgestellte fehlerhafte Beispiel nicht. Das ursprüngliche Ereignis und das fertige event-updated.json bleiben verfügbar.
Auch ein vorhandenes Ziel führt zu einer beabsichtigten Ablehnung. Du kannst dies mit WhatIf beobachten, das weiterhin vor jeder Autorisierung die Voraussetzungen prüft:
pwsh -NoLogo -NoProfile -File ./apply-room-changes.ps1 -EventPath ./event.json -PlanPath ./requests.json -OutputPath ./event-updated.json -WhatIf
$LASTEXITCODE
1
Erwarte Output already exists; choose a new path.. Eine Vorschau erlaubt nicht, ungültige Argumente zu ignorieren. Behalte das bestehende Ergebnis; ein neuer Aufruf benötigt einen neuen Ausgabepfad. Wenn du nach einer Skriptkorrektur absichtlich das kanonische Ergebnis dieses Labs neu erstellen willst, entferne nur deine eigene Ausgabe und wiederhole die gültige Bestätigung. Die Originale bleiben die Wiederherstellungsreferenz.
Die Abschlussprüfung liest das tatsächliche vollständige Ergebnis und die Erhaltungsbaseline. Sie testet deine originalen Skriptbytes in isolierten Ereignissen mit anderen IDs, Kapazitätsgleichheit, gleichnamigen Datensätzen, verschachtelten Daten und Fehlerpfaden. Gleichwertige Schleifen- und Objektimplementierungen sind willkommen. Verglichen werden JSON-Werte und -Typen, nicht Objekteigenschaftsreihenfolge, Befehlsschreibweise, Editorwahl oder Erfolgsmeldungen. Eine wiederholte Prüfung wendet dein echtes Ereignis nicht an, setzt es nicht zurück und repariert es nicht.
Zusammenfassung
Du hast eine irreführende Vorschau korrigiert, den gesamten Plan vor dem Schreiben geprüft und eine einzige native ShouldProcess-Entscheidung verwendet. WhatIf und No ließen die Arbeit unverändert; Yes speicherte ein vollständiges Ereignis mit den angeforderten ID-Beziehungen und allen übrigen verschachtelten Werten. Du hast diese echte Datei erneut geöffnet und bestätigt, dass eine spätere ungültige Anfrage oder eine bestehende Ausgabe abgelehnt werden, statt stillschweigend veröffentlicht oder ersetzt zu werden.



