Skip to content

Add preview Windows NVMe boot-driver recovery script - #153

Merged
Edwin Bernal Microsoft (EdwinBernal1) merged 3 commits into
Azure:mainfrom
EdwinBernal1:nvme-recovery
Sep 11, 2026
Merged

Edwin Bernal Microsoft (EdwinBernal1) merged 3 commits into
Azure:mainfrom
EdwinBernal1:nvme-recovery

Conversation

@EdwinBernal1

Copy link
Copy Markdown
Member

Draft only. Do not merge or publish a permanent run-id until the remaining work-order gates are complete.

Summary

Adds the Windows recovery script for inspecting, repairing, and rolling back the offline stornvme boot-driver configuration. The script defaults to read-only Report mode, refuses ambiguous or unsafe targets, backs up before Repair writes, verifies exact desired values after Repair, and verifies exact backup restoration after Rollback.

The permanent win-enable-nvme-boot-driver entry is intentionally withheld from map.json. Until binary SYSTEM-hive and live broken-VM validation are complete, test this branch through the work order's --preview workflow.

Requested by / source

Edwin Bernal / WORKORDER-repair-scripts.md

Type

[ ] Bug fix (patch) [x] Feature (preview-only; public run-id withheld) [ ] Breaking (major, sign-off attached) [ ] Docs

Changes

  • src/windows/win-enable-nvme-boot-driver.ps1
    • Adds Report, Repair, and Rollback modes for the offline stornvme service.
    • Uses merged offline disk, hive, strict control-set, and write-gate helpers.
    • Refuses missing drivers and ambiguous Windows installations.
    • Backs up before mutation and rereads exact desired values after Repair.
    • Restricts Rollback imports to the selected offline stornvme subtree and verifies exact restoration.
    • Does not write CriticalDeviceDatabase entries.
  • tests/test-win-enable-nvme-boot-driver.ps1
    • Covers read-only reporting, repair, idempotency, rollback, absent-value restoration, missing driver, ambiguous targets, explicit selection, unreadable hives, strict control-set selection, exact registry paths, write ordering, typed backup content, out-of-scope backup refusal, and import-failure cleanup.
  • map.json
    • No change. The permanent run-id remains unpublished.

Version & changelog

  • setup.py VERSION: not applicable; this PR does not change the Azure CLI extension.
  • HISTORY.rst entry added: no; this PR changes only the repair-script-library.

Testing

  • PowerShell parser: pass for production script and fixture suite.
  • tests/test-win-enable-nvme-boot-driver.ps1: pass.
  • tests/test-get-disk-partitions-v3.ps1: pass.
  • map.json parse, unique IDs, and path resolution: pass (28 entries).
  • Staged git diff --check: pass.
  • Security pattern scan: pass.
  • PSScriptAnalyzer: not run; module is not installed locally.

Remaining Release Gates

  • SCR-2: add and run binary offline SYSTEM-hive fixtures; current helper-boundary fixtures do not prove byte-level hive isolation.
  • SCR-4: live-validate resource-disk exclusion on both SCSI and NVMe repair VMs before any customer-disk write.
  • SCR-5: prove the full cycle on a genuinely non-booting VM: inject, controller flip, confirm failure, repair, restore, and confirm boot.
  • SCR-3: add the permanent map.json run-id only after SCR-2, SCR-4, and SCR-5 evidence is reviewed.

Cross-repo impact (repair-script-library)

  • Run-id / map.json / script changes: adds an unregistered script for preview validation; no map.json or Azure CLI extension change.
  • Preview testing depends on the existing az vm repair run --preview workflow.

Security review

  • Every registry mutation is preceded by Assert-OfflineTarget.
  • Rollback rejects registry sections outside the selected offline stornvme subtree, including mixed-hive files.
  • Failed imports do not perform post-import removals.
  • No Invoke-Expression, cmd /c, string-built shell command, CriticalDeviceDatabase write, credential handling, or secret logging is introduced.

Backward compatibility

  • Breaking changes: none. No existing script or run-id changes, and no permanent public contract is introduced while validation remains incomplete.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved rollback-safety and registry-fixture correctness issues remain, including one critical finding.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds a preview-only Windows PowerShell workflow for offline stornvme driver inspection, repair, and rollback.

Changes:

  • Implements guarded Report, Repair, and Rollback modes with backup and verification.
  • Adds fixture-based safety and recovery tests.
  • Keeps the permanent map.json entry unpublished pending validation.
File summaries
File Review findings
tests/test-win-enable-nvme-boot-driver.ps1 Moderate (2 votes): Test fixtures infer registry types from CLR values, exporting ImagePath as REG_SZ instead of the required REG_EXPAND_SZ.
src/windows/win-enable-nvme-boot-driver.ps1 Critical (2 votes): Rollback scope validation accepts deletion-section headers, allowing deletion of the selected service key. Moderate (1 vote each): Backup value tracking includes child sections; absent-value removal is not idempotent; rollback cannot restore a missing stornvme key.
Review details

Suppressed comments (5)

src/windows/win-enable-nvme-boot-driver.ps1:164

  • The post-repair check repeats a value-only comparison, so it can print VERIFIED when a required value has the right text/number but the wrong registry kind, such as REG_SZ ImagePath instead of REG_EXPAND_SZ. Read and compare GetValueKind() here as well so verification proves the exact desired state.
        foreach ($name in $desiredValues.Keys) {
            if ($verified.$name -ne $desiredValues[$name].Value) {
                throw "Verification failed for $servicePath\$name. Expected '$($desiredValues[$name].Value)', read '$($verified.$name)'. Roll back with Mode=Rollback BackupFile='$backupPath'."

src/windows/win-enable-nvme-boot-driver.ps1:148

  • The backup folder and filename are derived only from the drive letter and a second-resolution timestamp, so concurrent or repeated runs in the same second can overwrite the only rollback copy with another run's state. Use a per-run unique directory or filename and refuse reuse before exporting.
        $evidenceRoot = Join-Path $env:PUBLIC "Desktop\nvme-repair-$(Get-Date -Format yyyyMMddHHmmss)"
        New-Item -Path $evidenceRoot -ItemType Directory -Force -ErrorAction Stop | Out-Null
        $backupPath = Join-Path $evidenceRoot "$($offline.WindowsDrive.TrimEnd(':'))-stornvme-before.reg"

src/windows/win-enable-nvme-boot-driver.ps1:120

  • $backedUpValueNames is extracted from every section in the .reg file, including allowed child keys below stornvme. If a child key happens to contain a name such as ImagePath while the service root omitted that value, the root value is incorrectly treated as backed up, left in place after import, and the exact comparison then fails with an incomplete rollback. Track value names only in the exact $expectedKey section before removing missing root values.
            $backedUpValueNames = @([regex]::Matches($backupText, '(?m)^\s*"([^"]+)"=') |
                ForEach-Object { $_.Groups[1].Value })
            foreach ($name in $desiredValues.Keys) {
                if ($backedUpValueNames -contains $name) { continue }
                [void](Assert-OfflineTarget -Path $servicePath -Action "remove the offline stornvme $name value during rollback")

src/windows/win-enable-nvme-boot-driver.ps1:121

  • When a desired value is absent from the backup, rollback always calls Remove-ItemProperty after import. On a repeated rollback, or when that value is already absent in the target, the removal throws and a valid backup is reported as failed instead of being an exact no-op. Make this removal idempotent by skipping it when the value is not present after import, and add a repeated-rollback fixture.
            foreach ($name in $desiredValues.Keys) {
                if ($backedUpValueNames -contains $name) { continue }
                [void](Assert-OfflineTarget -Path $servicePath -Action "remove the offline stornvme $name value during rollback")
                Remove-ItemProperty -LiteralPath $servicePath -Name $name -Force -ErrorAction Stop

src/windows/win-enable-nvme-boot-driver.ps1:76

  • Rollback cannot restore a missing stornvme key because this presence check, followed immediately by Get-ItemProperty, runs before the Rollback branch. A valid Repair backup can recreate that key under Services, so defer the current-state key/value read for Rollback and let the guarded import plus export verification handle an absent target.
        if ((Get-OfflineHiveKeyState -HiveKey $servicePath) -ne 'Present') {
            throw "The offline stornvme service key '$servicePath' is not present. No changes were made."
        }

        $current = Get-ItemProperty -LiteralPath $servicePath -ErrorAction Stop
  • Files reviewed: 2/2 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/windows/win-enable-nvme-boot-driver.ps1 Outdated
Comment thread tests/test-win-enable-nvme-boot-driver.ps1 Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Unresolved critical rollback-safety findings and additional repair safeguards require human validation.

Review details

Suppressed comments (3)

src/windows/win-enable-nvme-boot-driver.ps1:52

  • Test-OfflinePath only checks existence, not that the path is a file, so a directory or other non-file item named stornvme.sys satisfies this fail-closed driver gate and Repair can proceed without a driver binary. Check for a leaf file here (or add an equivalent file-type check to the helper) before changing the hive.
    if (-not (Test-OfflinePath $driverPath)) {

src/windows/win-enable-nvme-boot-driver.ps1:173

  • If a property write fails here after an earlier property was changed, the outer catch reports only the provider exception and does not include $backupPath. That leaves a partially mutated offline service with no rollback path in the command output; wrap the post-export mutation/verification in error handling that preserves and reports the backup path.
        foreach ($change in $changes) {
            [void](Assert-OfflineTarget -Path $servicePath -Action "set the offline stornvme $($change.Name) value")
            $desired = $desiredValues[$change.Name]
            New-ItemProperty -LiteralPath $servicePath -Name $change.Name -Value $desired.Value -PropertyType $desired.Kind -Force -ErrorAction Stop | Out-Null
        }

src/windows/win-enable-nvme-boot-driver.ps1:74

  • Get-OfflineHiveKeyState can return Unknown for access-denied or indeterminate reads, but this condition allows that state through whenever Mode=Rollback; the script can then import into a target whose existing state was never safely resolved. Reject Unknown before the rollback exception, while continuing to allow Absent so a missing service key can be recreated.
        if ($Mode -ne 'Rollback' -and $serviceKeyState -ne 'Present') {
            throw "The offline stornvme service key '$servicePath' is not present. No changes were made."
  • Files reviewed: 2/2 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment thread src/windows/win-enable-nvme-boot-driver.ps1
Comment thread src/windows/win-enable-nvme-boot-driver.ps1
Both rollback checks that could reject a backup ran after reg.exe import, so a refused file had already changed the offline hive. The exact-root-section requirement now runs before the import, and a new preflight refuses when the offline subtree holds a subkey or value the backup does not restore - reg.exe import merges, so those would have survived the rollback and only surfaced as a verification failure once the hive was already modified. Adds regression coverage for all three shapes; each fails against the previous script.
@EdwinBernal1
Edwin Bernal Microsoft (EdwinBernal1) merged commit 6765727 into Azure:main Sep 11, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants