Compare commits
No commits in common. "2f0e176e820c4be07b902c4321fbec5d118ec52e" and "8b795547d33f686fcbc19784f4804228a3e3b0d2" have entirely different histories.
2f0e176e82
...
8b795547d3
26 changed files with 1695 additions and 952 deletions
242
CLAUDE.md
242
CLAUDE.md
|
|
@ -2,16 +2,14 @@
|
|||
|
||||
## Project context
|
||||
|
||||
MSP deployment tool for X9.cz - automated preparation of new Windows 10/11 computers.
|
||||
Go GUI launcher (xetup.exe) embeds PowerShell scripts, runs them sequentially, handles
|
||||
reboot cycles for Windows Update, and sends an email report when done.
|
||||
MSP deployment script for X9.cz - automated preparation of new Windows 10/11 computers for clients.
|
||||
Replaces ~3 hours of manual setup with a single PowerShell script (evolving toward Go TUI launcher).
|
||||
|
||||
**Key parameters:**
|
||||
- Target OS: Windows 10 and Windows 11 (x64), including unsupported HW
|
||||
- Execution: as Administrator on already-installed Windows (not WinPE/autounattend, not OOBE)
|
||||
- Execution: as Administrator on already-installed Windows (not WinPE/autounattend)
|
||||
- Volume: ~20 machines per month, various clients
|
||||
- Operator: MSP technician on-site at client
|
||||
- Entry point: xetup.exe only (no CLI script entry point)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -25,76 +23,27 @@ reboot cycles for Windows Update, and sends an email report when done.
|
|||
## Repo structure
|
||||
|
||||
```
|
||||
xetup/
|
||||
windows-deployment-new/
|
||||
├── CLAUDE.md <- this file
|
||||
├── SPEC.md <- technical specification
|
||||
├── embed.go <- embeds scripts/ and assets/ into binary
|
||||
├── cmd/xetup/
|
||||
│ ├── main.go <- entry point: extract, load config, launch GUI
|
||||
│ └── app.manifest <- Windows manifest (requireAdministrator)
|
||||
├── internal/
|
||||
│ ├── config/config.go <- Config struct, Load/Save, DefaultConfig
|
||||
│ ├── gui/gui.go <- Walk GUI: form → run → summary (3 phases)
|
||||
│ ├── runner/runner.go <- sequential PS script executor with log streaming
|
||||
│ ├── state/state.go <- JSON state file for reboot-resume persistence
|
||||
│ ├── prereboot/ <- autologon + X9-Resume scheduled task for reboot cycle
|
||||
│ ├── preflight/ <- pre-run checks (admin, winget, network, disk)
|
||||
│ └── report/report.go <- HTML email report via SMTP2Go
|
||||
├── Deploy-Windows.ps1 <- master script (entry point)
|
||||
├── scripts/
|
||||
│ ├── common.ps1 <- shared functions (Write-Log, Get-Feature, Load-Config)
|
||||
│ ├── 00-admin-account.ps1 <- create hidden admin account (adminx9, no password)
|
||||
│ ├── 00-admin-account.ps1 <- create hidden admin account
|
||||
│ ├── 01-bloatware.ps1 <- remove AppX, Capabilities, Features
|
||||
│ ├── 02-software.ps1 <- parallel winget installs + Adobe PDF default + Atera
|
||||
│ ├── 03-system-registry.ps1 <- HKLM tweaks, Edge policies, OneDrive, powercfg
|
||||
│ ├── 04-default-profile.ps1 <- NTUSER.DAT + HKCU + personalization (merged)
|
||||
│ ├── 07-backinfo.ps1 <- deploy BackInfo.exe + startup shortcut
|
||||
│ ├── 08-activation.ps1 <- Windows activation (OA3 → config key → GVLK)
|
||||
│ ├── 09-pc-identity.ps1 <- rename PC + C:\X9 folder (exit 9 on rename)
|
||||
│ ├── 10-network.ps1 <- Private profile, ping, Network Discovery
|
||||
│ ├── 11-dell-update.ps1 <- Dell Command | Update (auto-skip on non-Dell)
|
||||
│ └── 12-windows-update.ps1 <- PSWindowsUpdate reboot cycle (exit 9)
|
||||
│ ├── 02-software.ps1 <- winget installs + Adobe PDF default
|
||||
│ ├── 03-system-registry.ps1 <- HKLM tweaks
|
||||
│ ├── 04-default-profile.ps1 <- C:\Users\Default\NTUSER.DAT changes
|
||||
│ ├── 05-personalization.ps1 <- colors, wallpaper, theme
|
||||
│ ├── 06-scheduled-tasks.ps1 <- register scheduled tasks
|
||||
│ ├── 07-desktop-info.ps1 <- TO BE DELETED (replaced by BackInfo)
|
||||
│ └── 08-activation.ps1 <- Windows activation via slmgr
|
||||
├── config/
|
||||
│ └── config.json <- default config template
|
||||
│ └── config.json <- per-client config
|
||||
├── assets/
|
||||
│ ├── Backinfo/ <- BackInfo.exe + .ini
|
||||
│ ├── Backinfo/ <- BackInfo.exe + .ini + backinfo_W11.ps1
|
||||
│ └── Logo/ <- X9-ikona.ico, X9-logo.jpeg
|
||||
├── tests/
|
||||
│ └── Test-Deployment.ps1 <- post-deployment verification
|
||||
└── web/ <- xetup.x9.cz static site
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Execution flow
|
||||
|
||||
```
|
||||
xetup.exe start
|
||||
→ extract scripts/ and assets/ to temp dir
|
||||
→ state file exists? → resume mode (skip form, run pending steps)
|
||||
→ normal mode:
|
||||
1. Pre-flight checks (admin, winget, network, disk) shown in GUI
|
||||
2. Config form (PC name, key, profile, step checkboxes)
|
||||
3. Write runtime config JSON (reflects GUI selections)
|
||||
4. Run steps sequentially via powershell.exe -File -ConfigPath -LogFile
|
||||
5. Step exits 9? → save state, setup autologon + X9-Resume task, reboot
|
||||
6. After reboot → xetup resumes, runs remaining steps
|
||||
7. All done → cleanup autologon, send email report, show summary
|
||||
```
|
||||
|
||||
## Step execution order
|
||||
|
||||
```
|
||||
00 Admin account (adminx9)
|
||||
08 Windows activation
|
||||
01 Bloatware removal
|
||||
02 Software (parallel winget + Atera + PDF default)
|
||||
03 System Registry (HKLM + Edge policies)
|
||||
04 Default Profile + Personalization (single hive load)
|
||||
07 BackInfo
|
||||
10 Network discovery
|
||||
11 Dell Command | Update
|
||||
09 PC identity (rename triggers reboot via exit 9)
|
||||
12 Windows Update (reboot cycle via exit 9)
|
||||
└── tests/
|
||||
└── Test-Deployment.ps1 <- post-deployment verification
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -102,79 +51,115 @@ xetup.exe start
|
|||
## Conventions and rules
|
||||
|
||||
### PowerShell
|
||||
- All scripts use `common.ps1` (dot-sourced): Write-Log, Get-Feature, Load-Config
|
||||
- Scripts receive `-ConfigPath` (path to JSON) and `-LogFile` params
|
||||
- Scripts parse config themselves via `Load-Config $ConfigPath`
|
||||
- `$ErrorActionPreference = "Continue"` - scripts survive partial failures
|
||||
- Exit code 9 = "reboot required" - runner saves state and triggers restart
|
||||
- Log to `C:\Windows\Setup\Scripts\Deploy.log`
|
||||
- NO diacritics anywhere (encoding issues across systems)
|
||||
- NO emoticons
|
||||
- Always `#Requires -RunAsAdministrator` in master script
|
||||
- `$ErrorActionPreference = "Continue"` - script must survive partial failures
|
||||
- Log every step to `C:\Windows\Setup\Scripts\Deploy.log`
|
||||
- Logging via `Write-Log` function defined in master script
|
||||
- `Invoke-Step` function wraps every step - catches errors, logs, continues
|
||||
- Comments in English, code in English
|
||||
- NO diacritics - no accented characters anywhere: not in comments, not in user messages, not in log output
|
||||
- NO emoticons - not in comments, not in output messages
|
||||
- Reason: encoding issues across systems, log readability, compatibility
|
||||
|
||||
### Go / GUI
|
||||
- Walk-based GUI (Windows only, CGO required)
|
||||
- Cross-compile: `CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc GOOS=windows GOARCH=amd64`
|
||||
- Three phases: config form → live log → summary with reboot countdown
|
||||
- Features system: steps can have sub-features (checkboxes in GUI), controlled via config.features
|
||||
### Master script structure
|
||||
```powershell
|
||||
# 1. Load config.json
|
||||
# 2. Run individual scripts in order
|
||||
# 3. Print summary report at end (OK/ERROR counts)
|
||||
```
|
||||
|
||||
### Config
|
||||
- `config.json` is the template, `config-runtime.json` is written to temp at runtime
|
||||
- GUI regenerates runtime config before starting the run
|
||||
- `DefaultConfig()` in config.go provides sensible defaults when config.json is absent
|
||||
- Features default to `true` when missing from config
|
||||
### Master script switches
|
||||
| Switch | Behavior |
|
||||
|---|---|
|
||||
| `-SkipBloatware` | Skip step 1 |
|
||||
| `-SkipSoftware` | Skip step 2 |
|
||||
| `-SkipDefaultProfile` | Skip step 4 |
|
||||
| `-DryRun` | Run without changes, log only |
|
||||
|
||||
### Testing
|
||||
- Test VM: Windows 10/11 x64 on VMware ESXi
|
||||
- Before each test: take snapshot, after test: revert
|
||||
- Dev environment: x64 VM only (not ARM)
|
||||
- Test VM: Windows 10/11 x64 on VMware ESXi (X9.cz internal infrastructure)
|
||||
- Before each test: take snapshot
|
||||
- After test: revert snapshot
|
||||
- Dev environment: x64 VM only - NOT ARM (no Parallels/Apple Silicon for testing)
|
||||
|
||||
---
|
||||
|
||||
## Key implementation details
|
||||
## Important notes
|
||||
|
||||
### BackInfo (replaces custom DesktopInfo)
|
||||
BackInfo.exe IS used. Located in assets/Backinfo/. Deployment:
|
||||
1. Copy assets/Backinfo/ to C:\Program Files\Backinfo\
|
||||
2. Run backinfo_W11.ps1 (detects OS, writes registry, creates Startup shortcut)
|
||||
3. BackInfo.exe auto-starts on every logon, reads INI, renders BMP with system info
|
||||
- Configurable via BackInfo.ini (fonts, positions, data sources)
|
||||
- Displays: hostname (centered, large), username, OS, HW info, network info
|
||||
- DELETE 07-desktop-info.ps1 - no longer needed
|
||||
|
||||
### Adobe Reader as default PDF app
|
||||
- After install: set .pdf -> AcroRd32 association
|
||||
- Scheduled task PDF-DefaultApp restores association on every logon (guard against Edge overwriting it)
|
||||
- NOTE: UCPD.sys (kernel driver since Feb 2024) blocks UserChoice writes. Consider disabling UCPD during deployment.
|
||||
|
||||
### Default Profile
|
||||
- Changes to C:\Users\Default\NTUSER.DAT via reg load / reg unload
|
||||
- Applies to all new users - critical for MSP deployment
|
||||
- Currently logged-in user gets changes via direct write to HKCU
|
||||
|
||||
### Winget
|
||||
- Always use --accept-package-agreements --accept-source-agreements
|
||||
- Check winget availability before running installs
|
||||
- Log result of every install
|
||||
|
||||
### Atera Agent
|
||||
- Download: `Invoke-WebRequest -Uri "https://x9.servicedesk.atera.com/api/utils/agent-install/windows/?cid=31&aeid=50b72e7113e54a63ac76b96c54c7e337" -OutFile setup.msi`
|
||||
- Install: `msiexec /i setup.msi /qn`
|
||||
|
||||
### Admin account (adminx9)
|
||||
- No password (empty), hidden from login screen, Administrators group
|
||||
- NO PASSWORD (changed from previous version)
|
||||
- FullName = "X9.cz s.r.o." (via ADSI)
|
||||
- Also used by prereboot for autologon during reboot cycles
|
||||
|
||||
### Edge policies
|
||||
- Mandatory (`Policies\Microsoft\Edge`): HideFirstRunExperience, DefaultBrowserSettingEnabled, DiagnosticData, FeedbackSurveysEnabled
|
||||
- Recommended (`Policies\Microsoft\Edge\Recommended`): everything else (user can override)
|
||||
|
||||
### PDF default
|
||||
- Adobe Reader set via HKCR\.pdf after install
|
||||
- UCPD driver stopped during association write, restarted after
|
||||
|
||||
### Reboot-resume cycle
|
||||
- `prereboot_windows.go`: copies xetup.exe to stable path, sets autologon for adminx9, registers X9-Resume scheduled task
|
||||
- `state.go`: persists pending steps + accumulated results across reboots
|
||||
- Steps 09 (pcIdentity on rename) and 12 (windowsUpdate) can trigger exit 9
|
||||
- Cleanup: disables autologon, removes X9-Resume task
|
||||
|
||||
### Email report
|
||||
- Sent via SMTP2Go (mail-eu.smtp2go.com:2525) at end of deployment
|
||||
- From: xetup@x9.cz, To: net@x9.cz
|
||||
- Subject: "xetup report HOSTNAME"
|
||||
- HTML body with per-step status table
|
||||
|
||||
### Parallel winget
|
||||
- 02-software.ps1 launches all winget installs as background jobs (Start-Job)
|
||||
- Jobs run simultaneously, results collected after all complete
|
||||
- Hidden from login screen
|
||||
- Added to Administrators group
|
||||
|
||||
---
|
||||
|
||||
## DO NOT
|
||||
|
||||
- Do not use `$ErrorActionPreference = "Stop"` - scripts must survive partial failure
|
||||
- Do not remove Calculator (Microsoft.WindowsCalculator)
|
||||
- Do not use $ErrorActionPreference = "Stop" - script must survive partial failure
|
||||
- Do not remove Calculator (Microsoft.WindowsCalculator) - intentionally kept
|
||||
- Do not use ARM VM for testing
|
||||
- Do not write scripts depending on specific username
|
||||
- Do not write scripts depending on specific username - script is universal
|
||||
- Do not use hardcoded paths that do not exist on clean Windows
|
||||
- NO diacritics in any file
|
||||
- NO emoticons
|
||||
- Do not remove OneDrive policy-block-free (M365 must be able to reinstall it)
|
||||
- Do not remove RDP/RDS or Microsoft-RemoteDesktopConnection
|
||||
- Do not create Deploy-Windows.ps1 or other CLI entry points (xetup.exe is sole entry point)
|
||||
- NO diacritics - no accented characters in any part of any script
|
||||
- NO emoticons - none in comments, log messages or output
|
||||
- Do not remove OneDrive - must remain installable for M365
|
||||
- Do not remove RDP/RDS - must remain functional
|
||||
- Do not remove Microsoft-RemoteDesktopConnection from Optional Features
|
||||
|
||||
---
|
||||
|
||||
## Planned changes (from review v2, 2026-04-15)
|
||||
|
||||
### Must fix
|
||||
- [ ] Remove OneDrive uninstall from 03-system-registry.ps1 and 04-default-profile.ps1
|
||||
- [ ] Remove password from admin account, add FullName = "X9.cz s.r.o."
|
||||
- [ ] Delete 07-desktop-info.ps1, replace with BackInfo deployment step
|
||||
- [ ] Add powercfg settings (standby-timeout-ac 0, monitor-timeout-ac 60, etc.)
|
||||
- [ ] Add proxy auto-detect disable (AutoDetect = 0)
|
||||
- [ ] Add Atera Agent install step
|
||||
- [ ] Extend Edge policies (~15 more keys)
|
||||
|
||||
### New features (from colleague spec v2)
|
||||
- [ ] Taskbar pinned apps: admin vs user variants via XML layout + -ProfileType parameter
|
||||
- [ ] Explorer: ShowRecent=0, ShowFrequent=0, FullPath=1 in CabinetState
|
||||
- [ ] Network discovery: enable ping, set private network profile (post-restart step)
|
||||
- [ ] PC rename: Rename-Computer as final step before restart
|
||||
- [ ] C:\X9 directory structure with custom folder icon
|
||||
|
||||
### Architecture evolution
|
||||
- [ ] Go TUI launcher (xetup.exe) embedding PS scripts
|
||||
- [ ] spec.yaml as single source of truth
|
||||
- [ ] Web platform at xetup.x9.cz (Forgejo + docs + comments)
|
||||
- [ ] Self-update mechanism in xetup.exe
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -182,6 +167,9 @@ xetup.exe start
|
|||
|
||||
| # | Question | Status |
|
||||
|---|---|---|
|
||||
| 1 | Complete SW list for winget | TODO - list may be incomplete |
|
||||
| 2 | Atera MFA bypass | OPEN - does aeid parameter avoid MFA? |
|
||||
| 3 | `--resume` flag | Passed by prereboot task but not parsed - resume detected via state file |
|
||||
| 1 | BackInfo replacement | DONE - using BackInfo.exe from assets/ |
|
||||
| 2 | Complete SW list for winget | TODO - list incomplete |
|
||||
| 3 | Per-client variability via config.json | FUTURE |
|
||||
| 4 | Admin account adminx9 | DECIDED - no password, FullName "X9.cz s.r.o." |
|
||||
| 5 | UCPD driver workaround for PDF default | TODO - disable during deployment |
|
||||
| 6 | Atera MFA bypass | OPEN - does aeid parameter avoid MFA? |
|
||||
|
|
|
|||
276
Deploy-Windows.ps1
Normal file
276
Deploy-Windows.ps1
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
#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
|
||||
}
|
||||
319
SPEC.md
319
SPEC.md
|
|
@ -1,14 +1,16 @@
|
|||
# MSP Windows Deployment - Specification
|
||||
# MSP Windows Deployment - Specification (SPEC.md)
|
||||
|
||||
> Purpose: Automated preparation of new Windows 10/11 computers for X9.cz clients
|
||||
> Version: 0.2 (draft)
|
||||
> Author: X9.cz
|
||||
> Purpose: Automated preparation of new Windows 10/11 computers for clients
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
xetup.exe replaces ~3 hours of manual computer setup. GUI launcher embeds PowerShell
|
||||
scripts, runs them sequentially, handles reboot cycles, sends email report when done.
|
||||
Settings are applied to Default Profile (NTUSER.DAT) so every new user inherits them.
|
||||
Script replaces ~3 hours of manual computer setup. Run once as Administrator on
|
||||
already-installed Windows, performs everything automatically, saves result to Default
|
||||
Profile so settings apply to every subsequent user.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -16,157 +18,276 @@ Settings are applied to Default Profile (NTUSER.DAT) so every new user inherits
|
|||
|
||||
- Windows 10 or Windows 11 (x64)
|
||||
- Run as Administrator
|
||||
- Internet connection (for winget installs, Atera agent, Windows Update)
|
||||
- Post-OOBE (fully installed Windows with at least one user account)
|
||||
- Internet connection (for winget installs)
|
||||
- Computer received either as clean OEM install or with manufacturer pre-installed Windows
|
||||
|
||||
---
|
||||
|
||||
## Step execution order
|
||||
## What the script does NOT do
|
||||
|
||||
| # | Step | Script | Notes |
|
||||
|---|---|---|---|
|
||||
| 00 | Admin account | 00-admin-account.ps1 | adminx9, no password, hidden |
|
||||
| 08 | Windows activation | 08-activation.ps1 | OA3 → config key → GVLK fallback |
|
||||
| 01 | Bloatware removal | 01-bloatware.ps1 | AppX + Capabilities + Features |
|
||||
| 02 | Software install | 02-software.ps1 | Parallel winget + Atera + PDF default |
|
||||
| 03 | System registry | 03-system-registry.ps1 | HKLM tweaks, Edge, powercfg, WPAD |
|
||||
| 04 | Profile + personalization | 04-default-profile.ps1 | NTUSER.DAT + HKCU + theme |
|
||||
| 07 | BackInfo | 07-backinfo.ps1 | System info wallpaper overlay |
|
||||
| 10 | Network | 10-network.ps1 | Private profile, ping, discovery |
|
||||
| 11 | Dell Update | 11-dell-update.ps1 | Auto-skip on non-Dell HW |
|
||||
| 09 | PC identity | 09-pc-identity.ps1 | Rename + C:\X9 (reboot on rename) |
|
||||
| 12 | Windows Update | 12-windows-update.ps1 | Multi-round reboot cycle |
|
||||
- Does not install Windows (not an autounattend.xml for clean install)
|
||||
- Does not create images
|
||||
- Does not manage the computer ongoing (one-time deployment)
|
||||
|
||||
---
|
||||
|
||||
## Step 00 - Admin account
|
||||
## Script structure
|
||||
|
||||
Script is divided into steps. Each step logs its result. Steps can be skipped with switches.
|
||||
|
||||
---
|
||||
|
||||
## STEP 0a - Admin account
|
||||
|
||||
Creates local admin account `adminx9`:
|
||||
- No password (empty) - account is hidden, only accessible to technicians
|
||||
- FullName = "X9.cz s.r.o." (via ADSI)
|
||||
- Password from `config.json` (`adminAccount.password`)
|
||||
- Added to Administrators group
|
||||
- Hidden from login screen (SpecialAccounts\UserList = 0)
|
||||
- Password never expires
|
||||
- Password never expires, user cannot change password
|
||||
- Hidden from Windows login screen (SpecialAccounts\UserList = 0)
|
||||
|
||||
---
|
||||
|
||||
## Step 08 - Windows activation
|
||||
## STEP 0b - Windows activation
|
||||
|
||||
Priority: OA3 embedded key (BIOS/UEFI) → config.json productKey → GVLK by edition.
|
||||
Optional KMS server via config.json. Skips if already activated (LicenseStatus = 1).
|
||||
Activates Windows using product key from config:
|
||||
- Key from `config.json` (`activation.productKey`) - set to real MAK/retail key for production
|
||||
- Falls back to GVLK (KMS client key) matched by detected OS edition
|
||||
- Optional KMS server via `activation.kmsServer`
|
||||
- If already activated, skips silently
|
||||
|
||||
---
|
||||
|
||||
## Step 01 - Bloatware removal
|
||||
## STEP 1 - Bloatware removal
|
||||
|
||||
Removes ~35 AppX packages (Cortana, Copilot, Teams, Xbox, Skype, News, etc.),
|
||||
~14 Windows Capabilities (Fax, IE, WordPad, etc.), and Optional Features
|
||||
(PowerShell 2.0, Recall). Calculator intentionally kept.
|
||||
### 1a - AppX packages (UWP apps)
|
||||
|
||||
---
|
||||
Removed for all users (-AllUsers) and from provisioned packages (so they do not return for new users).
|
||||
|
||||
## Step 02 - Software installation
|
||||
|
||||
Parallel winget installs (Start-Job):
|
||||
|
||||
| Software | Winget ID |
|
||||
| Package | Description |
|
||||
|---|---|
|
||||
| 7-Zip | 7zip.7zip |
|
||||
| Adobe Acrobat Reader 64-bit | Adobe.Acrobat.Reader.64-bit |
|
||||
| OpenVPN Connect | OpenVPNTechnologies.OpenVPNConnect |
|
||||
| Microsoft.Microsoft3DViewer | 3D Viewer |
|
||||
| Microsoft.BingSearch | Bing Search |
|
||||
| Microsoft.WindowsCamera | Camera |
|
||||
| Clipchamp.Clipchamp | Clipchamp video editor |
|
||||
| Microsoft.WindowsAlarms | Clock / Alarm |
|
||||
| Microsoft.Copilot | Copilot AI |
|
||||
| Microsoft.549981C3F5F10 | Cortana |
|
||||
| Microsoft.Windows.DevHome | Dev Home |
|
||||
| MicrosoftCorporationII.MicrosoftFamily | Family Safety |
|
||||
| Microsoft.WindowsFeedbackHub | Feedback Hub |
|
||||
| Microsoft.Edge.GameAssist | Game Assist |
|
||||
| Microsoft.GetHelp | Help |
|
||||
| Microsoft.Getstarted | Tips / Get Started |
|
||||
| microsoft.windowscommunicationsapps | Mail and Calendar |
|
||||
| Microsoft.WindowsMaps | Maps |
|
||||
| Microsoft.MixedReality.Portal | Mixed Reality |
|
||||
| Microsoft.BingNews | News |
|
||||
| Microsoft.MicrosoftOfficeHub | Office Hub |
|
||||
| Microsoft.Office.OneNote | OneNote |
|
||||
| Microsoft.OutlookForWindows | Outlook (new) |
|
||||
| Microsoft.Paint | Paint (new UWP) |
|
||||
| Microsoft.MSPaint | Paint (legacy) |
|
||||
| Microsoft.People | People |
|
||||
| Microsoft.Windows.Photos | Photos |
|
||||
| Microsoft.PowerAutomateDesktop | Power Automate |
|
||||
| MicrosoftCorporationII.QuickAssist | Quick Assist |
|
||||
| Microsoft.SkypeApp | Skype |
|
||||
| Microsoft.ScreenSketch | Snipping Tool |
|
||||
| Microsoft.MicrosoftSolitaireCollection | Solitaire |
|
||||
| Microsoft.MicrosoftStickyNotes | Sticky Notes |
|
||||
| MicrosoftTeams / MSTeams | Teams (personal) |
|
||||
| Microsoft.Todos | To Do |
|
||||
| Microsoft.WindowsSoundRecorder | Voice Recorder |
|
||||
| Microsoft.Wallet | Wallet |
|
||||
| Microsoft.BingWeather | Weather |
|
||||
| Microsoft.WindowsTerminal | Windows Terminal |
|
||||
| Microsoft.Xbox.TCUI | Xbox UI |
|
||||
| Microsoft.XboxApp | Xbox |
|
||||
| Microsoft.XboxGameOverlay | Xbox Game Overlay |
|
||||
| Microsoft.XboxGamingOverlay | Xbox Gaming Overlay |
|
||||
| Microsoft.XboxIdentityProvider | Xbox Identity |
|
||||
| Microsoft.XboxSpeechToTextOverlay | Xbox Speech |
|
||||
| Microsoft.GamingApp | Gaming App |
|
||||
| Microsoft.YourPhone | Phone Link |
|
||||
| Microsoft.ZuneMusic | Music |
|
||||
| Microsoft.ZuneVideo | Movies and TV |
|
||||
|
||||
After Acrobat: UCPD driver stopped, .pdf → AcroExch.Document.DC set via HKCR, UCPD restarted.
|
||||
NOTE: Microsoft.WindowsCalculator is intentionally KEPT.
|
||||
|
||||
Atera RMM agent: downloaded from x9.servicedesk.atera.com, installed via msiexec /qn with -Wait.
|
||||
### 1b - Windows Capabilities
|
||||
|
||||
| Capability | Description |
|
||||
|---|---|
|
||||
| Print.Fax.Scan | Fax and Scan |
|
||||
| Language.Handwriting | Handwriting |
|
||||
| Browser.InternetExplorer | Internet Explorer |
|
||||
| MathRecognizer | Math Input |
|
||||
| OneCoreUAP.OneSync | OneSync |
|
||||
| OpenSSH.Client | OpenSSH client |
|
||||
| Microsoft.Windows.MSPaint | Paint (Win32) |
|
||||
| Microsoft.Windows.PowerShell.ISE | PowerShell ISE |
|
||||
| App.Support.QuickAssist | Quick Assist |
|
||||
| Microsoft.Windows.SnippingTool | Snipping Tool |
|
||||
| App.StepsRecorder | Steps Recorder |
|
||||
| Hello.Face.* | Windows Hello face |
|
||||
| Media.WindowsMediaPlayer | Windows Media Player |
|
||||
| Microsoft.Windows.WordPad | WordPad |
|
||||
|
||||
### 1c - Windows Optional Features
|
||||
|
||||
| Feature | Description |
|
||||
|---|---|
|
||||
| MediaPlayback | Media playback |
|
||||
| MicrosoftWindowsPowerShellV2Root | PowerShell 2.0 |
|
||||
| Microsoft-RemoteDesktopConnection | RDP client |
|
||||
| Recall | Windows Recall (AI) |
|
||||
| Microsoft-SnippingTool | Snipping Tool (feature) |
|
||||
|
||||
---
|
||||
|
||||
## Step 03 - System registry (HKLM)
|
||||
## STEP 2 - Software installation (winget)
|
||||
|
||||
Always applied: password max age unlimited, timezone (Central Europe Standard Time).
|
||||
| Software | Winget ID | Notes |
|
||||
|---|---|---|
|
||||
| 7-Zip | `7zip.7zip` | OK |
|
||||
| Adobe Acrobat Reader | `Adobe.Acrobat.Reader.64-bit` | OK, see note |
|
||||
| OpenVPN Connect | `OpenVPNTechnologies.OpenVPNConnect` | OK |
|
||||
| ... | ... | TODO: complete list |
|
||||
|
||||
Feature-toggled sections:
|
||||
- **systemTweaks**: BypassNRO, disable Teams auto-install, Widgets, GameDVR, Recall, Copilot search
|
||||
- **edgePolicies**: mandatory (first-run, telemetry) + recommended (UI defaults user can change)
|
||||
- **oneDriveUninstall**: removes consumer OneDrive (no policy block - M365 can reinstall)
|
||||
- **powercfg**: standby-ac=0, monitor-ac=60, standby-dc=30, monitor-dc=15
|
||||
- **proxyDisable**: WPAD auto-detect off
|
||||
> Adobe Acrobat Reader: After install, script sets .pdf -> AcroRd32 as default.
|
||||
> Scheduled task PDF-DefaultApp restores this association on every logon as a guard
|
||||
> against Edge overwriting it.
|
||||
|
||||
> BackInfo: NOT used. Replaced by custom PowerShell scheduled task DesktopInfo.
|
||||
> See STEP 7.
|
||||
|
||||
---
|
||||
|
||||
## Step 04 - Default Profile + Personalization
|
||||
## STEP 3 - System settings (HKLM - applies to whole system)
|
||||
|
||||
Single hive load of C:\Users\Default\NTUSER.DAT. All changes applied to both Default
|
||||
hive and current HKCU. Feature-toggled sections:
|
||||
|
||||
- **taskbarTweaks**: left alignment, hide Search/Copilot/TaskView/Widgets/Chat, show all
|
||||
tray icons, taskbar layout XML per ProfileType (default/admin/user), NumLock on
|
||||
- **startMenuTweaks**: empty pins, disable Bing search, disable Copilot, disable GameDVR
|
||||
- **explorerTweaks**: show extensions, LaunchTo=ThisPC, hide Recent/Frequent, full path
|
||||
|
||||
Personalization (always): dark shell / light apps, accent #223B47, transparency off,
|
||||
solid wallpaper #223B47 (BackInfo overwrites on logon).
|
||||
| Setting | Value | Notes |
|
||||
|---|---|---|
|
||||
| Disable NRO (bypass network check) | HKLM\...\OOBE\BypassNRO = 1 | |
|
||||
| Disable auto-install of Teams | ConfigureChatAutoInstall = 0 | |
|
||||
| Disable Cloud Optimized Content | DisableCloudOptimizedContent = 1 | |
|
||||
| Disable Widgets (News and Interests) | HKLM\...\Dsh\AllowNewsAndInterests = 0 | |
|
||||
| Edge - hide First Run Experience | HKLM\Policies\Edge\HideFirstRunExperience = 1 | |
|
||||
| Passwords - no expiration | net accounts /maxpwage:UNLIMITED | |
|
||||
| Time zone | Central Europe Standard Time | |
|
||||
| OneDrive - remove | Delete OneDriveSetup.exe + Start Menu lnk | |
|
||||
| Outlook (new) - disable auto-install | Delete UScheduler registry key | |
|
||||
| Disable GameDVR | AppCaptureEnabled = 0 | |
|
||||
|
||||
---
|
||||
|
||||
## Step 07 - BackInfo
|
||||
## STEP 4 - Default Profile (NTUSER.DAT)
|
||||
|
||||
Copies BackInfo.exe + INI to C:\Program Files\Backinfo\. Detects OS, writes OSName to
|
||||
registry. Creates startup shortcut for all users. BackInfo renders system info BMP as
|
||||
desktop wallpaper on every logon.
|
||||
Settings applied to C:\Users\Default\NTUSER.DAT - inherited by every new user on first logon.
|
||||
|
||||
Method: script loads Default hive (reg load), makes changes, unloads (reg unload).
|
||||
|
||||
| Setting | Key / Value | Description |
|
||||
|---|---|---|
|
||||
| Taskbar - align left | TaskbarAl = 0 | Win11 default is center |
|
||||
| Taskbar - hide Search box | SearchboxTaskbarMode = 0 | |
|
||||
| Taskbar - hide Copilot button | ShowCopilotButton = 0 | |
|
||||
| Taskbar - hide Task View button | ShowTaskViewButton = 0 | |
|
||||
| Taskbar - hide Widgets | TaskbarDa = 0 | |
|
||||
| Taskbar - hide Chat/Teams button | TaskbarMn = 0 | |
|
||||
| Taskbar - show all tray icons | Scheduled task ShowAllTrayIcons | Runs on every logon |
|
||||
| Taskbar - empty pinlist | TaskbarLayoutModification.xml | Removes default pinned apps |
|
||||
| Explorer - show file extensions | HideFileExt = 0 | |
|
||||
| Explorer - open to This PC | LaunchTo = 1 | Instead of Quick Access |
|
||||
| Start menu - empty pins | ConfigureStartPins = {"pinnedList":[]} | Win11 |
|
||||
| Start menu - disable Bing results | DisableSearchBoxSuggestions = 1 | |
|
||||
| Copilot - disable | TurnOffWindowsCopilot = 1 | |
|
||||
| GameDVR - disable | AppCaptureEnabled = 0 | |
|
||||
| OneDrive - remove RunOnce key | Delete OneDriveSetup from Run | |
|
||||
| Num Lock on startup - enable | InitialKeyboardIndicators = 2 | |
|
||||
| Accent color on title bars | ColorPrevalence = 1 | |
|
||||
|
||||
---
|
||||
|
||||
## Step 10 - Network
|
||||
## STEP 5 - Personalization (colors, wallpaper)
|
||||
|
||||
Sets all connected adapters to Private profile. Enables ICMP echo (ping) and Network
|
||||
Discovery firewall rules.
|
||||
Applied to both Default Profile and currently logged-in user.
|
||||
|
||||
| Setting | Value |
|
||||
|---|---|
|
||||
| System theme (taskbar, Start) | Dark |
|
||||
| App theme | Light |
|
||||
| Accent color | #223B47 (dark blue-gray) |
|
||||
| Accent color on Start and taskbar | Yes |
|
||||
| Accent color on title bars | Yes |
|
||||
| Transparency | Disabled |
|
||||
| Wallpaper | Solid color #223B47 (no image) |
|
||||
|
||||
NOTE: DesktopInfo scheduled task (STEP 7) will overwrite the wallpaper with a system
|
||||
info BMP. The solid color here is only a fallback if DesktopInfo is not running.
|
||||
|
||||
---
|
||||
|
||||
## Step 11 - Dell Command | Update
|
||||
## STEP 6 - Scheduled Tasks
|
||||
|
||||
Detects Dell hardware via Win32_ComputerSystem.Manufacturer. On non-Dell: skips silently.
|
||||
On Dell: installs DCU Universal via winget, runs dcu-cli.exe /applyUpdates with
|
||||
-reboot=disable. Feature-toggled: drivers/firmware and BIOS separately. Exit 9 when
|
||||
BIOS/firmware updates are staged (finalize on next restart).
|
||||
| Task | Trigger | Purpose |
|
||||
|---|---|---|
|
||||
| ShowAllTrayIcons | Every logon, every 1 min | Show all icons in system tray (Win11) |
|
||||
| UnlockStartLayout | Once after layout is applied | Unlock Start menu layout |
|
||||
| PDF-DefaultApp | Every logon | Restore .pdf -> Adobe Reader if Edge overwrote it |
|
||||
| DesktopInfo | Every logon | Render system info onto desktop wallpaper |
|
||||
|
||||
---
|
||||
|
||||
## Step 09 - PC identity
|
||||
## STEP 7 - DesktopInfo (BackInfo replacement)
|
||||
|
||||
Creates C:\X9\ directory (Logs, Scripts, Assets) with custom folder icon.
|
||||
Sets computer description. Renames computer if config.json pcName is set and differs
|
||||
from current. Exit 9 only when rename actually happened (restart required).
|
||||
Custom PowerShell scheduled task. No external dependencies.
|
||||
|
||||
**What it displays:**
|
||||
- Computer name (hostname)
|
||||
- IP address
|
||||
- Windows version and build
|
||||
- Logged-in username
|
||||
- Deployment date
|
||||
|
||||
**How it works:**
|
||||
1. PS script collects system info
|
||||
2. Renders text onto bitmap via WPF / System.Drawing
|
||||
3. Saves BMP to C:\Windows\Setup\Scripts\desktopinfo.bmp
|
||||
4. Sets BMP as desktop wallpaper via SystemParametersInfo
|
||||
5. Runs on every user logon via Scheduled Task
|
||||
|
||||
**Why not BackInfo:**
|
||||
- BackInfo has Win11 rendering issues requiring registry hacks
|
||||
- External EXE dependency is hard to distribute
|
||||
- Custom PS solution = full control, no dependencies, works on Win10 and Win11
|
||||
|
||||
---
|
||||
|
||||
## Step 12 - Windows Update
|
||||
## STEP 8 - Logging and output
|
||||
|
||||
Installs PSWindowsUpdate module, runs one update pass. Exit 9 when updates were installed
|
||||
(reboot needed for next round). Exit 0 when fully up to date. xetup state machine handles
|
||||
the reboot cycle automatically.
|
||||
- Every step writes to C:\Windows\Setup\Scripts\Deploy.log
|
||||
- Format: [HH:mm:ss] Step description - OK / ERROR: ...
|
||||
- At end: summary report (how many steps OK, how many failed)
|
||||
- Log stays on disk for diagnostics
|
||||
|
||||
---
|
||||
|
||||
## Config structure
|
||||
## Script switches
|
||||
|
||||
```json
|
||||
{
|
||||
"deployment": { "pcName": "", "pcDescription": "", "timezone": "...", "profileType": "default" },
|
||||
"adminAccount": { "username": "adminx9" },
|
||||
"activation": { "productKey": "", "kmsServer": "" },
|
||||
"software": { "install": [{ "name": "...", "wingetId": "..." }] },
|
||||
"steps": { "adminAccount": true, ... },
|
||||
"features": { "software": { "wingetInstalls": true, "pdfDefault": true, "ateraAgent": true }, ... },
|
||||
"bloatware": { "keepPackages": ["Microsoft.WindowsCalculator"] }
|
||||
}
|
||||
```
|
||||
| Switch | Behavior |
|
||||
|---|---|
|
||||
| `-SkipBloatware` | Skip step 1 |
|
||||
| `-SkipSoftware` | Skip step 2 |
|
||||
| `-SkipDefaultProfile` | Skip step 4 |
|
||||
| `-DryRun` | Run through steps without changes, log only |
|
||||
|
||||
---
|
||||
|
||||
## Email report
|
||||
## Open questions
|
||||
|
||||
Sent via SMTP2Go at end of deployment. HTML with per-step status table, timestamps,
|
||||
OK/ERROR/SKIPPED counts. Subject: "xetup report HOSTNAME".
|
||||
From: xetup@x9.cz, To: net@x9.cz.
|
||||
| # | Question | Status |
|
||||
|---|---|---|
|
||||
| 1 | BackInfo replacement | DONE - custom PS scheduled task DesktopInfo |
|
||||
| 2 | Complete SW list for winget | TODO |
|
||||
| 3 | Per-client variability via config.json | FUTURE |
|
||||
| 4 | Admin account adminx9 - script or manual? | DONE - script (00-admin-account.ps1) |
|
||||
|
|
|
|||
|
|
@ -1,62 +1,49 @@
|
|||
{
|
||||
"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",
|
||||
"profileType": "default"
|
||||
"locale": "cs-CZ"
|
||||
},
|
||||
"adminAccount": {
|
||||
"username": "adminx9"
|
||||
"username": "adminx9",
|
||||
"password": "AdminX9.AdminX9",
|
||||
"description": "X9 MSP admin account"
|
||||
},
|
||||
"activation": {
|
||||
"productKey": ""
|
||||
"productKey": "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX",
|
||||
"kmsServer": ""
|
||||
},
|
||||
"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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,17 +67,19 @@ func DefaultConfig() Config {
|
|||
},
|
||||
},
|
||||
Steps: map[string]bool{
|
||||
"adminAccount": true,
|
||||
"activation": true,
|
||||
"bloatware": true,
|
||||
"software": true,
|
||||
"systemRegistry": true,
|
||||
"defaultProfile": true,
|
||||
"backinfo": true,
|
||||
"network": true,
|
||||
"dellUpdate": true,
|
||||
"pcIdentity": true,
|
||||
"windowsUpdate": true,
|
||||
"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,
|
||||
},
|
||||
Features: Features{
|
||||
"software": {
|
||||
|
|
|
|||
|
|
@ -27,9 +27,7 @@ import (
|
|||
. "github.com/lxn/walk/declarative"
|
||||
|
||||
"git.xetup.x9.cz/x9/xetup/internal/config"
|
||||
"git.xetup.x9.cz/x9/xetup/internal/preflight"
|
||||
"git.xetup.x9.cz/x9/xetup/internal/prereboot"
|
||||
"git.xetup.x9.cz/x9/xetup/internal/report"
|
||||
"git.xetup.x9.cz/x9/xetup/internal/runner"
|
||||
"git.xetup.x9.cz/x9/xetup/internal/state"
|
||||
)
|
||||
|
|
@ -53,8 +51,6 @@ 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)
|
||||
|
|
@ -154,47 +150,12 @@ func formPhase(cfg config.Config, runCfg runner.RunConfig, cfgPath string) formR
|
|||
return out
|
||||
}
|
||||
|
||||
// Run pre-flight checks and build status widgets
|
||||
pfResults := preflight.RunAll()
|
||||
var pfWidgets []Widget
|
||||
allOK := true
|
||||
for _, r := range pfResults {
|
||||
color := walk.RGB(0, 140, 0)
|
||||
prefix := "\u2713 "
|
||||
if !r.OK {
|
||||
color = walk.RGB(200, 40, 40)
|
||||
prefix = "\u2717 "
|
||||
allOK = false
|
||||
}
|
||||
text := prefix + r.Name + ": " + r.Detail
|
||||
lbl := Label{Text: text, Font: Font{PointSize: 9}}
|
||||
_ = color // applied after window creation
|
||||
pfWidgets = append(pfWidgets, lbl)
|
||||
}
|
||||
_ = allOK
|
||||
|
||||
// Collect label pointers to set color after creation
|
||||
pfLabels := make([]*walk.Label, len(pfResults))
|
||||
for i := range pfWidgets {
|
||||
pfWidgets[i] = Label{
|
||||
AssignTo: &pfLabels[i],
|
||||
Text: pfWidgets[i].(Label).Text,
|
||||
Font: Font{PointSize: 9},
|
||||
}
|
||||
}
|
||||
|
||||
if err := (MainWindow{
|
||||
AssignTo: &mw,
|
||||
Title: "xetup \u2014 Windows deployment",
|
||||
Size: Size{Width: 760, Height: 740},
|
||||
Layout: VBox{},
|
||||
Children: []Widget{
|
||||
// ── Pre-flight checks ────────────────────────────────────────────
|
||||
Composite{
|
||||
Layout: VBox{MarginsZero: true},
|
||||
Children: pfWidgets,
|
||||
},
|
||||
HSeparator{},
|
||||
// ── Form fields ──────────────────────────────────────────────────
|
||||
Composite{
|
||||
Layout: Grid{Columns: 2},
|
||||
|
|
@ -316,17 +277,6 @@ func formPhase(cfg config.Config, runCfg runner.RunConfig, cfgPath string) formR
|
|||
return result
|
||||
}
|
||||
|
||||
// Apply colors to pre-flight labels (must be done after window creation)
|
||||
for i, r := range pfResults {
|
||||
if pfLabels[i] != nil {
|
||||
if r.OK {
|
||||
pfLabels[i].SetTextColor(walk.RGB(0, 140, 0))
|
||||
} else {
|
||||
pfLabels[i].SetTextColor(walk.RGB(200, 40, 40))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mw.Run()
|
||||
return result
|
||||
}
|
||||
|
|
@ -596,17 +546,14 @@ func donePhase(currentResults []runner.Result, prevResults []state.StepResult) {
|
|||
}
|
||||
|
||||
var rows []displayRow
|
||||
var emailRows []report.StepResult
|
||||
for _, r := range prevResults {
|
||||
rows = append(rows, displayRow{r.Num, r.Name, r.Status, r.Elapsed})
|
||||
emailRows = append(emailRows, report.StepResult{Num: r.Num, Name: r.Name, Status: r.Status, Elapsed: r.Elapsed})
|
||||
}
|
||||
for _, r := range currentResults {
|
||||
if r.NeedsReboot {
|
||||
continue
|
||||
}
|
||||
rows = append(rows, displayRow{r.Step.Num, r.Step.Name, r.Status, r.Elapsed})
|
||||
emailRows = append(emailRows, report.StepResult{Num: r.Step.Num, Name: r.Step.Name, Status: r.Status, Elapsed: r.Elapsed})
|
||||
}
|
||||
|
||||
ok, errs, skipped := 0, 0, 0
|
||||
|
|
@ -636,14 +583,6 @@ func donePhase(currentResults []runner.Result, prevResults []state.StepResult) {
|
|||
|
||||
summaryText := fmt.Sprintf("OK: %d CHYBY: %d PRESKOCENO: %d", ok, errs, skipped)
|
||||
|
||||
// Send email report (non-blocking, best-effort)
|
||||
go func() {
|
||||
if err := report.Send(emailRows); err != nil {
|
||||
// Log but don't block - deployment is done
|
||||
_ = err
|
||||
}
|
||||
}()
|
||||
|
||||
cancelReboot := make(chan struct{})
|
||||
|
||||
if err := (MainWindow{
|
||||
|
|
|
|||
|
|
@ -1,111 +0,0 @@
|
|||
//go:build windows
|
||||
|
||||
// Package preflight runs quick environment checks before deployment starts.
|
||||
// Each check returns a human-readable result; failures are warnings, not blockers.
|
||||
package preflight
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Result is one pre-flight check outcome.
|
||||
type Result struct {
|
||||
Name string
|
||||
OK bool
|
||||
Detail string
|
||||
}
|
||||
|
||||
// RunAll executes all pre-flight checks and returns results.
|
||||
func RunAll() []Result {
|
||||
return []Result{
|
||||
checkAdmin(),
|
||||
checkWinget(),
|
||||
checkNetwork(),
|
||||
checkDisk(),
|
||||
}
|
||||
}
|
||||
|
||||
func checkAdmin() Result {
|
||||
r := Result{Name: "Spusteno jako Administrator"}
|
||||
// If we got here via #Requires -RunAsAdministrator or manifested exe,
|
||||
// we're admin. Double-check via net session.
|
||||
cmd := exec.Command("net", "session")
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||
if err := cmd.Run(); err != nil {
|
||||
r.OK = false
|
||||
r.Detail = "Neni spusteno jako Administrator"
|
||||
return r
|
||||
}
|
||||
r.OK = true
|
||||
r.Detail = "OK"
|
||||
return r
|
||||
}
|
||||
|
||||
func checkWinget() Result {
|
||||
r := Result{Name: "Winget dostupny"}
|
||||
cmd := exec.Command("winget", "--version")
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
r.OK = false
|
||||
r.Detail = "winget nenalezen - software se nenainstaluje"
|
||||
return r
|
||||
}
|
||||
ver := strings.TrimSpace(string(out))
|
||||
r.OK = true
|
||||
r.Detail = ver
|
||||
return r
|
||||
}
|
||||
|
||||
func checkNetwork() Result {
|
||||
r := Result{Name: "Pripojeni k internetu"}
|
||||
// Try to resolve a well-known hostname
|
||||
cmd := exec.Command("powershell.exe", "-NonInteractive", "-Command",
|
||||
"[System.Net.Dns]::GetHostEntry('www.google.com').AddressList[0].IPAddressToString")
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil || strings.TrimSpace(string(out)) == "" {
|
||||
r.OK = false
|
||||
r.Detail = "DNS resolve selhal - Atera a winget nebudou fungovat"
|
||||
return r
|
||||
}
|
||||
r.OK = true
|
||||
r.Detail = "OK"
|
||||
return r
|
||||
}
|
||||
|
||||
func checkDisk() Result {
|
||||
r := Result{Name: "Volne misto na C:"}
|
||||
|
||||
kernel32 := syscall.NewLazyDLL("kernel32.dll")
|
||||
getDiskFreeSpaceEx := kernel32.NewProc("GetDiskFreeSpaceExW")
|
||||
|
||||
var freeBytesAvailable, totalBytes, totalFreeBytes uint64
|
||||
drive, _ := syscall.UTF16PtrFromString("C:\\")
|
||||
|
||||
ret, _, _ := getDiskFreeSpaceEx.Call(
|
||||
uintptr(unsafe.Pointer(drive)),
|
||||
uintptr(unsafe.Pointer(&freeBytesAvailable)),
|
||||
uintptr(unsafe.Pointer(&totalBytes)),
|
||||
uintptr(unsafe.Pointer(&totalFreeBytes)),
|
||||
)
|
||||
if ret == 0 {
|
||||
r.OK = false
|
||||
r.Detail = "Nelze zjistit volne misto"
|
||||
return r
|
||||
}
|
||||
|
||||
freeGB := float64(freeBytesAvailable) / (1024 * 1024 * 1024)
|
||||
if freeGB < 5 {
|
||||
r.OK = false
|
||||
r.Detail = fmt.Sprintf("%.1f GB - malo mista (min 5 GB)", freeGB)
|
||||
return r
|
||||
}
|
||||
r.OK = true
|
||||
r.Detail = fmt.Sprintf("%.1f GB volnych", freeGB)
|
||||
return r
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
//go:build !windows
|
||||
|
||||
package preflight
|
||||
|
||||
// Result is one pre-flight check outcome.
|
||||
type Result struct {
|
||||
Name string
|
||||
OK bool
|
||||
Detail string
|
||||
}
|
||||
|
||||
// RunAll returns empty results on non-Windows platforms.
|
||||
func RunAll() []Result { return nil }
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
// Package report sends a deployment summary email via SMTP.
|
||||
package report
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/smtp"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SMTP2Go relay configuration for X9.cz deployment reports.
|
||||
const (
|
||||
smtpHost = "mail-eu.smtp2go.com"
|
||||
smtpPort = "2525"
|
||||
smtpUser = "xetup"
|
||||
smtpPass = "M9ahxHOnJ8fM0CEF"
|
||||
mailFrom = "xetup@x9.cz"
|
||||
mailTo = "net@x9.cz"
|
||||
)
|
||||
|
||||
// StepResult holds one row of the deployment report.
|
||||
type StepResult struct {
|
||||
Num string
|
||||
Name string
|
||||
Status string // OK, ERROR, SKIPPED, CANCELLED
|
||||
Elapsed time.Duration
|
||||
}
|
||||
|
||||
// Send emails the deployment report. Non-fatal: returns error but caller
|
||||
// should log it and continue (deployment is already done).
|
||||
func Send(results []StepResult) error {
|
||||
hostname, _ := os.Hostname()
|
||||
now := time.Now().Format("2006-01-02 15:04")
|
||||
|
||||
subject := fmt.Sprintf("xetup report %s", hostname)
|
||||
body := buildHTML(results, hostname, now)
|
||||
|
||||
msg := strings.Join([]string{
|
||||
"From: " + mailFrom,
|
||||
"To: " + mailTo,
|
||||
"Subject: " + subject,
|
||||
"MIME-Version: 1.0",
|
||||
"Content-Type: text/html; charset=UTF-8",
|
||||
"",
|
||||
body,
|
||||
}, "\r\n")
|
||||
|
||||
auth := smtp.PlainAuth("", smtpUser, smtpPass, smtpHost)
|
||||
return smtp.SendMail(
|
||||
smtpHost+":"+smtpPort,
|
||||
auth,
|
||||
mailFrom,
|
||||
[]string{mailTo},
|
||||
[]byte(msg),
|
||||
)
|
||||
}
|
||||
|
||||
func buildHTML(results []StepResult, hostname, dateTime string) string {
|
||||
var ok, errs, skipped int
|
||||
var rows strings.Builder
|
||||
|
||||
for _, r := range results {
|
||||
color := "#333"
|
||||
icon := ""
|
||||
switch r.Status {
|
||||
case "OK":
|
||||
ok++
|
||||
color = "#2e7d32"
|
||||
icon = "✓"
|
||||
case "ERROR":
|
||||
errs++
|
||||
color = "#c62828"
|
||||
icon = "✗"
|
||||
default:
|
||||
skipped++
|
||||
color = "#9e9e9e"
|
||||
icon = "–"
|
||||
}
|
||||
|
||||
elapsed := ""
|
||||
if r.Elapsed > 0 {
|
||||
elapsed = r.Elapsed.Round(time.Second).String()
|
||||
}
|
||||
|
||||
fmt.Fprintf(&rows,
|
||||
`<tr><td style="padding:4px 8px;color:%s;font-size:16px;text-align:center">%s</td>`+
|
||||
`<td style="padding:4px 8px;color:#666">%s</td>`+
|
||||
`<td style="padding:4px 8px">%s</td>`+
|
||||
`<td style="padding:4px 8px;color:%s;font-weight:bold">%s</td>`+
|
||||
`<td style="padding:4px 8px;color:#999;text-align:right">%s</td></tr>`,
|
||||
color, icon, r.Num, r.Name, color, r.Status, elapsed)
|
||||
}
|
||||
|
||||
summaryColor := "#2e7d32"
|
||||
summaryText := "Deployment OK"
|
||||
if errs > 0 {
|
||||
summaryColor = "#c62828"
|
||||
summaryText = fmt.Sprintf("Deployment finished with %d error(s)", errs)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`<!DOCTYPE html>
|
||||
<html><head><meta charset="UTF-8"></head>
|
||||
<body style="font-family:Segoe UI,Arial,sans-serif;margin:0;padding:20px;background:#f5f5f5">
|
||||
<div style="max-width:640px;margin:0 auto;background:#fff;border-radius:8px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.1)">
|
||||
<div style="background:#223B47;padding:20px 24px;color:#fff">
|
||||
<h1 style="margin:0;font-size:20px">xetup report</h1>
|
||||
<p style="margin:6px 0 0;opacity:0.8">%s — %s</p>
|
||||
</div>
|
||||
<table style="width:100%%;border-collapse:collapse;margin:16px 0">
|
||||
<tr style="background:#f9f9f9">
|
||||
<th style="padding:6px 8px;text-align:center;width:30px"></th>
|
||||
<th style="padding:6px 8px;text-align:left;width:40px">Krok</th>
|
||||
<th style="padding:6px 8px;text-align:left">Nazev</th>
|
||||
<th style="padding:6px 8px;text-align:left;width:70px">Status</th>
|
||||
<th style="padding:6px 8px;text-align:right;width:60px">Cas</th>
|
||||
</tr>
|
||||
%s
|
||||
</table>
|
||||
<div style="padding:16px 24px;background:%s;color:#fff;text-align:center;font-weight:bold">
|
||||
%s — OK: %d CHYBY: %d PRESKOCENO: %d
|
||||
</div>
|
||||
</div>
|
||||
<p style="text-align:center;color:#999;font-size:12px;margin-top:16px">
|
||||
Odeslano z xetup.exe — log: C:\Windows\Setup\Scripts\Deploy.log
|
||||
</p>
|
||||
</body></html>`,
|
||||
hostname, dateTime,
|
||||
rows.String(),
|
||||
summaryColor, summaryText, ok, errs, skipped)
|
||||
}
|
||||
|
|
@ -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: "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"},
|
||||
{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"},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -228,9 +228,9 @@ func (r *Runner) runScript(ctx context.Context, step Step, cfgArg string) error
|
|||
"-LogFile", r.cfg.LogFile,
|
||||
}
|
||||
|
||||
// Pass config path - script loads JSON itself via common.ps1 Load-Config
|
||||
// Pass config object as JSON string (script reads it inline)
|
||||
if cfgArg != "" {
|
||||
args = append(args, "-ConfigPath", cfgArg)
|
||||
args = append(args, "-Config", fmt.Sprintf("(Get-Content '%s' | ConvertFrom-Json)", cfgArg))
|
||||
}
|
||||
|
||||
// ProfileType for step 04
|
||||
|
|
|
|||
|
|
@ -18,12 +18,18 @@
|
|||
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(
|
||||
[string]$ConfigPath,
|
||||
[object]$Config,
|
||||
[string]$LogFile
|
||||
)
|
||||
|
||||
. "$PSScriptRoot\common.ps1"
|
||||
$Config = Load-Config $ConfigPath
|
||||
$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
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Account config - no password by design
|
||||
|
|
|
|||
|
|
@ -14,12 +14,18 @@
|
|||
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(
|
||||
[string]$ConfigPath,
|
||||
[object]$Config,
|
||||
[string]$LogFile
|
||||
)
|
||||
|
||||
. "$PSScriptRoot\common.ps1"
|
||||
$Config = Load-Config $ConfigPath
|
||||
$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
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# 1a - AppX packages
|
||||
|
|
|
|||
|
|
@ -19,12 +19,30 @@
|
|||
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(
|
||||
[string]$ConfigPath,
|
||||
[object]$Config,
|
||||
[string]$LogFile
|
||||
)
|
||||
|
||||
. "$PSScriptRoot\common.ps1"
|
||||
$Config = Load-Config $ConfigPath
|
||||
$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 }
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Check winget availability
|
||||
|
|
@ -61,45 +79,25 @@ if (Get-Feature $Config "software" "wingetInstalls") {
|
|||
if (-not $Config -or -not $Config.software -or -not $Config.software.install) {
|
||||
Write-Log "No software list in config - skipping installs" -Level WARN
|
||||
} else {
|
||||
$packages = @($Config.software.install)
|
||||
Write-Log "Installing $($packages.Count) packages in parallel" -Level INFO
|
||||
foreach ($pkg in $Config.software.install) {
|
||||
Write-Log "Installing $($pkg.name) ($($pkg.wingetId))" -Level INFO
|
||||
$result = & winget install --id $pkg.wingetId `
|
||||
--silent `
|
||||
--accept-package-agreements `
|
||||
--accept-source-agreements `
|
||||
--disable-interactivity `
|
||||
2>&1
|
||||
|
||||
# Launch all winget installs as parallel background jobs
|
||||
$jobs = @()
|
||||
foreach ($pkg in $packages) {
|
||||
Write-Log " Starting: $($pkg.name) ($($pkg.wingetId))" -Level INFO
|
||||
$jobs += Start-Job -ArgumentList $pkg.wingetId, $pkg.name -ScriptBlock {
|
||||
param($wingetId, $name)
|
||||
$output = & winget install --id $wingetId `
|
||||
--silent `
|
||||
--accept-package-agreements `
|
||||
--accept-source-agreements `
|
||||
--disable-interactivity `
|
||||
2>&1
|
||||
[PSCustomObject]@{
|
||||
Name = $name
|
||||
WingetId = $wingetId
|
||||
ExitCode = $LASTEXITCODE
|
||||
Output = ($output -join "`n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Wait for all jobs and collect results
|
||||
Write-Log " Waiting for $($jobs.Count) installs to complete..." -Level INFO
|
||||
$jobs | Wait-Job | Out-Null
|
||||
|
||||
foreach ($job in $jobs) {
|
||||
$r = Receive-Job -Job $job
|
||||
if ($r.ExitCode -eq 0) {
|
||||
Write-Log " Installed OK: $($r.Name)" -Level OK
|
||||
} elseif ($r.ExitCode -eq -1978335189) {
|
||||
Write-Log " Already installed: $($r.Name)" -Level OK
|
||||
$exitCode = $LASTEXITCODE
|
||||
if ($exitCode -eq 0) {
|
||||
Write-Log " Installed OK: $($pkg.name)" -Level OK
|
||||
} elseif ($exitCode -eq -1978335189) {
|
||||
# 0x8A150011 = already installed
|
||||
Write-Log " Already installed: $($pkg.name)" -Level OK
|
||||
} else {
|
||||
Write-Log " Failed: $($r.Name) (exit $($r.ExitCode))" -Level ERROR
|
||||
Write-Log " Output: $($r.Output)" -Level ERROR
|
||||
Write-Log " Failed: $($pkg.name) (exit $exitCode)" -Level ERROR
|
||||
Write-Log " Output: $($result -join ' ')" -Level ERROR
|
||||
}
|
||||
Remove-Job -Job $job
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -197,20 +195,15 @@ if (Get-Feature $Config "software" "ateraAgent") {
|
|||
Invoke-WebRequest -Uri $ateraUrl -OutFile $ateraMsi -UseBasicParsing -ErrorAction Stop
|
||||
Write-Log " Download complete" -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 exit code: $($msiProc.ExitCode)" -Level WARN
|
||||
}
|
||||
$msiResult = & msiexec /i $ateraMsi /qn 2>&1
|
||||
Start-Sleep -Seconds 5
|
||||
|
||||
# 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
|
||||
if (Test-Path $ateraExe) {
|
||||
Write-Log " Atera agent installed" -Level OK
|
||||
} else {
|
||||
Write-Log " Atera agent binary not found at expected paths" -Level WARN
|
||||
Write-Log " Atera agent install may have failed - binary not found at expected path" -Level WARN
|
||||
Write-Log " msiexec output: $($msiResult -join ' ')" -Level WARN
|
||||
}
|
||||
}
|
||||
catch {
|
||||
|
|
|
|||
|
|
@ -28,12 +28,30 @@
|
|||
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(
|
||||
[string]$ConfigPath,
|
||||
[object]$Config,
|
||||
[string]$LogFile
|
||||
)
|
||||
|
||||
. "$PSScriptRoot\common.ps1"
|
||||
$Config = Load-Config $ConfigPath
|
||||
$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 }
|
||||
}
|
||||
|
||||
Add-Type -TypeDefinition @"
|
||||
using System;
|
||||
|
|
@ -292,60 +310,62 @@ if (Get-Feature $Config "systemRegistry" "systemTweaks") {
|
|||
# -----------------------------------------------------------------------
|
||||
if (Get-Feature $Config "systemRegistry" "edgePolicies") {
|
||||
Write-Log " Applying Edge policies" -Level INFO
|
||||
$edgeMandatory = "HKLM:\SOFTWARE\Policies\Microsoft\Edge"
|
||||
$edgeRecommended = "HKLM:\SOFTWARE\Policies\Microsoft\Edge\Recommended"
|
||||
$edgePath = "HKLM:\SOFTWARE\Policies\Microsoft\Edge"
|
||||
|
||||
# --- 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
|
||||
# UI / first run
|
||||
Set-Reg -Path $edgePath -Name "HideFirstRunExperience" -Value 1
|
||||
Set-Reg -Path $edgePath -Name "DefaultBrowserSettingEnabled" -Value 0
|
||||
|
||||
# Telemetry / feedback - always off
|
||||
Set-Reg -Path $edgeMandatory -Name "DiagnosticData" -Value 0
|
||||
Set-Reg -Path $edgeMandatory -Name "FeedbackSurveysEnabled" -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"
|
||||
|
||||
# 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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,48 +1,63 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Applies registry settings to the Default User profile, current user, and sets visual theme.
|
||||
Applies registry settings to the Default User profile and the current logged-in user.
|
||||
|
||||
.DESCRIPTION
|
||||
Loads C:\Users\Default\NTUSER.DAT as a temporary hive (HKU\DefaultProfile), applies
|
||||
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.
|
||||
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.
|
||||
|
||||
.ITEMS
|
||||
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.
|
||||
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.
|
||||
#>
|
||||
param(
|
||||
[string]$ConfigPath,
|
||||
[object]$Config,
|
||||
[string]$LogFile,
|
||||
[ValidateSet("default","admin","user")]
|
||||
[string]$ProfileType = "default"
|
||||
)
|
||||
|
||||
. "$PSScriptRoot\common.ps1"
|
||||
$Config = Load-Config $ConfigPath
|
||||
$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 }
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Helpers - apply registry to both Default hive and current HKCU
|
||||
# Helper - apply a registry setting to both Default hive and current HKCU
|
||||
# -----------------------------------------------------------------------
|
||||
function Grant-HiveWriteAccess {
|
||||
param([string]$HivePath)
|
||||
param([string]$HivePath) # full path e.g. "Registry::HKU\DefaultProfile\Software\..."
|
||||
# Grants Administrators FullControl on a loaded hive key with restricted ACL.
|
||||
try {
|
||||
$acl = Get-Acl -Path $HivePath -ErrorAction Stop
|
||||
$rule = New-Object System.Security.AccessControl.RegistryAccessRule(
|
||||
|
|
@ -62,7 +77,7 @@ function Grant-HiveWriteAccess {
|
|||
|
||||
function Set-ProfileReg {
|
||||
param(
|
||||
[string]$SubKey,
|
||||
[string]$SubKey, # relative to HKCU (e.g. "Software\Microsoft\...")
|
||||
[string]$Name,
|
||||
$Value,
|
||||
[string]$Type = "DWord"
|
||||
|
|
@ -77,6 +92,7 @@ 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 }
|
||||
|
|
@ -104,8 +120,28 @@ function Set-ProfileReg {
|
|||
}
|
||||
}
|
||||
|
||||
# Accent color #223B47 stored as ABGR DWORD: 0xFF473B22
|
||||
$AccentColorABGR = 0xFF473B22
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Load Default profile hive
|
||||
|
|
@ -126,33 +162,41 @@ if ($LASTEXITCODE -ne 0) {
|
|||
Write-Log "Default hive loaded" -Level OK
|
||||
|
||||
try {
|
||||
# ===================================================================
|
||||
# TASKBAR TWEAKS
|
||||
# ===================================================================
|
||||
# -----------------------------------------------------------------------
|
||||
# Taskbar tweaks (alignment, buttons, tray, layout XML)
|
||||
# -----------------------------------------------------------------------
|
||||
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
|
||||
# Win11: align taskbar to left (0 = left, 1 = center)
|
||||
Set-ProfileReg -SubKey $tbPath -Name "TaskbarAl" -Value 0
|
||||
|
||||
# Hide Search box - set both Win10 and Win11 locations
|
||||
# Hide Search box / button - Win10/11 (0 = hidden, 1 = icon, 2 = full box)
|
||||
# Note: Win11 uses Search subkey, Win10 uses Explorer\Advanced - set both
|
||||
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Search" `
|
||||
-Name "SearchboxTaskbarMode" -Value 0
|
||||
Set-ProfileReg -SubKey $tbPath -Name "SearchboxTaskbarMode" -Value 0
|
||||
|
||||
# Hide Task View, Widgets, Chat/Teams, Copilot buttons
|
||||
# Hide Task View button
|
||||
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: clear cached tray icon streams
|
||||
# Win11 workaround: clear cached tray icon streams so all icons appear on next login
|
||||
$trayNotifyKey = "HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify"
|
||||
if (Test-Path $trayNotifyKey) {
|
||||
Remove-ItemProperty -Path $trayNotifyKey -Name "IconStreams" -Force -ErrorAction SilentlyContinue
|
||||
|
|
@ -167,12 +211,21 @@ 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
|
||||
# Taskbar pinned apps layout (Win10/11)
|
||||
# ProfileType: default = empty, admin = Explorer+PS+Edge, user = Explorer+Edge
|
||||
# Note: TaskbarLayoutModification.xml locks the taskbar temporarily.
|
||||
# UnlockStartLayout scheduled task removes the lock 5 min after first boot
|
||||
# so users can then customize pins freely.
|
||||
# Win11 24H2+ may require ProvisionedLayoutModification.xml format instead.
|
||||
Write-Log " Writing taskbar layout (ProfileType=$ProfileType)" -Level INFO
|
||||
|
||||
# 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"
|
||||
|
||||
|
|
@ -186,6 +239,7 @@ 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"
|
||||
|
|
@ -211,12 +265,18 @@ try {
|
|||
<taskbar:DesktopApp DesktopApplicationLinkPath="%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\Microsoft Edge.lnk"/>
|
||||
'@
|
||||
}
|
||||
default {
|
||||
"user" {
|
||||
@'
|
||||
<taskbar:DesktopApp DesktopApplicationLinkPath="%APPDATA%\Microsoft\Windows\Start Menu\Programs\System Tools\File Explorer.lnk"/>
|
||||
<taskbar:DesktopApp DesktopApplicationLinkPath="%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\Microsoft Edge.lnk"/>
|
||||
'@
|
||||
}
|
||||
default {
|
||||
@'
|
||||
<taskbar:DesktopApp DesktopApplicationLinkPath="%APPDATA%\Microsoft\Windows\Start Menu\Programs\System Tools\File Explorer.lnk"/>
|
||||
<taskbar:DesktopApp DesktopApplicationLinkPath="%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\Microsoft Edge.lnk"/>
|
||||
'@
|
||||
} # explicit pins with Replace = no Store, no other defaults
|
||||
}
|
||||
|
||||
$taskbarLayoutXml = @"
|
||||
|
|
@ -242,33 +302,43 @@ $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
|
||||
# ===================================================================
|
||||
# -----------------------------------------------------------------------
|
||||
# Start menu tweaks (pins, Bing, Copilot, GameDVR)
|
||||
# -----------------------------------------------------------------------
|
||||
if (Get-Feature $Config "defaultProfile" "startMenuTweaks") {
|
||||
Write-Log "Applying Start menu tweaks" -Level STEP
|
||||
|
||||
# Disable Bing search suggestions in Start menu
|
||||
Set-ProfileReg -SubKey "Software\Policies\Microsoft\Windows\Explorer" `
|
||||
-Name "DisableSearchBoxSuggestions" -Value 1
|
||||
|
||||
# Win11: empty Start menu pins
|
||||
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Start" `
|
||||
-Name "ConfigureStartPins" `
|
||||
-Value '{"pinnedList":[]}' `
|
||||
-Type "String"
|
||||
|
||||
# Hide "Recently added" apps in Start menu
|
||||
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" `
|
||||
-Name "Start_TrackProgs" -Value 0
|
||||
|
||||
# Hide recently opened files/docs from Start menu
|
||||
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" `
|
||||
-Name "Start_TrackDocs" -Value 0
|
||||
|
||||
# Disable Copilot
|
||||
Set-ProfileReg -SubKey "Software\Policies\Microsoft\Windows\WindowsCopilot" `
|
||||
-Name "TurnOffWindowsCopilot" -Value 1
|
||||
|
||||
# Disable GameDVR
|
||||
Set-ProfileReg -SubKey "System\GameConfigStore" `
|
||||
-Name "GameDVR_Enabled" -Value 0
|
||||
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\GameDVR" `
|
||||
|
|
@ -277,72 +347,34 @@ $pinList
|
|||
Write-Log "startMenuTweaks feature disabled - skipping" -Level INFO
|
||||
}
|
||||
|
||||
# ===================================================================
|
||||
# EXPLORER TWEAKS
|
||||
# ===================================================================
|
||||
# -----------------------------------------------------------------------
|
||||
# Explorer tweaks (file extensions, LaunchTo, ShowRecent, FullPath)
|
||||
# -----------------------------------------------------------------------
|
||||
if (Get-Feature $Config "defaultProfile" "explorerTweaks") {
|
||||
Write-Log "Applying Explorer tweaks" -Level STEP
|
||||
|
||||
$advPath = "Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
|
||||
|
||||
# Show file extensions in Explorer
|
||||
Set-ProfileReg -SubKey $advPath -Name "HideFileExt" -Value 0
|
||||
|
||||
# Open Explorer to This PC instead of Quick Access
|
||||
Set-ProfileReg -SubKey $advPath -Name "LaunchTo" -Value 1
|
||||
|
||||
# Hide Recent files from Quick Access
|
||||
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer" `
|
||||
-Name "ShowRecent" -Value 0
|
||||
|
||||
# Hide Frequent folders from Quick Access
|
||||
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer" `
|
||||
-Name "ShowFrequent" -Value 0
|
||||
|
||||
# Show full path in Explorer title bar
|
||||
Set-ProfileReg -SubKey "Software\Microsoft\Windows\CurrentVersion\Explorer\CabinetState" `
|
||||
-Name "FullPath" -Value 1
|
||||
} else {
|
||||
Write-Log "explorerTweaks feature disabled - skipping" -Level INFO
|
||||
}
|
||||
|
||||
# ===================================================================
|
||||
# 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 {
|
||||
# -----------------------------------------------------------------------
|
||||
|
|
@ -362,23 +394,17 @@ finally {
|
|||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Apply wallpaper (solid color) to current desktop session
|
||||
# Restart Explorer to apply taskbar/tray changes to current session
|
||||
# -----------------------------------------------------------------------
|
||||
Write-Log "Setting desktop wallpaper to solid color" -Level INFO
|
||||
Write-Log "Restarting Explorer to apply taskbar changes" -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
|
||||
[WallpaperHelper]::SystemParametersInfo(20, 0, "", 3) | Out-Null
|
||||
Write-Log " Desktop wallpaper updated" -Level OK
|
||||
Stop-Process -Name explorer -Force -ErrorAction SilentlyContinue
|
||||
Start-Sleep -Seconds 2
|
||||
Start-Process explorer
|
||||
Write-Log "Explorer restarted" -Level OK
|
||||
}
|
||||
catch {
|
||||
Write-Log " Failed to update wallpaper: $_" -Level WARN
|
||||
Write-Log "Explorer restart failed (non-fatal): $_" -Level WARN
|
||||
}
|
||||
|
||||
Write-Log "Step 4 complete" -Level OK
|
||||
|
|
|
|||
208
scripts/05-personalization.ps1
Normal file
208
scripts/05-personalization.ps1
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
<#
|
||||
.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
|
||||
206
scripts/06-scheduled-tasks.ps1
Normal file
206
scripts/06-scheduled-tasks.ps1
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
<#
|
||||
.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
|
||||
|
|
@ -16,12 +16,18 @@
|
|||
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(
|
||||
[string]$ConfigPath,
|
||||
[object]$Config,
|
||||
[string]$LogFile
|
||||
)
|
||||
|
||||
. "$PSScriptRoot\common.ps1"
|
||||
$Config = Load-Config $ConfigPath
|
||||
$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
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Copy BackInfo assets to Program Files
|
||||
|
|
|
|||
253
scripts/07-desktop-info.ps1
Normal file
253
scripts/07-desktop-info.ps1
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
<#
|
||||
.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
|
||||
|
|
@ -17,12 +17,18 @@
|
|||
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(
|
||||
[string]$ConfigPath,
|
||||
[object]$Config,
|
||||
[string]$LogFile
|
||||
)
|
||||
|
||||
. "$PSScriptRoot\common.ps1"
|
||||
$Config = Load-Config $ConfigPath
|
||||
$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
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# KMS Generic Volume License Keys (GVLK)
|
||||
|
|
|
|||
|
|
@ -17,12 +17,18 @@
|
|||
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(
|
||||
[string]$ConfigPath,
|
||||
[object]$Config,
|
||||
[string]$LogFile
|
||||
)
|
||||
|
||||
. "$PSScriptRoot\common.ps1"
|
||||
$Config = Load-Config $ConfigPath
|
||||
$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
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# C:\X9 directory structure
|
||||
|
|
@ -111,7 +117,6 @@ 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) {
|
||||
|
|
@ -121,7 +126,6 @@ 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
|
||||
|
|
@ -132,9 +136,3 @@ 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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,12 +15,18 @@
|
|||
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(
|
||||
[string]$ConfigPath,
|
||||
[object]$Config,
|
||||
[string]$LogFile
|
||||
)
|
||||
|
||||
. "$PSScriptRoot\common.ps1"
|
||||
$Config = Load-Config $ConfigPath
|
||||
$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
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Set network profiles to Private
|
||||
|
|
|
|||
|
|
@ -17,12 +17,37 @@
|
|||
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(
|
||||
[string]$ConfigPath,
|
||||
[object]$Config,
|
||||
[string]$LogFile
|
||||
)
|
||||
|
||||
. "$PSScriptRoot\common.ps1"
|
||||
$Config = Load-Config $ConfigPath
|
||||
$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 }
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Detect Dell hardware
|
||||
|
|
|
|||
|
|
@ -18,12 +18,19 @@
|
|||
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(
|
||||
[string]$ConfigPath,
|
||||
[object]$Config,
|
||||
[string]$LogFile
|
||||
)
|
||||
|
||||
. "$PSScriptRoot\common.ps1"
|
||||
$Config = Load-Config $ConfigPath
|
||||
$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
|
||||
}
|
||||
|
||||
Write-Log "=== Step 12 - Windows Update ===" -Level STEP
|
||||
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
# 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
|
||||
}
|
||||
}
|
||||
|
|
@ -99,6 +99,7 @@ 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")
|
||||
}
|
||||
|
|
@ -116,24 +117,6 @@ 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
|
||||
# -----------------------------------------------------------------------
|
||||
|
|
@ -183,6 +166,10 @@ 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
|
||||
}
|
||||
|
|
@ -191,17 +178,12 @@ Test-Check "Time zone set" {
|
|||
(Get-TimeZone).Id -eq "Central Europe Standard Time"
|
||||
}
|
||||
|
||||
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
|
||||
Test-Check "Deployment date in registry" {
|
||||
(Get-RegValue "HKLM:\SOFTWARE\X9\Deployment" "DeployDate") -ne $null
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Current user (HKCU) - profile + personalization
|
||||
# Current user (HKCU) - personalization
|
||||
# -----------------------------------------------------------------------
|
||||
Write-Host ""
|
||||
Write-Host "--- User settings (current user) ---"
|
||||
|
|
@ -229,60 +211,41 @@ 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 hidden" {
|
||||
Test-Check "Start menu Recommended section 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
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# BackInfo
|
||||
# Scheduled tasks
|
||||
# -----------------------------------------------------------------------
|
||||
Write-Host ""
|
||||
Write-Host "--- BackInfo ---"
|
||||
Write-Host "--- Scheduled tasks ---"
|
||||
|
||||
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
|
||||
$tasks = @("ShowAllTrayIcons", "PDF-DefaultApp", "DesktopInfo", "UnlockStartLayout")
|
||||
foreach ($t in $tasks) {
|
||||
Test-Check "Task registered: $t" {
|
||||
Get-ScheduledTask -TaskName $t -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Network
|
||||
# DesktopInfo
|
||||
# -----------------------------------------------------------------------
|
||||
Write-Host ""
|
||||
Write-Host "--- Network ---"
|
||||
Write-Host "--- DesktopInfo ---"
|
||||
|
||||
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 "Render script exists" {
|
||||
Test-Path "C:\Windows\Setup\Scripts\DesktopInfo-Render.ps1"
|
||||
}
|
||||
|
||||
Test-Check "C:\\X9 has custom icon" {
|
||||
Test-Path "C:\X9\desktop.ini"
|
||||
Test-Check "BMP file exists" {
|
||||
Test-Path "C:\Windows\Setup\Scripts\desktopinfo.bmp"
|
||||
} -WarnOnly
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Reference in a new issue