Set Chrome as default and pin it, as far as Windows allows
Neither can be done for the running profile: UserChoice is hash-validated and guarded by UCPD.sys since Feb 2024, and taskbar pinning lost its shell verb years ago - both to stop installers helping themselves. What is supported is policy, which applies when a profile is created, so this writes the DefaultAssociationsConfiguration and taskbar layout XMLs for the next user and does the two things that help the current one: a desktop shortcut, and opening the Settings page where the browser choice actually has to be made. Folded into the enrolment script's existing elevation so the install still raises a single UAC prompt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
458d667ca6
commit
8b2e78d766
@@ -0,0 +1,126 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Make Chrome the default browser and pin it to the taskbar, as far as Windows
|
||||||
|
permits that from a script.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Which is not very far, and it is worth knowing why before reading the code.
|
||||||
|
|
||||||
|
The default-browser association lives under UserChoice, whose ProgId is
|
||||||
|
validated against a hash Windows will only generate from its own Settings
|
||||||
|
dialog — and since February 2024 a kernel driver (UCPD.sys) blocks writes to
|
||||||
|
that key regardless. Tools that forge the hash exist; they are working around
|
||||||
|
a security control and Microsoft breaks them on a schedule.
|
||||||
|
|
||||||
|
Taskbar pinning lost its shell verb years ago for the same reason: to stop
|
||||||
|
installers helping themselves to the taskbar.
|
||||||
|
|
||||||
|
What is supported is policy, and policy applies when a user profile is
|
||||||
|
created — so both settings here take effect for the NEXT new user to sign in,
|
||||||
|
not for the profile running this. For the current profile the honest answers
|
||||||
|
are a desktop shortcut, which this makes, and two clicks in Settings, which
|
||||||
|
this opens for you.
|
||||||
|
|
||||||
|
Needs elevation: both policies are machine-wide.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\configure-chrome-defaults.ps1
|
||||||
|
#>
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
# Skip opening the Settings page at the end.
|
||||||
|
[switch]$Quiet
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||||
|
if (-not ([Security.Principal.WindowsPrincipal]$identity).IsInRole(
|
||||||
|
[Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
||||||
|
throw 'Run this elevated — both policies are machine-wide.'
|
||||||
|
}
|
||||||
|
|
||||||
|
$chrome = @("$env:ProgramFiles\Google\Chrome\Application\chrome.exe",
|
||||||
|
"${env:ProgramFiles(x86)}\Google\Chrome\Application\chrome.exe",
|
||||||
|
"$env:LOCALAPPDATA\Google\Chrome\Application\chrome.exe") |
|
||||||
|
Where-Object { $_ -and (Test-Path $_) } | Select-Object -First 1
|
||||||
|
if (-not $chrome) { throw 'Google Chrome is not installed.' }
|
||||||
|
|
||||||
|
$dir = Join-Path $env:ProgramData 'AutoFirmer'
|
||||||
|
New-Item -ItemType Directory -Path $dir -Force | Out-Null
|
||||||
|
|
||||||
|
# ── Default browser ─────────────────────────────────────────────────────────
|
||||||
|
# Only the associations named here are overridden; everything absent from the
|
||||||
|
# file keeps whatever Windows would have chosen.
|
||||||
|
$assoc = Join-Path $dir 'default-associations.xml'
|
||||||
|
@'
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<DefaultAssociations>
|
||||||
|
<Association Identifier="http" ProgId="ChromeHTML" ApplicationName="Google Chrome" />
|
||||||
|
<Association Identifier="https" ProgId="ChromeHTML" ApplicationName="Google Chrome" />
|
||||||
|
<Association Identifier=".htm" ProgId="ChromeHTML" ApplicationName="Google Chrome" />
|
||||||
|
<Association Identifier=".html" ProgId="ChromeHTML" ApplicationName="Google Chrome" />
|
||||||
|
</DefaultAssociations>
|
||||||
|
'@ | Set-Content -Path $assoc -Encoding UTF8
|
||||||
|
|
||||||
|
$sys = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\System'
|
||||||
|
if (-not (Test-Path $sys)) { New-Item -Path $sys -Force | Out-Null }
|
||||||
|
New-ItemProperty -Path $sys -Name 'DefaultAssociationsConfiguration' `
|
||||||
|
-Value $assoc -PropertyType String -Force | Out-Null
|
||||||
|
Write-Host "Default-browser policy written: $assoc"
|
||||||
|
|
||||||
|
# ── Taskbar pin ─────────────────────────────────────────────────────────────
|
||||||
|
# A taskbar layout with no start layout beside it: the collection below adds the
|
||||||
|
# pin, and PinListPlacement="Replace" would wipe the user's own pins, so it is
|
||||||
|
# deliberately absent — ours is appended to whatever is already there.
|
||||||
|
$lnk = Join-Path $dir 'Google Chrome.lnk'
|
||||||
|
$shell = New-Object -ComObject WScript.Shell
|
||||||
|
$shortcut = $shell.CreateShortcut($lnk)
|
||||||
|
$shortcut.TargetPath = $chrome
|
||||||
|
$shortcut.Save()
|
||||||
|
|
||||||
|
$taskbar = Join-Path $dir 'TaskbarLayoutModification.xml'
|
||||||
|
@"
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LayoutModificationTemplate
|
||||||
|
xmlns="http://schemas.microsoft.com/Start/2014/LayoutModification"
|
||||||
|
xmlns:defaultlayout="http://schemas.microsoft.com/Start/2014/FullDefaultLayout"
|
||||||
|
xmlns:start="http://schemas.microsoft.com/Start/2014/StartLayout"
|
||||||
|
xmlns:taskbar="http://schemas.microsoft.com/Start/2014/TaskbarLayout"
|
||||||
|
Version="1">
|
||||||
|
<CustomTaskbarLayoutCollection>
|
||||||
|
<defaultlayout:TaskbarLayout>
|
||||||
|
<taskbar:TaskbarPinList>
|
||||||
|
<taskbar:DesktopApp DesktopApplicationLinkPath="$lnk" />
|
||||||
|
</taskbar:TaskbarPinList>
|
||||||
|
</defaultlayout:TaskbarLayout>
|
||||||
|
</CustomTaskbarLayoutCollection>
|
||||||
|
</LayoutModificationTemplate>
|
||||||
|
"@ | Set-Content -Path $taskbar -Encoding UTF8
|
||||||
|
|
||||||
|
$explorer = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Explorer'
|
||||||
|
if (-not (Test-Path $explorer)) { New-Item -Path $explorer -Force | Out-Null }
|
||||||
|
New-ItemProperty -Path $explorer -Name 'StartLayoutFile' -Value $taskbar -PropertyType ExpandString -Force | Out-Null
|
||||||
|
# 0 so the Start menu itself stays the user's to rearrange; only the taskbar
|
||||||
|
# collection above is applied.
|
||||||
|
New-ItemProperty -Path $explorer -Name 'LockedStartLayout' -Value 0 -PropertyType DWord -Force | Out-Null
|
||||||
|
Write-Host "Taskbar pin policy written: $taskbar"
|
||||||
|
|
||||||
|
# ── The current profile ─────────────────────────────────────────────────────
|
||||||
|
$desktop = [Environment]::GetFolderPath('CommonDesktopDirectory')
|
||||||
|
$deskLnk = $shell.CreateShortcut((Join-Path $desktop 'Google Chrome.lnk'))
|
||||||
|
$deskLnk.TargetPath = $chrome
|
||||||
|
$deskLnk.Save()
|
||||||
|
Write-Host 'Desktop shortcut created.'
|
||||||
|
|
||||||
|
Write-Host @'
|
||||||
|
|
||||||
|
Both policies apply when a user profile is first created, so this profile is
|
||||||
|
unchanged. For this one:
|
||||||
|
|
||||||
|
* Default browser: the Settings page is opening now - pick Google Chrome
|
||||||
|
under "Web browser". Windows allows no other way.
|
||||||
|
* Taskbar: right-click Chrome and choose "Pin to taskbar".
|
||||||
|
'@
|
||||||
|
|
||||||
|
if (-not $Quiet) { Start-Process 'ms-settings:defaultapps' }
|
||||||
@@ -43,7 +43,10 @@ param(
|
|||||||
[switch]$NoElevate,
|
[switch]$NoElevate,
|
||||||
|
|
||||||
# Fail rather than installing Chrome when it is missing.
|
# Fail rather than installing Chrome when it is missing.
|
||||||
[switch]$SkipChromeInstall
|
[switch]$SkipChromeInstall,
|
||||||
|
|
||||||
|
# Skip the default-browser and taskbar-pin policies.
|
||||||
|
[switch]$NoDesktopConfig
|
||||||
)
|
)
|
||||||
|
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
@@ -62,6 +65,7 @@ if (-not ([Security.Principal.WindowsPrincipal]$identity).IsInRole(
|
|||||||
Write-Host 'Asking for administrator rights — the enrolment token is machine-level.'
|
Write-Host 'Asking for administrator rights — the enrolment token is machine-level.'
|
||||||
$psArgs = @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', "`"$PSCommandPath`"", '-NoElevate')
|
$psArgs = @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', "`"$PSCommandPath`"", '-NoElevate')
|
||||||
if ($SkipChromeInstall) { $psArgs += '-SkipChromeInstall' }
|
if ($SkipChromeInstall) { $psArgs += '-SkipChromeInstall' }
|
||||||
|
if ($NoDesktopConfig) { $psArgs += '-NoDesktopConfig' }
|
||||||
if ($Remove) { $psArgs += '-Remove' }
|
if ($Remove) { $psArgs += '-Remove' }
|
||||||
else {
|
else {
|
||||||
$psArgs += @('-Token', $Token)
|
$psArgs += @('-Token', $Token)
|
||||||
@@ -177,6 +181,19 @@ if ($Mandatory) {
|
|||||||
Write-Host 'Enrolment is now mandatory — Chrome will not start unenrolled.'
|
Write-Host 'Enrolment is now mandatory — Chrome will not start unenrolled.'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Already elevated here, so folding this in keeps the whole install to one UAC
|
||||||
|
# prompt rather than two.
|
||||||
|
if (-not $NoDesktopConfig) {
|
||||||
|
$defaults = Join-Path (Split-Path -Parent $PSCommandPath) 'configure-chrome-defaults.ps1'
|
||||||
|
if (Test-Path $defaults) {
|
||||||
|
try {
|
||||||
|
& $defaults -Quiet
|
||||||
|
} catch {
|
||||||
|
Write-Warning "Default-browser and taskbar policies skipped: $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Write-Host @"
|
Write-Host @"
|
||||||
|
|
||||||
Next:
|
Next:
|
||||||
|
|||||||
Reference in New Issue
Block a user