Files
pxe-server/playbook/shopfloor-setup/Shopfloor/08-EdgeDefaultBrowser.ps1
cproudlock cb2a9d48a1 Shopfloor: Configure-PC tool, machine-number logon prompt, execution order fixes
New tools:

Configure-PC.bat/.ps1 - Interactive desktop tool for SupportUser to
configure a shopfloor PC after imaging. Two sections:
  1. Machine number: if UDC/eDNC are still at placeholder 9999, prompt
     to set the real number right now (updates UDC JSON + eDNC registry,
     restarts UDC.exe with new args).
  2. Auto-startup toggle: pick which apps start at user logon from a
     numbered list (UDC, eDNC, Defect Tracker, WJ Shopfloor, Plant Apps).
     Creates/removes .lnk files in AllUsers Startup folder. Toggle UI
     shows [ON]/[  ] state, safe to re-run anytime. Plant Apps URL
     resolved from .url file at runtime with hardcoded fallback to
     https://mes-wjefferson.apps.lr.geaerospace.net/run/...
  3. Item 6 in the toggle list: register/unregister a "Check Machine
     Number" logon task for standard (non-admin) users. When enabled,
     the task fires at every logon, checks for 9999, pops an InputBox
     if found, updates both apps, then unregisters itself on success.

Check-MachineNumber.ps1 - The logon task script. Runs as the logged-in
user (needs GUI for InputBox), not SYSTEM. Writing to ProgramData + HKLM
is possible because 02-MachineNumberACLs.ps1 pre-grants BUILTIN\Users
write access on the two specific targets during imaging.

02-MachineNumberACLs.ps1 - Standard type-specific script (runs after
01-eDNC.ps1). Opens C:\ProgramData\UDC\udc_settings.json for Users:Modify
and HKLM:\...\GE Aircraft Engines\DNC\General for Users:SetValue. Narrow
scope, not blanket admin.

Execution order fixes in Run-ShopfloorSetup.ps1:

The dispatcher now has two lists: $skipInBaseline (scripts NOT run in the
alphabetical baseline loop) and $runAfterTypeSpecific (scripts run
explicitly after type-specific scripts complete). This fixes the bug where
06/07 ran before 01-eDNC.ps1 installed DnC, so eDNC/NTLARS shortcuts were
silently skipped.

New execution order:
  Baseline: 00-PreInstall, 04-NetworkAndWinRM (skipping 05-08 + tools)
  Type-specific: 01-eDNC, 02-MachineNumberACLs
  Finalization: 06-OrganizeDesktop, 07-TaskbarLayout

06 internally calls 05 (Office shortcuts, Phase 0) and 08 (Edge config,
Phase 4) as sub-phases, so they also benefit from running late. Office
isn't installed until after the first reboot (ppkg streams C2R), so 05
no-ops at imaging time but succeeds when 06's SYSTEM logon task re-runs
it on the second boot. 08 resolves startup-tab URLs from .url files
delivered by DSC (even later); same self-heal via the logon task.

Other fixes in this commit:

- OpenText Setup-OpenText.ps1 Step 4: exclude WJ_Office.lnk, IBM_qks.lnk,
  mmcs.lnk desktop shortcuts (matching the Step 3 .hep profile exclusion
  from the previous commit). Removes stale copies from prior installs.
- 05-OfficeShortcuts.ps1: widened Office detection to 6 path variants
  covering C2R + MSI + Office15/16, with diagnostic output on miss.
- 06-OrganizeDesktop.ps1: removed Phase 3 (desktop-root pin copies for
  eDNC/NTLARS) so shortcuts live in Shopfloor Tools only, not duplicated
  at root. Emptied $keepAtRoot. Added Phase 0 (call 05) and Phase 4
  (call 08). Lazy folder creation + empty-folder cleanup. Scheduled task
  now runs as SYSTEM (was BUILTIN\Users with Limited which failed the
  admin check). Added NTLARS to 07's taskbar pin list.
- 08-EdgeDefaultBrowser.ps1: Plant Apps URL fallback hardcoded from
  device-config.yaml.
- All new scripts have Start-Transcript logging to C:\Logs\SFLD\ with
  timestamps and running-as identity.
- Run-ShopfloorSetup.ps1: Start-Transcript + Stop-Transcript wrapping
  entire dispatcher run, writes to C:\Logs\SFLD\shopfloor-setup.log.
  Configure-PC.bat added to SupportUser desktop copy list.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 08:44:28 -04:00

255 lines
12 KiB
PowerShell

# 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:<xml>
# 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
}
# ----------------------------------------------------------------------------
# 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 = @'
<?xml version="1.0" encoding="UTF-8"?>
<DefaultAssociations>
<Association Identifier=".htm" ProgId="MSEdgeHTM" ApplicationName="Microsoft Edge" />
<Association Identifier=".html" ProgId="MSEdgeHTM" ApplicationName="Microsoft Edge" />
<Association Identifier=".mht" ProgId="MSEdgeMHT" ApplicationName="Microsoft Edge" />
<Association Identifier=".mhtml" ProgId="MSEdgeMHT" ApplicationName="Microsoft Edge" />
<Association Identifier=".pdf" ProgId="MSEdgePDF" ApplicationName="Microsoft Edge" />
<Association Identifier=".svg" ProgId="MSEdgeSVG" ApplicationName="Microsoft Edge" />
<Association Identifier=".webp" ProgId="MSEdgeHTM" ApplicationName="Microsoft Edge" />
<Association Identifier="http" ProgId="MSEdgeHTM" ApplicationName="Microsoft Edge" />
<Association Identifier="https" ProgId="MSEdgeHTM" ApplicationName="Microsoft Edge" />
<Association Identifier="ftp" ProgId="MSEdgeHTM" ApplicationName="Microsoft Edge" />
<Association Identifier="microsoft-edge" ProgId="MSEdgeHTM" ApplicationName="Microsoft Edge" />
</DefaultAssociations>
'@
# 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 = @()
$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