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