# 08-EdgeDefaultBrowser.ps1 - Set Microsoft Edge as the default browser # and PDF handler for all new user profiles, and configure startup tabs # so Edge opens with Plant Apps / Shop Floor Homepage / Shopfloor Dashboard # when the end user first launches it. # # WHY THIS EXISTS: # The West Jefferson shopfloor ppkg installs Google Chrome alongside Edge. # On first URL click in a new Azure AD user profile, Windows pops a "Choose # your default app" picker because multiple browsers are installed and # nothing has been marked as default. End users hit this on every fresh PC # and it derails the shopfloor workflow. # # HOW IT WORKS: # Two layers, belt-and-suspenders: # # 1. DISM default-app-associations XML # Writes an XML file mapping http/https/.htm/.html/.pdf/.svg/etc to # Edge's ProgIds, then runs: # dism /Online /Import-DefaultAppAssociations: # This imports the XML into the Default User profile template. Any # NEW user profile created after this point inherits the associations # on first logon. Microsoft-supported, works across Win10/Win11. # # 2. Group Policy registry key # HKLM:\SOFTWARE\Policies\Microsoft\Windows\System\DefaultAssociationsConfiguration # points at the same XML file. This is the "Set a default associations # configuration file" GPO. With this set, Windows re-applies the XML # on every logon, not just once at profile creation - so Windows # update defaults-reset and similar edge cases don't un-do us. # # CAVEATS: # - Applies to NEW profiles on first logon. An existing profile that's # already been logged in (e.g. SupportUser) won't pick up the change # without a manual Settings > Default apps visit. Acceptable because # SupportUser isn't the end-user account. # - Does not UNinstall Chrome. Chrome remains available, it's just not # the default handler for URLs. # - Microsoft blocks programmatic UserChoice hash tampering since Win10 # 1703. The DISM + policy route is the only supported path, and it # only fires for new profiles - that's by design. $ErrorActionPreference = 'Stop' # ---------------------------------------------------------------------------- # Admin check # ---------------------------------------------------------------------------- $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $isAdmin) { Write-Host "" Write-Host "ERROR: 08-EdgeDefaultBrowser.ps1 must run as Administrator." -ForegroundColor Red Write-Host " Re-run from an elevated PowerShell." -ForegroundColor Red Write-Host "" exit 1 } # Load site config + PC profile . "$PSScriptRoot\lib\Get-PCProfile.ps1" # ---------------------------------------------------------------------------- # Sanity: Edge must be installed for this to mean anything # ---------------------------------------------------------------------------- $edgeCandidates = @( 'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe', 'C:\Program Files\Microsoft\Edge\Application\msedge.exe' ) $edgePath = $edgeCandidates | Where-Object { Test-Path -LiteralPath $_ } | Select-Object -First 1 if (-not $edgePath) { Write-Host "Microsoft Edge not found in either Program Files location - skipping default browser config." exit 0 } Write-Host "Found Edge at: $edgePath" # ---------------------------------------------------------------------------- # XML: Edge as default for web browsing + PDFs + common image formats # ---------------------------------------------------------------------------- $xml = @' '@ # Write to a stable location that persists across reimaging (C:\Enrollment is # the canonical staging dir in this repo, survives logoff/logon, referenced # by the GP policy below so it must be a path every user-session can read). $xmlDir = 'C:\Enrollment' $xmlPath = Join-Path $xmlDir 'edge-default-associations.xml' if (-not (Test-Path -LiteralPath $xmlDir)) { New-Item -ItemType Directory -Path $xmlDir -Force | Out-Null } Set-Content -LiteralPath $xmlPath -Value $xml -Encoding UTF8 -Force Write-Host "Wrote associations XML to: $xmlPath" # ---------------------------------------------------------------------------- # Layer 1: DISM import into the Default User profile template # ---------------------------------------------------------------------------- Write-Host "" Write-Host "Layer 1: dism /Online /Import-DefaultAppAssociations..." & dism.exe /Online /Import-DefaultAppAssociations:"$xmlPath" | Out-Null $dismExit = $LASTEXITCODE if ($dismExit -eq 0) { Write-Host " OK - defaults imported into Default User template." } else { Write-Warning " dism returned exit code $dismExit - import may not have applied." } # ---------------------------------------------------------------------------- # Layer 2: Group Policy registry key - enforces XML on every logon # ---------------------------------------------------------------------------- Write-Host "" Write-Host "Layer 2: Setting DefaultAssociationsConfiguration policy..." $polKey = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\System' if (-not (Test-Path $polKey)) { New-Item -Path $polKey -Force | Out-Null } Set-ItemProperty -Path $polKey -Name 'DefaultAssociationsConfiguration' -Value $xmlPath -Type String -Force Write-Host " OK - HKLM:\SOFTWARE\Policies\Microsoft\Windows\System\DefaultAssociationsConfiguration = $xmlPath" Write-Host "" Write-Host "================================================================" Write-Host "Edge default browser config applied." Write-Host "================================================================" # ---------------------------------------------------------------------------- # Edge startup tabs + homepage # # End users should see Plant Apps, the Shop Floor Homepage, and the # Shopfloor Dashboard open in three tabs the moment Edge launches. Rather # than hardcoding URLs in this script, we read them out of the .url files # that already live on the Public Desktop (copied there by ppkg). This # way, if an admin renames or retargets a .url file later, the script # picks up the new URL on the next imaging run without a code change. # # Fallback: if the .url file is missing, we fall back to a hardcoded URL # (only for the ones we know for sure). Plant Apps has no fallback because # I don't have its URL memorized. # # Machine policies (all under HKLM:\SOFTWARE\Policies\Microsoft\Edge): # RestoreOnStartup = 4 (open a specific set of URLs) # RestoreOnStartupURLs = subkey with "1","2","3"... = URLs in tab order # HomepageLocation = first startup URL (home button opens same) # HomepageIsNewTabPage = 0 (home button opens HomepageLocation, not NTP) # ShowHomeButton = 1 (show the home button in the toolbar) # ---------------------------------------------------------------------------- Write-Host "" Write-Host "Configuring Edge startup tabs..." function Get-UrlFromFile { param([string]$Path) try { $content = Get-Content -LiteralPath $Path -ErrorAction Stop $urlLine = $content | Where-Object { $_ -match '^URL=' } | Select-Object -First 1 if ($urlLine) { return ($urlLine -replace '^URL=', '').Trim() } } catch {} return $null } function Resolve-StartupUrl { param( [string]$BaseName, # .url filename without extension [string]$Fallback = '' ) # Look in Public Desktop root first, then Web Links subfolder (06's # sweeper may have moved it there already). $candidates = @( "C:\Users\Public\Desktop\$BaseName.url", "C:\Users\Public\Desktop\Web Links\$BaseName.url" ) foreach ($c in $candidates) { if (Test-Path -LiteralPath $c) { $url = Get-UrlFromFile $c if ($url) { Write-Host " resolved $BaseName -> $url (from $c)" return $url } } } if ($Fallback) { Write-Host " fallback $BaseName -> $Fallback (.url file not found)" -ForegroundColor DarkGray return $Fallback } Write-Warning " $BaseName : no .url file found and no fallback - will be skipped" return $null } # Tab order as requested: Plant Apps, Shop Floor Homepage, Shopfloor Dashboard $startupTabs = @() if ($siteConfig -and $siteConfig.edgeStartupTabs) { foreach ($tab in $siteConfig.edgeStartupTabs) { $fallback = if ($tab.fallbackUrlKey -and $siteConfig.urls) { $siteConfig.urls.$($tab.fallbackUrlKey) } else { '' } $url = Resolve-StartupUrl -BaseName $tab.baseName -Fallback $fallback if ($url) { $startupTabs += $url } } } else { $plantApps = Resolve-StartupUrl -BaseName 'Plant Apps' -Fallback 'https://mes-wjefferson.apps.lr.geaerospace.net/run/?app_name=Plant%20Applications' if ($plantApps) { $startupTabs += $plantApps } $shopFloorHome = Resolve-StartupUrl -BaseName 'WJ Shop Floor Homepage' -Fallback 'http://tsgwp00524.logon.ds.ge.com/' if ($shopFloorHome) { $startupTabs += $shopFloorHome } $dashboard = Resolve-StartupUrl -BaseName 'Shopfloor Dashboard' -Fallback 'https://tsgwp00525.wjs.geaerospace.net/shopdb/shopfloor-dashboard/' if ($dashboard) { $startupTabs += $dashboard } } if ($startupTabs.Count -eq 0) { Write-Warning "No startup tab URLs resolved - skipping Edge startup config." } else { $edgePolKey = 'HKLM:\SOFTWARE\Policies\Microsoft\Edge' if (-not (Test-Path $edgePolKey)) { New-Item -Path $edgePolKey -Force | Out-Null } # RestoreOnStartup = 4 means "open a specific set of URLs" (the list # lives under the RestoreOnStartupURLs subkey below). Set-ItemProperty -Path $edgePolKey -Name 'RestoreOnStartup' -Value 4 -Type DWord -Force # Build RestoreOnStartupURLs subkey: numbered string values "1","2","3" $urlsKey = Join-Path $edgePolKey 'RestoreOnStartupURLs' if (Test-Path $urlsKey) { # Wipe any prior entries so we don't leak stale tabs if the list # shrinks between runs Remove-Item -Path $urlsKey -Recurse -Force } New-Item -Path $urlsKey -Force | Out-Null for ($i = 0; $i -lt $startupTabs.Count; $i++) { $name = [string]($i + 1) Set-ItemProperty -Path $urlsKey -Name $name -Value $startupTabs[$i] -Type String -Force } # Homepage = the first startup tab. Home button opens it, not NTP. Set-ItemProperty -Path $edgePolKey -Name 'HomepageLocation' -Value $startupTabs[0] -Type String -Force Set-ItemProperty -Path $edgePolKey -Name 'HomepageIsNewTabPage' -Value 0 -Type DWord -Force Set-ItemProperty -Path $edgePolKey -Name 'ShowHomeButton' -Value 1 -Type DWord -Force Write-Host "" Write-Host "Edge startup tabs set ($($startupTabs.Count) tab(s)):" for ($i = 0; $i -lt $startupTabs.Count; $i++) { Write-Host " $($i + 1). $($startupTabs[$i])" } Write-Host "Homepage set to: $($startupTabs[0])" } Write-Host "" Write-Host "================================================================" Write-Host "08-EdgeDefaultBrowser.ps1 complete." Write-Host "Effective for new user profiles on first logon (Azure AD users)." Write-Host "Existing profiles will pick up the startup-tab policy on next" Write-Host "Edge launch - it's a machine-wide GPO." Write-Host "================================================================" exit 0