Prepare a USB stick with the post-install toolkit - #21
Open
DevSecNinja wants to merge 2 commits into
Open
Conversation
…allUsb) Adds a third, low-effort way to use the tool: flash a STOCK Windows 11 ISO (e.g. from a Visual Studio subscription) to a USB stick, then point prepare-usb.ps1 at that stick once. New-PostInstallUsb validates the target (removable volume root, real Windows Setup media, media architecture from the UEFI boot loader, catalog selection, free space), stages the toolkit into a folder at the stick root, and generates a self-contained bootstrap that self-elevates, copies itself to %ProgramData%\windows-iso-maker so it survives unplugging and reboots, and runs post-install.ps1 with the chosen profile under a transcript. In the default FirstLogon mode it also writes a MINIMAL Autounattend.xml containing only an oobeSystem FirstLogonCommands block, from the new templates/autounattend/firstlogon.xml.template. Windows Setup therefore stays completely stock (interactive edition, partitioning and OOBE, including an Entra ID sign-in) and no disk is ever wiped. -Mode Toolkit stages the toolkit without any answer file. New files are ASCII-only: Windows PowerShell 5.1 reads no-BOM files as ANSI, so an em dash inside a double-quoted string becomes a smart quote and breaks parsing. Verified the module imports and the bootstrap runs end to end under powershell.exe 5.1. Docs: new docs/usb.md, a "Three ways to use this tool" section in the README, and cross-links from post-install.md / autounattend.md. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c85d9b13-c960-464c-be72-29d9f33ce3b1
Follow-up to the review of New-PostInstallUsb. The staging half was sound; the first-logon execution model was where the real risk sat. Verified against Microsoft's documentation that FirstLogonCommands run elevated only when the first user to sign in is a local administrator - a standard user gets a consent prompt, and nothing runs if it is declined or if UAC is disabled. Whether an Entra ID account becomes a local admin is decided by Entra/Intune, so the automatic run is NOT guaranteed in the scenario this feature targets. The docs previously asserted Entra compatibility as fact; they now state the caveat, cite the source, and point at -Mode Toolkit as the hook-free alternative. Because a silent no-op on a brand-new machine is the worst possible failure: - The generated discovery command now writes a breadcrumb to %ProgramData%\windows-iso-maker\logs\bootstrap.log before it searches, and records explicitly when no toolkit is found instead of exiting 0 in silence. - The bootstrap logs before elevation, reports ELEVATION FAILED with guidance when the relaunch throws, and - found by running the real generated command end to end - now checks the elevated child's exit code, since Start-Process succeeding only means the process started. A child that died instantly was previously reported as "Elevated run finished". - Discovery moved into New-PostInstallDiscoveryCommand so it is unit tested, rejects quote characters in the folder name, and is asserted to contain no double quote (it is embedded in -Command "..." inside XML). Also: - Removable detection no longer relies on Win32_LogicalDisk DriveType alone. USB SSDs and USB-NVMe enclosures report as fixed, so a volume on a USB bus (MSFT_Disk BusType 7) is now accepted rather than requiring -Force. - Autounattend.xml is written as UTF-8 WITHOUT a BOM. Set-Content -Encoding UTF8 emits a BOM under PS 5.1, contradicting the repository's encoding rule. - -WslServicing and -WslAutoReboot are forwarded, closing a capability gap against post-install.ps1. - 11 new tests cover the CIM probing branch (previously uncovered, because a temp-dir fake USB is not a volume root), the discovery command, the bootstrap generator and the literal escaping. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c85d9b13-c960-464c-be72-29d9f33ce3b1
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.
Why
I download stock Windows 11 ISOs from the Visual Studio portal, and building a custom ISO is a lot of effort for something I do occasionally. What I actually want most of the time is: flash the stock ISO to a USB stick, install Windows, and have the documented change catalog applied without fetching this repo on the new machine.
post-install.ps1already covers "machine already exists". This adds the missing middle step for "new machine from my own media", without requiring the full offline build path.What this adds
prepare-usb.ps1->New-PostInstallUsb. You flash a stock ISO to a stick as usual, then point this at it once:It validates before writing anything (removable volume root, real Setup media, architecture from the UEFI boot loader, catalog-id validity, free space), stages the toolkit into a folder at the stick root, and generates a bootstrap that self-elevates, copies itself to
%ProgramData%\windows-iso-makerso it survives unplugging and reboots, and runspost-install.ps1under a transcript.In the default
FirstLogonmode it also writes a minimalAutounattend.xmlcontaining only anoobeSystemFirstLogonCommandsblock. Windows Setup stays completely stock: interactive edition, partitioning and OOBE, including Entra ID sign-in. NoDiskConfiguration, noWillWipeDisk, no edition selection, and no disk is ever wiped by this tool.-Mode Toolkitskips the answer file entirely.This is deliberately a different artifact from the full build answer file in
templates/autounattend/autounattend.xml.template, which does repartition, so a newfirstlogon.xml.templatewas added rather than reusing that one.The important caveat, and why the second commit exists
A rubber-duck review pushed back on the first-logon model, and it was right. Microsoft documents that
FirstLogonCommandsrun elevated only if the first user to sign in is a local administrator; a standard user gets a consent prompt, and nothing runs if it is declined or if UAC is disabled. Whether an Entra ID account becomes a local admin is decided by Entra/Intune, so the automatic run is not guaranteed in exactly the scenario this targets. The docs originally asserted Entra compatibility as fact; they now state the caveat, cite the source, and point at-Mode Toolkit.Since a silent no-op on a brand-new machine is the worst possible failure, the second commit makes every failure mode visible in
%ProgramData%\windows-iso-maker\logs\bootstrap.log: discovery logs before it searches and says so when it finds nothing, and the bootstrap reports elevation failures with guidance.Worth a careful look: running the real generated command end to end caught a bug the tests did not.
Start-Process -Verb RunAs -Waitsucceeding only means the process started, so a child that died immediately was reported as "Elevated run finished". It now checks the child's exit code.Notes for review
[scriptblock]::Createand by unit-testing the generators and the literal escaping. A static script plus a settings.psd1would be lintable, and is a reasonable follow-up if you'd prefer it.Win32_LogicalDiskDriveTypealone, since USB SSDs report as fixed. A volume on a USB bus (MSFT_DiskBusType7) is now accepted rather than requiring-Force..ps1files are ASCII-only. Windows PowerShell 5.1 reads no-BOM files as ANSI, so an em dash inside a double-quoted string becomes a smart quote and breaks parsing. This bit during development and is whyAutounattend.xmlis now written BOM-less viaUTF8Encoding($false)rather thanSet-Content -Encoding UTF8.Resolve-CatalogSelectionand the existing profiles unchanged.Validation
src,tests, and all dispatchers.powershell.exe5.1, not just the test host: module import, bootstrap execution, and the actual generated first-logon command driven from the rendered XML, covering both the "toolkit found" and "no toolkit found" paths (the latter via asubstdrive root).