Add per-feature toggles to PS scripts and Go TUI

- 02-software.ps1: wrap wingetInstalls, pdfDefault, ateraAgent in Get-Feature guards
- 03-system-registry.ps1: add Get-Feature, restructure into 5 gated blocks
  (systemTweaks, edgePolicies, oneDriveUninstall, powercfg, proxyDisable)
- 04-default-profile.ps1: add Get-Feature, wrap taskbarTweaks, startMenuTweaks,
  explorerTweaks; add missing explorerTweaks code (ShowRecent, ShowFrequent, FullPath)
- 11-dell-update.ps1: add Get-Feature, split update run by drivers/bios feature flags
- runner.go: add Feature/StepFeatures/SelectableItem/AllSelectableItems for TUI
- config.go: add Features type and defaults for all 4 gated steps
- tui.go: use AllSelectableItems for MultiSelect, build Features map in startRun,
  remove unused stepFeaturesMap variable
- xetup.exe: Windows amd64 build

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
X9 Dev 2026-04-16 11:11:51 +02:00
parent a10a3a8aa2
commit 5b53b2a0d6
8 changed files with 590 additions and 378 deletions

View file

@ -13,6 +13,7 @@ type Config struct {
Activation Activation `json:"activation"` Activation Activation `json:"activation"`
Software Software `json:"software"` Software Software `json:"software"`
Steps map[string]bool `json:"steps"` Steps map[string]bool `json:"steps"`
Features Features `json:"features"`
} }
type Deployment struct { type Deployment struct {
@ -40,6 +41,11 @@ type Software struct {
Install []SoftwareItem `json:"install"` Install []SoftwareItem `json:"install"`
} }
// Features holds per-step, per-feature toggle flags.
// Keys: stepID -> featureID -> enabled.
// A missing key defaults to true (feature enabled).
type Features map[string]map[string]bool
// DefaultConfig returns a config with sensible defaults. // DefaultConfig returns a config with sensible defaults.
func DefaultConfig() Config { func DefaultConfig() Config {
return Config{ return Config{
@ -74,6 +80,29 @@ func DefaultConfig() Config {
"network": true, "network": true,
"pcIdentity": true, "pcIdentity": true,
}, },
Features: Features{
"software": {
"wingetInstalls": true,
"pdfDefault": true,
"ateraAgent": true,
},
"systemRegistry": {
"systemTweaks": true,
"edgePolicies": true,
"oneDriveUninstall": true,
"powercfg": true,
"proxyDisable": true,
},
"defaultProfile": {
"taskbarTweaks": true,
"startMenuTweaks": true,
"explorerTweaks": true,
},
"dellUpdate": {
"drivers": true,
"bios": true,
},
},
} }
} }

View file

@ -40,6 +40,81 @@ func AllSteps() []Step {
} }
} }
// Feature is a single toggleable sub-item within a deployment step.
type Feature struct {
ID string
Label string
}
// StepFeatures returns per-step feature lists. Steps absent from this map
// have no sub-features and are controlled at the step level only.
func StepFeatures() map[string][]Feature {
return map[string][]Feature{
"software": {
{ID: "wingetInstalls", Label: "Instalace SW ze seznamu (winget)"},
{ID: "pdfDefault", Label: "Adobe Reader jako vychozi PDF"},
{ID: "ateraAgent", Label: "Atera RMM agent"},
},
"systemRegistry": {
{ID: "systemTweaks", Label: "Windows tweaky (Widgets, GameDVR, Recall...)"},
{ID: "edgePolicies", Label: "Edge policies (tlacitka, vyhledavac, telemetrie)"},
{ID: "oneDriveUninstall", Label: "OneDrive uninstall (consumer pre-install)"},
{ID: "powercfg", Label: "Nastaveni napajeni (timeout AC/DC)"},
{ID: "proxyDisable", Label: "Zakaz WPAD proxy auto-detect"},
},
"defaultProfile": {
{ID: "taskbarTweaks", Label: "Taskbar zarovnani, tlacitka, layout XML"},
{ID: "startMenuTweaks", Label: "Start menu cisteni pinu, Bing, Copilot"},
{ID: "explorerTweaks", Label: "Explorer pripony, LaunchTo, ShowRecent"},
},
"dellUpdate": {
{ID: "drivers", Label: "Dell drivery + firmware"},
{ID: "bios", Label: "Dell BIOS update"},
},
}
}
// SelectableItem is a single toggleable row in the TUI checklist.
// It represents either a whole step (FeatureID == "") or a specific feature.
type SelectableItem struct {
Key string // "stepID" or "stepID.featureID"
StepID string
FeatureID string // empty for step-level items
Label string
Num string
}
// AllSelectableItems returns the flat ordered list of all TUI toggle rows.
// Steps with features are expanded to individual feature rows.
// Steps without features appear as a single step-level row.
func AllSelectableItems() []SelectableItem {
steps := AllSteps()
features := StepFeatures()
var items []SelectableItem
for _, s := range steps {
feats, hasFeatures := features[s.ID]
if !hasFeatures {
items = append(items, SelectableItem{
Key: s.ID,
StepID: s.ID,
Label: s.Num + " " + s.Name,
Num: s.Num,
})
} else {
for _, f := range feats {
items = append(items, SelectableItem{
Key: s.ID + "." + f.ID,
StepID: s.ID,
FeatureID: f.ID,
Label: s.Num + " " + f.Label,
Num: s.Num,
})
}
}
}
return items
}
// RunConfig holds runtime parameters passed to each script. // RunConfig holds runtime parameters passed to each script.
type RunConfig struct { type RunConfig struct {
ScriptsDir string ScriptsDir string

View file

@ -54,7 +54,7 @@ type Model struct {
pcDesc string pcDesc string
productKey string productKey string
profileType string profileType string
selectedStepIDs []string selectedItemKeys []string
// runner state // runner state
r *runner.Runner r *runner.Runner
@ -90,10 +90,10 @@ func NewModel(cfg config.Config, runCfg runner.RunConfig) Model {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
func buildForm(m *Model) *huh.Form { func buildForm(m *Model) *huh.Form {
allSteps := runner.AllSteps() allItems := runner.AllSelectableItems()
opts := make([]huh.Option[string], len(allSteps)) opts := make([]huh.Option[string], len(allItems))
for i, s := range allSteps { for i, item := range allItems {
opts[i] = huh.NewOption(s.Num+" - "+s.Name, s.ID).Selected(true) opts[i] = huh.NewOption(item.Label, item.Key).Selected(true)
} }
return huh.NewForm( return huh.NewForm(
@ -121,9 +121,9 @@ func buildForm(m *Model) *huh.Form {
), ),
huh.NewGroup( huh.NewGroup(
huh.NewMultiSelect[string](). huh.NewMultiSelect[string]().
Title("Kroky k provedeni (mezernikem odzaskrtnout)"). Title("Kroky a nastaveni (mezernikem odzaskrtnout)").
Options(opts...). Options(opts...).
Value(&m.selectedStepIDs), Value(&m.selectedItemKeys),
), ),
) )
} }
@ -217,15 +217,42 @@ func (m Model) startRun() (Model, tea.Cmd) {
m.cfg.Deployment.ProfileType = m.profileType m.cfg.Deployment.ProfileType = m.profileType
m.runCfg.ProfileType = m.profileType m.runCfg.ProfileType = m.profileType
// build step list with Enabled flag from the user's checklist // Index selected keys for fast lookup
selected := make(map[string]bool, len(m.selectedStepIDs)) selectedSet := make(map[string]bool, len(m.selectedItemKeys))
for _, id := range m.selectedStepIDs { for _, key := range m.selectedItemKeys {
selected[id] = true selectedSet[key] = true
} }
// Build Features map: feature enabled = its composite key was selected
features := make(config.Features)
for _, item := range runner.AllSelectableItems() {
if item.FeatureID == "" {
continue
}
if features[item.StepID] == nil {
features[item.StepID] = make(map[string]bool)
}
features[item.StepID][item.FeatureID] = selectedSet[item.Key]
}
m.cfg.Features = features
// Determine which steps are enabled:
// - step with features: enabled if at least one feature selected
// - step without features: enabled if step key selected
stepEnabled := make(map[string]bool)
for _, item := range runner.AllSelectableItems() {
if item.FeatureID == "" {
stepEnabled[item.StepID] = selectedSet[item.Key]
} else if selectedSet[item.Key] {
// any feature selected enables the step
stepEnabled[item.StepID] = true
}
}
allSteps := runner.AllSteps() allSteps := runner.AllSteps()
steps := make([]runner.Step, len(allSteps)) steps := make([]runner.Step, len(allSteps))
for i, s := range allSteps { for i, s := range allSteps {
s.Enabled = selected[s.ID] s.Enabled = stepEnabled[s.ID]
steps[i] = s steps[i] = s
} }

View file

@ -31,6 +31,18 @@ function Write-Log {
Add-Content -Path $LogFile -Value $line -Encoding UTF8 Add-Content -Path $LogFile -Value $line -Encoding UTF8
} }
function Get-Feature {
param([object]$Cfg, [string]$StepID, [string]$FeatureID, [bool]$Default = $true)
try {
if ($null -eq $Cfg) { return $Default }
$stepFeatures = $Cfg.features.$StepID
if ($null -eq $stepFeatures) { return $Default }
$val = $stepFeatures.$FeatureID
if ($null -eq $val) { return $Default }
return [bool]$val
} catch { return $Default }
}
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
# Check winget availability # Check winget availability
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
@ -62,9 +74,10 @@ Write-Log "winget found: $($winget.Source -or $winget)" -Level OK
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
# Install packages from config # Install packages from config
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
if (-not $Config -or -not $Config.software -or -not $Config.software.install) { if (Get-Feature $Config "software" "wingetInstalls") {
if (-not $Config -or -not $Config.software -or -not $Config.software.install) {
Write-Log "No software list in config - skipping installs" -Level WARN Write-Log "No software list in config - skipping installs" -Level WARN
} else { } else {
foreach ($pkg in $Config.software.install) { foreach ($pkg in $Config.software.install) {
Write-Log "Installing $($pkg.name) ($($pkg.wingetId))" -Level INFO Write-Log "Installing $($pkg.name) ($($pkg.wingetId))" -Level INFO
$result = & winget install --id $pkg.wingetId ` $result = & winget install --id $pkg.wingetId `
@ -85,17 +98,15 @@ if (-not $Config -or -not $Config.software -or -not $Config.software.install) {
Write-Log " Output: $($result -join ' ')" -Level ERROR Write-Log " Output: $($result -join ' ')" -Level ERROR
} }
} }
}
} else {
Write-Log "wingetInstalls feature disabled - skipping" -Level INFO
} }
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
# Set Adobe Reader as default PDF app # Set Adobe Reader as default PDF app
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
$forcePdf = $true if (Get-Feature $Config "software" "pdfDefault") {
if ($Config -and $Config.pdfDefault) {
$forcePdf = [bool]$Config.pdfDefault.forceAdobeReader
}
if ($forcePdf) {
Write-Log "Setting Adobe Reader as default PDF app" -Level INFO Write-Log "Setting Adobe Reader as default PDF app" -Level INFO
# Stop UCPD driver before writing file association. # Stop UCPD driver before writing file association.
@ -160,17 +171,20 @@ if ($forcePdf) {
Write-Log " Could not restart UCPD: $_" -Level WARN Write-Log " Could not restart UCPD: $_" -Level WARN
} }
} }
} else {
Write-Log "pdfDefault feature disabled - skipping" -Level INFO
} }
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
# Install Atera RMM Agent # Install Atera RMM Agent
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
Write-Log "Installing Atera RMM Agent" -Level INFO if (Get-Feature $Config "software" "ateraAgent") {
Write-Log "Installing Atera RMM Agent" -Level INFO
$ateraUrl = "https://x9.servicedesk.atera.com/api/utils/agent-install/windows/?cid=31&aeid=50b72e7113e54a63ac76b96c54c7e337" $ateraUrl = "https://x9.servicedesk.atera.com/api/utils/agent-install/windows/?cid=31&aeid=50b72e7113e54a63ac76b96c54c7e337"
$ateraMsi = "$env:TEMP\AteraAgent.msi" $ateraMsi = "$env:TEMP\AteraAgent.msi"
try { try {
Write-Log " Downloading Atera agent..." -Level INFO Write-Log " Downloading Atera agent..." -Level INFO
Invoke-WebRequest -Uri $ateraUrl -OutFile $ateraMsi -UseBasicParsing -ErrorAction Stop Invoke-WebRequest -Uri $ateraUrl -OutFile $ateraMsi -UseBasicParsing -ErrorAction Stop
Write-Log " Download complete" -Level OK Write-Log " Download complete" -Level OK
@ -185,12 +199,15 @@ try {
Write-Log " Atera agent install may have failed - binary not found at expected path" -Level WARN Write-Log " Atera agent install may have failed - binary not found at expected path" -Level WARN
Write-Log " msiexec output: $($msiResult -join ' ')" -Level WARN Write-Log " msiexec output: $($msiResult -join ' ')" -Level WARN
} }
} }
catch { catch {
Write-Log " Atera agent download/install failed: $_" -Level ERROR Write-Log " Atera agent download/install failed: $_" -Level ERROR
} }
finally { finally {
Remove-Item $ateraMsi -Force -ErrorAction SilentlyContinue Remove-Item $ateraMsi -Force -ErrorAction SilentlyContinue
}
} else {
Write-Log "ateraAgent feature disabled - skipping" -Level INFO
} }
Write-Log "Step 2 complete" -Level OK Write-Log "Step 2 complete" -Level OK

View file

@ -40,6 +40,18 @@ function Write-Log {
Add-Content -Path $LogFile -Value $line -Encoding UTF8 Add-Content -Path $LogFile -Value $line -Encoding UTF8
} }
function Get-Feature {
param([object]$Cfg, [string]$StepID, [string]$FeatureID, [bool]$Default = $true)
try {
if ($null -eq $Cfg) { return $Default }
$stepFeatures = $Cfg.features.$StepID
if ($null -eq $stepFeatures) { return $Default }
$val = $stepFeatures.$FeatureID
if ($null -eq $val) { return $Default }
return [bool]$val
} catch { return $Default }
}
Add-Type -TypeDefinition @" Add-Type -TypeDefinition @"
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -206,91 +218,7 @@ function Remove-Reg {
Write-Log "3 - Applying HKLM system registry tweaks" -Level STEP Write-Log "3 - Applying HKLM system registry tweaks" -Level STEP
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
# Bypass Network Requirement on OOBE (BypassNRO) # Always-on: password and timezone (fundamental system settings)
# -----------------------------------------------------------------------
Set-Reg -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\OOBE" `
-Name "BypassNRO" -Value 1
# -----------------------------------------------------------------------
# Disable auto-install of Teams (Chat)
# -----------------------------------------------------------------------
Set-Reg -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Communications" `
-Name "ConfigureChatAutoInstall" -Value 0
# -----------------------------------------------------------------------
# Disable Cloud Optimized Content (ads in Start menu etc.)
# -----------------------------------------------------------------------
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CloudContent" `
-Name "DisableCloudOptimizedContent" -Value 1
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CloudContent" `
-Name "DisableWindowsConsumerFeatures" -Value 1
# -----------------------------------------------------------------------
# Disable Widgets (News and Interests)
# -----------------------------------------------------------------------
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Dsh" `
-Name "AllowNewsAndInterests" -Value 0
# -----------------------------------------------------------------------
# Microsoft Edge policies
# -----------------------------------------------------------------------
$edgePath = "HKLM:\SOFTWARE\Policies\Microsoft\Edge"
# UI / first run
Set-Reg -Path $edgePath -Name "HideFirstRunExperience" -Value 1
Set-Reg -Path $edgePath -Name "DefaultBrowserSettingEnabled" -Value 0
# New tab page / recommendations
Set-Reg -Path $edgePath -Name "NewTabPageContentEnabled" -Value 0
Set-Reg -Path $edgePath -Name "ShowRecommendationsEnabled" -Value 0
Set-Reg -Path $edgePath -Name "SpotlightExperiencesAndRecommendationsEnabled" -Value 0
Set-Reg -Path $edgePath -Name "PersonalizationReportingEnabled" -Value 0
# Shopping / rewards / sidebar
Set-Reg -Path $edgePath -Name "EdgeShoppingAssistantEnabled" -Value 0
Set-Reg -Path $edgePath -Name "ShowMicrosoftRewards" -Value 0
Set-Reg -Path $edgePath -Name "HubsSidebarEnabled" -Value 0
# Search suggestions
Set-Reg -Path $edgePath -Name "SearchSuggestEnabled" -Value 0
Set-Reg -Path $edgePath -Name "ImportOnEachLaunch" -Value 0
# Telemetry / feedback
Set-Reg -Path $edgePath -Name "DiagnosticData" -Value 0
Set-Reg -Path $edgePath -Name "FeedbackSurveysEnabled" -Value 0
Set-Reg -Path $edgePath -Name "EdgeCollectionsEnabled" -Value 0
# Toolbar buttons - show
Set-Reg -Path $edgePath -Name "FavoritesBarEnabled" -Value 1 # Favorites bar always visible
Set-Reg -Path $edgePath -Name "DownloadsButtonEnabled" -Value 1
Set-Reg -Path $edgePath -Name "HistoryButtonEnabled" -Value 1
Set-Reg -Path $edgePath -Name "PerformanceButtonEnabled" -Value 1 # Sleeping Tabs / Performance
# Toolbar buttons - hide
Set-Reg -Path $edgePath -Name "HomeButtonEnabled" -Value 0
Set-Reg -Path $edgePath -Name "SplitScreenEnabled" -Value 0
Set-Reg -Path $edgePath -Name "EdgeEDropEnabled" -Value 0 # Drop
Set-Reg -Path $edgePath -Name "WebCaptureEnabled" -Value 0 # Screenshot
Set-Reg -Path $edgePath -Name "ShareAllowed" -Value 0 # Share
# Default search engine: Google
# SearchProviderEnabled must be 1, SearchProviderName + URL set the provider
Set-Reg -Path $edgePath -Name "DefaultSearchProviderEnabled" -Value 1 -Type "DWord"
Set-Reg -Path $edgePath -Name "DefaultSearchProviderName" -Value "Google" -Type "String"
Set-Reg -Path $edgePath -Name "DefaultSearchProviderSearchURL" `
-Value "https://www.google.com/search?q={searchTerms}" -Type "String"
# Remove other search engines (empty list = no other providers besides default)
Set-Reg -Path $edgePath -Name "ManagedSearchEngines" `
-Value '[{"is_default":true,"name":"Google","search_url":"https://www.google.com/search?q={searchTerms}","keyword":"google.com"}]' `
-Type "String"
# Disable desktop shortcut on install/update
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\EdgeUpdate" `
-Name "CreateDesktopShortcutDefault" -Value 0
# -----------------------------------------------------------------------
# Password - no expiration
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
Write-Log " Setting password max age to UNLIMITED" -Level INFO Write-Log " Setting password max age to UNLIMITED" -Level INFO
$pwResult = & net accounts /maxpwage:UNLIMITED 2>&1 $pwResult = & net accounts /maxpwage:UNLIMITED 2>&1
@ -300,9 +228,6 @@ if ($LASTEXITCODE -eq 0) {
Write-Log " Failed to set password max age: $pwResult" -Level ERROR Write-Log " Failed to set password max age: $pwResult" -Level ERROR
} }
# -----------------------------------------------------------------------
# Time zone
# -----------------------------------------------------------------------
$tz = "Central Europe Standard Time" $tz = "Central Europe Standard Time"
if ($Config -and $Config.deployment -and $Config.deployment.timezone) { if ($Config -and $Config.deployment -and $Config.deployment.timezone) {
$tz = $Config.deployment.timezone $tz = $Config.deployment.timezone
@ -317,47 +242,36 @@ catch {
} }
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
# OneDrive - uninstall from clean Windows (no policy block) # System tweaks (Windows features, cloud noise, Xbox, Recall, Search UI)
# NOTE: No policy key is set intentionally - M365 installation can reinstall
# and run OneDrive normally. Policy DisableFileSyncNGSC would prevent that.
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
Write-Log " Uninstalling OneDrive" -Level INFO if (Get-Feature $Config "systemRegistry" "systemTweaks") {
Write-Log " Applying system tweaks" -Level INFO
# Remove OneDriveSetup.exe if present # Bypass Network Requirement on OOBE (BypassNRO)
$oneDrivePaths = @( Set-Reg -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\OOBE" `
"$env:SystemRoot\System32\OneDriveSetup.exe" -Name "BypassNRO" -Value 1
"$env:SystemRoot\SysWOW64\OneDriveSetup.exe"
)
foreach ($odPath in $oneDrivePaths) {
if (Test-Path $odPath) {
try {
# Uninstall first
& $odPath /uninstall 2>&1 | Out-Null
Write-Log " OneDrive uninstalled via $odPath" -Level OK
}
catch {
Write-Log " OneDrive uninstall failed: $_" -Level WARN
}
}
}
# Remove OneDrive Start Menu shortcut # Disable auto-install of Teams (Chat)
$odLnk = "$env:ProgramData\Microsoft\Windows\Start Menu\Programs\OneDrive.lnk" Set-Reg -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Communications" `
if (Test-Path $odLnk) { -Name "ConfigureChatAutoInstall" -Value 0
Remove-Item $odLnk -Force -ErrorAction SilentlyContinue
Write-Log " Removed OneDrive Start Menu shortcut" -Level OK
}
# ----------------------------------------------------------------------- # Disable Cloud Optimized Content (ads in Start menu etc.)
# Outlook (new) - disable auto-install via UScheduler Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CloudContent" `
# ----------------------------------------------------------------------- -Name "DisableCloudOptimizedContent" -Value 1
Write-Log " Disabling Outlook (new) auto-install" -Level INFO Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CloudContent" `
-Name "DisableWindowsConsumerFeatures" -Value 1
$uschedulerPaths = @( # Disable Widgets (News and Interests)
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Dsh" `
-Name "AllowNewsAndInterests" -Value 0
# Disable Outlook (new) auto-install via UScheduler
Write-Log " Disabling Outlook (new) auto-install" -Level INFO
$uschedulerPaths = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler_Oobe\OutlookUpdate" "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler_Oobe\OutlookUpdate"
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler\OutlookUpdate" "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler\OutlookUpdate"
) )
foreach ($uPath in $uschedulerPaths) { foreach ($uPath in $uschedulerPaths) {
if (Test-Path $uPath) { if (Test-Path $uPath) {
try { try {
Remove-Item -Path $uPath -Recurse -Force Remove-Item -Path $uPath -Recurse -Force
@ -367,58 +281,158 @@ foreach ($uPath in $uschedulerPaths) {
Write-Log " Failed to remove UScheduler key: $_" -Level WARN Write-Log " Failed to remove UScheduler key: $_" -Level WARN
} }
} }
}
# Disable GameDVR
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\GameDVR" `
-Name "AllowGameDVR" -Value 0
# Disable Recall (Windows AI feature)
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" `
-Name "DisableAIDataAnalysis" -Value 1
# Search on taskbar - hide via HKLM policy (Win11 22H2+ enforcement)
# User-level SearchboxTaskbarMode alone is insufficient on newer Win11 builds;
# this policy key ensures the setting survives Windows Updates.
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search" `
-Name "SearchOnTaskbarMode" -Value 0
# Start menu - hide Recommended section (Win11)
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer" `
-Name "HideRecommendedSection" -Value 1
} else {
Write-Log "systemTweaks feature disabled - skipping" -Level INFO
} }
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
# Disable GameDVR # Microsoft Edge policies
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\GameDVR" ` if (Get-Feature $Config "systemRegistry" "edgePolicies") {
-Name "AllowGameDVR" -Value 0 Write-Log " Applying Edge policies" -Level INFO
$edgePath = "HKLM:\SOFTWARE\Policies\Microsoft\Edge"
# UI / first run
Set-Reg -Path $edgePath -Name "HideFirstRunExperience" -Value 1
Set-Reg -Path $edgePath -Name "DefaultBrowserSettingEnabled" -Value 0
# New tab page / recommendations
Set-Reg -Path $edgePath -Name "NewTabPageContentEnabled" -Value 0
Set-Reg -Path $edgePath -Name "ShowRecommendationsEnabled" -Value 0
Set-Reg -Path $edgePath -Name "SpotlightExperiencesAndRecommendationsEnabled" -Value 0
Set-Reg -Path $edgePath -Name "PersonalizationReportingEnabled" -Value 0
# Shopping / rewards / sidebar
Set-Reg -Path $edgePath -Name "EdgeShoppingAssistantEnabled" -Value 0
Set-Reg -Path $edgePath -Name "ShowMicrosoftRewards" -Value 0
Set-Reg -Path $edgePath -Name "HubsSidebarEnabled" -Value 0
# Search suggestions
Set-Reg -Path $edgePath -Name "SearchSuggestEnabled" -Value 0
Set-Reg -Path $edgePath -Name "ImportOnEachLaunch" -Value 0
# Telemetry / feedback
Set-Reg -Path $edgePath -Name "DiagnosticData" -Value 0
Set-Reg -Path $edgePath -Name "FeedbackSurveysEnabled" -Value 0
Set-Reg -Path $edgePath -Name "EdgeCollectionsEnabled" -Value 0
# Toolbar buttons - show
Set-Reg -Path $edgePath -Name "FavoritesBarEnabled" -Value 1 # Favorites bar always visible
Set-Reg -Path $edgePath -Name "DownloadsButtonEnabled" -Value 1
Set-Reg -Path $edgePath -Name "HistoryButtonEnabled" -Value 1
Set-Reg -Path $edgePath -Name "PerformanceButtonEnabled" -Value 1 # Sleeping Tabs / Performance
# Toolbar buttons - hide
Set-Reg -Path $edgePath -Name "HomeButtonEnabled" -Value 0
Set-Reg -Path $edgePath -Name "SplitScreenEnabled" -Value 0
Set-Reg -Path $edgePath -Name "EdgeEDropEnabled" -Value 0 # Drop
Set-Reg -Path $edgePath -Name "WebCaptureEnabled" -Value 0 # Screenshot
Set-Reg -Path $edgePath -Name "ShareAllowed" -Value 0 # Share
# Default search engine: Google
# SearchProviderEnabled must be 1, SearchProviderName + URL set the provider
Set-Reg -Path $edgePath -Name "DefaultSearchProviderEnabled" -Value 1 -Type "DWord"
Set-Reg -Path $edgePath -Name "DefaultSearchProviderName" -Value "Google" -Type "String"
Set-Reg -Path $edgePath -Name "DefaultSearchProviderSearchURL" `
-Value "https://www.google.com/search?q={searchTerms}" -Type "String"
# Remove other search engines (empty list = no other providers besides default)
Set-Reg -Path $edgePath -Name "ManagedSearchEngines" `
-Value '[{"is_default":true,"name":"Google","search_url":"https://www.google.com/search?q={searchTerms}","keyword":"google.com"}]' `
-Type "String"
# Disable desktop shortcut on install/update
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\EdgeUpdate" `
-Name "CreateDesktopShortcutDefault" -Value 0
} else {
Write-Log "edgePolicies feature disabled - skipping" -Level INFO
}
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
# Disable Recall (Windows AI feature) # OneDrive - uninstall from clean Windows (no policy block)
# NOTE: No policy key is set intentionally - M365 installation can reinstall
# and run OneDrive normally. Policy DisableFileSyncNGSC would prevent that.
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" ` if (Get-Feature $Config "systemRegistry" "oneDriveUninstall") {
-Name "DisableAIDataAnalysis" -Value 1 Write-Log " Uninstalling OneDrive" -Level INFO
# ----------------------------------------------------------------------- $oneDrivePaths = @(
# Search on taskbar - hide via HKLM policy (Win11 22H2+ enforcement) "$env:SystemRoot\System32\OneDriveSetup.exe"
# User-level SearchboxTaskbarMode alone is insufficient on newer Win11 builds; "$env:SystemRoot\SysWOW64\OneDriveSetup.exe"
# this policy key ensures the setting survives Windows Updates. )
# ----------------------------------------------------------------------- foreach ($odPath in $oneDrivePaths) {
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search" ` if (Test-Path $odPath) {
-Name "SearchOnTaskbarMode" -Value 0 try {
& $odPath /uninstall 2>&1 | Out-Null
Write-Log " OneDrive uninstalled via $odPath" -Level OK
}
catch {
Write-Log " OneDrive uninstall failed: $_" -Level WARN
}
}
}
# ----------------------------------------------------------------------- # Remove OneDrive Start Menu shortcut
# Start menu - hide Recommended section (Win11) $odLnk = "$env:ProgramData\Microsoft\Windows\Start Menu\Programs\OneDrive.lnk"
# ----------------------------------------------------------------------- if (Test-Path $odLnk) {
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer" ` Remove-Item $odLnk -Force -ErrorAction SilentlyContinue
-Name "HideRecommendedSection" -Value 1 Write-Log " Removed OneDrive Start Menu shortcut" -Level OK
}
} else {
Write-Log "oneDriveUninstall feature disabled - skipping" -Level INFO
}
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
# Power configuration # Power configuration
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
Write-Log "Applying power configuration" -Level INFO if (Get-Feature $Config "systemRegistry" "powercfg") {
Write-Log " Applying power configuration" -Level INFO
$powercfg = @( $powercfgArgs = @(
@("/change", "standby-timeout-ac", "0"), # never sleep on AC @("/change", "standby-timeout-ac", "0"), # never sleep on AC
@("/change", "monitor-timeout-ac", "60"), # screen off after 60 min on AC @("/change", "monitor-timeout-ac", "60"), # screen off after 60 min on AC
@("/change", "standby-timeout-dc", "30"), # sleep after 30 min on battery @("/change", "standby-timeout-dc", "30"), # sleep after 30 min on battery
@("/change", "monitor-timeout-dc", "15") # screen off after 15 min on battery @("/change", "monitor-timeout-dc", "15") # screen off after 15 min on battery
) )
foreach ($args in $powercfg) { foreach ($a in $powercfgArgs) {
$result = & powercfg @args 2>&1 $result = & powercfg @a 2>&1
if ($LASTEXITCODE -eq 0) { if ($LASTEXITCODE -eq 0) {
Write-Log " powercfg $($args -join ' ')" -Level OK Write-Log " powercfg $($a -join ' ')" -Level OK
} else { } else {
Write-Log " powercfg $($args -join ' ') failed: $result" -Level WARN Write-Log " powercfg $($a -join ' ') failed: $result" -Level WARN
} }
}
} else {
Write-Log "powercfg feature disabled - skipping" -Level INFO
} }
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
# Proxy auto-detect disable (WPAD) # Proxy auto-detect disable (WPAD)
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings" ` if (Get-Feature $Config "systemRegistry" "proxyDisable") {
Write-Log " Disabling WPAD proxy auto-detect" -Level INFO
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings" `
-Name "AutoDetect" -Value 0 -Name "AutoDetect" -Value 0
} else {
Write-Log "proxyDisable feature disabled - skipping" -Level INFO
}
Write-Log "Step 3 complete" -Level OK Write-Log "Step 3 complete" -Level OK

View file

@ -39,6 +39,18 @@ function Write-Log {
Add-Content -Path $LogFile -Value $line -Encoding UTF8 Add-Content -Path $LogFile -Value $line -Encoding UTF8
} }
function Get-Feature {
param([object]$Cfg, [string]$StepID, [string]$FeatureID, [bool]$Default = $true)
try {
if ($null -eq $Cfg) { return $Default }
$stepFeatures = $Cfg.features.$StepID
if ($null -eq $stepFeatures) { return $Default }
$val = $stepFeatures.$FeatureID
if ($null -eq $val) { return $Default }
return [bool]$val
} catch { return $Default }
}
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
# Helper - apply a registry setting to both Default hive and current HKCU # Helper - apply a registry setting to both Default hive and current HKCU
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
@ -150,9 +162,10 @@ Write-Log "Default hive loaded" -Level OK
try { try {
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
# Taskbar settings (Win10 + Win11) # Taskbar tweaks (alignment, buttons, tray, layout XML)
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
Write-Log "Applying taskbar settings" -Level STEP if (Get-Feature $Config "defaultProfile" "taskbarTweaks") {
Write-Log "Applying taskbar tweaks" -Level STEP
$tbPath = "Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" $tbPath = "Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
@ -177,29 +190,18 @@ try {
# Hide Copilot button # Hide Copilot button
Set-ProfileReg -SubKey $tbPath -Name "ShowCopilotButton" -Value 0 Set-ProfileReg -SubKey $tbPath -Name "ShowCopilotButton" -Value 0
# Show file extensions in Explorer # Show all tray icons
Set-ProfileReg -SubKey $tbPath -Name "HideFileExt" -Value 0
# Open Explorer to This PC instead of Quick Access
Set-ProfileReg -SubKey $tbPath -Name "LaunchTo" -Value 1
# -----------------------------------------------------------------------
# System tray - show all icons
# -----------------------------------------------------------------------
# EnableAutoTray = 0 works on Win10; Win11 ignores it but set anyway # EnableAutoTray = 0 works on Win10; Win11 ignores it but set anyway
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer" ` Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer" `
-Name "EnableAutoTray" -Value 0 -Name "EnableAutoTray" -Value 0
# Win11 workaround: clear cached tray icon streams so all icons appear on next login # Win11 workaround: clear cached tray icon streams so all icons appear on next login
# Windows rebuilds the streams with all icons visible when no cache exists
$trayNotifyKey = "HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify" $trayNotifyKey = "HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify"
if (Test-Path $trayNotifyKey) { if (Test-Path $trayNotifyKey) {
Remove-ItemProperty -Path $trayNotifyKey -Name "IconStreams" -Force -ErrorAction SilentlyContinue Remove-ItemProperty -Path $trayNotifyKey -Name "IconStreams" -Force -ErrorAction SilentlyContinue
Remove-ItemProperty -Path $trayNotifyKey -Name "PastIconsStream" -Force -ErrorAction SilentlyContinue Remove-ItemProperty -Path $trayNotifyKey -Name "PastIconsStream" -Force -ErrorAction SilentlyContinue
Write-Log " Cleared TrayNotify icon streams (Win11 systray workaround)" -Level OK Write-Log " Cleared TrayNotify icon streams (Win11 systray workaround)" -Level OK
} }
# Also clear in Default hive so new users start with clean state
$defTrayKey = "Registry::HKU\DefaultProfile\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify" $defTrayKey = "Registry::HKU\DefaultProfile\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify"
if (Test-Path $defTrayKey) { if (Test-Path $defTrayKey) {
Remove-ItemProperty -Path $defTrayKey -Name "IconStreams" -Force -ErrorAction SilentlyContinue Remove-ItemProperty -Path $defTrayKey -Name "IconStreams" -Force -ErrorAction SilentlyContinue
@ -207,82 +209,24 @@ try {
Write-Log " Cleared TrayNotify icon streams in Default hive" -Level OK Write-Log " Cleared TrayNotify icon streams in Default hive" -Level OK
} }
# -----------------------------------------------------------------------
# Desktop icons - show This PC # Desktop icons - show This PC
# -----------------------------------------------------------------------
# CLSID {20D04FE0-3AEA-1069-A2D8-08002B30309D} = This PC / Computer # CLSID {20D04FE0-3AEA-1069-A2D8-08002B30309D} = This PC / Computer
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel" ` Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel" `
-Name "{20D04FE0-3AEA-1069-A2D8-08002B30309D}" -Value 0 -Name "{20D04FE0-3AEA-1069-A2D8-08002B30309D}" -Value 0
# -----------------------------------------------------------------------
# Start menu settings
# -----------------------------------------------------------------------
Write-Log "Applying Start menu settings" -Level STEP
# Disable Bing search suggestions in Start menu
Set-ProfileReg -SubKey "Software\Policies\Microsoft\Windows\Explorer" `
-Name "DisableSearchBoxSuggestions" -Value 1
# Win11: empty Start menu pins
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Start" `
-Name "ConfigureStartPins" `
-Value '{"pinnedList":[]}' `
-Type "String"
# Hide "Recently added" apps in Start menu
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" `
-Name "Start_TrackProgs" -Value 0
# Hide recently opened files/docs from Start menu
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" `
-Name "Start_TrackDocs" -Value 0
# -----------------------------------------------------------------------
# Copilot - disable
# -----------------------------------------------------------------------
Set-ProfileReg -SubKey "Software\Policies\Microsoft\Windows\WindowsCopilot" `
-Name "TurnOffWindowsCopilot" -Value 1
# -----------------------------------------------------------------------
# GameDVR - disable
# -----------------------------------------------------------------------
Set-ProfileReg -SubKey "System\GameConfigStore" `
-Name "GameDVR_Enabled" -Value 0
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\GameDVR" `
-Name "AppCaptureEnabled" -Value 0
# -----------------------------------------------------------------------
# Num Lock on startup
# -----------------------------------------------------------------------
Set-ProfileReg -SubKey "Control Panel\Keyboard" `
-Name "InitialKeyboardIndicators" -Value 2 -Type "String"
# -----------------------------------------------------------------------
# Accent color on title bars
# -----------------------------------------------------------------------
Set-ProfileReg -SubKey "Software\Microsoft\Windows\DWM" `
-Name "ColorPrevalence" -Value 1
# -----------------------------------------------------------------------
# Taskbar pinned apps layout (Win10/11) # Taskbar pinned apps layout (Win10/11)
# ProfileType: default = empty, admin = Explorer+PS+Edge, user = Explorer+Edge # ProfileType: default = empty, admin = Explorer+PS+Edge, user = Explorer+Edge
# Note: TaskbarLayoutModification.xml locks the taskbar temporarily. # Note: TaskbarLayoutModification.xml locks the taskbar temporarily.
# UnlockStartLayout scheduled task removes the lock 5 min after first boot # UnlockStartLayout scheduled task removes the lock 5 min after first boot
# so users can then customize pins freely. # so users can then customize pins freely.
# Win11 24H2+ may require ProvisionedLayoutModification.xml format instead. # Win11 24H2+ may require ProvisionedLayoutModification.xml format instead.
# ----------------------------------------------------------------------- Write-Log " Writing taskbar layout (ProfileType=$ProfileType)" -Level INFO
Write-Log "Writing taskbar layout (ProfileType=$ProfileType)" -Level INFO
$taskbarLayoutDir = "C:\Users\Default\AppData\Local\Microsoft\Windows\Shell" $taskbarLayoutDir = "C:\Users\Default\AppData\Local\Microsoft\Windows\Shell"
if (-not (Test-Path $taskbarLayoutDir)) { if (-not (Test-Path $taskbarLayoutDir)) {
New-Item -ItemType Directory -Path $taskbarLayoutDir -Force | Out-Null New-Item -ItemType Directory -Path $taskbarLayoutDir -Force | Out-Null
} }
# Build pin list based on profile type.
# Paths resolve relative to the new user at first login.
# Missing shortcuts are silently skipped by Windows.
$pinList = switch ($ProfileType) { $pinList = switch ($ProfileType) {
"admin" { "admin" {
@' @'
@ -320,6 +264,82 @@ $pinList
$taskbarLayoutXml | Set-Content -Path "$taskbarLayoutDir\LayoutModification.xml" -Encoding UTF8 -Force $taskbarLayoutXml | Set-Content -Path "$taskbarLayoutDir\LayoutModification.xml" -Encoding UTF8 -Force
Write-Log " Taskbar LayoutModification.xml written (profile: $ProfileType)" -Level OK Write-Log " Taskbar LayoutModification.xml written (profile: $ProfileType)" -Level OK
# NumLock on startup
Set-ProfileReg -SubKey "Control Panel\Keyboard" `
-Name "InitialKeyboardIndicators" -Value 2 -Type "String"
# Accent color on title bars
Set-ProfileReg -SubKey "Software\Microsoft\Windows\DWM" `
-Name "ColorPrevalence" -Value 1
} else {
Write-Log "taskbarTweaks feature disabled - skipping" -Level INFO
}
# -----------------------------------------------------------------------
# Start menu tweaks (pins, Bing, Copilot, GameDVR)
# -----------------------------------------------------------------------
if (Get-Feature $Config "defaultProfile" "startMenuTweaks") {
Write-Log "Applying Start menu tweaks" -Level STEP
# Disable Bing search suggestions in Start menu
Set-ProfileReg -SubKey "Software\Policies\Microsoft\Windows\Explorer" `
-Name "DisableSearchBoxSuggestions" -Value 1
# Win11: empty Start menu pins
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Start" `
-Name "ConfigureStartPins" `
-Value '{"pinnedList":[]}' `
-Type "String"
# Hide "Recently added" apps in Start menu
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" `
-Name "Start_TrackProgs" -Value 0
# Hide recently opened files/docs from Start menu
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" `
-Name "Start_TrackDocs" -Value 0
# Disable Copilot
Set-ProfileReg -SubKey "Software\Policies\Microsoft\Windows\WindowsCopilot" `
-Name "TurnOffWindowsCopilot" -Value 1
# Disable GameDVR
Set-ProfileReg -SubKey "System\GameConfigStore" `
-Name "GameDVR_Enabled" -Value 0
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\GameDVR" `
-Name "AppCaptureEnabled" -Value 0
} else {
Write-Log "startMenuTweaks feature disabled - skipping" -Level INFO
}
# -----------------------------------------------------------------------
# Explorer tweaks (file extensions, LaunchTo, ShowRecent, FullPath)
# -----------------------------------------------------------------------
if (Get-Feature $Config "defaultProfile" "explorerTweaks") {
Write-Log "Applying Explorer tweaks" -Level STEP
$advPath = "Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
# Show file extensions in Explorer
Set-ProfileReg -SubKey $advPath -Name "HideFileExt" -Value 0
# Open Explorer to This PC instead of Quick Access
Set-ProfileReg -SubKey $advPath -Name "LaunchTo" -Value 1
# Hide Recent files from Quick Access
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer" `
-Name "ShowRecent" -Value 0
# Hide Frequent folders from Quick Access
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer" `
-Name "ShowFrequent" -Value 0
# Show full path in Explorer title bar
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer\CabinetState" `
-Name "FullPath" -Value 1
} else {
Write-Log "explorerTweaks feature disabled - skipping" -Level INFO
}
} }
finally { finally {
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------

View file

@ -36,6 +36,18 @@ function Write-Log {
} }
} }
function Get-Feature {
param([object]$Cfg, [string]$StepID, [string]$FeatureID, [bool]$Default = $true)
try {
if ($null -eq $Cfg) { return $Default }
$stepFeatures = $Cfg.features.$StepID
if ($null -eq $stepFeatures) { return $Default }
$val = $stepFeatures.$FeatureID
if ($null -eq $val) { return $Default }
return [bool]$val
} catch { return $Default }
}
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
# Detect Dell hardware # Detect Dell hardware
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
@ -95,28 +107,46 @@ if (-not $dcuCli) {
Write-Log " dcu-cli.exe found: $dcuCli" -Level OK Write-Log " dcu-cli.exe found: $dcuCli" -Level OK
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
# Run all available updates (drivers, firmware, BIOS) # Run updates - categories controlled by feature flags
# -reboot=disable -> no mid-deployment reboot; BIOS/firmware staged for next restart # -reboot=disable -> no mid-deployment reboot; BIOS/firmware staged for next restart
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
Write-Log "Running Dell Command | Update (all categories, no auto-reboot)..." -Level STEP $runDrivers = Get-Feature $Config "dellUpdate" "drivers"
Write-Log " This may take several minutes depending on available updates" -Level INFO $runBios = Get-Feature $Config "dellUpdate" "bios"
$dcuOutput = & $dcuCli /applyUpdates -silent -reboot=disable 2>&1 if (-not $runDrivers -and -not $runBios) {
$exitCode = $LASTEXITCODE Write-Log "Both drivers and bios features disabled - skipping update run" -Level INFO
$dcuOutput | ForEach-Object { Write-Log " [DCU] $_" -Level INFO } } else {
# Build update type list from enabled features
$updateTypes = @()
if ($runDrivers) {
$updateTypes += "driver"
$updateTypes += "firmware"
}
if ($runBios) {
$updateTypes += "bios"
}
$updateTypeArg = $updateTypes -join ","
Write-Log " DCU exit code: $exitCode" -Level INFO Write-Log "Running Dell Command | Update (updateType=$updateTypeArg, no auto-reboot)..." -Level STEP
Write-Log " This may take several minutes depending on available updates" -Level INFO
# Dell Command | Update exit codes: $dcuOutput = & $dcuCli /applyUpdates -silent -reboot=disable "-updateType=$updateTypeArg" 2>&1
# 0 = completed, no updates required or updates applied (no reboot needed) $exitCode = $LASTEXITCODE
# 1 = updates applied, reboot required to finalize BIOS/firmware $dcuOutput | ForEach-Object { Write-Log " [DCU] $_" -Level INFO }
# 5 = no applicable updates found for this system
# others = error or partial failure Write-Log " DCU exit code: $exitCode" -Level INFO
switch ($exitCode) {
# Dell Command | Update exit codes:
# 0 = completed, no updates required or updates applied (no reboot needed)
# 1 = updates applied, reboot required to finalize BIOS/firmware
# 5 = no applicable updates found for this system
# others = error or partial failure
switch ($exitCode) {
0 { Write-Log "Dell Command | Update: complete (no reboot required)" -Level OK } 0 { Write-Log "Dell Command | Update: complete (no reboot required)" -Level OK }
1 { Write-Log "Dell Command | Update: updates staged - BIOS/firmware will finalize on restart" -Level OK } 1 { Write-Log "Dell Command | Update: updates staged - BIOS/firmware will finalize on restart" -Level OK }
5 { Write-Log "Dell Command | Update: no applicable updates for this model" -Level OK } 5 { Write-Log "Dell Command | Update: no applicable updates for this model" -Level OK }
default { Write-Log "Dell Command | Update: exit code $exitCode - review DCU log in C:\ProgramData\Dell\UpdateService\Logs" -Level WARN } default { Write-Log "Dell Command | Update: exit code $exitCode - review DCU log in C:\ProgramData\Dell\UpdateService\Logs" -Level WARN }
}
} }
Write-Log "Step 11 complete" -Level OK Write-Log "Step 11 complete" -Level OK

BIN
xetup.exe Executable file

Binary file not shown.