Some checks failed
release / build-and-release (push) Failing after 32s
Critical fixes: - Fix resume mode: StepsByIDs returned Enabled=false, all resume steps would be SKIPPED (deployment could never resume after reboot) - Add reboot loop protection: per-step retry counter (max 5) prevents infinite reboot cycles when a step always exits with code 9 - Block reboot when state.Save() fails in resumePhase (prevents state loss leading to full restart from scratch) - Atomic state file write (write-to-tmp + rename) prevents JSON corruption on BSOD/power loss mid-write - Script watchdog: kills scripts after 30 min of no output (resets on each line, so active long-running scripts are never killed) - Fix copyFile: check Close() error explicitly instead of deferred close that silently drops flush errors (e.g. disk full) High severity: - Cleanup() now logs errors instead of silently ignoring them - Email report: 3 retries with backoff + always saves C:\X9\report.html - Winget parallel jobs: 10 min timeout, kill hung jobs - UCPD stop verification: 2s wait + state check before PDF association - Atera installer: /qn -> /qb so MFA window can appear - GVLK activation: match by EditionID (registry, not localized) instead of fragile OS caption string matching Medium severity: - Default profile hive unload: retry loop (5 attempts, increasing delay) - LayoutModification.xml: UTF-8 without BOM (PS 5.1 Set-Content adds BOM) - Set-Reg SYSTEM task: try/finally ensures temp file + task cleanup - Windows Update: @($available).Count for PS 5.1 single-result edge case - config.json: add missing kmsServer field in activation section Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
468 lines
23 KiB
PowerShell
468 lines
23 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Applies system-wide registry settings and power configuration (HKLM).
|
|
|
|
.DESCRIPTION
|
|
Sets machine-wide registry tweaks under HKLM that apply to all users. Disables
|
|
unwanted telemetry and cloud features, configures Edge policies, sets power plan
|
|
timeouts, and disables proxy auto-detect. Uninstalls the pre-installed OneDrive
|
|
consumer version via OneDriveSetup.exe /uninstall - intentional for a clean MSP
|
|
deployment baseline. No DisableFileSyncNGSC policy key is set, so M365 installation
|
|
can install and run its own OneDrive version without restriction.
|
|
|
|
.ITEMS
|
|
bypass-nro-oobe-bypassnro-1: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\OOBE\BypassNRO = 1. Bypasses the "Let's connect you to a network" OOBE screen. Enables offline Windows setup without forcing a Microsoft account login.
|
|
zakaz-auto-instalace-teams: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Communications\ConfigureChatAutoInstall = 0. Prevents Windows from auto-installing Teams Personal during OOBE or after Cumulative Updates.
|
|
zakaz-cloud-optimized-content: ContentDeliveryManager\DisableCloudOptimizedContent = 1. Stops Windows from pushing sponsored app suggestions, tips from Microsoft servers, and "Get even more from Windows" prompts.
|
|
zakaz-widgets-news-and-interests: HKLM\SOFTWARE\Policies\Microsoft\Dsh\AllowNewsAndInterests = 0. Disables the Widgets taskbar button and panel (news feed, weather, stocks). Not relevant for business deployments.
|
|
hesla-bez-expirace-net-accounts-maxpwage: net accounts /maxpwage:UNLIMITED. Sets the local password expiration policy to never. MSP-managed machines handle password rotation via other means (Atera, domain policy, manual).
|
|
casova-zona-central-europe-standard-time: Set-TimeZone -Id "Central Europe Standard Time". UTC+1 (UTC+2 in summer DST). Applied system-wide. Critical for correct log timestamps, scheduled task timing, and calendar sync.
|
|
zakaz-gamedvr: HKLM\SOFTWARE\Policies\Microsoft\Windows\GameDVR\AppCaptureEnabled = 0. Disables Xbox Game Bar screen capture overlay. Reduces background resource usage and eliminates unintended capture prompts on business machines.
|
|
zakaz-smart-app-control: HKLM\SYSTEM\CurrentControlSet\Control\CI\Policy\VerifiedAndReputablePolicyState = 0. Disables Windows 11 Smart App Control (Intelligent App Control). Prevents SAC from blocking unrecognized business software during deployment. 0=Off, 1=Evaluation, 2=Enforcing. Setting to 0 is permanent - cannot be re-enabled without OS reset.
|
|
zakaz-hibernace-powercfg-h-off: powercfg /hibernate off. Disables hibernation entirely. Removes hiberfil.sys, prevents hibernate-related power state issues, and ensures clean shutdown behavior on business machines.
|
|
edge-skryt-first-run-experience: HideFirstRunExperience=1 + DefaultBrowserSettingEnabled=0 (Mandatory). Suppresses Edge welcome wizard and default browser prompts on first launch.
|
|
edge-telemetrie-mandatory: DiagnosticData=0, FeedbackSurveysEnabled=0 (Mandatory). Telemetry and feedback always off - user cannot change.
|
|
edge-initial-preferences: Writes C:\Program Files (x86)\Microsoft\Edge\Application\initial_preferences. Sets Edge UI defaults (clean NTP, favorites bar visible, home button hidden, search suggestions off) that the user can freely override in Edge settings. Read by Edge once on first profile creation.
|
|
onedrive-uninstall-intentional: Uninstalls the pre-installed OneDrive consumer version via OneDriveSetup.exe /uninstall and removes Start Menu shortcut. Intentional for clean MSP deployment baseline. No DisableFileSyncNGSC policy key is set - M365 installation can reinstall and run OneDrive normally. Only the stock consumer pre-install is removed.
|
|
powercfg-nastaveni-spotreba-energie: powercfg /change: standby-timeout-ac 0 (never sleep on AC), monitor-timeout-ac 60 (screen off after 60 min on AC), standby-timeout-dc 30 (sleep after 30 min on battery), monitor-timeout-dc 15 (screen off after 15 min on battery). Applied to active power plan.
|
|
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,
|
|
[string]$LogFile
|
|
)
|
|
|
|
. "$PSScriptRoot\common.ps1"
|
|
$Config = Load-Config $ConfigPath
|
|
|
|
Add-Type -TypeDefinition @"
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
public class RegPrivilege {
|
|
[DllImport("advapi32.dll", ExactSpelling=true, SetLastError=true)]
|
|
static extern bool AdjustTokenPrivileges(IntPtr htok, bool disAll, ref TokPriv1Luid newState, int len, IntPtr prev, IntPtr relen);
|
|
[DllImport("kernel32.dll", ExactSpelling=true)]
|
|
static extern IntPtr GetCurrentProcess();
|
|
[DllImport("advapi32.dll", ExactSpelling=true, SetLastError=true)]
|
|
static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
|
|
[DllImport("advapi32.dll", SetLastError=true)]
|
|
static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
|
|
[StructLayout(LayoutKind.Sequential, Pack=1)]
|
|
struct TokPriv1Luid { public int Count; public long Luid; public int Attr; }
|
|
const int TOKEN_QUERY = 0x8;
|
|
const int TOKEN_ADJUST = 0x20;
|
|
const int SE_PRIVILEGE_ENABLED = 2;
|
|
public static bool Enable(string privilege) {
|
|
IntPtr htok = IntPtr.Zero;
|
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST | TOKEN_QUERY, ref htok)) return false;
|
|
TokPriv1Luid tp; tp.Count = 1; tp.Luid = 0; tp.Attr = SE_PRIVILEGE_ENABLED;
|
|
if (!LookupPrivilegeValue(null, privilege, ref tp.Luid)) return false;
|
|
return AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
|
|
}
|
|
}
|
|
"@ -ErrorAction SilentlyContinue
|
|
|
|
function Grant-RegWriteAccess {
|
|
param([string]$Path)
|
|
# Grants Administrators FullControl on a TrustedInstaller-owned registry key.
|
|
# Enables SeTakeOwnershipPrivilege + SeRestorePrivilege to override ACL.
|
|
try {
|
|
[RegPrivilege]::Enable("SeTakeOwnershipPrivilege") | Out-Null
|
|
[RegPrivilege]::Enable("SeRestorePrivilege") | Out-Null
|
|
|
|
$hive = $Path -replace '^(HKLM|HKCU|HKU|HKCR|HKCC):\\.*', '$1'
|
|
$subkey = $Path -replace '^(HKLM|HKCU|HKU|HKCR|HKCC):\\', ''
|
|
$rootKey = switch ($hive) {
|
|
"HKLM" { [Microsoft.Win32.Registry]::LocalMachine }
|
|
"HKCU" { [Microsoft.Win32.Registry]::CurrentUser }
|
|
"HKCR" { [Microsoft.Win32.Registry]::ClassesRoot }
|
|
}
|
|
|
|
# Take ownership (requires SeTakeOwnershipPrivilege)
|
|
$key = $rootKey.OpenSubKey($subkey,
|
|
[Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree,
|
|
[System.Security.AccessControl.RegistryRights]::TakeOwnership)
|
|
if ($key) {
|
|
$acl = $key.GetAccessControl([System.Security.AccessControl.AccessControlSections]::None)
|
|
$acl.SetOwner([System.Security.Principal.NTAccount]"BUILTIN\Administrators")
|
|
$key.SetAccessControl($acl)
|
|
$key.Close()
|
|
}
|
|
|
|
# Grant FullControl to Administrators (requires ChangePermissions)
|
|
$key = $rootKey.OpenSubKey($subkey,
|
|
[Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree,
|
|
[System.Security.AccessControl.RegistryRights]::ChangePermissions)
|
|
if ($key) {
|
|
$acl = $key.GetAccessControl()
|
|
$rule = New-Object System.Security.AccessControl.RegistryAccessRule(
|
|
"BUILTIN\Administrators",
|
|
[System.Security.AccessControl.RegistryRights]::FullControl,
|
|
[System.Security.AccessControl.InheritanceFlags]"ContainerInherit,ObjectInherit",
|
|
[System.Security.AccessControl.PropagationFlags]::None,
|
|
[System.Security.AccessControl.AccessControlType]::Allow)
|
|
$acl.SetAccessRule($rule)
|
|
$key.SetAccessControl($acl)
|
|
$key.Close()
|
|
}
|
|
Write-Log " ACL fixed for $Path" -Level INFO
|
|
}
|
|
catch {
|
|
Write-Log " Grant-RegWriteAccess failed for $Path - $_" -Level WARN
|
|
}
|
|
}
|
|
|
|
function Set-Reg {
|
|
param(
|
|
[string]$Path,
|
|
[string]$Name,
|
|
$Value,
|
|
[string]$Type = "DWord"
|
|
)
|
|
try {
|
|
if (-not (Test-Path $Path)) {
|
|
New-Item -Path $Path -Force -ErrorAction Stop | Out-Null
|
|
}
|
|
Set-ItemProperty -Path $Path -Name $Name -Value $Value -Type $Type -Force -ErrorAction Stop
|
|
Write-Log " SET $Path\$Name = $Value" -Level OK
|
|
}
|
|
catch {
|
|
# Retry 1: grant write access via ACL manipulation
|
|
try {
|
|
Grant-RegWriteAccess -Path $Path
|
|
if (-not (Test-Path $Path)) {
|
|
New-Item -Path $Path -Force -ErrorAction Stop | Out-Null
|
|
}
|
|
Set-ItemProperty -Path $Path -Name $Name -Value $Value -Type $Type -Force -ErrorAction Stop
|
|
Write-Log " SET $Path\$Name = $Value (after ACL fix)" -Level OK
|
|
return
|
|
}
|
|
catch { }
|
|
|
|
# Retry 2: write via scheduled task running as SYSTEM
|
|
# SYSTEM has full registry access regardless of key ACL
|
|
$tempScript = $null
|
|
$taskName = $null
|
|
try {
|
|
$regType = switch ($Type) {
|
|
"DWord" { "REG_DWORD" }
|
|
"String" { "REG_SZ" }
|
|
"ExpandString"{ "REG_EXPAND_SZ" }
|
|
"MultiString" { "REG_MULTI_SZ" }
|
|
"QWord" { "REG_QWORD" }
|
|
default { "REG_DWORD" }
|
|
}
|
|
# Convert registry PS path to reg.exe path
|
|
$regPath = $Path -replace '^HKLM:\\', 'HKLM\' `
|
|
-replace '^HKCU:\\', 'HKCU\' `
|
|
-replace '^HKCR:\\', 'HKCR\'
|
|
$tempScript = "$env:TEMP\set-reg-system-$([System.IO.Path]::GetRandomFileName()).ps1"
|
|
"reg add `"$regPath`" /v `"$Name`" /t $regType /d $Value /f" |
|
|
Set-Content -Path $tempScript -Encoding UTF8
|
|
|
|
$taskName = "TempRegFix-$([System.IO.Path]::GetRandomFileName())"
|
|
$action = New-ScheduledTaskAction -Execute "cmd.exe" `
|
|
-Argument "/c reg add `"$regPath`" /v `"$Name`" /t $regType /d $Value /f"
|
|
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest
|
|
$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Seconds 30)
|
|
$task = New-ScheduledTask -Action $action -Principal $principal -Settings $settings
|
|
|
|
Register-ScheduledTask -TaskName $taskName -InputObject $task -Force | Out-Null
|
|
Start-ScheduledTask -TaskName $taskName
|
|
Start-Sleep -Seconds 2
|
|
|
|
# Verify it was written
|
|
$written = (Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue).$Name
|
|
if ($null -ne $written) {
|
|
Write-Log " SET $Path\$Name = $Value (via SYSTEM task)" -Level OK
|
|
} else {
|
|
Write-Log " FAILED $Path\$Name - SYSTEM task ran but value not found" -Level ERROR
|
|
}
|
|
}
|
|
catch {
|
|
Write-Log " FAILED $Path\$Name - $_" -Level ERROR
|
|
}
|
|
finally {
|
|
if ($taskName) {
|
|
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
|
|
}
|
|
if ($tempScript) {
|
|
Remove-Item $tempScript -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function Remove-Reg {
|
|
param([string]$Path, [string]$Name)
|
|
try {
|
|
if (Test-Path $Path) {
|
|
Remove-ItemProperty -Path $Path -Name $Name -Force -ErrorAction SilentlyContinue
|
|
Write-Log " REMOVED $Path\$Name" -Level OK
|
|
}
|
|
}
|
|
catch {
|
|
Write-Log " FAILED removing $Path\$Name - $_" -Level ERROR
|
|
}
|
|
}
|
|
|
|
Write-Log "3 - Applying HKLM system registry tweaks" -Level STEP
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Always-on: password and timezone (fundamental system settings)
|
|
# -----------------------------------------------------------------------
|
|
Write-Log " Setting password max age to UNLIMITED" -Level INFO
|
|
$pwResult = & net accounts /maxpwage:UNLIMITED 2>&1
|
|
if ($LASTEXITCODE -eq 0) {
|
|
Write-Log " Password max age set to UNLIMITED" -Level OK
|
|
} else {
|
|
Write-Log " Failed to set password max age: $pwResult" -Level ERROR
|
|
}
|
|
|
|
$tz = "Central Europe Standard Time"
|
|
if ($Config -and $Config.deployment -and $Config.deployment.timezone) {
|
|
$tz = $Config.deployment.timezone
|
|
}
|
|
Write-Log " Setting time zone: $tz" -Level INFO
|
|
try {
|
|
Set-TimeZone -Id $tz -ErrorAction Stop
|
|
Write-Log " Time zone set: $tz" -Level OK
|
|
}
|
|
catch {
|
|
Write-Log " Failed to set time zone: $_" -Level ERROR
|
|
}
|
|
|
|
# -----------------------------------------------------------------------
|
|
# System tweaks (Windows features, cloud noise, Xbox, Recall, Search UI)
|
|
# -----------------------------------------------------------------------
|
|
if (Get-Feature $Config "systemRegistry" "systemTweaks") {
|
|
Write-Log " Applying system tweaks" -Level INFO
|
|
|
|
# Bypass Network Requirement on OOBE (BypassNRO)
|
|
Set-Reg -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\OOBE" `
|
|
-Name "BypassNRO" -Value 1
|
|
|
|
# Disable auto-install of Teams (Chat)
|
|
Set-Reg -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Communications" `
|
|
-Name "ConfigureChatAutoInstall" -Value 0
|
|
|
|
# Disable Cloud Optimized Content (ads in Start menu etc.)
|
|
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CloudContent" `
|
|
-Name "DisableCloudOptimizedContent" -Value 1
|
|
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CloudContent" `
|
|
-Name "DisableWindowsConsumerFeatures" -Value 1
|
|
|
|
# Disable Widgets (News and Interests)
|
|
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Dsh" `
|
|
-Name "AllowNewsAndInterests" -Value 0
|
|
|
|
# Disable Outlook (new) auto-install via UScheduler
|
|
Write-Log " Disabling Outlook (new) auto-install" -Level INFO
|
|
$uschedulerPaths = @(
|
|
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler_Oobe\OutlookUpdate"
|
|
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Orchestrator\UScheduler\OutlookUpdate"
|
|
)
|
|
foreach ($uPath in $uschedulerPaths) {
|
|
if (Test-Path $uPath) {
|
|
try {
|
|
Remove-Item -Path $uPath -Recurse -Force
|
|
Write-Log " Removed UScheduler key: $uPath" -Level OK
|
|
}
|
|
catch {
|
|
Write-Log " Failed to remove UScheduler key: $_" -Level WARN
|
|
}
|
|
}
|
|
}
|
|
|
|
# Disable GameDVR
|
|
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\GameDVR" `
|
|
-Name "AllowGameDVR" -Value 0
|
|
|
|
# Disable Recall (Windows AI feature)
|
|
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsAI" `
|
|
-Name "DisableAIDataAnalysis" -Value 1
|
|
|
|
# Search on taskbar - hide via HKLM policy (Win11 22H2+ enforcement)
|
|
# User-level SearchboxTaskbarMode alone is insufficient on newer Win11 builds;
|
|
# this policy key ensures the setting survives Windows Updates.
|
|
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Windows Search" `
|
|
-Name "SearchOnTaskbarMode" -Value 0
|
|
|
|
# Start menu - hide Recommended section (Win11)
|
|
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer" `
|
|
-Name "HideRecommendedSection" -Value 1
|
|
|
|
# Disable Smart App Control (Intelligent App Control) - Win11 only key, safe to set on Win10
|
|
# 0=Off, 1=Evaluation, 2=Enforcing. Protected key - Set-Reg SYSTEM task fallback handles ACL.
|
|
Set-Reg -Path "HKLM:\SYSTEM\CurrentControlSet\Control\CI\Policy" `
|
|
-Name "VerifiedAndReputablePolicyState" -Value 0
|
|
} else {
|
|
Write-Log "systemTweaks feature disabled - skipping" -Level INFO
|
|
}
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Microsoft Edge policies + initial_preferences
|
|
#
|
|
# Strategy (two-tier):
|
|
# Mandatory GPO - only privacy/first-run items (user cannot change)
|
|
# initial_preferences - all UI defaults (user CAN change freely in Edge settings)
|
|
#
|
|
# initial_preferences is read by Edge ONCE when a new user profile is created.
|
|
# It has no effect on profiles that already exist (i.e. Edge was already launched).
|
|
# For a clean deployment where xetup runs before any user opens Edge this is reliable.
|
|
# -----------------------------------------------------------------------
|
|
if (Get-Feature $Config "systemRegistry" "edgePolicies") {
|
|
Write-Log " Applying Edge policies" -Level INFO
|
|
$edgeMandatory = "HKLM:\SOFTWARE\Policies\Microsoft\Edge"
|
|
|
|
# --- Mandatory: privacy and first-run suppression only ---
|
|
Set-Reg -Path $edgeMandatory -Name "HideFirstRunExperience" -Value 1
|
|
Set-Reg -Path $edgeMandatory -Name "DefaultBrowserSettingEnabled" -Value 0
|
|
Set-Reg -Path $edgeMandatory -Name "DiagnosticData" -Value 0
|
|
Set-Reg -Path $edgeMandatory -Name "FeedbackSurveysEnabled" -Value 0
|
|
|
|
# Suppress desktop shortcut creation on Edge install/update (EdgeUpdate key)
|
|
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\EdgeUpdate" `
|
|
-Name "CreateDesktopShortcutDefault" -Value 0
|
|
|
|
# --- initial_preferences: UI defaults the user can override ---
|
|
# Path: Edge Application dir (stable channel on x64 Windows)
|
|
$edgeAppDir = "C:\Program Files (x86)\Microsoft\Edge\Application"
|
|
$edgeInitPrefPath = "$edgeAppDir\initial_preferences"
|
|
|
|
if (Test-Path $edgeAppDir) {
|
|
# Build JSON as hashtable - ConvertTo-Json handles serialization
|
|
$prefs = [ordered]@{
|
|
# Favorites bar always visible
|
|
bookmark_bar = [ordered]@{ show_on_all_tabs = $true }
|
|
|
|
browser = [ordered]@{
|
|
show_home_button = $false # hide home button
|
|
}
|
|
|
|
# Disable search-as-you-type suggestions (privacy + less noise)
|
|
search = [ordered]@{ suggest_enabled = $false }
|
|
|
|
# First-run / import suppression (belt-and-suspenders alongside Mandatory policy)
|
|
distribution = [ordered]@{
|
|
suppress_first_run_default_browser_prompt = $true
|
|
skip_first_run_ui = $true
|
|
do_not_create_desktop_shortcut = $true
|
|
make_chrome_default = $false
|
|
import_bookmarks = $false
|
|
import_history = $false
|
|
import_home_page = $false
|
|
import_search_engine = $false
|
|
show_welcome_page = $false
|
|
}
|
|
|
|
# No first-run tabs (e.g. Edge welcome page)
|
|
first_run_tabs = @()
|
|
|
|
# Edge-specific defaults (best-effort: key names are internal/semi-documented)
|
|
# These set sane defaults; if Edge ignores an unknown key it is a no-op.
|
|
edge = [ordered]@{
|
|
newTabPageContentEnabled = $false # clean new tab page
|
|
newTabPageQuickLinksEnabled = $false # no quick links on NTP
|
|
newTabPageBackgroundEnabled = $false # no background image on NTP
|
|
showMicrosoftRewards = $false # no rewards badge
|
|
collections_enabled = $false # no Collections
|
|
shopping_list_enabled = $false # no shopping assistant
|
|
share_experience_enabled = $false # no Share button
|
|
}
|
|
}
|
|
|
|
try {
|
|
$json = $prefs | ConvertTo-Json -Depth 6
|
|
$utf8 = New-Object System.Text.UTF8Encoding $false # no BOM
|
|
[System.IO.File]::WriteAllText($edgeInitPrefPath, $json, $utf8)
|
|
Write-Log " Edge initial_preferences written: $edgeInitPrefPath" -Level OK
|
|
}
|
|
catch {
|
|
Write-Log " Failed to write Edge initial_preferences: $_" -Level WARN
|
|
}
|
|
} else {
|
|
Write-Log " Edge application dir not found - initial_preferences skipped" -Level WARN
|
|
}
|
|
} else {
|
|
Write-Log "edgePolicies feature disabled - skipping" -Level INFO
|
|
}
|
|
|
|
# -----------------------------------------------------------------------
|
|
# OneDrive - uninstall from clean Windows (no policy block)
|
|
# NOTE: No policy key is set intentionally - M365 installation can reinstall
|
|
# and run OneDrive normally. Policy DisableFileSyncNGSC would prevent that.
|
|
# -----------------------------------------------------------------------
|
|
if (Get-Feature $Config "systemRegistry" "oneDriveUninstall") {
|
|
Write-Log " Uninstalling OneDrive" -Level INFO
|
|
|
|
$oneDrivePaths = @(
|
|
"$env:SystemRoot\System32\OneDriveSetup.exe"
|
|
"$env:SystemRoot\SysWOW64\OneDriveSetup.exe"
|
|
)
|
|
foreach ($odPath in $oneDrivePaths) {
|
|
if (Test-Path $odPath) {
|
|
try {
|
|
& $odPath /uninstall 2>&1 | Out-Null
|
|
Write-Log " OneDrive uninstalled via $odPath" -Level OK
|
|
}
|
|
catch {
|
|
Write-Log " OneDrive uninstall failed: $_" -Level WARN
|
|
}
|
|
}
|
|
}
|
|
|
|
# Remove OneDrive Start Menu shortcut
|
|
$odLnk = "$env:ProgramData\Microsoft\Windows\Start Menu\Programs\OneDrive.lnk"
|
|
if (Test-Path $odLnk) {
|
|
Remove-Item $odLnk -Force -ErrorAction SilentlyContinue
|
|
Write-Log " Removed OneDrive Start Menu shortcut" -Level OK
|
|
}
|
|
} else {
|
|
Write-Log "oneDriveUninstall feature disabled - skipping" -Level INFO
|
|
}
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Power configuration
|
|
# -----------------------------------------------------------------------
|
|
if (Get-Feature $Config "systemRegistry" "powercfg") {
|
|
Write-Log " Applying power configuration" -Level INFO
|
|
|
|
# Disable hibernation (removes hiberfil.sys, prevents hibernate-related power issues)
|
|
$hibResult = & powercfg /hibernate off 2>&1
|
|
if ($LASTEXITCODE -eq 0) {
|
|
Write-Log " powercfg /hibernate off" -Level OK
|
|
} else {
|
|
Write-Log " powercfg /hibernate off failed: $hibResult" -Level WARN
|
|
}
|
|
|
|
$powercfgArgs = @(
|
|
@("/change", "standby-timeout-ac", "0"), # never sleep on AC
|
|
@("/change", "monitor-timeout-ac", "60"), # screen off after 60 min on AC
|
|
@("/change", "standby-timeout-dc", "30"), # sleep after 30 min on battery
|
|
@("/change", "monitor-timeout-dc", "15") # screen off after 15 min on battery
|
|
)
|
|
foreach ($a in $powercfgArgs) {
|
|
$result = & powercfg @a 2>&1
|
|
if ($LASTEXITCODE -eq 0) {
|
|
Write-Log " powercfg $($a -join ' ')" -Level OK
|
|
} else {
|
|
Write-Log " powercfg $($a -join ' ') failed: $result" -Level WARN
|
|
}
|
|
}
|
|
} else {
|
|
Write-Log "powercfg feature disabled - skipping" -Level INFO
|
|
}
|
|
|
|
# -----------------------------------------------------------------------
|
|
# Proxy auto-detect disable (WPAD)
|
|
# -----------------------------------------------------------------------
|
|
if (Get-Feature $Config "systemRegistry" "proxyDisable") {
|
|
Write-Log " Disabling WPAD proxy auto-detect" -Level INFO
|
|
Set-Reg -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings" `
|
|
-Name "AutoDetect" -Value 0
|
|
} else {
|
|
Write-Log "proxyDisable feature disabled - skipping" -Level INFO
|
|
}
|
|
|
|
Write-Log "Step 3 complete" -Level OK
|