Add per-feature toggles to PS scripts and Go TUI
- 02-software.ps1: wrap wingetInstalls, pdfDefault, ateraAgent in Get-Feature guards - 03-system-registry.ps1: add Get-Feature, restructure into 5 gated blocks (systemTweaks, edgePolicies, oneDriveUninstall, powercfg, proxyDisable) - 04-default-profile.ps1: add Get-Feature, wrap taskbarTweaks, startMenuTweaks, explorerTweaks; add missing explorerTweaks code (ShowRecent, ShowFrequent, FullPath) - 11-dell-update.ps1: add Get-Feature, split update run by drivers/bios feature flags - runner.go: add Feature/StepFeatures/SelectableItem/AllSelectableItems for TUI - config.go: add Features type and defaults for all 4 gated steps - tui.go: use AllSelectableItems for MultiSelect, build Features map in startRun, remove unused stepFeaturesMap variable - xetup.exe: Windows amd64 build Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a10a3a8aa2
commit
5b53b2a0d6
8 changed files with 590 additions and 378 deletions
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ type Model struct {
|
|||
pcDesc string
|
||||
productKey string
|
||||
profileType string
|
||||
selectedStepIDs []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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,6 +74,7 @@ Write-Log "winget found: $($winget.Source -or $winget)" -Level OK
|
|||
# -----------------------------------------------------------------------
|
||||
# Install packages from config
|
||||
# -----------------------------------------------------------------------
|
||||
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 {
|
||||
|
|
@ -86,16 +99,14 @@ if (-not $Config -or -not $Config.software -or -not $Config.software.install) {
|
|||
}
|
||||
}
|
||||
}
|
||||
} 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,11 +171,14 @@ if ($forcePdf) {
|
|||
Write-Log " Could not restart UCPD: $_" -Level WARN
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Write-Log "pdfDefault feature disabled - skipping" -Level INFO
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Install Atera RMM Agent
|
||||
# -----------------------------------------------------------------------
|
||||
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"
|
||||
|
|
@ -192,5 +206,8 @@ catch {
|
|||
finally {
|
||||
Remove-Item $ateraMsi -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
} else {
|
||||
Write-Log "ateraAgent feature disabled - skipping" -Level INFO
|
||||
}
|
||||
|
||||
Write-Log "Step 2 complete" -Level OK
|
||||
|
|
|
|||
|
|
@ -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,35 +218,97 @@ function Remove-Reg {
|
|||
Write-Log "3 - Applying HKLM system registry tweaks" -Level STEP
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Bypass Network Requirement on OOBE (BypassNRO)
|
||||
# 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
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Log " Password max age set to UNLIMITED" -Level OK
|
||||
} else {
|
||||
Write-Log " Failed to set password max age: $pwResult" -Level ERROR
|
||||
}
|
||||
|
||||
$tz = "Central Europe Standard Time"
|
||||
if ($Config -and $Config.deployment -and $Config.deployment.timezone) {
|
||||
$tz = $Config.deployment.timezone
|
||||
}
|
||||
Write-Log " Setting time zone: $tz" -Level INFO
|
||||
try {
|
||||
Set-TimeZone -Id $tz -ErrorAction Stop
|
||||
Write-Log " Time zone set: $tz" -Level OK
|
||||
}
|
||||
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
|
||||
|
|
@ -288,32 +362,8 @@ Set-Reg -Path $edgePath -Name "ManagedSearchEngines" `
|
|||
# Disable desktop shortcut on install/update
|
||||
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\EdgeUpdate" `
|
||||
-Name "CreateDesktopShortcutDefault" -Value 0
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Password - no expiration
|
||||
# -----------------------------------------------------------------------
|
||||
Write-Log " Setting password max age to UNLIMITED" -Level INFO
|
||||
$pwResult = & net accounts /maxpwage:UNLIMITED 2>&1
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Log " Password max age set to UNLIMITED" -Level OK
|
||||
} else {
|
||||
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
|
||||
}
|
||||
Write-Log " Setting time zone: $tz" -Level INFO
|
||||
try {
|
||||
Set-TimeZone -Id $tz -ErrorAction Stop
|
||||
Write-Log " Time zone set: $tz" -Level OK
|
||||
}
|
||||
catch {
|
||||
Write-Log " Failed to set time zone: $_" -Level ERROR
|
||||
Write-Log "edgePolicies feature disabled - skipping" -Level INFO
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
|
@ -321,9 +371,9 @@ catch {
|
|||
# NOTE: No policy key is set intentionally - M365 installation can reinstall
|
||||
# and run OneDrive normally. Policy DisableFileSyncNGSC would prevent that.
|
||||
# -----------------------------------------------------------------------
|
||||
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"
|
||||
|
|
@ -331,7 +381,6 @@ $oneDrivePaths = @(
|
|||
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
|
||||
}
|
||||
|
|
@ -347,78 +396,43 @@ 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
|
||||
} else {
|
||||
Write-Log "oneDriveUninstall feature disabled - skipping" -Level INFO
|
||||
}
|
||||
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
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Power configuration
|
||||
# -----------------------------------------------------------------------
|
||||
if (Get-Feature $Config "systemRegistry" "powercfg") {
|
||||
Write-Log " Applying power configuration" -Level INFO
|
||||
|
||||
$powercfg = @(
|
||||
$powercfgArgs = @(
|
||||
@("/change", "standby-timeout-ac", "0"), # never sleep on AC
|
||||
@("/change", "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
|
||||
foreach ($a in $powercfgArgs) {
|
||||
$result = & powercfg @a 2>&1
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Log " powercfg $($args -join ' ')" -Level OK
|
||||
Write-Log " powercfg $($a -join ' ')" -Level OK
|
||||
} else {
|
||||
Write-Log " powercfg $($args -join ' ') failed: $result" -Level WARN
|
||||
Write-Log " powercfg $($a -join ' ') failed: $result" -Level WARN
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Write-Log "powercfg feature disabled - skipping" -Level INFO
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Proxy auto-detect disable (WPAD)
|
||||
# -----------------------------------------------------------------------
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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,9 +162,10 @@ 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"
|
||||
|
||||
|
|
@ -177,29 +190,18 @@ try {
|
|||
# Hide Copilot button
|
||||
Set-ProfileReg -SubKey $tbPath -Name "ShowCopilotButton" -Value 0
|
||||
|
||||
# Show file extensions in Explorer
|
||||
Set-ProfileReg -SubKey $tbPath -Name "HideFileExt" -Value 0
|
||||
|
||||
# Open Explorer to This PC instead of Quick Access
|
||||
Set-ProfileReg -SubKey $tbPath -Name "LaunchTo" -Value 1
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# System tray - show all icons
|
||||
# -----------------------------------------------------------------------
|
||||
# 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
|
||||
|
||||
# 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
|
||||
}
|
||||
|
||||
# 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
|
||||
|
|
@ -207,72 +209,17 @@ try {
|
|||
Write-Log " Cleared TrayNotify icon streams in Default hive" -Level OK
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# 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"
|
||||
|
|
@ -280,9 +227,6 @@ try {
|
|||
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" {
|
||||
@'
|
||||
|
|
@ -320,6 +264,82 @@ $pinList
|
|||
$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 {
|
||||
# -----------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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,13 +107,30 @@ 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
|
||||
$runDrivers = Get-Feature $Config "dellUpdate" "drivers"
|
||||
$runBios = Get-Feature $Config "dellUpdate" "bios"
|
||||
|
||||
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 "Running Dell Command | Update (updateType=$updateTypeArg, no auto-reboot)..." -Level STEP
|
||||
Write-Log " This may take several minutes depending on available updates" -Level INFO
|
||||
|
||||
$dcuOutput = & $dcuCli /applyUpdates -silent -reboot=disable 2>&1
|
||||
$dcuOutput = & $dcuCli /applyUpdates -silent -reboot=disable "-updateType=$updateTypeArg" 2>&1
|
||||
$exitCode = $LASTEXITCODE
|
||||
$dcuOutput | ForEach-Object { Write-Log " [DCU] $_" -Level INFO }
|
||||
|
||||
|
|
@ -118,5 +147,6 @@ switch ($exitCode) {
|
|||
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
|
||||
|
|
|
|||
BIN
xetup.exe
Executable file
BIN
xetup.exe
Executable file
Binary file not shown.
Loading…
Reference in a new issue