Implement full deployment script suite (steps 1-7)
- Deploy-Windows.ps1: master script with Write-Log, Invoke-Step, summary report, DryRun support
- 01-bloatware.ps1: remove AppX packages, Windows Capabilities, Optional Features
- 02-software.ps1: winget installs from config.json, set Adobe Reader as default PDF app
- 03-system-registry.ps1: HKLM tweaks (NRO bypass, Teams, Widgets, Edge, OneDrive, GameDVR, Recall, timezone)
- 04-default-profile.ps1: NTUSER.DAT changes for taskbar, Explorer, Start menu, NumLock, Copilot
- 05-personalization.ps1: dark/light theme, accent color #223B47, transparency off, wallpaper
- 06-scheduled-tasks.ps1: ShowAllTrayIcons, PDF-DefaultApp, UnlockStartLayout tasks
- 07-desktop-info.ps1: DesktopInfo render script (System.Drawing BMP), scheduled task, deploy date registry
- tests/Test-Deployment.ps1: post-deployment verification, 30+ checks
- CLAUDE.md: add Czech communication preference
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 09:44:38 +01:00
|
|
|
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"
|
|
|
|
|
Add-Content -Path $LogFile -Value $line -Encoding UTF8
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
|
# 1a - AppX packages
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
|
$AppxToRemove = @(
|
|
|
|
|
"Microsoft.Microsoft3DViewer"
|
|
|
|
|
"Microsoft.BingSearch"
|
|
|
|
|
"Microsoft.WindowsCamera"
|
|
|
|
|
"Clipchamp.Clipchamp"
|
|
|
|
|
"Microsoft.WindowsAlarms"
|
|
|
|
|
"Microsoft.Copilot"
|
|
|
|
|
"Microsoft.549981C3F5F10"
|
|
|
|
|
"Microsoft.Windows.DevHome"
|
|
|
|
|
"MicrosoftCorporationII.MicrosoftFamily"
|
|
|
|
|
"Microsoft.WindowsFeedbackHub"
|
|
|
|
|
"Microsoft.Edge.GameAssist"
|
|
|
|
|
"Microsoft.GetHelp"
|
|
|
|
|
"Microsoft.Getstarted"
|
|
|
|
|
"microsoft.windowscommunicationsapps"
|
|
|
|
|
"Microsoft.WindowsMaps"
|
|
|
|
|
"Microsoft.MixedReality.Portal"
|
|
|
|
|
"Microsoft.BingNews"
|
|
|
|
|
"Microsoft.MicrosoftOfficeHub"
|
|
|
|
|
"Microsoft.Office.OneNote"
|
|
|
|
|
"Microsoft.OutlookForWindows"
|
|
|
|
|
"Microsoft.Paint"
|
|
|
|
|
"Microsoft.MSPaint"
|
|
|
|
|
"Microsoft.People"
|
|
|
|
|
"Microsoft.Windows.Photos"
|
|
|
|
|
"Microsoft.PowerAutomateDesktop"
|
|
|
|
|
"MicrosoftCorporationII.QuickAssist"
|
|
|
|
|
"Microsoft.SkypeApp"
|
|
|
|
|
"Microsoft.ScreenSketch"
|
|
|
|
|
"Microsoft.MicrosoftSolitaireCollection"
|
|
|
|
|
"Microsoft.MicrosoftStickyNotes"
|
|
|
|
|
"MicrosoftTeams"
|
|
|
|
|
"MSTeams"
|
|
|
|
|
"Microsoft.Todos"
|
|
|
|
|
"Microsoft.WindowsSoundRecorder"
|
|
|
|
|
"Microsoft.Wallet"
|
|
|
|
|
"Microsoft.BingWeather"
|
|
|
|
|
"Microsoft.WindowsTerminal"
|
|
|
|
|
"Microsoft.Xbox.TCUI"
|
|
|
|
|
"Microsoft.XboxApp"
|
|
|
|
|
"Microsoft.XboxGameOverlay"
|
|
|
|
|
"Microsoft.XboxGamingOverlay"
|
|
|
|
|
"Microsoft.XboxIdentityProvider"
|
|
|
|
|
"Microsoft.XboxSpeechToTextOverlay"
|
|
|
|
|
"Microsoft.GamingApp"
|
|
|
|
|
"Microsoft.YourPhone"
|
|
|
|
|
"Microsoft.ZuneMusic"
|
|
|
|
|
"Microsoft.ZuneVideo"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Packages to always keep
|
|
|
|
|
$KeepPackages = @("Microsoft.WindowsCalculator")
|
|
|
|
|
if ($Config -and $Config.bloatware -and $Config.bloatware.keepPackages) {
|
|
|
|
|
$KeepPackages += $Config.bloatware.keepPackages
|
|
|
|
|
}
|
|
|
|
|
$KeepPackages = $KeepPackages | Select-Object -Unique
|
|
|
|
|
|
|
|
|
|
Write-Log "1a - Removing AppX packages" -Level STEP
|
|
|
|
|
|
|
|
|
|
foreach ($pkg in $AppxToRemove) {
|
|
|
|
|
if ($KeepPackages -contains $pkg) {
|
|
|
|
|
Write-Log " KEEP $pkg" -Level INFO
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Installed packages (current user + all users)
|
|
|
|
|
$installed = Get-AppxPackage -Name $pkg -AllUsers -ErrorAction SilentlyContinue
|
|
|
|
|
if ($installed) {
|
|
|
|
|
try {
|
|
|
|
|
$installed | Remove-AppxPackage -AllUsers -ErrorAction Stop
|
|
|
|
|
Write-Log " Removed AppxPackage: $pkg" -Level OK
|
|
|
|
|
}
|
|
|
|
|
catch {
|
|
|
|
|
Write-Log " Failed to remove AppxPackage $pkg - $_" -Level WARN
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Provisioned packages (for new users)
|
|
|
|
|
$provisioned = Get-AppxProvisionedPackage -Online -ErrorAction SilentlyContinue |
|
|
|
|
|
Where-Object { $_.DisplayName -eq $pkg }
|
|
|
|
|
if ($provisioned) {
|
|
|
|
|
try {
|
|
|
|
|
$provisioned | Remove-AppxProvisionedPackage -Online -ErrorAction Stop | Out-Null
|
|
|
|
|
Write-Log " Removed provisioned: $pkg" -Level OK
|
|
|
|
|
}
|
|
|
|
|
catch {
|
|
|
|
|
Write-Log " Failed to remove provisioned $pkg - $_" -Level WARN
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (-not $installed -and -not $provisioned) {
|
|
|
|
|
Write-Log " Not found (already removed): $pkg" -Level INFO
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
|
# 1b - Windows Capabilities
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
|
$CapabilitiesToRemove = @(
|
|
|
|
|
"Print.Fax.Scan"
|
|
|
|
|
"Language.Handwriting"
|
|
|
|
|
"Browser.InternetExplorer"
|
|
|
|
|
"MathRecognizer"
|
|
|
|
|
"OneCoreUAP.OneSync"
|
|
|
|
|
"OpenSSH.Client"
|
|
|
|
|
"Microsoft.Windows.MSPaint"
|
|
|
|
|
"Microsoft.Windows.PowerShell.ISE"
|
|
|
|
|
"App.Support.QuickAssist"
|
|
|
|
|
"Microsoft.Windows.SnippingTool"
|
|
|
|
|
"App.StepsRecorder"
|
|
|
|
|
"Hello.Face"
|
|
|
|
|
"Media.WindowsMediaPlayer"
|
|
|
|
|
"Microsoft.Windows.WordPad"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
Write-Log "1b - Removing Windows Capabilities" -Level STEP
|
|
|
|
|
|
|
|
|
|
$installedCaps = Get-WindowsCapability -Online -ErrorAction SilentlyContinue
|
|
|
|
|
|
|
|
|
|
foreach ($cap in $CapabilitiesToRemove) {
|
|
|
|
|
# Match by prefix (e.g. Hello.Face matches Hello.Face.20134.0.0.0)
|
|
|
|
|
$matches = $installedCaps | Where-Object {
|
|
|
|
|
$_.Name -like "$cap*" -and $_.State -eq "Installed"
|
|
|
|
|
}
|
|
|
|
|
if ($matches) {
|
|
|
|
|
foreach ($c in $matches) {
|
|
|
|
|
try {
|
|
|
|
|
Remove-WindowsCapability -Online -Name $c.Name -ErrorAction Stop | Out-Null
|
|
|
|
|
Write-Log " Removed capability: $($c.Name)" -Level OK
|
|
|
|
|
}
|
|
|
|
|
catch {
|
|
|
|
|
Write-Log " Failed to remove capability $($c.Name) - $_" -Level WARN
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
Write-Log " Not found or not installed: $cap" -Level INFO
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
|
# 1c - Windows Optional Features
|
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
|
$FeaturesToDisable = @(
|
|
|
|
|
"MediaPlayback"
|
|
|
|
|
"MicrosoftWindowsPowerShellV2Root"
|
|
|
|
|
"Microsoft-RemoteDesktopConnection"
|
|
|
|
|
"Recall"
|
|
|
|
|
"Microsoft-SnippingTool"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
Write-Log "1c - Disabling Windows Optional Features" -Level STEP
|
|
|
|
|
|
|
|
|
|
foreach ($feat in $FeaturesToDisable) {
|
|
|
|
|
$feature = Get-WindowsOptionalFeature -Online -FeatureName $feat -ErrorAction SilentlyContinue
|
|
|
|
|
if ($feature -and $feature.State -eq "Enabled") {
|
|
|
|
|
try {
|
|
|
|
|
Disable-WindowsOptionalFeature -Online -FeatureName $feat -NoRestart -ErrorAction Stop | Out-Null
|
|
|
|
|
Write-Log " Disabled feature: $feat" -Level OK
|
|
|
|
|
}
|
|
|
|
|
catch {
|
|
|
|
|
Write-Log " Failed to disable feature $feat - $_" -Level WARN
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
Write-Log " Not enabled or not found: $feat" -Level INFO
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Write-Log "Step 1 complete" -Level OK
|