commit 5c0f11370fbacd810e23953447c4e4b939cc28a3 Author: Tero Date: Fri Aug 28 09:31:47 2026 +0300 first commit diff --git a/Install-WSL2Ubuntu.ps1 b/Install-WSL2Ubuntu.ps1 new file mode 100644 index 0000000..58fa6a1 --- /dev/null +++ b/Install-WSL2Ubuntu.ps1 @@ -0,0 +1,109 @@ +<# +.SYNOPSIS + Enables all Windows features required for WSL 2 on Windows 11 and installs the latest Ubuntu distribution. + +.DESCRIPTION + This script must be run as Administrator. It will: + 1. Enable the "Microsoft-Windows-Subsystem-Linux" and "VirtualMachinePlatform" optional features. + 2. Set WSL 2 as the default WSL version. + 3. Install/update the WSL kernel components. + 4. Install the latest Ubuntu distribution from the Microsoft Store catalog (via `wsl --install -d Ubuntu`). + + A restart may be required after enabling the Windows features for the first time. If a restart is + needed, the script will detect it, prompt you, and re-run automatically after reboot (via a scheduled + task) unless you decline. + +.NOTES + Run this script from an elevated PowerShell prompt: + powershell -ExecutionPolicy Bypass -File .\Install-WSL2Ubuntu.ps1 +#> + +[CmdletBinding()] +param( + [string]$Distribution = "Ubuntu" +) + +$ErrorActionPreference = "Stop" + +function Test-IsAdmin { + $identity = [Security.Principal.WindowsIdentity]::GetCurrent() + $principal = New-Object Security.Principal.WindowsPrincipal($identity) + return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +} + +function Write-Step { + param([string]$Message) + Write-Host "`n==> $Message" -ForegroundColor Cyan +} + +if (-not (Test-IsAdmin)) { + Write-Error "This script must be run as Administrator. Right-click PowerShell and choose 'Run as Administrator', then re-run this script." + exit 1 +} + +# --- Step 1: Enable required Windows optional features --- +Write-Step "Enabling 'Windows Subsystem for Linux' feature" +$wslFeature = Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux -NoRestart + +Write-Step "Enabling 'Virtual Machine Platform' feature" +$vmpFeature = Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform -NoRestart + +$restartNeeded = $wslFeature.RestartNeeded -or $vmpFeature.RestartNeeded + +if ($restartNeeded) { + Write-Host "`nA restart is required to finish enabling the WSL 2 prerequisites." -ForegroundColor Yellow + $answer = Read-Host "Restart now and continue installation automatically after reboot? (Y/N)" + + if ($answer -match '^[Yy]') { + Write-Step "Scheduling this script to resume after reboot" + + $scriptPath = $MyInvocation.MyCommand.Path + $taskName = "Resume-WSL2Install" + + $action = New-ScheduledTaskAction -Execute "powershell.exe" ` + -Argument "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`" -Distribution `"$Distribution`"" + $trigger = New-ScheduledTaskTrigger -AtLogOn + $principal = New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME" -RunLevel Highest + + Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Force | Out-Null + + Write-Host "Restarting in 10 seconds... (Ctrl+C to cancel)" -ForegroundColor Yellow + Start-Sleep -Seconds 10 + Restart-Computer -Force + exit 0 + } + else { + Write-Host "Please restart your computer manually, then re-run this script to continue." -ForegroundColor Yellow + exit 0 + } +} + +# If we get here after a reboot via the scheduled task, clean it up. +$existingTask = Get-ScheduledTask -TaskName "Resume-WSL2Install" -ErrorAction SilentlyContinue +if ($existingTask) { + Unregister-ScheduledTask -TaskName "Resume-WSL2Install" -Confirm:$false +} + +# --- Step 2: Update WSL and set default version to 2 --- +Write-Step "Updating the WSL kernel components" +wsl --update + +Write-Step "Setting WSL default version to 2" +wsl --set-default-version 2 + +# --- Step 3: Install the latest Ubuntu distribution --- +Write-Step "Checking for an existing '$Distribution' installation" +$installedDistros = (wsl --list --quiet 2>$null) -replace "`0", "" + +if ($installedDistros -match [regex]::Escape($Distribution)) { + Write-Host "'$Distribution' is already installed. Skipping installation." -ForegroundColor Green +} +else { + Write-Step "Installing '$Distribution' (latest version from Microsoft Store)" + wsl --install -d $Distribution --no-launch +} + +Write-Step "Done" +Write-Host "WSL 2 features are enabled and '$Distribution' has been installed." -ForegroundColor Green +Write-Host "Launch it by running: wsl -d $Distribution" -ForegroundColor Green +Write-Host "On first launch you'll be asked to create a Unix username and password." -ForegroundColor Green diff --git a/New-Win11GeneralizedImage.ps1 b/New-Win11GeneralizedImage.ps1 new file mode 100644 index 0000000..15c317b --- /dev/null +++ b/New-Win11GeneralizedImage.ps1 @@ -0,0 +1,240 @@ +<# +.SYNOPSIS + Prepares a Windows 11 Pro reference machine and creates a generalized (sysprepped) WIM image for deployment. + +.DESCRIPTION + This script must be run as Administrator on the reference machine. It operates in two modes: + + -Mode Sysprep (default) + 1. Optionally removes provisioned AppX packages that should not ship in the image. + 2. Runs Disk Cleanup / component store cleanup (DISM /StartComponentCleanup) to shrink the image. + 3. Generates an unattend.xml (if one is not supplied) that skips OOBE prompts and re-seals the OS. + 4. Runs `sysprep.exe /oobe /generalize /shutdown /unattend:`, which shuts the machine down when done. + + After the machine shuts down, boot it from a WinPE / Windows PE media (e.g. from a WDS, MDT, or + a USB WinPE stick) to actually capture the disk, since Windows cannot capture its own running volume. + + -Mode Capture + Captures the generalized OS volume into a .wim file using DISM. This must be run from WinPE (or + another OS) where the sysprepped Windows partition is NOT the running OS - i.e. boot the reference + machine from WinPE media after sysprep has shut it down, then run this script (or copy it onto the + WinPE media) with -Mode Capture. + +.PARAMETER Mode + 'Sysprep' to generalize the reference machine, or 'Capture' to capture the offline volume to a WIM. + +.PARAMETER UnattendPath + Path to an unattend.xml to pass to sysprep. If omitted in Sysprep mode, a minimal one is generated. + +.PARAMETER RemoveProvisionedApps + In Sysprep mode, an optional list of AppX package name patterns (wildcards allowed) to remove before + sysprep, e.g. "*Xbox*","*Solitaire*". Defaults to none (nothing removed) unless specified. + +.PARAMETER SourceDrive + In Capture mode, the drive letter of the generalized Windows volume to capture (e.g. "D:"). Must not + be the currently running OS volume. + +.PARAMETER DestinationWim + In Capture mode, the full path of the output .wim file, e.g. "D:\Images\Win11Pro.wim". + +.PARAMETER ImageName + In Capture mode, the friendly name stored in the WIM image metadata. Defaults to "Windows 11 Pro - Deployment". + +.PARAMETER Compress + In Capture mode, DISM compression level: "fast", "max", or "none". Defaults to "max". + +.EXAMPLE + # On the reference machine, generalize and shut down: + .\New-Win11GeneralizedImage.ps1 -Mode Sysprep -RemoveProvisionedApps "*Xbox*","*Solitaire*" + +.EXAMPLE + # Booted into WinPE with the sysprepped volume mounted as D: and a USB drive as E:: + .\New-Win11GeneralizedImage.ps1 -Mode Capture -SourceDrive D: -DestinationWim "E:\Images\Win11Pro.wim" + +.NOTES + Run this script from an elevated PowerShell prompt: + powershell -ExecutionPolicy Bypass -File .\New-Win11GeneralizedImage.ps1 -Mode Sysprep +#> + +[CmdletBinding()] +param( + [ValidateSet("Sysprep", "Capture")] + [string]$Mode = "Sysprep", + + [string]$UnattendPath, + + [string[]]$RemoveProvisionedApps = @(), + + [string]$SourceDrive, + + [string]$DestinationWim, + + [string]$ImageName = "Windows 11 Pro - Deployment", + + [ValidateSet("fast", "max", "none")] + [string]$Compress = "max" +) + +$ErrorActionPreference = "Stop" + +function Test-IsAdmin { + $identity = [Security.Principal.WindowsIdentity]::GetCurrent() + $principal = New-Object Security.Principal.WindowsPrincipal($identity) + return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +} + +function Write-Step { + param([string]$Message) + Write-Host "`n==> $Message" -ForegroundColor Cyan +} + +if (-not (Test-IsAdmin)) { + Write-Error "This script must be run as Administrator. Right-click PowerShell and choose 'Run as Administrator', then re-run this script." + exit 1 +} + +function New-DefaultUnattendXml { + param([string]$Path) + + $xml = @' + + + + + + true + true + true + true + Work + 3 + true + true + + + + + + 1 + + + +'@ + + Set-Content -Path $Path -Value $xml -Encoding UTF8 +} + +function Invoke-Sysprep { + Write-Step "Preparing reference machine for generalization" + + if ($RemoveProvisionedApps.Count -gt 0) { + Write-Step "Removing provisioned AppX packages matching: $($RemoveProvisionedApps -join ', ')" + $provisioned = Get-AppxProvisionedPackage -Online + + foreach ($pattern in $RemoveProvisionedApps) { + $matches = $provisioned | Where-Object { $_.DisplayName -like $pattern } + foreach ($pkg in $matches) { + Write-Host " Removing $($pkg.DisplayName)" -ForegroundColor DarkGray + try { + Remove-AppxProvisionedPackage -Online -PackageName $pkg.PackageName -ErrorAction Stop | Out-Null + } + catch { + Write-Warning " Failed to remove $($pkg.DisplayName): $($_.Exception.Message)" + } + } + } + } + + Write-Step "Cleaning up the component store (this can take several minutes)" + Dism.exe /Online /Cleanup-Image /StartComponentCleanup /ResetBase | Out-Null + + if (-not $UnattendPath) { + $UnattendPath = Join-Path -Path $env:TEMP -ChildPath "unattend-generalize.xml" + Write-Step "No -UnattendPath supplied, generating a default unattend file at $UnattendPath" + New-DefaultUnattendXml -Path $UnattendPath + } + elseif (-not (Test-Path -Path $UnattendPath)) { + Write-Error "The specified -UnattendPath '$UnattendPath' does not exist." + exit 1 + } + + $sysprepExe = Join-Path -Path $env:WINDIR -ChildPath "System32\Sysprep\sysprep.exe" + if (-not (Test-Path -Path $sysprepExe)) { + Write-Error "sysprep.exe was not found at '$sysprepExe'." + exit 1 + } + + Write-Step "Running sysprep /oobe /generalize /shutdown (the machine will power off when finished)" + Write-Host "Unattend file: $UnattendPath" -ForegroundColor DarkGray + + & $sysprepExe /oobe /generalize /shutdown /unattend:$UnattendPath + + Write-Host "`nSysprep started. Windows will shut down automatically once generalization completes." -ForegroundColor Yellow + Write-Host "Boot this machine from WinPE media next, then run this script with -Mode Capture to create the WIM." -ForegroundColor Yellow +} + +function Invoke-Capture { + if (-not $SourceDrive) { + Write-Error "-SourceDrive is required in Capture mode (e.g. -SourceDrive D:)." + exit 1 + } + if (-not $DestinationWim) { + Write-Error "-DestinationWim is required in Capture mode (e.g. -DestinationWim E:\Images\Win11Pro.wim)." + exit 1 + } + + $normalizedSource = $SourceDrive.TrimEnd('\') + if (-not ($normalizedSource -match '^[A-Za-z]:$')) { + Write-Error "-SourceDrive must be a drive letter like 'D:'." + exit 1 + } + + $runningSystemDrive = $env:SystemDrive + if ($normalizedSource -ieq $runningSystemDrive) { + Write-Error "The source drive '$normalizedSource' appears to be the currently running OS volume. Capture must be performed from WinPE with the target volume offline, not from within the running OS." + exit 1 + } + + if (-not (Test-Path -Path "$normalizedSource\Windows")) { + Write-Error "No Windows installation found at '$normalizedSource\Windows'. Verify -SourceDrive points to the generalized volume." + exit 1 + } + + $destDir = Split-Path -Path $DestinationWim -Parent + if ($destDir -and -not (Test-Path -Path $destDir)) { + Write-Step "Creating destination folder $destDir" + New-Item -Path $destDir -ItemType Directory -Force | Out-Null + } + + if (Test-Path -Path $DestinationWim) { + Write-Warning "Destination file '$DestinationWim' already exists and will be overwritten." + Remove-Item -Path $DestinationWim -Force + } + + Write-Step "Capturing '$normalizedSource\' to '$DestinationWim' (this can take a long time)" + + Dism.exe /Capture-Image ` + /ImageFile:"$DestinationWim" ` + /CaptureDir:"$normalizedSource\" ` + /Name:"$ImageName" ` + /Compress:$Compress ` + /CheckIntegrity + + if ($LASTEXITCODE -ne 0) { + Write-Error "DISM capture failed with exit code $LASTEXITCODE." + exit $LASTEXITCODE + } + + Write-Host "`nImage captured successfully: $DestinationWim" -ForegroundColor Green +} + +switch ($Mode) { + "Sysprep" { Invoke-Sysprep } + "Capture" { Invoke-Capture } +} diff --git a/New-WinPEUsbMedia.ps1 b/New-WinPEUsbMedia.ps1 new file mode 100644 index 0000000..cd03ed8 --- /dev/null +++ b/New-WinPEUsbMedia.ps1 @@ -0,0 +1,148 @@ +<# +.SYNOPSIS + Builds a bootable WinPE USB drive that can be used to run New-Win11GeneralizedImage.ps1 -Mode Capture. + +.DESCRIPTION + This script must be run as Administrator on a machine that has the Windows ADK and the + "Windows PE add-on for the ADK" installed. It will: + 1. Locate copype.cmd / MakeWinPEMedia.cmd from the installed ADK. + 2. Build a WinPE working folder for the specified architecture (copype). + 3. Optionally inject PowerShell + network/storage support and a custom startup script. + 4. Copy New-Win11GeneralizedImage.ps1 (and any extra files) onto the WinPE media so it's + available once booted. + 5. Format the target USB drive and copy the bootable WinPE media onto it (MakeWinPEMedia /UFD). + + WARNING: The target USB drive is completely erased. Double-check -UsbDriveLetter before running. + +.PARAMETER UsbDriveLetter + The drive letter of the USB flash drive to turn into bootable WinPE media, e.g. "F:". This drive + will be wiped. + +.PARAMETER Architecture + WinPE architecture to build: amd64 (default), x86, or arm64. + +.PARAMETER WorkingDirectory + Folder used to stage the WinPE image files. Defaults to "C:\WinPE_". + +.PARAMETER IncludeCaptureScript + Path to New-Win11GeneralizedImage.ps1 (or any other script) to copy onto the WinPE media so it can + be run directly after boot. Defaults to New-Win11GeneralizedImage.ps1 next to this script, if present. + +.EXAMPLE + .\New-WinPEUsbMedia.ps1 -UsbDriveLetter F: -Architecture amd64 + +.NOTES + Requires the Windows ADK + WinPE add-on: https://learn.microsoft.com/windows-hardware/get-started/adk-install + Run from an elevated "Deployment and Imaging Tools Environment" or a normal elevated PowerShell prompt. +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [ValidatePattern('^[A-Za-z]:$')] + [string]$UsbDriveLetter, + + [ValidateSet("amd64", "x86", "arm64")] + [string]$Architecture = "amd64", + + [string]$WorkingDirectory, + + [string]$IncludeCaptureScript = (Join-Path -Path $PSScriptRoot -ChildPath "New-Win11GeneralizedImage.ps1") +) + +$ErrorActionPreference = "Stop" + +function Test-IsAdmin { + $identity = [Security.Principal.WindowsIdentity]::GetCurrent() + $principal = New-Object Security.Principal.WindowsPrincipal($identity) + return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +} + +function Write-Step { + param([string]$Message) + Write-Host "`n==> $Message" -ForegroundColor Cyan +} + +if (-not (Test-IsAdmin)) { + Write-Error "This script must be run as Administrator. Right-click PowerShell and choose 'Run as Administrator', then re-run this script." + exit 1 +} + +if (-not $WorkingDirectory) { + $WorkingDirectory = "C:\WinPE_$Architecture" +} + +# Locate the ADK deployment tools that ship copype.cmd / MakeWinPEMedia.cmd +$adkRoots = @( + "${env:ProgramFiles(x86)}\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment", + "$env:ProgramFiles\Windows Kits\10\Assessment and Deployment Kit\Windows Preinstallation Environment" +) +$dandiScript = $null +foreach ($root in $adkRoots) { + $candidate = Join-Path -Path (Split-Path -Path $root -Parent) -ChildPath "DandISetEnv.bat" + if (Test-Path -Path $candidate) { + $dandiScript = $candidate + break + } +} + +if (-not $dandiScript) { + Write-Error "Could not find the Windows ADK Deployment Tools environment (DandISetEnv.bat). Install the Windows ADK and the 'Windows PE add-on for the ADK' first: https://learn.microsoft.com/windows-hardware/get-started/adk-install" + exit 1 +} + +Write-Step "Using ADK deployment tools environment: $dandiScript" + +# Import the ADK's Deployment Tools environment variables (sets copype/MakeWinPEMedia on PATH) into this session +$envVars = cmd /c "`"$dandiScript`" && set" +foreach ($line in $envVars) { + if ($line -match '^([^=]+)=(.*)$') { + [System.Environment]::SetEnvironmentVariable($Matches[1], $Matches[2], "Process") + } +} + +$copype = (Get-Command copype.cmd -ErrorAction SilentlyContinue) +$makeWinPEMedia = (Get-Command MakeWinPEMedia.cmd -ErrorAction SilentlyContinue) +if (-not $copype -or -not $makeWinPEMedia) { + Write-Error "copype.cmd / MakeWinPEMedia.cmd were not found on PATH even after loading the ADK environment." + exit 1 +} + +if (Test-Path -Path $WorkingDirectory) { + Write-Step "Removing existing working directory $WorkingDirectory" + Remove-Item -Path $WorkingDirectory -Recurse -Force +} + +Write-Step "Building WinPE working folder ($Architecture) at $WorkingDirectory" +& $copype.Source $Architecture $WorkingDirectory +if ($LASTEXITCODE -ne 0) { + Write-Error "copype failed with exit code $LASTEXITCODE." + exit $LASTEXITCODE +} + +if ($IncludeCaptureScript -and (Test-Path -Path $IncludeCaptureScript)) { + Write-Step "Copying $IncludeCaptureScript onto the WinPE media" + $mediaRoot = Join-Path -Path $WorkingDirectory -ChildPath "media" + Copy-Item -Path $IncludeCaptureScript -Destination (Join-Path -Path $mediaRoot -ChildPath (Split-Path -Path $IncludeCaptureScript -Leaf)) -Force +} +else { + Write-Warning "IncludeCaptureScript '$IncludeCaptureScript' not found, skipping." +} + +Write-Host "`nThe target drive $UsbDriveLetter will be ERASED." -ForegroundColor Yellow +$confirm = Read-Host "Type YES to continue and format $UsbDriveLetter as bootable WinPE USB media" +if ($confirm -ne "YES") { + Write-Host "Aborted, no changes made to $UsbDriveLetter." -ForegroundColor Yellow + exit 0 +} + +Write-Step "Formatting $UsbDriveLetter and copying bootable WinPE media (MakeWinPEMedia /UFD)" +& $makeWinPEMedia.Source /UFD $WorkingDirectory $UsbDriveLetter /f +if ($LASTEXITCODE -ne 0) { + Write-Error "MakeWinPEMedia failed with exit code $LASTEXITCODE." + exit $LASTEXITCODE +} + +Write-Host "`nWinPE USB media created successfully on $UsbDriveLetter." -ForegroundColor Green +Write-Host "Boot the reference machine from this USB drive, then from the WinPE command prompt run:" -ForegroundColor Green +Write-Host " powershell -ExecutionPolicy Bypass -File X:\New-Win11GeneralizedImage.ps1 -Mode Capture -SourceDrive D: -DestinationWim E:\Images\Win11Pro.wim" -ForegroundColor DarkGray diff --git a/README.md b/README.md new file mode 100644 index 0000000..26e39cc --- /dev/null +++ b/README.md @@ -0,0 +1,126 @@ +# powershell-scripts + +A collection of standalone PowerShell scripts for Windows setup and deployment tasks. All scripts must be run from an elevated (Administrator) PowerShell prompt. + +## Contents + +- [Install-WSL2Ubuntu.ps1](#install-wsl2ubuntups1) — Enable WSL 2 and install Ubuntu. +- [New-Win11GeneralizedImage.ps1](#new-win11generalizedimageps1) — Generalize a Windows 11 Pro reference machine and capture it to a deployment WIM. +- [New-WinPEUsbMedia.ps1](#new-winpeusbmediaps1) — Build a bootable WinPE USB drive for use with the capture script. + +--- + +## Install-WSL2Ubuntu.ps1 + +Enables the Windows features required for WSL 2 and installs the latest Ubuntu distribution. + +**What it does:** +1. Enables the `Microsoft-Windows-Subsystem-Linux` and `VirtualMachinePlatform` optional features. +2. Sets WSL 2 as the default WSL version. +3. Installs/updates the WSL kernel components. +4. Installs the latest Ubuntu distribution via `wsl --install -d Ubuntu`. +5. If a restart is required after enabling the features, prompts to reboot and automatically resumes the script after sign-in (via a scheduled task). + +**Parameters:** + +| Parameter | Default | Description | +|---|---|---| +| `-Distribution` | `Ubuntu` | The WSL distribution name to install. | + +**Usage:** +```powershell +powershell -ExecutionPolicy Bypass -File .\Install-WSL2Ubuntu.ps1 +``` + +--- + +## New-Win11GeneralizedImage.ps1 + +Prepares a Windows 11 Pro reference machine and creates a generalized (sysprepped) WIM image for deployment. Operates in two modes. + +### Mode: Sysprep (default) + +Run **on the reference machine** while it's still the running OS. + +1. Optionally removes provisioned AppX packages matching supplied wildcard patterns. +2. Runs `DISM /Online /Cleanup-Image /StartComponentCleanup /ResetBase` to shrink the component store. +3. Generates a minimal `unattend.xml` (skips OOBE prompts) if one isn't supplied. +4. Runs `sysprep.exe /oobe /generalize /shutdown /unattend:`. The machine powers off automatically once done. + +> Windows cannot capture its own running volume — after sysprep shuts the machine down, boot it from WinPE media (see [New-WinPEUsbMedia.ps1](#new-winpeusbmediaps1)) to actually capture the image. + +**Parameters:** + +| Parameter | Default | Description | +|---|---|---| +| `-Mode` | `Sysprep` | `Sysprep` or `Capture`. | +| `-UnattendPath` | (generated) | Path to a custom unattend.xml. | +| `-RemoveProvisionedApps` | `@()` | Wildcard patterns of AppX packages to remove, e.g. `"*Xbox*","*Solitaire*"`. | + +**Usage:** +```powershell +.\New-Win11GeneralizedImage.ps1 -Mode Sysprep -RemoveProvisionedApps "*Xbox*","*Solitaire*" +``` + +### Mode: Capture + +Run **from WinPE**, after the reference machine has shut down from sysprep, with the generalized Windows volume mounted as an offline drive letter (not the running OS). + +Captures the offline volume into a `.wim` file using `DISM /Capture-Image`. Refuses to run against the currently running OS volume as a safety check. + +**Parameters:** + +| Parameter | Default | Description | +|---|---|---| +| `-SourceDrive` | *(required)* | Drive letter of the generalized volume, e.g. `D:`. | +| `-DestinationWim` | *(required)* | Output path for the `.wim` file, e.g. `E:\Images\Win11Pro.wim`. | +| `-ImageName` | `Windows 11 Pro - Deployment` | Friendly name stored in the WIM metadata. | +| `-Compress` | `max` | DISM compression level: `fast`, `max`, or `none`. | + +**Usage:** +```powershell +.\New-Win11GeneralizedImage.ps1 -Mode Capture -SourceDrive D: -DestinationWim "E:\Images\Win11Pro.wim" +``` + +--- + +## New-WinPEUsbMedia.ps1 + +Builds a bootable WinPE USB drive that can be used to run `New-Win11GeneralizedImage.ps1 -Mode Capture`. + +**Prerequisites:** the [Windows ADK](https://learn.microsoft.com/windows-hardware/get-started/adk-install) and the "Windows PE add-on for the ADK" must be installed. Both can be installed via winget: +```powershell +winget install --id Microsoft.WindowsADK +winget install --id Microsoft.WindowsADK.WinPEAddon +``` + +**What it does:** +1. Locates `copype.cmd` / `MakeWinPEMedia.cmd` from the installed ADK and loads the Deployment Tools environment. +2. Runs `copype` to stage a WinPE working folder for the chosen architecture. +3. Copies `New-Win11GeneralizedImage.ps1` onto the WinPE media so it's available after boot. +4. Prompts for confirmation, then formats the target USB drive and copies the bootable WinPE media onto it via `MakeWinPEMedia /UFD`. + +> ⚠️ The target USB drive is completely erased. Double-check `-UsbDriveLetter` before confirming. + +**Parameters:** + +| Parameter | Default | Description | +|---|---|---| +| `-UsbDriveLetter` | *(required)* | Drive letter of the USB flash drive to format, e.g. `F:`. | +| `-Architecture` | `amd64` | WinPE architecture: `amd64`, `x86`, or `arm64`. | +| `-WorkingDirectory` | `C:\WinPE_` | Folder used to stage the WinPE image files. | +| `-IncludeCaptureScript` | `New-Win11GeneralizedImage.ps1` next to this script | Extra script copied onto the WinPE media. | + +**Usage:** +```powershell +.\New-WinPEUsbMedia.ps1 -UsbDriveLetter F: -Architecture amd64 +``` + +--- + +## Typical end-to-end workflow + +1. `New-WinPEUsbMedia.ps1` — build the WinPE capture USB (one-time, on any machine with the ADK installed). +2. `New-Win11GeneralizedImage.ps1 -Mode Sysprep` — generalize the reference machine; it shuts down automatically. +3. Boot the reference machine from the WinPE USB. +4. `New-Win11GeneralizedImage.ps1 -Mode Capture` — capture the generalized volume to a `.wim` file for deployment.