Add win-fix-bcd - #157
Open
Marcus Ferreira (mvaferreira) wants to merge 2 commits into
Open
Add win-fix-bcd#157Marcus Ferreira (mvaferreira) wants to merge 2 commits into
Marcus Ferreira (mvaferreira) wants to merge 2 commits into
Conversation
Repairs the boot configuration of an offline Windows disk so the firmware can find and start Windows again. Targets the "Boot Configuration Data" family of failures, not a damaged partition.
- Get-BcdEntryFinding consumed Get-BcdInventory without checking EnumSucceeded. When bcdedit /enum all fails the helper returns Loaders=@(), which is identical to a store with no loader entry, so a store that was merely locked or access-denied was renamed and rebuilt, discarding the serial console settings and timeouts the script promises to preserve. An unreadable store is now a distinct non-repairable finding that reports the bcdedit exit code. - The boot manager branch escalated any binary that was not IsLikelyMicrosoft to a destructive rebuild. Test-OfflineFileSignature returns Confidence='None' with IsLikelyMicrosoft=false for a compressed boot stub, which is the ordinary result for Gen1 bootmgr, so a healthy Gen1 image could have its store rebuilt and then fail the post-repair re-detect. Only a definitive negative (Confidence='High' and not Microsoft), a missing file or a zero-byte file now escalates. - Invoke-Revert acted on absolute paths and a disk number read from a manifest on the broken disk with no validation. It now requires the manifest to match the store path and disk number this run resolved, and passes both paths through Assert-OfflineTarget before copying. - The revert manifest is written before the first write instead of after the last one, so a failure part way through no longer leaves a modified store with nothing recorded to put back. The catch block now names the backup. - bcdboot is invoked directly with an argument array instead of through cmd.exe /c, which expanded %VAR% inside the quoted Windows path. - Refuse to guess when several Windows installations are attached and no windowsDrive was supplied, matching win-enable-nvme-boot-driver. - Added the missing finally: release the drive letters Get-OfflineWindowsDisk assigned and flush the log buffer on the failure paths too. - BootPartitionActive findings are marked repaired after their own attempt. - Header no longer points at run ids that may not exist in the installed library. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b4446db8-dda7-4210-a934-5b9ae9e3fb15
Author
Review findings addressed —
|
| ID | Finding | Change |
|---|---|---|
| C1 | Get-BcdInventory output consumed without an EnumSucceeded guard, so a store that could not be read was treated as empty and destroyed |
a failed enumeration now raises BcdStoreUnreadable with -Tier 'None'; the script refuses rather than rebuilding over a store it could not read |
| C2 | Revert manifest values used unvalidated — arbitrary overwrite, and the rescue VM's own active flag could be set | Invoke-Revert validates the manifest before acting: StorePath and DiskNumber must match the disk in hand, and Assert-OfflineTarget guards both write paths |
| C3 | The -not $signature.IsLikelyMicrosoft branch escalated an inconclusive signature check to a full store Rebuild |
that branch removed; an inconclusive signature no longer justifies a rebuild |
| W1 | No finally: drive letters leaked and the log was not flushed on failure |
finally added, with $backupPath hoisted so it is in scope |
| W2 | Dangling run-id references (win-fix-boot-partition, win-fix-code-integrity) |
corrected to ids that exist in map.json |
| W3 | Revert manifest written after the repair, and the backup path was not named in the catch |
Save-RevertManifest -ActivatedPartition 0 is written before the first write; the catch names the backup file |
| W4 | Multiple Windows candidates were not refused | refuses when @($offline.Candidates).Count -gt 1 rather than guessing |
| W5 | bcdboot invoked through cmd.exe, exposing the command line to percent expansion |
invoked directly as & bcdboot.exe … 2>&1, no cmd.exe |
Validation
Break → detect → repair → detect → az vm repair restore → confirm the guest boots, all through
the product path with --preview pointing at the candidate branch.
| Image | Generation / layout | Build | Result |
|---|---|---|---|
WindowsServer:2022-datacenter-g2 |
Gen2 / UEFI | 20348 | 11/11 passed |
The two changes most likely to over-tighten were specifically exercised:
- C3 did not suppress a legitimate rebuild. With the BCD store genuinely missing, the script
still escalated correctly — "Repair would rebuild the store with bcdboot, because there is no
sound entry left to correct" — rebuilt it, and the post-repair detect reported "the boot
configuration on this disk is consistent". The VM booted. - W5 produced a working store. Calling
bcdbootdirectly rather than throughcmd.exeyielded
a bootable UEFI configuration on a Gen2 disk.
Provenance
- Reviewed head:
057d5a2a375e92b1540f00576beb694e602535fd - Final tested head:
cdf4a13 - Review prompt:
PR-Review-Agent.md, SHA2565451BA5C325F61E34FE63B9AF99322A17D70E55210CAF23F5CE27E0F839D8F70 - Counts derived from the report tables: 3 Criticals, 5 Warnings (7 Info items not itemised here)
- The original agent report is tied to head
057d5a2aand is historical: the code has changed since. - Quality gates on the final head: PowerShell 7 and 5.1 parse clean, PSSAnalyzer at parity with the
reviewed head, repository pre-commit hook passing, CRLF line endings preserved.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
win-fix-bcd— a single repair scenario, 1025 lines, plus its ownmap.jsonentry.Repairs the boot configuration of an offline Windows disk so the firmware can find and start Windows again. Targets the "Boot Configuration Data" family of failures, not a damaged partition.
The catalog entry a support engineer reads when choosing it:
How it works
Runs against the broken OS disk attached to a rescue VM by "az vm repair create". It answers the question "why can the boot manager not start this Windows installation" using only evidence read from the offline disk, and then changes only what the evidence names.
The script repairs in two tiers, and which tier runs is decided by the evidence:
Parameters
detectOnlyrebuildrevertwindowsDriveConventions followed
.\src\windows\common\setup\init.ps1and returns$STATUS_SUCCESSor$STATUS_ERROR.Write-Host.az vm run-commandkeeps only the last 4096 characters of the output stream, so a summary printed first is the first thing a long run loses.PSUseShouldProcessForStateChangingFunctionson internal helper functions. The script is invoked non-interactively byaz vm repair run, so-WhatIf/-Confirmon an internal function are unreachable; the reporting-only mode is thedetectOnlyparameter instead.Testing
Exercised through the
az vm repair run --previewproduct path against real Azure VMs: a lab VM is broken deliberately to create the fault, the repair is run against the attached disk from a rescue VM, and the VM is confirmed to boot and behave afterwards. Detection, restraint on a healthy image, and the repair itself are each checked.Series
Second wave of four independent scenario PRs, after #149, #150, #151 and #152. The shared helpers
these scripts dot-source are already merged in #143, #146 and #147. This PR adds no helper files and
changes no existing scenario; its only existing-file change is appending this run-id to
map.json,inserted into whatever upstream carries now so every existing entry is preserved byte for byte.