From 71471a2fe40008d738e9542dda13cb451ddcf7b1 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 3 Sep 2026 12:09:04 +0000 Subject: [PATCH] fix: PS7 double-install, false hostname failure; WinUtil Standard runs by default - Start.ps1: poll up to 20s (and log winget's exit code) after installing PowerShell 7 via WinGet instead of a single check 5s later, so a slow-to-register install isn't mistaken for a failure that then also triggers the MSI fallback, installing PowerShell 7 twice. - Set-HostName.ps1: exit 0 (not 1) when no valid BIOS serial number is found. That's an expected, already-handled case (common on VMs), not a failure - it was making Deploy.ps1 report "Some deployment steps failed" on otherwise-clean runs. - Deploy.ps1 / Apply-Tweaks.ps1: -Tweaks now defaults to Yes (including under -NonInteractive), so the WinUtil Standard preset just runs instead of requiring an explicit opt-in. -Tweaks No still skips it. - README: drop the "Option 3" windeploy.stensel.nl one-liner - it pointed at the pre-migration repo and isn't part of this one. - Docs and changelog updated to match. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01AsMBvgEn1Zd9f2dneJDJWf --- CHANGELOG.md | 9 ++++--- README.md | 37 ++++++++++------------------- Scripts/Deploy.ps1 | 8 +++++-- Scripts/Deployment/Apply-Tweaks.ps1 | 11 +++++---- Scripts/Deployment/Set-HostName.ps1 | 2 +- Scripts/Start.ps1 | 25 ++++++++++++++----- src/content/_index.md | 4 ++-- src/content/_index.nl.md | 4 ++-- 8 files changed, 55 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c450442..575c380 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,13 +20,16 @@ Repository moved from `Stensel8/WinDeploy` to `THectic-NL/WinDeploy`. - All `Stensel8/WinDeploy` references (README, SECURITY.md, `Docs/autounattend.xml`, `Scripts/Start.ps1`, `Scripts/Deploy.ps1`, old changelog release links) now point at `THectic-NL/WinDeploy`. - `.github/workflows/validate.yml`: the syntax-check and helper-function-test jobs merged into one job (same runner, one less billed minute), path-scoped to `Scripts/**`, otherwise unchanged. - `CONTRIBUTING.md`, `.github/pull_request_template.md`: extended with the site's bilingual-content and Hugo-build checks, alongside the existing Windows-testing requirement, which stays required for anything under `Scripts/` or `Docs/`. +- `Apply-Tweaks.ps1` / `Deploy.ps1`: the WinUtil `Standard` preset now runs by default (`-Tweaks` defaults to `Yes`, including under `-NonInteractive`) instead of asking Y/N. Pass `-Tweaks No` to skip it, or `-Tweaks Ask` to get the old prompt back. +- README's "Option 3" `windeploy.stensel.nl` one-liner removed. It pointed at the old repository and isn't part of this one; Option 2 (direct execution against the latest `THectic-NL/WinDeploy` release) is the only one-liner now. + +### Fixed +- `Start.ps1`: installing PowerShell 7 via WinGet was checked only once, 5 seconds after the install command returned. On a slower box that check could run before the install had actually finished writing `pwsh.exe`, so the script treated a still-succeeding WinGet install as failed and ran the MSI installer as well - installing PowerShell 7 twice (two separate Start Menu entries). Now polls for up to 20 seconds and logs WinGet's own exit code before falling back. +- `Set-HostName.ps1`: exited with code 1 when no valid BIOS serial number was found (e.g. on VMs, or hardware without a real factory serial) even though this is an expected, already-handled condition with the same "set it manually" guidance the other non-fatal branches in this script give. `Deploy.ps1` counted that exit code as a failed step, so a run with nothing else wrong still ended with "Some deployment steps failed." Now exits 0, matching every other branch in the script. ### Removed - `.github/workflows/codeql.yml`, `dependency-review.yml`, `security.yml`, `stale.yml`, in favour of the leaner CI set above (PSScriptAnalyzer + Trivy + actionlint, no DevSkim/Semgrep/CodeQL-for-Actions/dependency-review/stale-bot). This mirrors what already happened to BypassNRO; flagging it here since it is a real reduction in automated security-scanning coverage, not just a rename. -### Notes -- `windeploy.stensel.nl` (the "Option 3" one-liner in the README) is an external redirect that pointed at the old repository. It is not part of this repository and needs to be repointed or retired separately. - --- ## [0.8.0] - 2026-08-29 diff --git a/README.md b/README.md index b73ca37..9a789fd 100644 --- a/README.md +++ b/README.md @@ -50,16 +50,6 @@ Zero-touch Windows deployment with automatic driver updates, application install iex (irm "https://raw.githubusercontent.com/THectic-NL/WinDeploy/$((irm https://api.github.com/repos/THectic-NL/WinDeploy/releases/latest).tag_name)/Scripts/Start.ps1") ``` -### Option 3: One-liner - -> [!NOTE] -> `windeploy.stensel.nl` pointed at Option 2 before this project moved to THectic-NL. That redirect needs to be repointed (or retired) separately — it isn't part of this repository. - -```powershell -# Run as Administrator in PowerShell 7 -iex (irm windeploy.stensel.nl) -``` - --- ## How It Works @@ -84,11 +74,8 @@ graph TD K3 --> L L[Install Applications] L --> M[Remove Bloatware] - M --> M2{Run WinUtil tweaks?} - M2 -->|Y| M3[Apply WinUtil preset] - M2 -->|N / timeout| N - M3 --> N - N[Apply Theme] + M --> M3[Apply WinUtil Standard preset] + M3 --> N[Apply Theme] N --> O[Set Hostname] O --> P[Install Windows Updates] P --> Q[Complete] @@ -98,14 +85,14 @@ graph TD ### Interactive steps -BitLocker (in `Harden-Windows.ps1`) and the WinUtil tweaks (`Apply-Tweaks.ps1`) each ask Y/N before running. Both time out after 90 seconds and default to **No**, so an unattended run never stalls. Everything else is applied automatically. +BitLocker (in `Harden-Windows.ps1`) asks Y/N before running; it times out after 90 seconds and defaults to **No**, so an unattended run never stalls. The WinUtil tweaks (`Apply-Tweaks.ps1`) run automatically with the `Standard` preset - pass `-Tweaks No` to skip them, or `-Tweaks Ask` to be prompted instead. Everything else is applied automatically. ```powershell -.\Deploy.ps1 -NonInteractive # no prompts, both skipped -.\Deploy.ps1 -BitLocker Yes -Tweaks Yes # no prompts, both applied +.\Deploy.ps1 -NonInteractive # no prompts; BitLocker skipped, WinUtil Standard preset still runs +.\Deploy.ps1 -BitLocker Yes -Tweaks No # no prompts; BitLocker applied, WinUtil skipped ``` -The `autounattend.xml` USB deployment passes `-NonInteractive` automatically. +The `autounattend.xml` USB deployment passes `-NonInteractive` automatically, so it also gets the WinUtil `Standard` preset unattended. --- @@ -171,14 +158,16 @@ Opt-in, asks Y/N. On yes: `C:` is encrypted with XTS-AES-256 (used space only, T --- -## Optional tweaks (WinUtil) +## WinUtil tweaks -`Apply-Tweaks.ps1` runs a [WinUtil](https://github.com/ChrisTitusTech/winutil) preset after a Y/N prompt, in its own process. Standard creates a restore point, then disables activity history, location, telemetry, consumer features, Delivery Optimization and Explorer folder-type auto-discovery, sets non-essential services to manual, and cleans temp files. +`Apply-Tweaks.ps1` runs a [WinUtil](https://github.com/ChrisTitusTech/winutil) preset in its own process, by default with no prompt. Standard creates a restore point, then disables activity history, location, telemetry, consumer features, Delivery Optimization and Explorer folder-type auto-discovery, sets non-essential services to manual, and cleans temp files. ```powershell -.\Apply-Tweaks.ps1 -Tweaks Yes # Standard preset -.\Apply-Tweaks.ps1 -Tweaks Yes -Preset Minimal -.\Apply-Tweaks.ps1 -Tweaks Yes -Preset Advanced # also removes OneDrive, widgets, Windows AI +.\Apply-Tweaks.ps1 # Standard preset, no prompt (default) +.\Apply-Tweaks.ps1 -Tweaks No # skip it +.\Apply-Tweaks.ps1 -Tweaks Ask # ask Y/N instead +.\Apply-Tweaks.ps1 -Preset Minimal +.\Apply-Tweaks.ps1 -Preset Advanced # also removes OneDrive, widgets, Windows AI ``` --- diff --git a/Scripts/Deploy.ps1 b/Scripts/Deploy.ps1 index 91a3a3c..a4252ba 100644 --- a/Scripts/Deploy.ps1 +++ b/Scripts/Deploy.ps1 @@ -8,8 +8,12 @@ param( [ValidateSet('Ask', 'Yes', 'No')] [string]$BitLocker = 'Ask', + # Defaults to Yes: the WinUtil Standard preset's QoL tweaks (disables + # telemetry, consumer features, Delivery Optimization, etc.) are wanted on + # every deployment, not opt-in. Pass -Tweaks No to skip it, or -Tweaks Ask + # to be prompted instead. [ValidateSet('Ask', 'Yes', 'No')] - [string]$Tweaks = 'Ask' + [string]$Tweaks = 'Yes' ) Set-StrictMode -Version Latest @@ -17,7 +21,7 @@ $ErrorActionPreference = 'Continue' if ($NonInteractive) { if ($BitLocker -eq 'Ask') { $BitLocker = 'No' } - if ($Tweaks -eq 'Ask') { $Tweaks = 'No' } + if ($Tweaks -eq 'Ask') { $Tweaks = 'Yes' } } # Waits for Enter, but never longer than $TimeoutSeconds, so an unattended diff --git a/Scripts/Deployment/Apply-Tweaks.ps1 b/Scripts/Deployment/Apply-Tweaks.ps1 index f0aad77..49b5cef 100644 --- a/Scripts/Deployment/Apply-Tweaks.ps1 +++ b/Scripts/Deployment/Apply-Tweaks.ps1 @@ -3,8 +3,9 @@ # Applies a WinUtil (ChrisTitusTech) tweak preset. # Standalone script - can be deployed via any management tool. # -# This step downloads and runs a THIRD-PARTY script from christitus.com, so it -# is opt-in: the operator has to confirm with Y before anything runs. +# This step downloads and runs a THIRD-PARTY script from christitus.com. It +# runs by default (-Tweaks Yes) since stock Windows' defaults are exactly what +# it's meant to fix; pass -Tweaks No to skip it or -Tweaks Ask to be prompted. # ============================================================================ #requires -Version 5.1 @@ -12,11 +13,11 @@ [CmdletBinding()] param( - # Ask = prompt the operator (default) - # Yes = run without prompting + # Ask = prompt the operator + # Yes = run without prompting (default) # No = skip this step [ValidateSet('Ask', 'Yes', 'No')] - [string]$Tweaks = 'Ask', + [string]$Tweaks = 'Yes', # WinUtil preset to apply. See Get-PresetSummary below for what each does. [ValidateSet('Standard', 'Minimal', 'Advanced')] diff --git a/Scripts/Deployment/Set-HostName.ps1 b/Scripts/Deployment/Set-HostName.ps1 index e0e2275..1270c94 100644 --- a/Scripts/Deployment/Set-HostName.ps1 +++ b/Scripts/Deployment/Set-HostName.ps1 @@ -28,7 +28,7 @@ try { if ([string]::IsNullOrWhiteSpace($Serial) -or $Serial.Length -lt 5) { Write-DeployLog "No valid serial number found. Unable to set hostname automatically. Please set the hostname manually." -IsError:$true Write-Output "No serial number found. Please set the computer name manually." - exit 1 + exit 0 } $LastFive = $Serial.Substring($Serial.Length - 5).ToUpper() $Hostname = "PC-$LastFive" diff --git a/Scripts/Start.ps1 b/Scripts/Start.ps1 index 3eef48e..467c54e 100644 --- a/Scripts/Start.ps1 +++ b/Scripts/Start.ps1 @@ -108,13 +108,26 @@ function Install-Pwsh7 { Write-Host "Attempting installation via WinGet..." -ForegroundColor Cyan try { & winget install --id Microsoft.PowerShell --silent --accept-package-agreements --accept-source-agreements 2>&1 - Start-Sleep -Seconds 5 - Update-EnvironmentPath - $path = Get-Pwsh7Path - if ($path) { - Write-Host "PowerShell 7 installed successfully via WinGet" -ForegroundColor Green - return $true + $wingetExitCode = $LASTEXITCODE + if ($wingetExitCode -and $wingetExitCode -ne 0) { + Write-Warning "WinGet install returned exit code $wingetExitCode" } + + # winget itself already waited for the installer to finish, but the + # file/registry write can lag a moment behind that. Poll instead of a + # single fixed sleep so a slow-to-appear pwsh.exe isn't mistaken for a + # failed install - that false negative used to trigger the MSI method + # too, installing PowerShell 7 twice. + for ($i = 0; $i -lt 10; $i++) { + Start-Sleep -Seconds 2 + Update-EnvironmentPath + $path = Get-Pwsh7Path + if ($path) { + Write-Host "PowerShell 7 installed successfully via WinGet" -ForegroundColor Green + return $true + } + } + Write-Warning "WinGet did not report an error, but pwsh.exe was not found after installing" } catch { Write-Warning "WinGet installation failed: $_" } diff --git a/src/content/_index.md b/src/content/_index.md index 1b34646..624c75c 100644 --- a/src/content/_index.md +++ b/src/content/_index.md @@ -53,11 +53,11 @@ For USB / offline installs via `autounattend.xml`, and the full configuration re | Hardening | SMB signing, LSA protection, HVCI, Defender ASR rules, screen lock — see below | | Applications | WinGet + Microsoft 365 via CDN/ODT | | Bloatware removal | Removes consumer apps, blocks their reinstall via policy | -| Tweaks *(opt-in)* | A [WinUtil](https://github.com/ChrisTitusTech/winutil) preset, behind a Y/N prompt | +| Tweaks | A [WinUtil](https://github.com/ChrisTitusTech/winutil) `Standard` preset, applied automatically | | Theme, hostname | Dark mode, `PC-` naming | | Windows Update | Installs everything available | -Two steps ask before they run: **BitLocker** and the **WinUtil tweaks**. Both time out after 90 seconds and default to no, so an unattended run never stalls. Pass `-NonInteractive` to skip both outright. +**BitLocker** asks before it runs, times out after 90 seconds and defaults to no, so an unattended run never stalls. Pass `-NonInteractive` to skip it outright. The **WinUtil tweaks** apply automatically with no prompt; pass `-Tweaks No` to skip them. ## Security hardening diff --git a/src/content/_index.nl.md b/src/content/_index.nl.md index 34cccaa..bceea9f 100644 --- a/src/content/_index.nl.md +++ b/src/content/_index.nl.md @@ -53,11 +53,11 @@ Voor USB/offline installaties via `autounattend.xml`, en de volledige configurat | Hardening | SMB signing, LSA-protection, HVCI, Defender ASR-regels, schermvergrendeling — zie hieronder | | Applicaties | WinGet + Microsoft 365 via CDN/ODT | | Bloatware verwijderen | Verwijdert consumer-apps, blokkeert herinstallatie via policy | -| Tweaks *(optioneel)* | Een [WinUtil](https://github.com/ChrisTitusTech/winutil)-preset, achter een Y/N-prompt | +| Tweaks | Een [WinUtil](https://github.com/ChrisTitusTech/winutil) `Standard`-preset, automatisch toegepast | | Thema, hostnaam | Dark mode, `PC-`-naamgeving | | Windows Update | Installeert alles wat beschikbaar is | -Twee stappen vragen eerst om bevestiging: **BitLocker** en de **WinUtil-tweaks**. Beide lopen na 90 seconden af en kiezen dan standaard nee, zodat een onbeheerde run nooit vastloopt. Geef `-NonInteractive` mee om beide direct over te slaan. +**BitLocker** vraagt eerst om bevestiging, loopt na 90 seconden af en kiest dan standaard nee, zodat een onbeheerde run nooit vastloopt. Geef `-NonInteractive` mee om dit over te slaan. De **WinUtil-tweaks** worden automatisch toegepast, zonder prompt; geef `-Tweaks No` mee om ze over te slaan. ## Security hardening