diff --git a/internal/config/config.go b/internal/config/config.go
index b50d2a2..8800eb9 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -13,6 +13,7 @@ type Config struct {
Activation Activation `json:"activation"`
Software Software `json:"software"`
Steps map[string]bool `json:"steps"`
+ Features Features `json:"features"`
}
type Deployment struct {
@@ -40,6 +41,11 @@ type Software struct {
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.
func DefaultConfig() Config {
return Config{
@@ -74,6 +80,29 @@ func DefaultConfig() Config {
"network": 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,
+ },
+ },
}
}
diff --git a/internal/runner/runner.go b/internal/runner/runner.go
index 452addc..5594ef4 100644
--- a/internal/runner/runner.go
+++ b/internal/runner/runner.go
@@ -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.
type RunConfig struct {
ScriptsDir string
diff --git a/internal/tui/tui.go b/internal/tui/tui.go
index a63d458..58c6e00 100644
--- a/internal/tui/tui.go
+++ b/internal/tui/tui.go
@@ -50,11 +50,11 @@ type Model struct {
cfg config.Config
// form-bound values (huh writes here via pointer)
- pcName string
- pcDesc string
- productKey string
- profileType string
- selectedStepIDs []string
+ pcName string
+ pcDesc string
+ productKey string
+ profileType string
+ selectedItemKeys []string
// runner state
r *runner.Runner
@@ -90,10 +90,10 @@ func NewModel(cfg config.Config, runCfg runner.RunConfig) Model {
// --------------------------------------------------------------------------
func buildForm(m *Model) *huh.Form {
- allSteps := runner.AllSteps()
- opts := make([]huh.Option[string], len(allSteps))
- for i, s := range allSteps {
- opts[i] = huh.NewOption(s.Num+" - "+s.Name, s.ID).Selected(true)
+ allItems := runner.AllSelectableItems()
+ opts := make([]huh.Option[string], len(allItems))
+ for i, item := range allItems {
+ opts[i] = huh.NewOption(item.Label, item.Key).Selected(true)
}
return huh.NewForm(
@@ -121,9 +121,9 @@ func buildForm(m *Model) *huh.Form {
),
huh.NewGroup(
huh.NewMultiSelect[string]().
- Title("Kroky k provedeni (mezernikem odzaskrtnout)").
+ Title("Kroky a nastaveni (mezernikem odzaskrtnout)").
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.runCfg.ProfileType = m.profileType
- // build step list with Enabled flag from the user's checklist
- selected := make(map[string]bool, len(m.selectedStepIDs))
- for _, id := range m.selectedStepIDs {
- selected[id] = true
+ // Index selected keys for fast lookup
+ selectedSet := make(map[string]bool, len(m.selectedItemKeys))
+ for _, key := range m.selectedItemKeys {
+ 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()
steps := make([]runner.Step, len(allSteps))
for i, s := range allSteps {
- s.Enabled = selected[s.ID]
+ s.Enabled = stepEnabled[s.ID]
steps[i] = s
}
diff --git a/scripts/02-software.ps1 b/scripts/02-software.ps1
index 0276a9f..d6176f5 100644
--- a/scripts/02-software.ps1
+++ b/scripts/02-software.ps1
@@ -31,6 +31,18 @@ function Write-Log {
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
# -----------------------------------------------------------------------
@@ -62,40 +74,39 @@ Write-Log "winget found: $($winget.Source -or $winget)" -Level OK
# -----------------------------------------------------------------------
# Install packages from config
# -----------------------------------------------------------------------
-if (-not $Config -or -not $Config.software -or -not $Config.software.install) {
- Write-Log "No software list in config - skipping installs" -Level WARN
-} else {
- foreach ($pkg in $Config.software.install) {
- Write-Log "Installing $($pkg.name) ($($pkg.wingetId))" -Level INFO
- $result = & winget install --id $pkg.wingetId `
- --silent `
- --accept-package-agreements `
- --accept-source-agreements `
- --disable-interactivity `
- 2>&1
+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
+ } else {
+ foreach ($pkg in $Config.software.install) {
+ Write-Log "Installing $($pkg.name) ($($pkg.wingetId))" -Level INFO
+ $result = & winget install --id $pkg.wingetId `
+ --silent `
+ --accept-package-agreements `
+ --accept-source-agreements `
+ --disable-interactivity `
+ 2>&1
- $exitCode = $LASTEXITCODE
- if ($exitCode -eq 0) {
- Write-Log " Installed OK: $($pkg.name)" -Level OK
- } elseif ($exitCode -eq -1978335189) {
- # 0x8A150011 = already installed
- Write-Log " Already installed: $($pkg.name)" -Level OK
- } else {
- Write-Log " Failed: $($pkg.name) (exit $exitCode)" -Level ERROR
- Write-Log " Output: $($result -join ' ')" -Level ERROR
+ $exitCode = $LASTEXITCODE
+ if ($exitCode -eq 0) {
+ Write-Log " Installed OK: $($pkg.name)" -Level OK
+ } elseif ($exitCode -eq -1978335189) {
+ # 0x8A150011 = already installed
+ Write-Log " Already installed: $($pkg.name)" -Level OK
+ } else {
+ Write-Log " Failed: $($pkg.name) (exit $exitCode)" -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
# -----------------------------------------------------------------------
-$forcePdf = $true
-if ($Config -and $Config.pdfDefault) {
- $forcePdf = [bool]$Config.pdfDefault.forceAdobeReader
-}
-
-if ($forcePdf) {
+if (Get-Feature $Config "software" "pdfDefault") {
Write-Log "Setting Adobe Reader as default PDF app" -Level INFO
# Stop UCPD driver before writing file association.
@@ -160,37 +171,43 @@ if ($forcePdf) {
Write-Log " Could not restart UCPD: $_" -Level WARN
}
}
+} else {
+ Write-Log "pdfDefault feature disabled - skipping" -Level INFO
}
# -----------------------------------------------------------------------
# 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"
-$ateraMsi = "$env:TEMP\AteraAgent.msi"
+ $ateraUrl = "https://x9.servicedesk.atera.com/api/utils/agent-install/windows/?cid=31&aeid=50b72e7113e54a63ac76b96c54c7e337"
+ $ateraMsi = "$env:TEMP\AteraAgent.msi"
-try {
- Write-Log " Downloading Atera agent..." -Level INFO
- Invoke-WebRequest -Uri $ateraUrl -OutFile $ateraMsi -UseBasicParsing -ErrorAction Stop
- Write-Log " Download complete" -Level OK
+ try {
+ Write-Log " Downloading Atera agent..." -Level INFO
+ Invoke-WebRequest -Uri $ateraUrl -OutFile $ateraMsi -UseBasicParsing -ErrorAction Stop
+ Write-Log " Download complete" -Level OK
- $msiResult = & msiexec /i $ateraMsi /qn 2>&1
- Start-Sleep -Seconds 5
+ $msiResult = & msiexec /i $ateraMsi /qn 2>&1
+ Start-Sleep -Seconds 5
- $ateraExe = "$env:ProgramFiles\ATERA Networks\AteraAgent\AteraAgent.exe"
- if (Test-Path $ateraExe) {
- Write-Log " Atera agent installed" -Level OK
- } else {
- Write-Log " Atera agent install may have failed - binary not found at expected path" -Level WARN
- Write-Log " msiexec output: $($msiResult -join ' ')" -Level WARN
+ $ateraExe = "$env:ProgramFiles\ATERA Networks\AteraAgent\AteraAgent.exe"
+ if (Test-Path $ateraExe) {
+ Write-Log " Atera agent installed" -Level OK
+ } else {
+ Write-Log " Atera agent install may have failed - binary not found at expected path" -Level WARN
+ Write-Log " msiexec output: $($msiResult -join ' ')" -Level WARN
+ }
}
-}
-catch {
- Write-Log " Atera agent download/install failed: $_" -Level ERROR
-}
-finally {
- Remove-Item $ateraMsi -Force -ErrorAction SilentlyContinue
+ catch {
+ Write-Log " Atera agent download/install failed: $_" -Level ERROR
+ }
+ finally {
+ Remove-Item $ateraMsi -Force -ErrorAction SilentlyContinue
+ }
+} else {
+ Write-Log "ateraAgent feature disabled - skipping" -Level INFO
}
Write-Log "Step 2 complete" -Level OK
diff --git a/scripts/03-system-registry.ps1 b/scripts/03-system-registry.ps1
index 3217bff..8f7e290 100644
--- a/scripts/03-system-registry.ps1
+++ b/scripts/03-system-registry.ps1
@@ -40,6 +40,18 @@ function Write-Log {
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 @"
using System;
using System.Runtime.InteropServices;
@@ -206,91 +218,7 @@ function Remove-Reg {
Write-Log "3 - Applying HKLM system registry tweaks" -Level STEP
# -----------------------------------------------------------------------
-# Bypass Network Requirement on OOBE (BypassNRO)
-# -----------------------------------------------------------------------
-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
+# Always-on: password and timezone (fundamental system settings)
# -----------------------------------------------------------------------
Write-Log " Setting password max age to UNLIMITED" -Level INFO
$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
}
-# -----------------------------------------------------------------------
-# Time zone
-# -----------------------------------------------------------------------
$tz = "Central Europe Standard Time"
if ($Config -and $Config.deployment -and $Config.deployment.timezone) {
$tz = $Config.deployment.timezone
@@ -316,109 +241,198 @@ catch {
Write-Log " Failed to set time zone: $_" -Level ERROR
}
+# -----------------------------------------------------------------------
+# System tweaks (Windows features, cloud noise, Xbox, Recall, Search UI)
+# -----------------------------------------------------------------------
+if (Get-Feature $Config "systemRegistry" "systemTweaks") {
+ Write-Log " Applying system tweaks" -Level INFO
+
+ # Bypass Network Requirement on OOBE (BypassNRO)
+ 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
+
+ # 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\OutlookUpdate"
+ )
+ foreach ($uPath in $uschedulerPaths) {
+ if (Test-Path $uPath) {
+ try {
+ Remove-Item -Path $uPath -Recurse -Force
+ Write-Log " Removed UScheduler key: $uPath" -Level OK
+ }
+ catch {
+ 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
+}
+
+# -----------------------------------------------------------------------
+# Microsoft Edge policies
+# -----------------------------------------------------------------------
+if (Get-Feature $Config "systemRegistry" "edgePolicies") {
+ 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
+}
+
# -----------------------------------------------------------------------
# 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.
# -----------------------------------------------------------------------
-Write-Log " Uninstalling OneDrive" -Level INFO
+if (Get-Feature $Config "systemRegistry" "oneDriveUninstall") {
+ Write-Log " Uninstalling OneDrive" -Level INFO
-# Remove OneDriveSetup.exe if present
-$oneDrivePaths = @(
- "$env:SystemRoot\System32\OneDriveSetup.exe"
- "$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
+ $oneDrivePaths = @(
+ "$env:SystemRoot\System32\OneDriveSetup.exe"
+ "$env:SystemRoot\SysWOW64\OneDriveSetup.exe"
+ )
+ foreach ($odPath in $oneDrivePaths) {
+ if (Test-Path $odPath) {
+ 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
-$odLnk = "$env:ProgramData\Microsoft\Windows\Start Menu\Programs\OneDrive.lnk"
-if (Test-Path $odLnk) {
- Remove-Item $odLnk -Force -ErrorAction SilentlyContinue
- Write-Log " Removed OneDrive Start Menu shortcut" -Level OK
-}
-
-# -----------------------------------------------------------------------
-# Outlook (new) - disable 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\OutlookUpdate"
-)
-foreach ($uPath in $uschedulerPaths) {
- if (Test-Path $uPath) {
- try {
- Remove-Item -Path $uPath -Recurse -Force
- Write-Log " Removed UScheduler key: $uPath" -Level OK
- }
- catch {
- Write-Log " Failed to remove UScheduler key: $_" -Level WARN
- }
+ # Remove OneDrive Start Menu shortcut
+ $odLnk = "$env:ProgramData\Microsoft\Windows\Start Menu\Programs\OneDrive.lnk"
+ if (Test-Path $odLnk) {
+ Remove-Item $odLnk -Force -ErrorAction SilentlyContinue
+ Write-Log " Removed OneDrive Start Menu shortcut" -Level OK
}
+} else {
+ Write-Log "oneDriveUninstall feature disabled - skipping" -Level INFO
}
-# -----------------------------------------------------------------------
-# 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
-
# -----------------------------------------------------------------------
# Power configuration
# -----------------------------------------------------------------------
-Write-Log "Applying power configuration" -Level INFO
+if (Get-Feature $Config "systemRegistry" "powercfg") {
+ Write-Log " Applying power configuration" -Level INFO
-$powercfg = @(
- @("/change", "standby-timeout-ac", "0"), # never sleep 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", "monitor-timeout-dc", "15") # screen off after 15 min on battery
-)
-foreach ($args in $powercfg) {
- $result = & powercfg @args 2>&1
- if ($LASTEXITCODE -eq 0) {
- Write-Log " powercfg $($args -join ' ')" -Level OK
- } else {
- Write-Log " powercfg $($args -join ' ') failed: $result" -Level WARN
+ $powercfgArgs = @(
+ @("/change", "standby-timeout-ac", "0"), # never sleep 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", "monitor-timeout-dc", "15") # screen off after 15 min on battery
+ )
+ foreach ($a in $powercfgArgs) {
+ $result = & powercfg @a 2>&1
+ if ($LASTEXITCODE -eq 0) {
+ Write-Log " powercfg $($a -join ' ')" -Level OK
+ } else {
+ Write-Log " powercfg $($a -join ' ') failed: $result" -Level WARN
+ }
}
+} else {
+ Write-Log "powercfg feature disabled - skipping" -Level INFO
}
# -----------------------------------------------------------------------
# Proxy auto-detect disable (WPAD)
# -----------------------------------------------------------------------
-Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings" `
- -Name "AutoDetect" -Value 0
+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
+} else {
+ Write-Log "proxyDisable feature disabled - skipping" -Level INFO
+}
Write-Log "Step 3 complete" -Level OK
diff --git a/scripts/04-default-profile.ps1 b/scripts/04-default-profile.ps1
index 96adbd0..fd08f7b 100644
--- a/scripts/04-default-profile.ps1
+++ b/scripts/04-default-profile.ps1
@@ -39,6 +39,18 @@ function Write-Log {
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
# -----------------------------------------------------------------------
@@ -150,157 +162,89 @@ Write-Log "Default hive loaded" -Level OK
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"
- # Win11: align taskbar to left (0 = left, 1 = center)
- Set-ProfileReg -SubKey $tbPath -Name "TaskbarAl" -Value 0
+ # Win11: align taskbar to left (0 = left, 1 = center)
+ Set-ProfileReg -SubKey $tbPath -Name "TaskbarAl" -Value 0
- # Hide Search box / button - Win10/11 (0 = hidden, 1 = icon, 2 = full box)
- # Note: Win11 uses Search subkey, Win10 uses Explorer\Advanced - set both
- Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Search" `
- -Name "SearchboxTaskbarMode" -Value 0
- Set-ProfileReg -SubKey $tbPath -Name "SearchboxTaskbarMode" -Value 0
+ # Hide Search box / button - Win10/11 (0 = hidden, 1 = icon, 2 = full box)
+ # Note: Win11 uses Search subkey, Win10 uses Explorer\Advanced - set both
+ Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Search" `
+ -Name "SearchboxTaskbarMode" -Value 0
+ Set-ProfileReg -SubKey $tbPath -Name "SearchboxTaskbarMode" -Value 0
- # Hide Task View button
- Set-ProfileReg -SubKey $tbPath -Name "ShowTaskViewButton" -Value 0
+ # Hide Task View button
+ Set-ProfileReg -SubKey $tbPath -Name "ShowTaskViewButton" -Value 0
- # Hide Widgets button
- Set-ProfileReg -SubKey $tbPath -Name "TaskbarDa" -Value 0
+ # Hide Widgets button
+ Set-ProfileReg -SubKey $tbPath -Name "TaskbarDa" -Value 0
- # Hide Chat / Teams button
- Set-ProfileReg -SubKey $tbPath -Name "TaskbarMn" -Value 0
+ # Hide Chat / Teams button
+ Set-ProfileReg -SubKey $tbPath -Name "TaskbarMn" -Value 0
- # Hide Copilot button
- Set-ProfileReg -SubKey $tbPath -Name "ShowCopilotButton" -Value 0
+ # Hide Copilot button
+ Set-ProfileReg -SubKey $tbPath -Name "ShowCopilotButton" -Value 0
- # Show file extensions in Explorer
- Set-ProfileReg -SubKey $tbPath -Name "HideFileExt" -Value 0
+ # Show all tray icons
+ # EnableAutoTray = 0 works on Win10; Win11 ignores it but set anyway
+ Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer" `
+ -Name "EnableAutoTray" -Value 0
- # Open Explorer to This PC instead of Quick Access
- Set-ProfileReg -SubKey $tbPath -Name "LaunchTo" -Value 1
+ # Win11 workaround: clear cached tray icon streams so all icons appear on next login
+ $trayNotifyKey = "HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify"
+ if (Test-Path $trayNotifyKey) {
+ Remove-ItemProperty -Path $trayNotifyKey -Name "IconStreams" -Force -ErrorAction SilentlyContinue
+ Remove-ItemProperty -Path $trayNotifyKey -Name "PastIconsStream" -Force -ErrorAction SilentlyContinue
+ Write-Log " Cleared TrayNotify icon streams (Win11 systray workaround)" -Level OK
+ }
+ $defTrayKey = "Registry::HKU\DefaultProfile\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify"
+ if (Test-Path $defTrayKey) {
+ Remove-ItemProperty -Path $defTrayKey -Name "IconStreams" -Force -ErrorAction SilentlyContinue
+ Remove-ItemProperty -Path $defTrayKey -Name "PastIconsStream" -Force -ErrorAction SilentlyContinue
+ Write-Log " Cleared TrayNotify icon streams in Default hive" -Level OK
+ }
- # -----------------------------------------------------------------------
- # System tray - show all icons
- # -----------------------------------------------------------------------
- # EnableAutoTray = 0 works on Win10; Win11 ignores it but set anyway
- Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer" `
- -Name "EnableAutoTray" -Value 0
+ # Desktop icons - show This PC
+ # CLSID {20D04FE0-3AEA-1069-A2D8-08002B30309D} = This PC / Computer
+ Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel" `
+ -Name "{20D04FE0-3AEA-1069-A2D8-08002B30309D}" -Value 0
- # 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"
- if (Test-Path $trayNotifyKey) {
- Remove-ItemProperty -Path $trayNotifyKey -Name "IconStreams" -Force -ErrorAction SilentlyContinue
- Remove-ItemProperty -Path $trayNotifyKey -Name "PastIconsStream" -Force -ErrorAction SilentlyContinue
- Write-Log " Cleared TrayNotify icon streams (Win11 systray workaround)" -Level OK
- }
+ # Taskbar pinned apps layout (Win10/11)
+ # ProfileType: default = empty, admin = Explorer+PS+Edge, user = Explorer+Edge
+ # Note: TaskbarLayoutModification.xml locks the taskbar temporarily.
+ # UnlockStartLayout scheduled task removes the lock 5 min after first boot
+ # so users can then customize pins freely.
+ # Win11 24H2+ may require ProvisionedLayoutModification.xml format instead.
+ Write-Log " Writing taskbar layout (ProfileType=$ProfileType)" -Level INFO
- # 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"
- if (Test-Path $defTrayKey) {
- Remove-ItemProperty -Path $defTrayKey -Name "IconStreams" -Force -ErrorAction SilentlyContinue
- Remove-ItemProperty -Path $defTrayKey -Name "PastIconsStream" -Force -ErrorAction SilentlyContinue
- Write-Log " Cleared TrayNotify icon streams in Default hive" -Level OK
- }
+ $taskbarLayoutDir = "C:\Users\Default\AppData\Local\Microsoft\Windows\Shell"
+ if (-not (Test-Path $taskbarLayoutDir)) {
+ New-Item -ItemType Directory -Path $taskbarLayoutDir -Force | Out-Null
+ }
- # -----------------------------------------------------------------------
- # Desktop icons - show This PC
- # -----------------------------------------------------------------------
- # CLSID {20D04FE0-3AEA-1069-A2D8-08002B30309D} = This PC / Computer
- Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel" `
- -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)
- # ProfileType: default = empty, admin = Explorer+PS+Edge, user = Explorer+Edge
- # Note: TaskbarLayoutModification.xml locks the taskbar temporarily.
- # UnlockStartLayout scheduled task removes the lock 5 min after first boot
- # so users can then customize pins freely.
- # Win11 24H2+ may require ProvisionedLayoutModification.xml format instead.
- # -----------------------------------------------------------------------
- Write-Log "Writing taskbar layout (ProfileType=$ProfileType)" -Level INFO
-
- $taskbarLayoutDir = "C:\Users\Default\AppData\Local\Microsoft\Windows\Shell"
- if (-not (Test-Path $taskbarLayoutDir)) {
- 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) {
- "admin" {
+ $pinList = switch ($ProfileType) {
+ "admin" {
@'
'@
- }
- "user" {
+ }
+ "user" {
@'
'@
+ }
+ default { "" } # empty = clean slate
}
- default { "" } # empty = clean slate
- }
- $taskbarLayoutXml = @"
+ $taskbarLayoutXml = @"
"@
- $taskbarLayoutXml | Set-Content -Path "$taskbarLayoutDir\LayoutModification.xml" -Encoding UTF8 -Force
- Write-Log " Taskbar LayoutModification.xml written (profile: $ProfileType)" -Level OK
+ $taskbarLayoutXml | Set-Content -Path "$taskbarLayoutDir\LayoutModification.xml" -Encoding UTF8 -Force
+ 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 {
# -----------------------------------------------------------------------
diff --git a/scripts/11-dell-update.ps1 b/scripts/11-dell-update.ps1
index aaf5f08..ed17d67 100644
--- a/scripts/11-dell-update.ps1
+++ b/scripts/11-dell-update.ps1
@@ -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
# -----------------------------------------------------------------------
@@ -95,28 +107,46 @@ if (-not $dcuCli) {
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
# -----------------------------------------------------------------------
-Write-Log "Running Dell Command | Update (all categories, no auto-reboot)..." -Level STEP
-Write-Log " This may take several minutes depending on available updates" -Level INFO
+$runDrivers = Get-Feature $Config "dellUpdate" "drivers"
+$runBios = Get-Feature $Config "dellUpdate" "bios"
-$dcuOutput = & $dcuCli /applyUpdates -silent -reboot=disable 2>&1
-$exitCode = $LASTEXITCODE
-$dcuOutput | ForEach-Object { Write-Log " [DCU] $_" -Level INFO }
+if (-not $runDrivers -and -not $runBios) {
+ Write-Log "Both drivers and bios features disabled - skipping update run" -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:
-# 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 }
- 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 }
- default { Write-Log "Dell Command | Update: exit code $exitCode - review DCU log in C:\ProgramData\Dell\UpdateService\Logs" -Level WARN }
+ $dcuOutput = & $dcuCli /applyUpdates -silent -reboot=disable "-updateType=$updateTypeArg" 2>&1
+ $exitCode = $LASTEXITCODE
+ $dcuOutput | ForEach-Object { Write-Log " [DCU] $_" -Level INFO }
+
+ Write-Log " DCU exit code: $exitCode" -Level INFO
+
+ # 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 }
+ 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 }
+ 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
diff --git a/xetup.exe b/xetup.exe
new file mode 100755
index 0000000..42b2275
Binary files /dev/null and b/xetup.exe differ