diff --git a/Deploy-Windows.ps1 b/Deploy-Windows.ps1 deleted file mode 100644 index 0ac6eeb..0000000 --- a/Deploy-Windows.ps1 +++ /dev/null @@ -1,276 +0,0 @@ -#Requires -RunAsAdministrator - -[CmdletBinding()] -param( - [switch]$SkipBloatware, - [switch]$SkipSoftware, - [switch]$SkipDefaultProfile, - [switch]$DryRun, - [ValidateSet("default","admin","user")] - [string]$ProfileType = "default" -) - -$ErrorActionPreference = "Continue" - -# ----------------------------------------------------------------------- -# Paths -# ----------------------------------------------------------------------- -$ScriptRoot = $PSScriptRoot -$LogDir = "C:\Windows\Setup\Scripts" -$LogFile = "$LogDir\Deploy.log" -$ConfigFile = "$ScriptRoot\config\config.json" - -# ----------------------------------------------------------------------- -# Logging -# ----------------------------------------------------------------------- -function Write-Log { - param( - [string]$Message, - [ValidateSet("INFO","OK","ERROR","WARN","STEP")] - [string]$Level = "INFO" - ) - $timestamp = Get-Date -Format "HH:mm:ss" - $line = "[$timestamp] [$Level] $Message" - Add-Content -Path $LogFile -Value $line -Encoding UTF8 - switch ($Level) { - "OK" { Write-Host $line -ForegroundColor Green } - "ERROR" { Write-Host $line -ForegroundColor Red } - "WARN" { Write-Host $line -ForegroundColor Yellow } - "STEP" { Write-Host $line -ForegroundColor Cyan } - default { Write-Host $line } - } -} - -# ----------------------------------------------------------------------- -# Step runner - catches errors, logs, always continues -# ----------------------------------------------------------------------- -$StepResults = [System.Collections.Generic.List[hashtable]]::new() - -function Invoke-Step { - param( - [string]$Name, - [scriptblock]$Action - ) - - Write-Log "---- $Name ----" -Level STEP - - if ($DryRun) { - Write-Log "DryRun - skipping execution" -Level WARN - $StepResults.Add(@{ Name = $Name; Status = "DRYRUN" }) - return - } - - try { - & $Action - Write-Log "$Name - OK" -Level OK - $StepResults.Add(@{ Name = $Name; Status = "OK" }) - } - catch { - Write-Log "$Name - ERROR: $_" -Level ERROR - $StepResults.Add(@{ Name = $Name; Status = "ERROR" }) - } -} - -# ----------------------------------------------------------------------- -# Init -# ----------------------------------------------------------------------- -if (-not (Test-Path $LogDir)) { - New-Item -ItemType Directory -Path $LogDir -Force | Out-Null -} - -Write-Log "========================================" -Level INFO -Write-Log "Deploy-Windows.ps1 started" -Level INFO -Write-Log "Computer: $env:COMPUTERNAME" -Level INFO -Write-Log "User: $env:USERNAME" -Level INFO -Write-Log "Date: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -Level INFO -if ($DryRun) { Write-Log "Mode: DRY RUN" -Level WARN } -Write-Log "========================================" -Level INFO - -# ----------------------------------------------------------------------- -# Load config -# ----------------------------------------------------------------------- -$Config = $null -Invoke-Step -Name "Load config.json" -Action { - if (-not (Test-Path $ConfigFile)) { - throw "config.json not found: $ConfigFile" - } - $script:Config = Get-Content $ConfigFile -Raw -Encoding UTF8 | ConvertFrom-Json - Write-Log "Config loaded from $ConfigFile" -Level INFO -} - -# ----------------------------------------------------------------------- -# Build step enable/disable map from config + CLI overrides -# ----------------------------------------------------------------------- -$stepsEnabled = @{ - adminAccount = $true - bloatware = $true - software = $true - systemRegistry = $true - defaultProfile = $true - personalization = $true - scheduledTasks = $true - backinfo = $true - network = $true - pcIdentity = $true - activation = $true - dellUpdate = $true -} -if ($Config -and $Config.steps) { - foreach ($key in @($stepsEnabled.Keys)) { - $val = $Config.steps.$key - if ($null -ne $val) { $stepsEnabled[$key] = [bool]$val } - } -} -# CLI switches override config.steps -if ($SkipBloatware) { $stepsEnabled['bloatware'] = $false } -if ($SkipSoftware) { $stepsEnabled['software'] = $false } -if ($SkipDefaultProfile) { $stepsEnabled['defaultProfile'] = $false } - -function Skip-Step { - param([string]$Name) - Write-Log "$Name - SKIPPED (disabled in config)" -Level WARN - $StepResults.Add(@{ Name = $Name; Status = "SKIPPED" }) -} - -# ----------------------------------------------------------------------- -# Step 0a - Admin account -# ----------------------------------------------------------------------- -if ($stepsEnabled['adminAccount']) { - Invoke-Step -Name "Step 0a - Admin account" -Action { - & "$ScriptRoot\scripts\00-admin-account.ps1" -Config $Config -LogFile $LogFile - } -} else { Skip-Step "Step 0a - Admin account" } - -# ----------------------------------------------------------------------- -# Step 0b - Windows activation -# ----------------------------------------------------------------------- -if ($stepsEnabled['activation']) { - Invoke-Step -Name "Step 0b - Windows activation" -Action { - & "$ScriptRoot\scripts\08-activation.ps1" -Config $Config -LogFile $LogFile - } -} else { Skip-Step "Step 0b - Windows activation" } - -# ----------------------------------------------------------------------- -# Step 1 - Bloatware removal -# ----------------------------------------------------------------------- -if ($stepsEnabled['bloatware']) { - Invoke-Step -Name "Step 1 - Bloatware removal" -Action { - & "$ScriptRoot\scripts\01-bloatware.ps1" -Config $Config -LogFile $LogFile - } -} else { Skip-Step "Step 1 - Bloatware removal" } - -# ----------------------------------------------------------------------- -# Step 2 - Software installation -# ----------------------------------------------------------------------- -if ($stepsEnabled['software']) { - Invoke-Step -Name "Step 2 - Software installation" -Action { - & "$ScriptRoot\scripts\02-software.ps1" -Config $Config -LogFile $LogFile - } -} else { Skip-Step "Step 2 - Software installation" } - -# ----------------------------------------------------------------------- -# Step 3 - System registry (HKLM) -# ----------------------------------------------------------------------- -if ($stepsEnabled['systemRegistry']) { - Invoke-Step -Name "Step 3 - System registry" -Action { - & "$ScriptRoot\scripts\03-system-registry.ps1" -Config $Config -LogFile $LogFile - } -} else { Skip-Step "Step 3 - System registry" } - -# ----------------------------------------------------------------------- -# Step 4 - Default profile (NTUSER.DAT) -# ----------------------------------------------------------------------- -if ($stepsEnabled['defaultProfile']) { - Invoke-Step -Name "Step 4 - Default profile" -Action { - & "$ScriptRoot\scripts\04-default-profile.ps1" -Config $Config -LogFile $LogFile -ProfileType $ProfileType - } -} else { Skip-Step "Step 4 - Default profile" } - -# ----------------------------------------------------------------------- -# Step 5 - Personalization -# ----------------------------------------------------------------------- -if ($stepsEnabled['personalization']) { - Invoke-Step -Name "Step 5 - Personalization" -Action { - & "$ScriptRoot\scripts\05-personalization.ps1" -Config $Config -LogFile $LogFile - } -} else { Skip-Step "Step 5 - Personalization" } - -# ----------------------------------------------------------------------- -# Step 6 - Scheduled tasks -# ----------------------------------------------------------------------- -if ($stepsEnabled['scheduledTasks']) { - Invoke-Step -Name "Step 6 - Scheduled tasks" -Action { - & "$ScriptRoot\scripts\06-scheduled-tasks.ps1" -Config $Config -LogFile $LogFile - } -} else { Skip-Step "Step 6 - Scheduled tasks" } - -# ----------------------------------------------------------------------- -# Step 7 - BackInfo -# ----------------------------------------------------------------------- -if ($stepsEnabled['backinfo']) { - Invoke-Step -Name "Step 7 - BackInfo" -Action { - & "$ScriptRoot\scripts\07-backinfo.ps1" -Config $Config -LogFile $LogFile - } -} else { Skip-Step "Step 7 - BackInfo" } - -# ----------------------------------------------------------------------- -# Step 9 - Network -# ----------------------------------------------------------------------- -if ($stepsEnabled['network']) { - Invoke-Step -Name "Step 9 - Network" -Action { - & "$ScriptRoot\scripts\10-network.ps1" -Config $Config -LogFile $LogFile - } -} else { Skip-Step "Step 9 - Network" } - -# ----------------------------------------------------------------------- -# Step 11 - Dell Command | Update (auto-skipped on non-Dell hardware) -# ----------------------------------------------------------------------- -if ($stepsEnabled['dellUpdate']) { - Invoke-Step -Name "Step 11 - Dell Command | Update" -Action { - & "$ScriptRoot\scripts\11-dell-update.ps1" -Config $Config -LogFile $LogFile - } -} else { Skip-Step "Step 11 - Dell Command | Update" } - -# ----------------------------------------------------------------------- -# Step 10 - PC identity (rename + C:\X9) - runs last, rename needs restart -# ----------------------------------------------------------------------- -if ($stepsEnabled['pcIdentity']) { - Invoke-Step -Name "Step 10 - PC identity" -Action { - & "$ScriptRoot\scripts\09-pc-identity.ps1" -Config $Config -LogFile $LogFile - } -} else { Skip-Step "Step 10 - PC identity" } - -# ----------------------------------------------------------------------- -# Summary -# ----------------------------------------------------------------------- -Write-Log "========================================" -Level INFO -Write-Log "SUMMARY" -Level INFO -Write-Log "========================================" -Level INFO - -$countOK = ($StepResults | Where-Object { $_.Status -eq "OK" }).Count -$countError = ($StepResults | Where-Object { $_.Status -eq "ERROR" }).Count -$countSkipped = ($StepResults | Where-Object { $_.Status -eq "SKIPPED" }).Count -$countDryRun = ($StepResults | Where-Object { $_.Status -eq "DRYRUN" }).Count - -foreach ($r in $StepResults) { - $lvl = switch ($r.Status) { - "OK" { "OK" } - "ERROR" { "ERROR" } - "SKIPPED" { "WARN" } - "DRYRUN" { "WARN" } - } - Write-Log "$($r.Status.PadRight(8)) $($r.Name)" -Level $lvl -} - -Write-Log "----------------------------------------" -Level INFO -Write-Log "OK: $countOK ERROR: $countError SKIPPED: $countSkipped DRYRUN: $countDryRun" -Level INFO -Write-Log "Log saved to: $LogFile" -Level INFO -Write-Log "========================================" -Level INFO - -if ($countError -gt 0) { - Write-Log "Deployment finished with errors. Review log: $LogFile" -Level ERROR - exit 1 -} else { - Write-Log "Deployment finished successfully." -Level OK - exit 0 -} diff --git a/config/config.json b/config/config.json index 138c78b..ef5c37b 100644 --- a/config/config.json +++ b/config/config.json @@ -1,49 +1,62 @@ { - "steps": { - "adminAccount": true, - "bloatware": true, - "software": true, - "systemRegistry": true, - "defaultProfile": true, - "personalization": true, - "scheduledTasks": true, - "desktopInfo": true, - "activation": true - }, "deployment": { + "pcName": "", + "pcDescription": "", "timezone": "Central Europe Standard Time", - "locale": "cs-CZ" + "profileType": "default" }, "adminAccount": { - "username": "adminx9", - "password": "AdminX9.AdminX9", - "description": "X9 MSP admin account" + "username": "adminx9" }, "activation": { - "productKey": "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX", - "kmsServer": "" + "productKey": "" }, "software": { "install": [ - { "name": "7-Zip", "wingetId": "7zip.7zip" }, - { "name": "Adobe Acrobat Reader","wingetId": "Adobe.Acrobat.Reader.64-bit" }, - { "name": "OpenVPN Connect", "wingetId": "OpenVPNTechnologies.OpenVPNConnect" } + { "name": "7-Zip", "wingetId": "7zip.7zip" }, + { "name": "Adobe Acrobat Reader", "wingetId": "Adobe.Acrobat.Reader.64-bit" }, + { "name": "OpenVPN Connect", "wingetId": "OpenVPNTechnologies.OpenVPNConnect" } ] }, + "steps": { + "adminAccount": true, + "activation": true, + "bloatware": true, + "software": true, + "systemRegistry": true, + "defaultProfile": true, + "backinfo": true, + "network": true, + "dellUpdate": true, + "pcIdentity": true, + "windowsUpdate": true + }, + "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 + } + }, "bloatware": { "keepPackages": [ "Microsoft.WindowsCalculator" ] - }, - "desktopInfo": { - "enabled": true, - "position": "bottomRight", - "fontSize": 12, - "fontColor": "#FFFFFF", - "backgroundColor": "transparent" - }, - "pdfDefault": { - "forceAdobeReader": true, - "scheduledTaskEnabled": true } } diff --git a/internal/config/config.go b/internal/config/config.go index 988fbea..a4588a0 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -67,19 +67,17 @@ func DefaultConfig() Config { }, }, Steps: map[string]bool{ - "adminAccount": true, - "bloatware": true, - "software": true, - "systemRegistry": true, - "defaultProfile": true, - "personalization": true, - "scheduledTasks": true, - "backinfo": true, - "activation": true, - "dellUpdate": true, - "windowsUpdate": true, - "network": true, - "pcIdentity": true, + "adminAccount": true, + "activation": true, + "bloatware": true, + "software": true, + "systemRegistry": true, + "defaultProfile": true, + "backinfo": true, + "network": true, + "dellUpdate": true, + "pcIdentity": true, + "windowsUpdate": true, }, Features: Features{ "software": { diff --git a/internal/gui/gui.go b/internal/gui/gui.go index 9964ff2..688c277 100644 --- a/internal/gui/gui.go +++ b/internal/gui/gui.go @@ -51,6 +51,8 @@ func Run(cfg config.Config, runCfg runner.RunConfig, cfgPath string) { cfgPath = res.cfgPath case "run": runCfg.ProfileType = res.cfg.Deployment.ProfileType + // Update runtime config so scripts see the user's GUI selections + _ = config.Save(res.cfg, runCfg.ConfigPath) results, needsReboot := runPhase(runCfg, res.steps, false) if needsReboot { prepareRebootAndRestart(res.cfg, res.steps, results, cfgPath, runCfg) diff --git a/internal/runner/runner.go b/internal/runner/runner.go index ed88355..1628fc7 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -30,21 +30,21 @@ type Step struct { } // AllSteps returns the ordered list of deployment steps. +// Order matters: activation early (unlocks features), pcIdentity late (rename +// needs reboot), windowsUpdate last (reboot cycle). func AllSteps() []Step { return []Step{ - {ID: "adminAccount", Num: "00", Name: "Admin ucet", ScriptName: "00-admin-account.ps1"}, - {ID: "bloatware", Num: "01", Name: "Bloatware removal", ScriptName: "01-bloatware.ps1"}, - {ID: "software", Num: "02", Name: "Software (winget)", ScriptName: "02-software.ps1"}, - {ID: "systemRegistry", Num: "03", Name: "System Registry (HKLM)", ScriptName: "03-system-registry.ps1"}, - {ID: "defaultProfile", Num: "04", Name: "Default Profile", ScriptName: "04-default-profile.ps1"}, - {ID: "personalization", Num: "05", Name: "Personalizace", ScriptName: "05-personalization.ps1"}, - {ID: "scheduledTasks", Num: "06", Name: "Scheduled Tasks", ScriptName: "06-scheduled-tasks.ps1"}, - {ID: "backinfo", Num: "07", Name: "BackInfo", ScriptName: "07-backinfo.ps1"}, - {ID: "activation", Num: "08", Name: "Windows aktivace", ScriptName: "08-activation.ps1"}, - {ID: "pcIdentity", Num: "09", Name: "PC identita", ScriptName: "09-pc-identity.ps1"}, - {ID: "network", Num: "10", Name: "Network discovery", ScriptName: "10-network.ps1"}, - {ID: "dellUpdate", Num: "11", Name: "Dell Command | Update", ScriptName: "11-dell-update.ps1"}, - {ID: "windowsUpdate", Num: "12", Name: "Windows Update", ScriptName: "12-windows-update.ps1"}, + {ID: "adminAccount", Num: "00", Name: "Admin ucet", ScriptName: "00-admin-account.ps1"}, + {ID: "activation", Num: "08", Name: "Windows aktivace", ScriptName: "08-activation.ps1"}, + {ID: "bloatware", Num: "01", Name: "Bloatware removal", ScriptName: "01-bloatware.ps1"}, + {ID: "software", Num: "02", Name: "Software (winget)", ScriptName: "02-software.ps1"}, + {ID: "systemRegistry", Num: "03", Name: "System Registry (HKLM)", ScriptName: "03-system-registry.ps1"}, + {ID: "defaultProfile", Num: "04", Name: "Profil + personalizace", ScriptName: "04-default-profile.ps1"}, + {ID: "backinfo", Num: "07", Name: "BackInfo", ScriptName: "07-backinfo.ps1"}, + {ID: "network", Num: "10", Name: "Network discovery", ScriptName: "10-network.ps1"}, + {ID: "dellUpdate", Num: "11", Name: "Dell Command | Update", ScriptName: "11-dell-update.ps1"}, + {ID: "pcIdentity", Num: "09", Name: "PC identita", ScriptName: "09-pc-identity.ps1"}, + {ID: "windowsUpdate", Num: "12", Name: "Windows Update", ScriptName: "12-windows-update.ps1"}, } } @@ -228,9 +228,9 @@ func (r *Runner) runScript(ctx context.Context, step Step, cfgArg string) error "-LogFile", r.cfg.LogFile, } - // Pass config object as JSON string (script reads it inline) + // Pass config path - script loads JSON itself via common.ps1 Load-Config if cfgArg != "" { - args = append(args, "-Config", fmt.Sprintf("(Get-Content '%s' | ConvertFrom-Json)", cfgArg)) + args = append(args, "-ConfigPath", cfgArg) } // ProfileType for step 04 diff --git a/scripts/00-admin-account.ps1 b/scripts/00-admin-account.ps1 index 82d9bb3..b5081f5 100644 --- a/scripts/00-admin-account.ps1 +++ b/scripts/00-admin-account.ps1 @@ -18,18 +18,12 @@ fullname-x9-cz-s-r-o-via-adsi: Sets FullName property via [ADSI] so the account shows as "X9.cz s.r.o." in User Accounts panel, Event Viewer, and audit logs. #> param( - [object]$Config, + [string]$ConfigPath, [string]$LogFile ) -$ErrorActionPreference = "Continue" - -function Write-Log { - param([string]$Message, [string]$Level = "INFO") - $line = "[$(Get-Date -Format 'HH:mm:ss')] [$Level] $Message" - $null = New-Item -ItemType Directory -Force -Path (Split-Path $LogFile -Parent) -ErrorAction SilentlyContinue - Add-Content -Path $LogFile -Value $line -Encoding UTF8 -} +. "$PSScriptRoot\common.ps1" +$Config = Load-Config $ConfigPath # ----------------------------------------------------------------------- # Account config - no password by design diff --git a/scripts/01-bloatware.ps1 b/scripts/01-bloatware.ps1 index fe9108b..deb4750 100644 --- a/scripts/01-bloatware.ps1 +++ b/scripts/01-bloatware.ps1 @@ -14,18 +14,12 @@ windows-optional-features-ps-2-0-mediapl: Disabled via Disable-WindowsOptionalFeature: PowerShell 2.0 (security risk - allows unsigned script execution bypass on older hosts), MediaPlayback, Windows Recall (AI screenshot surveillance), Snipping Tool optional component. #> param( - [object]$Config, + [string]$ConfigPath, [string]$LogFile ) -$ErrorActionPreference = "Continue" - -function Write-Log { - param([string]$Message, [string]$Level = "INFO") - $line = "[$(Get-Date -Format 'HH:mm:ss')] [$Level] $Message" - $null = New-Item -ItemType Directory -Force -Path (Split-Path $LogFile -Parent) -ErrorAction SilentlyContinue - Add-Content -Path $LogFile -Value $line -Encoding UTF8 -} +. "$PSScriptRoot\common.ps1" +$Config = Load-Config $ConfigPath # ----------------------------------------------------------------------- # 1a - AppX packages diff --git a/scripts/02-software.ps1 b/scripts/02-software.ps1 index a74af8b..553874a 100644 --- a/scripts/02-software.ps1 +++ b/scripts/02-software.ps1 @@ -19,30 +19,12 @@ ucpd-sys-kernel-driver-od-feb-2024-bloku: UCPD.sys (User Choice Protection Driver) is stopped before the PDF association write and restarted after. Pattern: Stop-Service ucpd -> set HKCR\.pdf -> Start-Service ucpd. Implemented in this script. #> param( - [object]$Config, + [string]$ConfigPath, [string]$LogFile ) -$ErrorActionPreference = "Continue" - -function Write-Log { - param([string]$Message, [string]$Level = "INFO") - $line = "[$(Get-Date -Format 'HH:mm:ss')] [$Level] $Message" - $null = New-Item -ItemType Directory -Force -Path (Split-Path $LogFile -Parent) -ErrorAction SilentlyContinue - 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 } -} +. "$PSScriptRoot\common.ps1" +$Config = Load-Config $ConfigPath # ----------------------------------------------------------------------- # Check winget availability @@ -195,15 +177,20 @@ if (Get-Feature $Config "software" "ateraAgent") { 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 - - $ateraExe = "$env:ProgramFiles\ATERA Networks\AteraAgent\AteraAgent.exe" - if (Test-Path $ateraExe) { - Write-Log " Atera agent installed" -Level OK + $msiProc = Start-Process msiexec -ArgumentList "/i `"$ateraMsi`" /qn" -Wait -PassThru + if ($msiProc.ExitCode -eq 0) { + Write-Log " Atera agent installed (msiexec exit 0)" -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 + Write-Log " Atera agent install exit code: $($msiProc.ExitCode)" -Level WARN + } + + # Verify binary exists + $ateraExe = "$env:ProgramFiles\ATERA Networks\AteraAgent\AteraAgent.exe" + $ateraExe86 = "${env:ProgramFiles(x86)}\ATERA Networks\AteraAgent\AteraAgent.exe" + if ((Test-Path $ateraExe) -or (Test-Path $ateraExe86)) { + Write-Log " Atera agent binary verified" -Level OK + } else { + Write-Log " Atera agent binary not found at expected paths" -Level WARN } } catch { diff --git a/scripts/03-system-registry.ps1 b/scripts/03-system-registry.ps1 index 93b7176..036623f 100644 --- a/scripts/03-system-registry.ps1 +++ b/scripts/03-system-registry.ps1 @@ -28,30 +28,12 @@ proxy-auto-detect-zakaz-autodetect-0: HKLM\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings\AutoDetect = 0. Disables WPAD (Web Proxy Auto-Discovery). Eliminates startup delays from WPAD DNS lookup and prevents MITM via rogue WPAD on untrusted networks. #> param( - [object]$Config, + [string]$ConfigPath, [string]$LogFile ) -$ErrorActionPreference = "Continue" - -function Write-Log { - param([string]$Message, [string]$Level = "INFO") - $line = "[$(Get-Date -Format 'HH:mm:ss')] [$Level] $Message" - $null = New-Item -ItemType Directory -Force -Path (Split-Path $LogFile -Parent) -ErrorAction SilentlyContinue - 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 } -} +. "$PSScriptRoot\common.ps1" +$Config = Load-Config $ConfigPath Add-Type -TypeDefinition @" using System; @@ -310,62 +292,60 @@ if (Get-Feature $Config "systemRegistry" "systemTweaks") { # ----------------------------------------------------------------------- if (Get-Feature $Config "systemRegistry" "edgePolicies") { Write-Log " Applying Edge policies" -Level INFO - $edgePath = "HKLM:\SOFTWARE\Policies\Microsoft\Edge" + $edgeMandatory = "HKLM:\SOFTWARE\Policies\Microsoft\Edge" + $edgeRecommended = "HKLM:\SOFTWARE\Policies\Microsoft\Edge\Recommended" - # UI / first run - Set-Reg -Path $edgePath -Name "HideFirstRunExperience" -Value 1 - Set-Reg -Path $edgePath -Name "DefaultBrowserSettingEnabled" -Value 0 + # --- Mandatory (user cannot override, locked in Edge UI) --- + # First run / default browser nag + Set-Reg -Path $edgeMandatory -Name "HideFirstRunExperience" -Value 1 + Set-Reg -Path $edgeMandatory -Name "DefaultBrowserSettingEnabled" -Value 0 - # New tab page - disable all visual clutter - Set-Reg -Path $edgePath -Name "NewTabPageContentEnabled" -Value 0 # feed / obsah - Set-Reg -Path $edgePath -Name "NewTabPageQuickLinksEnabled" -Value 0 # rychle odkazy - Set-Reg -Path $edgePath -Name "NewTabPageBackgroundEnabled" -Value 0 # pozadi - Set-Reg -Path $edgePath -Name "NewTabPageAllowedBackgroundTypes" -Value 3 # 3 = only solid color - 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" + # Telemetry / feedback - always off + Set-Reg -Path $edgeMandatory -Name "DiagnosticData" -Value 0 + Set-Reg -Path $edgeMandatory -Name "FeedbackSurveysEnabled" -Value 0 # Disable desktop shortcut on install/update Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\EdgeUpdate" ` -Name "CreateDesktopShortcutDefault" -Value 0 + + # --- Recommended (sets default, user can change in Edge settings) --- + # New tab page - clean defaults + Set-Reg -Path $edgeRecommended -Name "NewTabPageContentEnabled" -Value 0 + Set-Reg -Path $edgeRecommended -Name "NewTabPageQuickLinksEnabled" -Value 0 + Set-Reg -Path $edgeRecommended -Name "NewTabPageBackgroundEnabled" -Value 0 + Set-Reg -Path $edgeRecommended -Name "NewTabPageAllowedBackgroundTypes" -Value 3 + Set-Reg -Path $edgeRecommended -Name "ShowRecommendationsEnabled" -Value 0 + Set-Reg -Path $edgeRecommended -Name "SpotlightExperiencesAndRecommendationsEnabled" -Value 0 + Set-Reg -Path $edgeRecommended -Name "PersonalizationReportingEnabled" -Value 0 + + # Shopping / rewards / sidebar + Set-Reg -Path $edgeRecommended -Name "EdgeShoppingAssistantEnabled" -Value 0 + Set-Reg -Path $edgeRecommended -Name "ShowMicrosoftRewards" -Value 0 + Set-Reg -Path $edgeRecommended -Name "HubsSidebarEnabled" -Value 0 + Set-Reg -Path $edgeRecommended -Name "EdgeCollectionsEnabled" -Value 0 + + # Search suggestions / import + Set-Reg -Path $edgeRecommended -Name "SearchSuggestEnabled" -Value 0 + Set-Reg -Path $edgeRecommended -Name "ImportOnEachLaunch" -Value 0 + + # Toolbar buttons - show + Set-Reg -Path $edgeRecommended -Name "FavoritesBarEnabled" -Value 1 + Set-Reg -Path $edgeRecommended -Name "DownloadsButtonEnabled" -Value 1 + Set-Reg -Path $edgeRecommended -Name "HistoryButtonEnabled" -Value 1 + Set-Reg -Path $edgeRecommended -Name "PerformanceButtonEnabled" -Value 1 + + # Toolbar buttons - hide + Set-Reg -Path $edgeRecommended -Name "HomeButtonEnabled" -Value 0 + Set-Reg -Path $edgeRecommended -Name "SplitScreenEnabled" -Value 0 + Set-Reg -Path $edgeRecommended -Name "EdgeEDropEnabled" -Value 0 + Set-Reg -Path $edgeRecommended -Name "WebCaptureEnabled" -Value 0 + Set-Reg -Path $edgeRecommended -Name "ShareAllowed" -Value 0 + + # Default search engine: Google + Set-Reg -Path $edgeRecommended -Name "DefaultSearchProviderEnabled" -Value 1 -Type "DWord" + Set-Reg -Path $edgeRecommended -Name "DefaultSearchProviderName" -Value "Google" -Type "String" + Set-Reg -Path $edgeRecommended -Name "DefaultSearchProviderSearchURL" ` + -Value "https://www.google.com/search?q={searchTerms}" -Type "String" } else { Write-Log "edgePolicies feature disabled - skipping" -Level INFO } diff --git a/scripts/04-default-profile.ps1 b/scripts/04-default-profile.ps1 index 6295d82..943d252 100644 --- a/scripts/04-default-profile.ps1 +++ b/scripts/04-default-profile.ps1 @@ -1,63 +1,48 @@ <# .SYNOPSIS - Applies registry settings to the Default User profile and the current logged-in user. + Applies registry settings to the Default User profile, current user, and sets visual theme. .DESCRIPTION Loads C:\Users\Default\NTUSER.DAT as a temporary hive (HKU\DefaultProfile), applies - all settings, then unloads it. Every new user account created on this machine inherits - these settings on first logon. The same settings are applied directly to the current - user's HKCU. Does NOT block OneDrive re-launch - the Explorer namespace CLSID and RunOnce entries have been removed. + all taskbar, Start menu, Explorer, and personalization settings, then unloads it. + Every new user account inherits these settings on first logon. The same settings are + applied directly to the current user's HKCU. Visual identity: dark taskbar/Start with + accent color #223B47 (deep blue-gray), light app mode, no transparency. Wallpaper is + set to a solid color matching the accent - BackInfo.exe overwrites it on every logon. .ITEMS - taskbar-zarovnat-vlevo-taskbaral-0: TaskbarAl = 0 in Explorer\Advanced. Windows 11 default is center-aligned (TaskbarAl = 1). Left alignment matches Windows 10 muscle memory and is strongly preferred by business users transitioning from Win10. - taskbar-skryt-search-copilot-task-view-w: Hides Search box (SearchboxTaskbarMode=0), Copilot button (ShowCopilotButton=0), Task View (ShowTaskViewButton=0), Widgets (TaskbarDa=0), Chat/Teams (TaskbarMn=0). Reduces taskbar clutter to just pinned apps and running processes. - taskbar-zobrazit-vsechny-ikonky-v-tray-s: Registers scheduled task that sets EnableAutoTray=0 on logon (repeat every 1 min). Windows 11 periodically re-hides tray icons - this task forces all icons visible so users can see VPN status, antivirus, backup, etc. - taskbar-vyprazdnit-pinlist-taskbarlayout: Deploys TaskbarLayoutModification.xml. ProfileType=default: empty pins (clean slate). ProfileType=admin: Explorer+PowerShell+Edge. ProfileType=user: Explorer+Edge. Lock is removed by UnlockStartLayout task 5 min after first boot so users can customize. - explorer-zobrazovat-pripony-souboru-hide: HideFileExt = 0 in Explorer\Advanced. Shows file extensions (.docx, .exe, .pdf, .ps1) in File Explorer. Essential for recognizing file types, avoiding phishing (fake .pdf.exe), and general IT work. - explorer-otevrit-na-this-pc-launchto-1: LaunchTo = 1. File Explorer opens to "This PC" (drives view) instead of Quick Access. More useful on fresh machines where Quick Access history is empty and irrelevant. - start-menu-vyprazdnit-piny-win11: ConfigureStartPins = {"pinnedList":[]} applied via registry. Removes all default Start menu tiles (Edge, Teams, Store, Office, Solitaire, etc.) from the Windows 11 Start grid. User starts with an empty, clean Start menu. - start-menu-zakaz-bing-vyhledavani: DisableSearchBoxSuggestions = 1 in Software\Policies\Microsoft\Windows. Disables web search, Bing suggestions, and online results in Start menu search. Search returns only local apps, files, and settings. - copilot-zakaz-turnoffwindowscopilot-1: TurnOffWindowsCopilot = 1 in SOFTWARE\Policies\Microsoft\Windows\WindowsCopilot. Disables the Windows Copilot sidebar entirely. Not suitable for most client environments (data privacy, AI usage policies). - numlock-zapnout-pri-startu-initialkeyboa: InitialKeyboardIndicators = 2 in Default profile. Ensures NumLock is enabled when Windows starts. Standard expectation for users working with numeric data - prevents confusion on data entry. - accent-barva-na-titulnich-listech-colorp: ColorPrevalence = 1 in Personalize key. Shows the X9.cz accent color (#223B47) on window title bars and borders. Gives all windows a consistent branded appearance. - onedrive-runonce-klic-je-tady-smazat: REMOVED. The RunOnce key deletion and Explorer namespace CLSID removal were deleted - those registry tweaks prevented a freshly installed OneDrive (e.g. for M365) from launching. OneDrive AppX uninstall in step 01 is intentional; blocking re-launch is not. - explorer-showrecent-0-showfrequent-0: ShowRecent=0 and ShowFrequent=0 in HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer. Hides Recent files and Frequent folders from Quick Access. Privacy improvement and cleaner File Explorer on fresh deployments. - explorer-fullpath-1-cabinetstate: FullPath=1 in HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\CabinetState. Displays the full directory path (e.g. C:\Users\jan\Documents\Projekty) in the File Explorer title bar instead of just the folder name. + taskbar-zarovnat-vlevo-taskbaral-0: TaskbarAl = 0 in Explorer\Advanced. Left alignment matches Windows 10 muscle memory. + taskbar-skryt-search-copilot-task-view-w: Hides Search box, Copilot, Task View, Widgets, Chat/Teams buttons. + taskbar-zobrazit-vsechny-ikonky-v-tray: EnableAutoTray=0 and TrayNotify icon streams cleared. + taskbar-vyprazdnit-pinlist-taskbarlayout: Deploys TaskbarLayoutModification.xml per ProfileType. + explorer-zobrazovat-pripony-souboru-hide: HideFileExt = 0. Shows file extensions in Explorer. + explorer-otevrit-na-this-pc-launchto-1: LaunchTo = 1. Explorer opens to This PC. + explorer-showrecent-0-showfrequent-0: ShowRecent=0, ShowFrequent=0. Hides recent/frequent from Quick Access. + explorer-fullpath-1-cabinetstate: FullPath=1 in CabinetState. Full path in Explorer title bar. + start-menu-vyprazdnit-piny-win11: ConfigureStartPins = {"pinnedList":[]}. Empty Start menu grid. + start-menu-zakaz-bing-vyhledavani: DisableSearchBoxSuggestions = 1. Local search only. + copilot-zakaz-turnoffwindowscopilot-1: TurnOffWindowsCopilot = 1. Disables Copilot sidebar. + numlock-zapnout-pri-startu: InitialKeyboardIndicators = 2. + system-tema-taskbar-start-dark: SystemUsesLightTheme=0. Dark shell, light apps. + accent-barva-223b47: AccentColor 0xFF473B22, ColorPrevalence=1, taskbar/Start branded. + pruhlednost-vypnuta: EnableTransparency=0. + tapeta-jednobarevna-223b47: Solid color #223B47 via SystemParametersInfo. BackInfo overwrites on logon. #> param( - [object]$Config, + [string]$ConfigPath, [string]$LogFile, [ValidateSet("default","admin","user")] [string]$ProfileType = "default" ) -$ErrorActionPreference = "Continue" - -function Write-Log { - param([string]$Message, [string]$Level = "INFO") - $line = "[$(Get-Date -Format 'HH:mm:ss')] [$Level] $Message" - $null = New-Item -ItemType Directory -Force -Path (Split-Path $LogFile -Parent) -ErrorAction SilentlyContinue - 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 } -} +. "$PSScriptRoot\common.ps1" +$Config = Load-Config $ConfigPath # ----------------------------------------------------------------------- -# Helper - apply a registry setting to both Default hive and current HKCU +# Helpers - apply registry to both Default hive and current HKCU # ----------------------------------------------------------------------- function Grant-HiveWriteAccess { - param([string]$HivePath) # full path e.g. "Registry::HKU\DefaultProfile\Software\..." - # Grants Administrators FullControl on a loaded hive key with restricted ACL. + param([string]$HivePath) try { $acl = Get-Acl -Path $HivePath -ErrorAction Stop $rule = New-Object System.Security.AccessControl.RegistryAccessRule( @@ -77,7 +62,7 @@ function Grant-HiveWriteAccess { function Set-ProfileReg { param( - [string]$SubKey, # relative to HKCU (e.g. "Software\Microsoft\...") + [string]$SubKey, [string]$Name, $Value, [string]$Type = "DWord" @@ -92,7 +77,6 @@ function Set-ProfileReg { Set-ItemProperty -Path $defPath -Name $Name -Value $Value -Type $Type -Force -ErrorAction Stop } catch { - # Retry after granting write access to parent key try { $parentPath = $defPath -replace '\\[^\\]+$', '' if (Test-Path $parentPath) { Grant-HiveWriteAccess -HivePath $parentPath } @@ -120,28 +104,8 @@ function Set-ProfileReg { } } -function Remove-ProfileReg { - param([string]$SubKey, [string]$Name) - - $defPath = "Registry::HKU\DefaultProfile\$SubKey" - try { - if (Test-Path $defPath) { - Remove-ItemProperty -Path $defPath -Name $Name -Force -ErrorAction SilentlyContinue - } - } - catch { } - - $hkcuPath = "HKCU:\$SubKey" - try { - if (Test-Path $hkcuPath) { - Remove-ItemProperty -Path $hkcuPath -Name $Name -Force -ErrorAction SilentlyContinue - Write-Log " REMOVED $SubKey\$Name" -Level OK - } - } - catch { - Write-Log " FAILED removing $SubKey\$Name - $_" -Level ERROR - } -} +# Accent color #223B47 stored as ABGR DWORD: 0xFF473B22 +$AccentColorABGR = 0xFF473B22 # ----------------------------------------------------------------------- # Load Default profile hive @@ -162,41 +126,33 @@ if ($LASTEXITCODE -ne 0) { Write-Log "Default hive loaded" -Level OK try { - # ----------------------------------------------------------------------- - # Taskbar tweaks (alignment, buttons, tray, layout XML) - # ----------------------------------------------------------------------- + # =================================================================== + # TASKBAR TWEAKS + # =================================================================== if (Get-Feature $Config "defaultProfile" "taskbarTweaks") { Write-Log "Applying taskbar tweaks" -Level STEP $tbPath = "Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" - # Win11: align taskbar to left (0 = left, 1 = center) + # Win11: align taskbar to left 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 + # Hide Search box - set both Win10 and Win11 locations Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Search" ` -Name "SearchboxTaskbarMode" -Value 0 Set-ProfileReg -SubKey $tbPath -Name "SearchboxTaskbarMode" -Value 0 - # Hide Task View button + # Hide Task View, Widgets, Chat/Teams, Copilot buttons Set-ProfileReg -SubKey $tbPath -Name "ShowTaskViewButton" -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 Copilot button Set-ProfileReg -SubKey $tbPath -Name "ShowCopilotButton" -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 - # Win11 workaround: clear cached tray icon streams so all icons appear on next login + # Win11: clear cached tray icon streams $trayNotifyKey = "HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify" if (Test-Path $trayNotifyKey) { Remove-ItemProperty -Path $trayNotifyKey -Name "IconStreams" -Force -ErrorAction SilentlyContinue @@ -211,21 +167,12 @@ try { } # 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 - # 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. + # Taskbar pinned apps layout Write-Log " Writing taskbar layout (ProfileType=$ProfileType)" -Level INFO - # Ensure File Explorer shortcut exists in Default profile's Start Menu. - # On a clean Windows 11 install the System Tools folder may be missing - # from C:\Users\Default\AppData\Roaming - without it the XML pin is silently skipped. $wsh = New-Object -ComObject WScript.Shell $defRoaming = "C:\Users\Default\AppData\Roaming\Microsoft\Windows\Start Menu\Programs" @@ -239,7 +186,6 @@ try { Write-Log " Created File Explorer.lnk in Default profile Start Menu" -Level OK } - # Same for PowerShell (admin profile) if ($ProfileType -eq "admin") { $psLnkDir = "$defRoaming\Windows PowerShell" $psLnk = "$psLnkDir\Windows PowerShell.lnk" @@ -263,12 +209,6 @@ try { -'@ - } - "user" { -@' - - '@ } default { @@ -276,7 +216,7 @@ try { '@ - } # explicit pins with Replace = no Store, no other defaults + } } $taskbarLayoutXml = @" @@ -302,43 +242,33 @@ $pinList # 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) - # ----------------------------------------------------------------------- + # =================================================================== + # START MENU TWEAKS + # =================================================================== 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" ` @@ -347,34 +277,72 @@ $pinList Write-Log "startMenuTweaks feature disabled - skipping" -Level INFO } - # ----------------------------------------------------------------------- - # Explorer tweaks (file extensions, LaunchTo, ShowRecent, FullPath) - # ----------------------------------------------------------------------- + # =================================================================== + # EXPLORER TWEAKS + # =================================================================== 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 } + + # =================================================================== + # PERSONALIZATION (theme, accent color, wallpaper) + # =================================================================== + Write-Log "Applying personalization (theme, accent, wallpaper)" -Level STEP + + # Dark shell (taskbar, Start), light apps + Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Themes\Personalize" ` + -Name "SystemUsesLightTheme" -Value 0 + Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Themes\Personalize" ` + -Name "AppsUseLightTheme" -Value 1 + + # Accent color on Start and taskbar + Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Themes\Personalize" ` + -Name "ColorPrevalence" -Value 1 + + # Transparency disabled + Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Themes\Personalize" ` + -Name "EnableTransparency" -Value 0 + + # Accent color #223B47 + Set-ProfileReg -SubKey "Software\Microsoft\Windows\DWM" ` + -Name "AccentColor" -Value $AccentColorABGR -Type "DWord" + Set-ProfileReg -SubKey "Software\Microsoft\Windows\DWM" ` + -Name "ColorizationColor" -Value $AccentColorABGR -Type "DWord" + Set-ProfileReg -SubKey "Software\Microsoft\Windows\DWM" ` + -Name "ColorizationAfterglow" -Value $AccentColorABGR -Type "DWord" + Set-ProfileReg -SubKey "Software\Microsoft\Windows\DWM" ` + -Name "ColorPrevalence" -Value 1 + + # Taskbar accent color (Explorer\Accent, not DWM) + Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer\Accent" ` + -Name "AccentColorMenu" -Value $AccentColorABGR -Type "DWord" + Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer\Accent" ` + -Name "StartColorMenu" -Value $AccentColorABGR -Type "DWord" + + # Wallpaper - solid color #223B47 (BackInfo overwrites on logon) + Set-ProfileReg -SubKey "Control Panel\Colors" ` + -Name "Background" -Value "34 59 71" -Type "String" + Set-ProfileReg -SubKey "Control Panel\Desktop" ` + -Name "Wallpaper" -Value "" -Type "String" + Set-ProfileReg -SubKey "Control Panel\Desktop" ` + -Name "WallpaperStyle" -Value "0" -Type "String" + Set-ProfileReg -SubKey "Control Panel\Desktop" ` + -Name "TileWallpaper" -Value "0" -Type "String" } finally { # ----------------------------------------------------------------------- @@ -394,17 +362,23 @@ finally { } # ----------------------------------------------------------------------- -# Restart Explorer to apply taskbar/tray changes to current session +# Apply wallpaper (solid color) to current desktop session # ----------------------------------------------------------------------- -Write-Log "Restarting Explorer to apply taskbar changes" -Level INFO +Write-Log "Setting desktop wallpaper to solid color" -Level INFO try { - Stop-Process -Name explorer -Force -ErrorAction SilentlyContinue - Start-Sleep -Seconds 2 - Start-Process explorer - Write-Log "Explorer restarted" -Level OK + Add-Type -TypeDefinition @" +using System; +using System.Runtime.InteropServices; +public class WallpaperHelper { + [DllImport("user32.dll", CharSet=CharSet.Auto)] + public static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni); +} +"@ -ErrorAction SilentlyContinue + [WallpaperHelper]::SystemParametersInfo(20, 0, "", 3) | Out-Null + Write-Log " Desktop wallpaper updated" -Level OK } catch { - Write-Log "Explorer restart failed (non-fatal): $_" -Level WARN + Write-Log " Failed to update wallpaper: $_" -Level WARN } Write-Log "Step 4 complete" -Level OK diff --git a/scripts/05-personalization.ps1 b/scripts/05-personalization.ps1 deleted file mode 100644 index aa879cd..0000000 --- a/scripts/05-personalization.ps1 +++ /dev/null @@ -1,208 +0,0 @@ -<# -.SYNOPSIS - Sets system colors, wallpaper, and visual theme. - -.DESCRIPTION - Applies X9.cz visual identity: dark taskbar/Start with accent color #223B47 - (deep blue-gray), light app mode, no transparency. Wallpaper is set to a solid - color matching the accent. BackInfo.exe (Step 07) overwrites the wallpaper with - a live system info BMP on every logon - solid color is only the fallback. - -.ITEMS - system-tema-taskbar-start-dark: SystemUsesLightTheme=0 in Themes\Personalize. Dark mode for shell (taskbar, Start menu, Action Center, notification area). Does NOT affect application windows - those stay light. Reduces eye strain in dim environments. - aplikacni-tema-light: AppsUseLightTheme=1. Application windows (File Explorer, Settings, Calculator, etc.) use white/light backgrounds. Majority of business applications (Office, browsers) also respect this and show light mode. - accent-barva-223b47-tmave-modroseda: AccentColor DWORD = 0xFF473B22 (stored as ABGR: A=FF, B=47, G=3B, R=22). The deep blue-gray #223B47 is the X9.cz brand color, also used as the solid wallpaper background. - accent-barva-na-start-a-taskbaru-ano: ColorPrevalence=1. Applies accent color to taskbar background and Start menu surface. The taskbar becomes the brand color instead of default black, creating a distinct recognizable look on X9.cz-deployed machines. - pruhlednost-vypnuta: EnableTransparency=0. Disables Aero translucency on taskbar and Start. Improves text readability on the taskbar, reduces subtle GPU usage, and looks more professional/consistent on business machines. - tapeta-jednobarevna-223b47-bez-obrazku: Wallpaper set to solid color #223B47 via SystemParametersInfo(SPI_SETDESKWALLPAPER). BackInfo.exe generates a BMP with hostname, username, OS, network info and sets it as wallpaper on every logon. Solid color = fallback only. -#> -param( - [object]$Config, - [string]$LogFile -) - -$ErrorActionPreference = "Continue" - -function Write-Log { - param([string]$Message, [string]$Level = "INFO") - $line = "[$(Get-Date -Format 'HH:mm:ss')] [$Level] $Message" - $null = New-Item -ItemType Directory -Force -Path (Split-Path $LogFile -Parent) -ErrorAction SilentlyContinue - Add-Content -Path $LogFile -Value $line -Encoding UTF8 -} - -# Accent color #223B47 stored as ABGR DWORD: 0xFF473B22 -# A=FF B=47 G=3B R=22 -> 0xFF473B22 = 4283612962 -$AccentColorABGR = 0xFF473B22 - -# Gradient colors (Windows generates these automatically but we set them explicitly) -# AccentPalette is 32 bytes - 8 shades of the accent color (BGRA each) -# We use the same color for all shades as a safe default -$AccentColorHex = "#223B47" - -function Set-Reg { - param([string]$Path, [string]$Name, $Value, [string]$Type = "DWord") - try { - if (-not (Test-Path $Path)) { New-Item -Path $Path -Force | Out-Null } - Set-ItemProperty -Path $Path -Name $Name -Value $Value -Type $Type -Force - Write-Log " SET $Path\$Name = $Value" -Level OK - } - catch { - Write-Log " FAILED $Path\$Name - $_" -Level ERROR - } -} - -function Apply-ThemeSettings { - param([string]$HiveRoot) # "HKCU:" or "Registry::HKU\DefaultProfile" - - # ----------------------------------------------------------------------- - # System theme - Dark (taskbar, Start, action center) - # ----------------------------------------------------------------------- - Set-Reg -Path "$HiveRoot\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize" ` - -Name "SystemUsesLightTheme" -Value 0 - - # ----------------------------------------------------------------------- - # App theme - Light - # ----------------------------------------------------------------------- - Set-Reg -Path "$HiveRoot\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize" ` - -Name "AppsUseLightTheme" -Value 1 - - # ----------------------------------------------------------------------- - # Accent color on Start and taskbar - # ----------------------------------------------------------------------- - Set-Reg -Path "$HiveRoot\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize" ` - -Name "ColorPrevalence" -Value 1 - - # ----------------------------------------------------------------------- - # Transparency effects - disabled - # ----------------------------------------------------------------------- - Set-Reg -Path "$HiveRoot\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize" ` - -Name "EnableTransparency" -Value 0 - - # ----------------------------------------------------------------------- - # Accent color - # ----------------------------------------------------------------------- - Set-Reg -Path "$HiveRoot\Software\Microsoft\Windows\DWM" ` - -Name "AccentColor" -Value $AccentColorABGR -Type "DWord" - - Set-Reg -Path "$HiveRoot\Software\Microsoft\Windows\DWM" ` - -Name "ColorizationColor" -Value $AccentColorABGR -Type "DWord" - - Set-Reg -Path "$HiveRoot\Software\Microsoft\Windows\DWM" ` - -Name "ColorizationAfterglow" -Value $AccentColorABGR -Type "DWord" - - # Accent color on title bars and borders - Set-Reg -Path "$HiveRoot\Software\Microsoft\Windows\DWM" ` - -Name "ColorPrevalence" -Value 1 - - # ----------------------------------------------------------------------- - # Accent color for taskbar - # Windows taskbar reads AccentColorMenu from Explorer\Accent, NOT DWM\AccentColor. - # Without this key the taskbar keeps the Windows default accent (light blue). - # ----------------------------------------------------------------------- - Set-Reg -Path "$HiveRoot\Software\Microsoft\Windows\CurrentVersion\Explorer\Accent" ` - -Name "AccentColorMenu" -Value $AccentColorABGR -Type "DWord" - - Set-Reg -Path "$HiveRoot\Software\Microsoft\Windows\CurrentVersion\Explorer\Accent" ` - -Name "StartColorMenu" -Value $AccentColorABGR -Type "DWord" - - # ----------------------------------------------------------------------- - # Wallpaper - solid color #223B47 (fallback before DesktopInfo runs) - # ----------------------------------------------------------------------- - # Background color as decimal RGB - Set-Reg -Path "$HiveRoot\Control Panel\Colors" ` - -Name "Background" -Value "34 59 71" -Type "String" - - # Empty Wallpaper path = solid color from Background key above. - # Without this, new users created from Default hive inherit a broken/missing - # wallpaper path and Windows falls back to black desktop. - Set-Reg -Path "$HiveRoot\Control Panel\Desktop" ` - -Name "Wallpaper" -Value "" -Type "String" - - Set-Reg -Path "$HiveRoot\Control Panel\Desktop" ` - -Name "WallpaperStyle" -Value "0" -Type "String" - - Set-Reg -Path "$HiveRoot\Control Panel\Desktop" ` - -Name "TileWallpaper" -Value "0" -Type "String" - - # ----------------------------------------------------------------------- - # Desktop icons - show This PC - # ----------------------------------------------------------------------- - Set-Reg -Path "$HiveRoot\Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel" ` - -Name "{20D04FE0-3AEA-1069-A2D8-08002B30309D}" -Value 0 -} - -# ----------------------------------------------------------------------- -# Load Default hive -# ----------------------------------------------------------------------- -$hivePath = "C:\Users\Default\NTUSER.DAT" -$hiveKey = "DefaultProfile" - -Write-Log "Loading Default hive for personalization" -Level INFO - -& reg unload "HKU\$hiveKey" 2>&1 | Out-Null -$loadResult = & reg load "HKU\$hiveKey" $hivePath 2>&1 -if ($LASTEXITCODE -ne 0) { - Write-Log "Failed to load Default hive: $loadResult" -Level ERROR - Write-Log "Applying personalization to current user only" -Level WARN - - Write-Log "Applying theme to current user (HKCU)" -Level STEP - Apply-ThemeSettings -HiveRoot "HKCU:" - - # Set wallpaper via SystemParametersInfo for current user - Add-Type -TypeDefinition @" -using System; -using System.Runtime.InteropServices; -public class WallpaperHelper { - [DllImport("user32.dll", CharSet=CharSet.Auto)] - public static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni); -} -"@ -ErrorAction SilentlyContinue - [WallpaperHelper]::SystemParametersInfo(20, 0, "", 3) | Out-Null - exit 0 -} - -try { - Write-Log "Applying theme to Default hive" -Level STEP - Apply-ThemeSettings -HiveRoot "Registry::HKU\DefaultProfile" - - Write-Log "Applying theme to current user (HKCU)" -Level STEP - Apply-ThemeSettings -HiveRoot "HKCU:" -} -finally { - [GC]::Collect() - [GC]::WaitForPendingFinalizers() - Start-Sleep -Milliseconds 500 - - $unloadResult = & reg unload "HKU\$hiveKey" 2>&1 - if ($LASTEXITCODE -eq 0) { - Write-Log "Default hive unloaded" -Level OK - } else { - Write-Log "Failed to unload Default hive: $unloadResult" -Level ERROR - } -} - -# ----------------------------------------------------------------------- -# Apply wallpaper (solid color) to current desktop session -# ----------------------------------------------------------------------- -Write-Log "Setting desktop wallpaper to solid color" -Level INFO - -try { - Add-Type -TypeDefinition @" -using System; -using System.Runtime.InteropServices; -public class WallpaperHelper { - [DllImport("user32.dll", CharSet=CharSet.Auto)] - public static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni); -} -"@ -ErrorAction SilentlyContinue - - # SPI_SETDESKTOPWALLPAPER=20, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE=3 - # Empty string = solid color defined in Control Panel\Colors\Background - [WallpaperHelper]::SystemParametersInfo(20, 0, "", 3) | Out-Null - Write-Log " Desktop wallpaper updated" -Level OK -} -catch { - Write-Log " Failed to update wallpaper: $_" -Level WARN -} - -Write-Log "Step 5 complete" -Level OK diff --git a/scripts/06-scheduled-tasks.ps1 b/scripts/06-scheduled-tasks.ps1 deleted file mode 100644 index 304c6ee..0000000 --- a/scripts/06-scheduled-tasks.ps1 +++ /dev/null @@ -1,206 +0,0 @@ -<# -.SYNOPSIS - Registers logon scheduled tasks to maintain per-user settings that Windows resets. - -.DESCRIPTION - Creates scheduled tasks under Task Scheduler that run at user logon (and optionally - on a timer) to enforce settings that Windows tends to revert. Tasks are registered - in the Default profile task store so new user accounts inherit them automatically. - Note: PDF-DefaultApp task has been removed - PDF default is set once during deployment. - -.ITEMS - showalltrayicons-pri-logonu-kazdou-1-min: Task 'ShowAllTrayIcons': runs at logon, repeats every 1 minute. Sets HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\EnableAutoTray=0. Windows 11 re-enables auto-hiding of tray icons after updates and sometimes after logon - the 1-min repeat ensures permanent override. - unlockstartlayout-jednou-po-aplikaci-lay: Task 'UnlockStartLayout': runs once, 30 seconds after logon. Clears the Start menu layout lock bit that is set when ConfigureStartPins is applied. Without this, users cannot pin or unpin apps from Start after deployment. - pdf-defaultapp-pri-kazdem-logonu: REMOVED. PDF default is set once during deployment (step 02) with UCPD service stopped. The scheduled task is no longer needed. -#> -param( - [object]$Config, - [string]$LogFile -) - -$ErrorActionPreference = "Continue" - -function Write-Log { - param([string]$Message, [string]$Level = "INFO") - $line = "[$(Get-Date -Format 'HH:mm:ss')] [$Level] $Message" - $null = New-Item -ItemType Directory -Force -Path (Split-Path $LogFile -Parent) -ErrorAction SilentlyContinue - Add-Content -Path $LogFile -Value $line -Encoding UTF8 -} - -$ScriptDir = "C:\Windows\Setup\Scripts" -if (-not (Test-Path $ScriptDir)) { - New-Item -ItemType Directory -Path $ScriptDir -Force | Out-Null -} - -function Register-Task { - param( - [string]$TaskName, - [string]$Description, - [object]$Action, - [object[]]$Triggers, - [string]$RunLevel = "Highest" - ) - try { - # Remove existing task with same name - Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -ErrorAction SilentlyContinue - - $settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Minutes 5) ` - -MultipleInstances IgnoreNew ` - -StartWhenAvailable - - $principal = New-ScheduledTaskPrincipal -GroupId "Users" ` - -RunLevel $RunLevel - - $task = New-ScheduledTask -Action $Action ` - -Trigger $Triggers ` - -Settings $settings ` - -Principal $principal ` - -Description $Description - - Register-ScheduledTask -TaskName $TaskName -InputObject $task -Force | Out-Null - Write-Log " Registered task: $TaskName" -Level OK - } - catch { - Write-Log " Failed to register task $TaskName - $_" -Level ERROR - } -} - -# ----------------------------------------------------------------------- -# Task: ShowAllTrayIcons -# Runs on logon: clears TrayNotify icon cache and restarts Explorer so all -# tray icons are visible on first login (Win10: EnableAutoTray=0, Win11: cache clear) -# ----------------------------------------------------------------------- -Write-Log "Registering task: ShowAllTrayIcons" -Level STEP - -$showTrayScript = "$ScriptDir\ShowAllTrayIcons.ps1" -@' -# Win10: disable auto-hiding of tray icons -$regPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer" -Set-ItemProperty -Path $regPath -Name "EnableAutoTray" -Value 0 -Force -ErrorAction SilentlyContinue - -# Win11: clear icon stream cache so all icons become visible after Explorer restart -$trayPath = "HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify" -if (Test-Path $trayPath) { - Remove-ItemProperty -Path $trayPath -Name "IconStreams" -Force -ErrorAction SilentlyContinue - Remove-ItemProperty -Path $trayPath -Name "PastIconsStream" -Force -ErrorAction SilentlyContinue -} - -# Restart Explorer to apply changes -Stop-Process -Name explorer -Force -ErrorAction SilentlyContinue -Start-Sleep -Milliseconds 1500 -if (-not (Get-Process explorer -ErrorAction SilentlyContinue)) { - Start-Process explorer -} -'@ | Set-Content -Path $showTrayScript -Encoding UTF8 -Force - -$showTrayAction = New-ScheduledTaskAction -Execute "powershell.exe" ` - -Argument "-NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$showTrayScript`"" -$showTrayTrigger = New-ScheduledTaskTrigger -AtLogOn - -Register-Task -TaskName "ShowAllTrayIcons" ` - -Description "Show all system tray icons for current user" ` - -Action $showTrayAction ` - -Triggers $showTrayTrigger - -# ----------------------------------------------------------------------- -Write-Log "Registering task: PDF-DefaultApp" -Level STEP - -$pdfScript = "$ScriptDir\PDF-DefaultApp.ps1" -@' -# Restore .pdf -> Adobe Reader HKCR association (system-wide). -# Runs as SYSTEM so it can write to HKCR regardless of Edge updates. -# Note: HKCU UserChoice requires Windows Hash validation and cannot be -# set reliably via registry; HKCR provides the system-wide fallback. -$acroPaths = @( - "$env:ProgramFiles\Adobe\Acrobat DC\Acrobat\Acrobat.exe" - "${env:ProgramFiles(x86)}\Adobe\Acrobat DC\Acrobat\Acrobat.exe" - "${env:ProgramFiles(x86)}\Adobe\Acrobat Reader DC\Reader\AcroRd32.exe" - "$env:ProgramFiles\Adobe\Acrobat Reader DC\Reader\AcroRd32.exe" - "${env:ProgramFiles(x86)}\Adobe\Reader\Reader\AcroRd32.exe" -) -$acroExe = $acroPaths | Where-Object { Test-Path $_ } | Select-Object -First 1 -if (-not $acroExe) { exit 0 } - -$progId = "AcroExch.Document.DC" -$openCmd = "`"$acroExe`" `"%1`"" - -# HKCR\.pdf -if (-not (Test-Path "HKCR:\.pdf")) { New-Item -Path "HKCR:\.pdf" -Force | Out-Null } -$current = (Get-ItemProperty -Path "HKCR:\.pdf" -Name "(Default)" -ErrorAction SilentlyContinue)."(Default)" -if ($current -ne $progId) { - Set-ItemProperty -Path "HKCR:\.pdf" -Name "(Default)" -Value $progId -Force -} - -# HKCR\AcroExch.Document.DC\shell\open\command -$cmdPath = "HKCR:\$progId\shell\open\command" -if (-not (Test-Path $cmdPath)) { New-Item -Path $cmdPath -Force | Out-Null } -Set-ItemProperty -Path $cmdPath -Name "(Default)" -Value $openCmd -Force -'@ | Set-Content -Path $pdfScript -Encoding UTF8 -Force - -$pdfAction = New-ScheduledTaskAction -Execute "powershell.exe" ` - -Argument "-NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$pdfScript`"" -$pdfTrigger = New-ScheduledTaskTrigger -AtLogOn - -# Runs as SYSTEM to allow HKCR writes (system-wide file association) -$pdfPrincipal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest -$pdfSettings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Minutes 2) ` - -MultipleInstances IgnoreNew ` - -StartWhenAvailable -$pdfTask = New-ScheduledTask -Action $pdfAction ` - -Trigger $pdfTrigger ` - -Settings $pdfSettings ` - -Principal $pdfPrincipal ` - -Description "Restore Adobe Reader as default PDF app on logon" -try { - Unregister-ScheduledTask -TaskName "PDF-DefaultApp" -Confirm:$false -ErrorAction SilentlyContinue - Register-ScheduledTask -TaskName "PDF-DefaultApp" -InputObject $pdfTask -Force | Out-Null - Write-Log " Registered task: PDF-DefaultApp" -Level OK -} -catch { - Write-Log " Failed to register task PDF-DefaultApp - $_" -Level ERROR -} - -# ----------------------------------------------------------------------- -# Task: UnlockStartLayout -# Runs once after deployment to unlock the Start menu layout -# so users can still customize it later -# ----------------------------------------------------------------------- -Write-Log "Registering task: UnlockStartLayout" -Level STEP - -$unlockScript = "$ScriptDir\UnlockStartLayout.ps1" -@' -# Remove Start layout lock so users can modify it -$layoutXml = "C:\Users\Default\AppData\Local\Microsoft\Windows\Shell\LayoutModification.xml" -if (Test-Path $layoutXml) { - Remove-Item $layoutXml -Force -ErrorAction SilentlyContinue -} - -# Unregister self after running once -Unregister-ScheduledTask -TaskName "UnlockStartLayout" -Confirm:$false -ErrorAction SilentlyContinue -'@ | Set-Content -Path $unlockScript -Encoding UTF8 -Force - -$unlockAction = New-ScheduledTaskAction -Execute "powershell.exe" ` - -Argument "-NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$unlockScript`"" -# Trigger: 5 minutes after system startup, once -$unlockTrigger = New-ScheduledTaskTrigger -AtStartup -$unlockTrigger.Delay = "PT5M" - -$unlockPrincipal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest -$unlockSettings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Minutes 10) ` - -StartWhenAvailable -$unlockTask = New-ScheduledTask -Action $unlockAction ` - -Trigger $unlockTrigger ` - -Settings $unlockSettings ` - -Principal $unlockPrincipal ` - -Description "Unlock Start menu layout 5 min after first boot" - -try { - Unregister-ScheduledTask -TaskName "UnlockStartLayout" -Confirm:$false -ErrorAction SilentlyContinue - Register-ScheduledTask -TaskName "UnlockStartLayout" -InputObject $unlockTask -Force | Out-Null - Write-Log " Registered task: UnlockStartLayout" -Level OK -} -catch { - Write-Log " Failed to register task UnlockStartLayout - $_" -Level ERROR -} - -Write-Log "Step 6 complete" -Level OK diff --git a/scripts/07-backinfo.ps1 b/scripts/07-backinfo.ps1 index eacae46..c8c4ce7 100644 --- a/scripts/07-backinfo.ps1 +++ b/scripts/07-backinfo.ps1 @@ -16,18 +16,12 @@ 07-desktop-info-ps1-smazat-nahrazeno: 07-desktop-info.ps1 is superseded by this script. BackInfo.exe is the preferred approach - stable on Win10 and Win11, configurable via INI, already present in assets. #> param( - [object]$Config, + [string]$ConfigPath, [string]$LogFile ) -$ErrorActionPreference = "Continue" - -function Write-Log { - param([string]$Message, [string]$Level = "INFO") - $line = "[$(Get-Date -Format 'HH:mm:ss')] [$Level] $Message" - $null = New-Item -ItemType Directory -Force -Path (Split-Path $LogFile -Parent) -ErrorAction SilentlyContinue - Add-Content -Path $LogFile -Value $line -Encoding UTF8 -} +. "$PSScriptRoot\common.ps1" +$Config = Load-Config $ConfigPath # ----------------------------------------------------------------------- # Copy BackInfo assets to Program Files diff --git a/scripts/07-desktop-info.ps1 b/scripts/07-desktop-info.ps1 deleted file mode 100644 index 5aa6f7f..0000000 --- a/scripts/07-desktop-info.ps1 +++ /dev/null @@ -1,253 +0,0 @@ -<# -.SYNOPSIS - DEPRECATED - delete this script. Replaced by BackInfo.exe. - -.DESCRIPTION - Original custom PowerShell approach to render system info onto the desktop wallpaper - using WPF (System.Windows.Media / System.Drawing). Superseded by BackInfo.exe which - is already present in assets/Backinfo/ and handles Win10/Win11 natively. - ACTION REQUIRED: Delete this file. Add a BackInfo deployment step to the master script. - -.ITEMS - 07-desktop-info-ps1-smazat-stary-pristup: DELETE THIS FILE. The WPF rendering approach had compatibility issues on some Windows editions and required maintaining complex PS rendering code. BackInfo.exe is a mature, stable replacement already bundled in assets/Backinfo/. - zkopirovat-assets-backinfo-do-c-program-: NEW STEP (in master script): Copy assets/Backinfo/ to C:\Program Files\Backinfo\ on the target machine. Includes BackInfo.exe, BackInfo.ini (display config), and backinfo_W11.ps1 (setup helper). - spustit-backinfo-w11-ps1-detekce-os-regi: Run backinfo_W11.ps1 after file copy. Detects Win10 vs Win11, writes the required registry key for wallpaper rendering compatibility, and creates a Startup shortcut in the All Users Startup folder. - backinfo-exe-v-assets-backinfo-k-dispozi: BackInfo.exe reads BackInfo.ini on each run. INI configures: font size and family, position of each info block, which data sources to show (hostname, username, OS version, CPU, RAM, disk, IP address, domain). - backinfo-auto-start-pri-kazdem-logonu-vi: The Startup shortcut created by backinfo_W11.ps1 ensures BackInfo.exe runs on every user logon. It re-reads live system data each time, so the wallpaper BMP always shows current information (username changes, IP changes, etc.). -#> -param( - [object]$Config, - [string]$LogFile -) - -$ErrorActionPreference = "Continue" - -function Write-Log { - param([string]$Message, [string]$Level = "INFO") - $line = "[$(Get-Date -Format 'HH:mm:ss')] [$Level] $Message" - $null = New-Item -ItemType Directory -Force -Path (Split-Path $LogFile -Parent) -ErrorAction SilentlyContinue - Add-Content -Path $LogFile -Value $line -Encoding UTF8 -} - -$ScriptDir = "C:\Windows\Setup\Scripts" -$RenderScript = "$ScriptDir\DesktopInfo-Render.ps1" -$BmpPath = "$ScriptDir\desktopinfo.bmp" - -if (-not (Test-Path $ScriptDir)) { - New-Item -ItemType Directory -Path $ScriptDir -Force | Out-Null -} - -# ----------------------------------------------------------------------- -# Write the rendering script (runs on every logon as the user) -# Layout: hostname (large bold, centered), then detail lines centered -# ----------------------------------------------------------------------- -Write-Log "Writing DesktopInfo render script to $RenderScript" -Level INFO - -$renderContent = @' -# DesktopInfo-Render.ps1 -# Collects system info and renders it centered on the desktop wallpaper. -# Runs on every user logon via Scheduled Task. - -$ErrorActionPreference = "Continue" -$LogFile = "C:\Windows\Setup\Scripts\desktopinfo.log" -function Write-Log { - param([string]$Message, [string]$Level = "INFO") - Add-Content -Path $LogFile -Value "[$(Get-Date -Format 'HH:mm:ss')] [$Level] $Message" -Encoding UTF8 -} -Write-Log "DesktopInfo render started" -Level INFO - -Add-Type -AssemblyName System.Drawing -Add-Type -AssemblyName System.Windows.Forms -Add-Type -TypeDefinition @" -using System; -using System.Runtime.InteropServices; -public class WallpaperApi { - [DllImport("user32.dll", CharSet=CharSet.Auto)] - public static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni); -} -"@ -ErrorAction SilentlyContinue - -# ----------------------------------------------------------------------- -# Collect system info -# ----------------------------------------------------------------------- -Write-Log "Collecting system info" -$hostname = $env:COMPUTERNAME -$userDomain = $env:USERDOMAIN -$userName = $env:USERNAME -$loggedUser = if ($userDomain -and $userDomain -ne $hostname) { "$userDomain\$userName" } else { "$hostname\$userName" } - -$osInfo = Get-CimInstance Win32_OperatingSystem -ErrorAction SilentlyContinue -$osName = if ($osInfo) { $osInfo.Caption -replace "^Microsoft\s*", "" } else { "Windows" } -$ramGB = if ($osInfo) { [math]::Round($osInfo.TotalVisibleMemorySize / 1024 / 1024, 1) } else { "?" } - -$cpuInfo = Get-CimInstance Win32_Processor -ErrorAction SilentlyContinue | Select-Object -First 1 -$cpuCount = if ($cpuInfo) { $cpuInfo.NumberOfLogicalProcessors } else { "?" } -$cpuSpeed = if ($cpuInfo) { $cpuInfo.MaxClockSpeed } else { "?" } - -$ips = (Get-NetIPAddress -AddressFamily IPv4 -ErrorAction SilentlyContinue | - Where-Object { $_.IPAddress -ne "127.0.0.1" -and $_.PrefixOrigin -ne "WellKnown" } | - Select-Object -ExpandProperty IPAddress) -join ", " -if (-not $ips) { $ips = "N/A" } - -$csInfo = Get-CimInstance Win32_ComputerSystem -ErrorAction SilentlyContinue -$domain = if ($csInfo -and $csInfo.PartOfDomain) { $csInfo.Domain } ` - elseif ($csInfo -and $csInfo.Workgroup) { $csInfo.Workgroup.ToLower() } ` - else { "N/A" } - -Write-Log "hostname=$hostname user=$loggedUser os=$osName ram=$($ramGB)GB cpu=${cpuCount}x${cpuSpeed}MHz ips=$ips domain=$domain" - -# ----------------------------------------------------------------------- -# Screen dimensions -# ----------------------------------------------------------------------- -$screen = [System.Windows.Forms.Screen]::PrimaryScreen -$width = if ($screen) { $screen.Bounds.Width } else { 1920 } -$height = if ($screen) { $screen.Bounds.Height } else { 1080 } -Write-Log "screen=${width}x${height}" - -# ----------------------------------------------------------------------- -# Create bitmap and graphics context -# ----------------------------------------------------------------------- -$bmp = New-Object System.Drawing.Bitmap($width, $height) -$g = [System.Drawing.Graphics]::FromImage($bmp) -$g.TextRenderingHint = [System.Drawing.Text.TextRenderingHint]::AntiAlias -$g.Clear([System.Drawing.ColorTranslator]::FromHtml("#556364")) - -# ----------------------------------------------------------------------- -# Fonts and brushes -# ----------------------------------------------------------------------- -$fontName = "Segoe UI" -$fontTitle = New-Object System.Drawing.Font($fontName, 36, [System.Drawing.FontStyle]::Bold) -$fontBold = New-Object System.Drawing.Font($fontName, 14, [System.Drawing.FontStyle]::Bold) -$fontReg = New-Object System.Drawing.Font($fontName, 14, [System.Drawing.FontStyle]::Regular) -$brushWhite = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::White) -$brushGray = New-Object System.Drawing.SolidBrush([System.Drawing.ColorTranslator]::FromHtml("#C8D2D2")) - -# ----------------------------------------------------------------------- -# Lines: text, font, brush -# ----------------------------------------------------------------------- -$texts = @( - $hostname - "Logged on user: $loggedUser" - "OS: $osName" - "CPU: $cpuCount at $cpuSpeed MHz RAM: $($ramGB)GB" - "IPv4 address: $ips Machine domain: $domain" -) -$fonts = @( $fontTitle, $fontReg, $fontBold, $fontReg, $fontReg ) -$brushes = @( $brushWhite, $brushGray, $brushGray, $brushGray, $brushGray ) - -# ----------------------------------------------------------------------- -# Measure total block height, then center vertically -# ----------------------------------------------------------------------- -$lineSpacing = 8 -$heights = @() -for ($i = 0; $i -lt $texts.Count; $i++) { - $heights += [int]($g.MeasureString($texts[$i], $fonts[$i]).Height) -} -$totalH = ($heights | Measure-Object -Sum).Sum + $lineSpacing * ($texts.Count - 1) -$currentY = [int](($height - $totalH) / 2) - -# ----------------------------------------------------------------------- -# Draw each line centered horizontally -# ----------------------------------------------------------------------- -for ($i = 0; $i -lt $texts.Count; $i++) { - $sz = $g.MeasureString($texts[$i], $fonts[$i]) - $x = [int](($width - $sz.Width) / 2) - $g.DrawString($texts[$i], $fonts[$i], $brushes[$i], [float]$x, [float]$currentY) - $currentY += $heights[$i] + $lineSpacing -} - -$g.Dispose() - -# ----------------------------------------------------------------------- -# Save and set as wallpaper -# ----------------------------------------------------------------------- -$bmpPath = "C:\Windows\Setup\Scripts\desktopinfo.bmp" -Write-Log "Saving BMP: $bmpPath" -$bmp.Save($bmpPath, [System.Drawing.Imaging.ImageFormat]::Bmp) -$bmp.Dispose() - -# Clear Windows wallpaper cache so it reloads from our BMP -# Without this, Windows reuses TranscodedWallpaper and ignores the updated file -$transcodedPath = "$env:APPDATA\Microsoft\Windows\Themes\TranscodedWallpaper" -if (Test-Path $transcodedPath) { - Remove-Item $transcodedPath -Force -ErrorAction SilentlyContinue - Write-Log "Cleared TranscodedWallpaper cache" -} - -# SPI_SETDESKTOPWALLPAPER=20, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE=3 -$result = [WallpaperApi]::SystemParametersInfo(20, 0, $bmpPath, 3) -Write-Log "SystemParametersInfo result: $result" -Write-Log "DesktopInfo render complete" -Level INFO -'@ - -$renderContent | Set-Content -Path $RenderScript -Encoding UTF8 -Force -Write-Log "Render script written" -Level OK - -# ----------------------------------------------------------------------- -# Store deployment date in registry (used for reference) -# ----------------------------------------------------------------------- -Write-Log "Storing deployment date in registry" -Level INFO -try { - if (-not (Test-Path "HKLM:\SOFTWARE\X9\Deployment")) { - New-Item -Path "HKLM:\SOFTWARE\X9\Deployment" -Force | Out-Null - } - $existingDate = (Get-ItemProperty -Path "HKLM:\SOFTWARE\X9\Deployment" ` - -Name "DeployDate" -ErrorAction SilentlyContinue).DeployDate - if (-not $existingDate) { - Set-ItemProperty -Path "HKLM:\SOFTWARE\X9\Deployment" ` - -Name "DeployDate" ` - -Value (Get-Date -Format "yyyy-MM-dd") ` - -Force - Write-Log " DeployDate set: $(Get-Date -Format 'yyyy-MM-dd')" -Level OK - } else { - Write-Log " DeployDate already set: $existingDate" -Level INFO - } -} -catch { - Write-Log " Failed to set DeployDate: $_" -Level ERROR -} - -# ----------------------------------------------------------------------- -# Register scheduled task: DesktopInfo -# Runs the render script on every user logon -# ----------------------------------------------------------------------- -Write-Log "Registering task: DesktopInfo" -Level STEP - -try { - Unregister-ScheduledTask -TaskName "DesktopInfo" -Confirm:$false -ErrorAction SilentlyContinue - - $action = New-ScheduledTaskAction -Execute "powershell.exe" ` - -Argument "-NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$RenderScript`"" - $trigger = New-ScheduledTaskTrigger -AtLogOn - $trigger.Delay = "PT20S" # wait for network to be available - $settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Minutes 2) ` - -MultipleInstances IgnoreNew ` - -StartWhenAvailable - $principal = New-ScheduledTaskPrincipal -GroupId "Users" -RunLevel Limited - - $task = New-ScheduledTask -Action $action ` - -Trigger $trigger ` - -Settings $settings ` - -Principal $principal ` - -Description "Render system info onto desktop wallpaper on logon" - - Register-ScheduledTask -TaskName "DesktopInfo" -InputObject $task -Force | Out-Null - Write-Log "Task DesktopInfo registered" -Level OK -} -catch { - Write-Log "Failed to register DesktopInfo task: $_" -Level ERROR -} - -# ----------------------------------------------------------------------- -# Run once immediately for current user -# ----------------------------------------------------------------------- -Write-Log "Running DesktopInfo render now for current user" -Level INFO -try { - & powershell.exe -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File $RenderScript - Write-Log "DesktopInfo rendered" -Level OK -} -catch { - Write-Log "DesktopInfo render failed: $_" -Level WARN -} - -Write-Log "Step 7 complete" -Level OK diff --git a/scripts/08-activation.ps1 b/scripts/08-activation.ps1 index e8c8013..e933f96 100644 --- a/scripts/08-activation.ps1 +++ b/scripts/08-activation.ps1 @@ -17,18 +17,12 @@ typ-klice-mak-vs-kms-vs-retail: Key type selection depends on client's Microsoft licensing: MAK = volume license key activates online against Microsoft (limited activations), KMS = requires KMS server on network (VLSC subscription), Retail = individual license from Microsoft Store or OEM. #> param( - [object]$Config, + [string]$ConfigPath, [string]$LogFile ) -$ErrorActionPreference = "Continue" - -function Write-Log { - param([string]$Message, [string]$Level = "INFO") - $line = "[$(Get-Date -Format 'HH:mm:ss')] [$Level] $Message" - $null = New-Item -ItemType Directory -Force -Path (Split-Path $LogFile -Parent) -ErrorAction SilentlyContinue - Add-Content -Path $LogFile -Value $line -Encoding UTF8 -} +. "$PSScriptRoot\common.ps1" +$Config = Load-Config $ConfigPath # ----------------------------------------------------------------------- # KMS Generic Volume License Keys (GVLK) diff --git a/scripts/09-pc-identity.ps1 b/scripts/09-pc-identity.ps1 index 17f71c3..b11ebce 100644 --- a/scripts/09-pc-identity.ps1 +++ b/scripts/09-pc-identity.ps1 @@ -17,18 +17,12 @@ cx9-vlastni-ikonka-desktop-ini: Copies X9-ikona.ico to C:\X9\ and creates Desktop.ini with IconResource entry. Sets System+Hidden attributes on Desktop.ini and ReadOnly on C:\X9\ so Explorer displays the custom folder icon. #> param( - [object]$Config, + [string]$ConfigPath, [string]$LogFile ) -$ErrorActionPreference = "Continue" - -function Write-Log { - param([string]$Message, [string]$Level = "INFO") - $line = "[$(Get-Date -Format 'HH:mm:ss')] [$Level] $Message" - $null = New-Item -ItemType Directory -Force -Path (Split-Path $LogFile -Parent) -ErrorAction SilentlyContinue - Add-Content -Path $LogFile -Value $line -Encoding UTF8 -} +. "$PSScriptRoot\common.ps1" +$Config = Load-Config $ConfigPath # ----------------------------------------------------------------------- # C:\X9 directory structure @@ -117,6 +111,7 @@ if ($Config -and $Config.deployment -and $Config.deployment.pcName) { $pcName = $Config.deployment.pcName.Trim() } +$renamed = $false if ($pcName -and $pcName -ne "") { $currentName = $env:COMPUTERNAME if ($currentName -eq $pcName) { @@ -126,6 +121,7 @@ if ($pcName -and $pcName -ne "") { try { Rename-Computer -NewName $pcName -Force -ErrorAction Stop Write-Log " Computer renamed to '$pcName' (restart required)" -Level OK + $renamed = $true } catch { Write-Log " Failed to rename computer: $_" -Level ERROR @@ -136,3 +132,9 @@ if ($pcName -and $pcName -ne "") { } Write-Log "Step 9 complete" -Level OK + +# Signal reboot only when rename actually happened +if ($renamed) { + Write-Log "Step 9 - reboot required for rename (exit 9)" -Level OK + exit 9 +} diff --git a/scripts/10-network.ps1 b/scripts/10-network.ps1 index 3c33582..b0d2686 100644 --- a/scripts/10-network.ps1 +++ b/scripts/10-network.ps1 @@ -15,18 +15,12 @@ zapnout-network-discovery: Enables the Network Discovery firewall rule group (FPS-NB_Name-In-UDP, LLMNR, etc.) for Private and Domain profiles via Set-NetFirewallRule. Allows this PC to appear in Network Neighborhood and browse other machines. #> param( - [object]$Config, + [string]$ConfigPath, [string]$LogFile ) -$ErrorActionPreference = "Continue" - -function Write-Log { - param([string]$Message, [string]$Level = "INFO") - $line = "[$(Get-Date -Format 'HH:mm:ss')] [$Level] $Message" - $null = New-Item -ItemType Directory -Force -Path (Split-Path $LogFile -Parent) -ErrorAction SilentlyContinue - Add-Content -Path $LogFile -Value $line -Encoding UTF8 -} +. "$PSScriptRoot\common.ps1" +$Config = Load-Config $ConfigPath # ----------------------------------------------------------------------- # Set network profiles to Private diff --git a/scripts/11-dell-update.ps1 b/scripts/11-dell-update.ps1 index f955a25..0fb4493 100644 --- a/scripts/11-dell-update.ps1 +++ b/scripts/11-dell-update.ps1 @@ -17,37 +17,12 @@ bios-firmware-staging-reboot: BIOS and firmware updates are staged by DCU and finalize on the next system restart. The deployment already ends with a restart (step 09 - computer rename), so no extra reboot is needed. #> param( - [object]$Config, + [string]$ConfigPath, [string]$LogFile ) -$ErrorActionPreference = "Continue" - -function Write-Log { - param([string]$Message, [string]$Level = "INFO") - $line = "[$(Get-Date -Format 'HH:mm:ss')] [$Level] $Message" - $null = New-Item -ItemType Directory -Force -Path (Split-Path $LogFile -Parent) -ErrorAction SilentlyContinue - Add-Content -Path $LogFile -Value $line -Encoding UTF8 - switch ($Level) { - "OK" { Write-Host $line -ForegroundColor Green } - "ERROR" { Write-Host $line -ForegroundColor Red } - "WARN" { Write-Host $line -ForegroundColor Yellow } - "STEP" { Write-Host $line -ForegroundColor Cyan } - default { Write-Host $line } - } -} - -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 } -} +. "$PSScriptRoot\common.ps1" +$Config = Load-Config $ConfigPath # ----------------------------------------------------------------------- # Detect Dell hardware diff --git a/scripts/12-windows-update.ps1 b/scripts/12-windows-update.ps1 index a84a0e6..184f063 100644 --- a/scripts/12-windows-update.ps1 +++ b/scripts/12-windows-update.ps1 @@ -18,19 +18,12 @@ spustit-kolo-windows-update: One update pass without reboot. Exits 9 when updates were applied (more rounds needed). Exits 0 when system is fully up to date. #> param( - [object]$Config, + [string]$ConfigPath, [string]$LogFile ) -$ErrorActionPreference = "Continue" - -function Write-Log { - param([string]$Message, [string]$Level = "INFO") - $line = "[$(Get-Date -Format 'HH:mm:ss')] [$Level] $Message" - $null = New-Item -ItemType Directory -Force -Path (Split-Path $LogFile -Parent) -ErrorAction SilentlyContinue - Add-Content -Path $LogFile -Value $line -Encoding UTF8 - Write-Output $line -} +. "$PSScriptRoot\common.ps1" +$Config = Load-Config $ConfigPath Write-Log "=== Step 12 - Windows Update ===" -Level STEP diff --git a/scripts/common.ps1 b/scripts/common.ps1 new file mode 100644 index 0000000..ec55dde --- /dev/null +++ b/scripts/common.ps1 @@ -0,0 +1,44 @@ +# common.ps1 - shared functions for all deployment scripts +# Dot-source at the top of each script: . "$PSScriptRoot\common.ps1" +# Requires $LogFile variable set in the calling script's scope. + +$ErrorActionPreference = "Continue" + +function Write-Log { + param([string]$Message, [string]$Level = "INFO") + $line = "[$(Get-Date -Format 'HH:mm:ss')] [$Level] $Message" + if ($LogFile) { + $null = New-Item -ItemType Directory -Force -Path (Split-Path $LogFile -Parent) -ErrorAction SilentlyContinue + Add-Content -Path $LogFile -Value $line -Encoding UTF8 + } + Write-Output $line +} + +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 } +} + +function Load-Config { + param([string]$Path) + if (-not $Path -or -not (Test-Path $Path)) { + Write-Log "No config file at: $Path" -Level WARN + return $null + } + try { + $cfg = Get-Content $Path -Raw -Encoding UTF8 | ConvertFrom-Json + Write-Log "Config loaded from $Path" -Level INFO + return $cfg + } + catch { + Write-Log "Failed to parse config: $_" -Level ERROR + return $null + } +} diff --git a/tests/Test-Deployment.ps1 b/tests/Test-Deployment.ps1 index 9e35d8e..b817952 100644 --- a/tests/Test-Deployment.ps1 +++ b/tests/Test-Deployment.ps1 @@ -99,7 +99,6 @@ Test-Check "Windows activated" { Write-Host "" Write-Host "--- Software ---" Test-Check "7-Zip installed" { - (Get-AppxPackage -Name "7zip.7zip" -ErrorAction SilentlyContinue) -or (Test-Path "${env:ProgramFiles}\7-Zip\7z.exe") -or (Test-Path "${env:ProgramFiles(x86)}\7-Zip\7z.exe") } @@ -117,6 +116,24 @@ Test-Check "OpenVPN Connect installed" { -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -like "OpenVPN*" }) } -WarnOnly +Test-Check "Atera agent installed" { + (Test-Path "$env:ProgramFiles\ATERA Networks\AteraAgent\AteraAgent.exe") -or + (Test-Path "${env:ProgramFiles(x86)}\ATERA Networks\AteraAgent\AteraAgent.exe") +} -WarnOnly + +# ----------------------------------------------------------------------- +# PDF default +# ----------------------------------------------------------------------- +Write-Host "" +Write-Host "--- PDF default ---" +Test-Check "HKCR .pdf set to AcroExch" { + if (-not (Get-PSDrive -Name HKCR -ErrorAction SilentlyContinue)) { + New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT | Out-Null + } + $val = (Get-ItemProperty -Path "HKCR:\.pdf" -Name "(Default)" -ErrorAction SilentlyContinue)."(Default)" + $val -eq "AcroExch.Document.DC" +} -WarnOnly + # ----------------------------------------------------------------------- # Bloatware # ----------------------------------------------------------------------- @@ -166,10 +183,6 @@ Test-Check "Edge First Run hidden" { (Get-RegValue "HKLM:\SOFTWARE\Policies\Microsoft\Edge" "HideFirstRunExperience") -eq 1 } -Test-Check "OneDrive disabled via policy" { - (Get-RegValue "HKLM:\SOFTWARE\Policies\Microsoft\Windows\OneDrive" "DisableFileSyncNGSC") -eq 1 -} - Test-Check "GameDVR disabled" { (Get-RegValue "HKLM:\SOFTWARE\Policies\Microsoft\Windows\GameDVR" "AllowGameDVR") -eq 0 } @@ -178,12 +191,17 @@ Test-Check "Time zone set" { (Get-TimeZone).Id -eq "Central Europe Standard Time" } -Test-Check "Deployment date in registry" { - (Get-RegValue "HKLM:\SOFTWARE\X9\Deployment" "DeployDate") -ne $null -} +Test-Check "Standby timeout AC = 0 (never)" { + $val = & powercfg /query SCHEME_CURRENT SUB_SLEEP STANDBYIDLE 2>&1 | Select-String "Current AC Power Setting Index" + $val -match "0x00000000" +} -WarnOnly + +Test-Check "WPAD proxy disabled" { + (Get-RegValue "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings" "AutoDetect") -eq 0 +} -WarnOnly # ----------------------------------------------------------------------- -# Current user (HKCU) - personalization +# Current user (HKCU) - profile + personalization # ----------------------------------------------------------------------- Write-Host "" Write-Host "--- User settings (current user) ---" @@ -211,41 +229,60 @@ Test-Check "File extensions visible" { Test-Check "Explorer opens to This PC" { (Get-RegValue "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" "LaunchTo") -eq 1 } + Test-Check "This PC icon on desktop" { (Get-RegValue "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel" "{20D04FE0-3AEA-1069-A2D8-08002B30309D}") -eq 0 } -Test-Check "Start menu Recommended section hidden" { + +Test-Check "Start menu Recommended hidden" { (Get-RegValue "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer" "HideRecommendedSection") -eq 1 } + Test-Check "Start menu recently added hidden" { (Get-RegValue "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" "Start_TrackProgs") -eq 0 } # ----------------------------------------------------------------------- -# Scheduled tasks +# BackInfo # ----------------------------------------------------------------------- Write-Host "" -Write-Host "--- Scheduled tasks ---" +Write-Host "--- BackInfo ---" -$tasks = @("ShowAllTrayIcons", "PDF-DefaultApp", "DesktopInfo", "UnlockStartLayout") -foreach ($t in $tasks) { - Test-Check "Task registered: $t" { - Get-ScheduledTask -TaskName $t -ErrorAction SilentlyContinue - } +Test-Check "BackInfo.exe deployed" { + Test-Path "C:\Program Files\Backinfo\BackInfo.exe" +} + +Test-Check "BackInfo startup shortcut" { + Test-Path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\BackInfo.lnk" +} + +Test-Check "BackInfo OSName registry" { + (Get-RegValue "HKLM:\SOFTWARE\BackInfo" "OSName") -ne $null } # ----------------------------------------------------------------------- -# DesktopInfo +# Network # ----------------------------------------------------------------------- Write-Host "" -Write-Host "--- DesktopInfo ---" +Write-Host "--- Network ---" -Test-Check "Render script exists" { - Test-Path "C:\Windows\Setup\Scripts\DesktopInfo-Render.ps1" +Test-Check "Network profile Private" { + $profiles = Get-NetConnectionProfile -ErrorAction SilentlyContinue + -not ($profiles | Where-Object { $_.NetworkCategory -ne "Private" }) +} -WarnOnly + +# ----------------------------------------------------------------------- +# C:\X9 directory +# ----------------------------------------------------------------------- +Write-Host "" +Write-Host "--- PC identity ---" + +Test-Check "C:\\X9 directory exists" { + Test-Path "C:\X9" } -Test-Check "BMP file exists" { - Test-Path "C:\Windows\Setup\Scripts\desktopinfo.bmp" +Test-Check "C:\\X9 has custom icon" { + Test-Path "C:\X9\desktop.ini" } -WarnOnly # -----------------------------------------------------------------------