Shopfloor: desktop folder org, taskbar pins, Edge defaults
Three new baseline scripts that run during shopfloor imaging to clean up
the end-user Public Desktop. Before this, Azure AD users logged into a
shopfloor PC and saw 20+ loose shortcuts at the desktop root (Office
apps, OpenText sessions, WJ web portals, DNC utilities, Defect Tracker,
plus .url files for every intranet page) with no organization. End users
couldn't find anything.
06-OrganizeDesktop.ps1 - Single source of truth for Public Desktop layout
Phase 1: sweeps loose shortcuts at the desktop root into three category
folders - Office\, Shopfloor Tools\, Web Links\ - by filename regex,
extension, and .lnk target resolution. Allowlists eDNC.lnk and
NTLARS.lnk to stay at root since end users click them too often.
Unknown items are left at the root on purpose (never delete).
Phase 2: materializes specific app shortcuts into Shopfloor Tools\.
UDC / eDNC / NTLARS are built fresh from their .exe paths; WJ
Shopfloor and Defect_Tracker are MSI-advertised (empty TargetPath,
Darwin descriptor) so we copy the existing .lnk from wherever it
lives via a multi-location lookup. Each entry is conditional on its
source being present - script runs cleanly on PC types without DnC.
Phase 3: drops eDNC.lnk and NTLARS.lnk at desktop root from the
Shopfloor Tools\ copies, so end users have both a folder version
and a quick-access root version.
Phase 4: registers an "Organize Public Desktop" scheduled task that
re-runs phase 1 at every logon. Shortcuts added later by DSC /
Intune / msiexec get filed automatically without another imaging
pass. Admin check at the top, -ErrorAction Stop on Register-
ScheduledTask and directory creation so failures are caught
instead of printing false success.
07-TaskbarLayout.ps1 - Minimal taskbar pinner
Checks which .lnk files 06 created in Shopfloor Tools\, then writes
LayoutModification.xml to the Default User profile with taskbar pins
in order: Edge, WJ Shopfloor, UDC, eDNC, Defect_Tracker. No shortcut
creation in this script - all shortcut management lives in 06.
Missing .lnks are skipped (PC types without DnC just get fewer pins).
Applies on first logon of new user profiles (Azure AD users after
enrollment). Existing profiles don't re-read Default User - Windows
design limitation since 1703, no programmatic fix.
08-EdgeDefaultBrowser.ps1 - Edge as default browser + startup tabs
Motivated by the ppkg installing Chrome alongside Edge: new Azure AD
users hit a "Choose your default app" picker on first URL click
because nothing is marked default. Two layers:
1. dism /Online /Import-DefaultAppAssociations:<xml> writes an XML
with Edge ProgIds for http/https/.htm/.html/.pdf/.svg/.webp into
the Default User profile template. New profiles inherit.
2. HKLM:\SOFTWARE\Policies\Microsoft\Windows\System\
DefaultAssociationsConfiguration registry value (the "Set a
default associations configuration file" GPO) points at the same
XML so Windows re-applies on every logon, catching Windows-update
defaults-reset cases.
Leaves Chrome installed, just not the default URL handler.
Also sets Edge startup tabs via machine-wide policies under
HKLM:\SOFTWARE\Policies\Microsoft\Edge:
RestoreOnStartup = 4 (open specific URLs)
RestoreOnStartupURLs = Plant Apps, WJ Shop Floor Homepage, Shopfloor
Dashboard (tab order per spec)
HomepageLocation = first tab (Plant Apps)
HomepageIsNewTabPage = 0
ShowHomeButton = 1
URLs are resolved dynamically from the .url files on the Public
Desktop (or Web Links\ after the sweep), so if WJDT changes a URL
later the script picks it up without a code change. Fallbacks are
hardcoded for the two portals we have URLs memorized for; Plant Apps
has no fallback and will be skipped if the .url file is missing.
Test workflow: admin-check in all three scripts fails fast on non-
elevated runs instead of spamming half-successful Access Denied output
like the first draft did.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
418
playbook/shopfloor-setup/Shopfloor/06-OrganizeDesktop.ps1
Normal file
418
playbook/shopfloor-setup/Shopfloor/06-OrganizeDesktop.ps1
Normal file
@@ -0,0 +1,418 @@
|
|||||||
|
# 06-OrganizeDesktop.ps1 - Single source of truth for the Public Desktop
|
||||||
|
# layout. Builds three category folders and fills them, so end users see
|
||||||
|
# a clean desktop instead of 20+ loose icons.
|
||||||
|
#
|
||||||
|
# Creates three subfolders under C:\Users\Public\Desktop:
|
||||||
|
# Office\ - Excel, Word, PowerPoint, Outlook, OneNote, etc.
|
||||||
|
# Shopfloor Tools\ - UDC, eDNC, HostExplorer, MarkZebra, PC-DMIS, Hexagon,
|
||||||
|
# WJ Shopfloor, Defect_Tracker, NTLARS, etc.
|
||||||
|
# Web Links\ - .url shortcuts (internal portals)
|
||||||
|
#
|
||||||
|
# Two phases:
|
||||||
|
# PHASE 1 (Invoke-DesktopSweep): walks everything at the desktop ROOT,
|
||||||
|
# classifies each .lnk / .url by filename / extension / .lnk target, and
|
||||||
|
# moves it into the right category folder. Unknown items are LEFT AT
|
||||||
|
# THE ROOT on purpose. eDNC.lnk and NTLARS.lnk are allowlisted to stay
|
||||||
|
# at the root - end users click them too often to bury in a folder.
|
||||||
|
#
|
||||||
|
# PHASE 2 (Add-ShopfloorToolsApps): materializes specific app shortcuts
|
||||||
|
# into Shopfloor Tools\ from known .exe paths (UDC, eDNC, NTLARS). For
|
||||||
|
# MSI-advertised shortcuts that don't have a resolvable target (WJ
|
||||||
|
# Shopfloor, Defect_Tracker), finds the existing .lnk wherever it lives
|
||||||
|
# and copies it in. This runs AFTER the sweep so it overwrites any
|
||||||
|
# stale copies the sweeper may have moved in.
|
||||||
|
#
|
||||||
|
# Also registers an "Organize Public Desktop" scheduled task that re-runs
|
||||||
|
# the sweep (phase 1) at every logon. Phase 2 doesn't re-run - the
|
||||||
|
# shortcuts it creates are stable and 06 on first run populates them.
|
||||||
|
#
|
||||||
|
# 07-TaskbarLayout.ps1 reads the result of this script and writes
|
||||||
|
# LayoutModification.xml to pin specific shortcuts to the taskbar.
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Admin check - writing to C:\Users\Public\Desktop\* and registering
|
||||||
|
# system-wide scheduled tasks both require elevation. Fail fast if not
|
||||||
|
# admin so we don't spam half-successful garbage across the output.
|
||||||
|
# ============================================================================
|
||||||
|
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||||
|
if (-not $isAdmin) {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "ERROR: 06-OrganizeDesktop.ps1 must run as Administrator." -ForegroundColor Red
|
||||||
|
Write-Host " Re-run from an elevated PowerShell:" -ForegroundColor Red
|
||||||
|
Write-Host " Start -> type 'powershell' -> right-click -> Run as administrator" -ForegroundColor Red
|
||||||
|
Write-Host ""
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$publicDesktop = 'C:\Users\Public\Desktop'
|
||||||
|
$shopfloorToolsDir = Join-Path $publicDesktop 'Shopfloor Tools'
|
||||||
|
$scriptPath = $MyInvocation.MyCommand.Path
|
||||||
|
|
||||||
|
# Filenames that always stay at the desktop root regardless of classification.
|
||||||
|
# End users click these many times a day and an extra folder click is real
|
||||||
|
# friction. Phase 2 also drops these here as a post-sweep safety net.
|
||||||
|
$keepAtRoot = @(
|
||||||
|
'eDNC.lnk',
|
||||||
|
'NTLARS.lnk'
|
||||||
|
)
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Phase 1: Sweep loose shortcuts at desktop root into category folders
|
||||||
|
# ============================================================================
|
||||||
|
function Invoke-DesktopSweep {
|
||||||
|
param([string]$DesktopPath)
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $DesktopPath)) {
|
||||||
|
Write-Host "Public desktop not found at $DesktopPath - skipping sweep."
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
# Category definitions. Order matters: first match wins. Each category
|
||||||
|
# can classify by file Extension, file Name (regex), or .lnk Target
|
||||||
|
# (regex, resolved via WScript.Shell).
|
||||||
|
$categories = [ordered]@{
|
||||||
|
'Office' = @{
|
||||||
|
Name = @(
|
||||||
|
'^(Excel|Word|PowerPoint|Outlook|OneNote|Access|Publisher|OneDrive)(\s|$)'
|
||||||
|
)
|
||||||
|
Target = @(
|
||||||
|
'\\(EXCEL|WINWORD|POWERPNT|OUTLOOK|ONENOTE|MSACCESS|MSPUB)\.EXE$',
|
||||||
|
'\\OneDrive\.exe$'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
'Shopfloor Tools' = @{
|
||||||
|
Name = @(
|
||||||
|
'^UDC',
|
||||||
|
'eDNC', '\bDNC\b', 'DncMain', 'GE DNC', 'NTLARS',
|
||||||
|
'Host\s*Explorer', 'ShopFloor', 'TN3270', 'TN5250', 'HE\s*3270', 'HE\s*5250',
|
||||||
|
'OpenText',
|
||||||
|
'Defect[_\s-]?Tracker',
|
||||||
|
'MarkZebra', 'Zebra',
|
||||||
|
'PC-?DMIS',
|
||||||
|
'Hexagon', 'CLM\s*Tools',
|
||||||
|
'Blancco',
|
||||||
|
'Keyence'
|
||||||
|
)
|
||||||
|
Target = @(
|
||||||
|
'\\UDC\.exe$',
|
||||||
|
'DncMain\.exe$', 'NTLARS\.exe$', 'DNCRemote',
|
||||||
|
'HostExplorer\.exe$', 'hostex32\.exe$', 'humicon15\.exe$',
|
||||||
|
'PC-?DMIS',
|
||||||
|
'Hexagon',
|
||||||
|
'Defect_Tracker\.application$'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
'Web Links' = @{
|
||||||
|
Extension = @('.url')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ensure category folders exist
|
||||||
|
foreach ($cat in $categories.Keys) {
|
||||||
|
$dir = Join-Path $DesktopPath $cat
|
||||||
|
if (-not (Test-Path -LiteralPath $dir)) {
|
||||||
|
try {
|
||||||
|
New-Item -ItemType Directory -Path $dir -Force -ErrorAction Stop | Out-Null
|
||||||
|
} catch {
|
||||||
|
Write-Warning "Failed to create category folder '$cat' : $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# WScript.Shell for resolving .lnk targets
|
||||||
|
$shell = $null
|
||||||
|
try { $shell = New-Object -ComObject WScript.Shell } catch {}
|
||||||
|
|
||||||
|
# Only sweep files at the desktop root, never recurse into the category
|
||||||
|
# folders themselves (that would loop things back).
|
||||||
|
$items = @(Get-ChildItem -LiteralPath $DesktopPath -File -ErrorAction SilentlyContinue)
|
||||||
|
|
||||||
|
$moved = 0
|
||||||
|
$skipped = 0
|
||||||
|
|
||||||
|
foreach ($item in $items) {
|
||||||
|
# Allowlist check: never sweep items the user wants to keep visible
|
||||||
|
if ($keepAtRoot -contains $item.Name) {
|
||||||
|
Write-Host " keep: $($item.Name) (allowlisted)"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
$category = $null
|
||||||
|
|
||||||
|
# 1. Extension match (cheapest)
|
||||||
|
foreach ($cat in $categories.Keys) {
|
||||||
|
$exts = $categories[$cat].Extension
|
||||||
|
if ($exts -and ($exts -contains $item.Extension)) {
|
||||||
|
$category = $cat
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. File name regex match
|
||||||
|
if (-not $category) {
|
||||||
|
foreach ($cat in $categories.Keys) {
|
||||||
|
$pats = $categories[$cat].Name
|
||||||
|
if (-not $pats) { continue }
|
||||||
|
foreach ($p in $pats) {
|
||||||
|
if ($item.BaseName -match $p) {
|
||||||
|
$category = $cat
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($category) { break }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3. .lnk target resolution
|
||||||
|
if (-not $category -and $item.Extension -ieq '.lnk' -and $shell) {
|
||||||
|
try {
|
||||||
|
$sc = $shell.CreateShortcut($item.FullName)
|
||||||
|
$target = $sc.TargetPath
|
||||||
|
if ($target) {
|
||||||
|
foreach ($cat in $categories.Keys) {
|
||||||
|
$pats = $categories[$cat].Target
|
||||||
|
if (-not $pats) { continue }
|
||||||
|
foreach ($p in $pats) {
|
||||||
|
if ($target -match $p) {
|
||||||
|
$category = $cat
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($category) { break }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($category) {
|
||||||
|
$destDir = Join-Path $DesktopPath $category
|
||||||
|
$destPath = Join-Path $destDir $item.Name
|
||||||
|
try {
|
||||||
|
Move-Item -LiteralPath $item.FullName -Destination $destPath -Force -ErrorAction Stop
|
||||||
|
Write-Host " moved: $($item.Name) -> $category\"
|
||||||
|
$moved++
|
||||||
|
} catch {
|
||||||
|
Write-Warning "Failed to move $($item.Name) : $_"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$skipped++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($shell) {
|
||||||
|
try { [System.Runtime.InteropServices.Marshal]::ReleaseComObject($shell) | Out-Null } catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Sweep complete: $moved moved, $skipped unclassified (left at root)."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Phase 2: Materialize specific app shortcuts into Shopfloor Tools\
|
||||||
|
#
|
||||||
|
# Runs AFTER the sweep. Creates fresh .lnk files for apps we can target by
|
||||||
|
# .exe path (UDC, eDNC, NTLARS) and copies existing .lnk files for apps
|
||||||
|
# that are MSI-advertised and have no resolvable target (WJ Shopfloor,
|
||||||
|
# Defect_Tracker). Overwrites whatever the sweeper may have moved in -
|
||||||
|
# this is intentional because fresh shortcuts are more reliable than
|
||||||
|
# whatever install crud might be floating around.
|
||||||
|
#
|
||||||
|
# Each app is CONDITIONAL - if its source isn't present on this PC, the
|
||||||
|
# entry is silently skipped. Safe to run on all shopfloor PC types.
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
function New-ShopfloorLnk {
|
||||||
|
param(
|
||||||
|
[string]$Path,
|
||||||
|
[string]$Target,
|
||||||
|
[string]$Arguments = '',
|
||||||
|
[string]$WorkingDirectory = ''
|
||||||
|
)
|
||||||
|
$wsh = New-Object -ComObject WScript.Shell
|
||||||
|
try {
|
||||||
|
$sc = $wsh.CreateShortcut($Path)
|
||||||
|
$sc.TargetPath = $Target
|
||||||
|
if ($Arguments) { $sc.Arguments = $Arguments }
|
||||||
|
if ($WorkingDirectory) { $sc.WorkingDirectory = $WorkingDirectory }
|
||||||
|
elseif ($Target) {
|
||||||
|
$parent = Split-Path -Parent $Target
|
||||||
|
if ($parent) { $sc.WorkingDirectory = $parent }
|
||||||
|
}
|
||||||
|
$sc.IconLocation = "$Target,0"
|
||||||
|
$sc.Save()
|
||||||
|
return $true
|
||||||
|
} catch {
|
||||||
|
Write-Warning "Failed to create $Path : $_"
|
||||||
|
return $false
|
||||||
|
} finally {
|
||||||
|
try { [System.Runtime.InteropServices.Marshal]::ReleaseComObject($wsh) | Out-Null } catch {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Look up an existing .lnk file across the paths where installers / ppkgs
|
||||||
|
# typically drop them. Used for apps where we don't build the .lnk from
|
||||||
|
# an .exe (because the target is an MSI-advertised Darwin descriptor).
|
||||||
|
function Find-ExistingLnk {
|
||||||
|
param([string]$Filename)
|
||||||
|
|
||||||
|
$candidates = @(
|
||||||
|
(Join-Path $publicDesktop $Filename),
|
||||||
|
(Join-Path $shopfloorToolsDir $Filename),
|
||||||
|
"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\$Filename",
|
||||||
|
"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Shopfloor Tools\$Filename"
|
||||||
|
)
|
||||||
|
foreach ($c in $candidates) {
|
||||||
|
if (Test-Path -LiteralPath $c) { return $c }
|
||||||
|
}
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
function Add-ShopfloorToolsApps {
|
||||||
|
if (-not (Test-Path -LiteralPath $shopfloorToolsDir)) {
|
||||||
|
try {
|
||||||
|
New-Item -ItemType Directory -Path $shopfloorToolsDir -Force -ErrorAction Stop | Out-Null
|
||||||
|
} catch {
|
||||||
|
Write-Warning "Failed to create $shopfloorToolsDir : $_"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# App registry - each entry describes how to materialize one shortcut
|
||||||
|
# into Shopfloor Tools\.
|
||||||
|
#
|
||||||
|
# Kind = 'exe' -> build a fresh .lnk from ExePath
|
||||||
|
# Kind = 'existing' -> copy an existing .lnk via Find-ExistingLnk
|
||||||
|
$apps = @(
|
||||||
|
@{ Name = 'UDC'; Kind = 'exe'; ExePath = 'C:\Program Files\UDC\UDC.exe' }
|
||||||
|
@{ Name = 'eDNC'; Kind = 'exe'; ExePath = 'C:\Program Files (x86)\Dnc\bin\DncMain.exe' }
|
||||||
|
@{ Name = 'NTLARS'; Kind = 'exe'; ExePath = 'C:\Program Files (x86)\Dnc\Common\NTLARS.exe' }
|
||||||
|
@{ Name = 'WJ Shopfloor'; Kind = 'existing'; SourceName = 'WJ Shopfloor.lnk' }
|
||||||
|
@{ Name = 'Defect_Tracker'; Kind = 'existing'; SourceName = 'Defect_Tracker.lnk' }
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach ($app in $apps) {
|
||||||
|
$dest = Join-Path $shopfloorToolsDir "$($app.Name).lnk"
|
||||||
|
|
||||||
|
switch ($app.Kind) {
|
||||||
|
'exe' {
|
||||||
|
if (Test-Path -LiteralPath $app.ExePath) {
|
||||||
|
if (New-ShopfloorLnk -Path $dest -Target $app.ExePath) {
|
||||||
|
Write-Host " created: $($app.Name) -> $dest"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host " skip: $($app.Name) - target not installed ($($app.ExePath))" -ForegroundColor DarkGray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
'existing' {
|
||||||
|
$src = Find-ExistingLnk $app.SourceName
|
||||||
|
if ($src) {
|
||||||
|
try {
|
||||||
|
Copy-Item -LiteralPath $src -Destination $dest -Force -ErrorAction Stop
|
||||||
|
Write-Host " copied: $($app.Name) from $src"
|
||||||
|
} catch {
|
||||||
|
Write-Warning "Failed to copy $src -> $dest : $_"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host " skip: $($app.Name) - no source .lnk found" -ForegroundColor DarkGray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Phase 3: Pin eDNC and NTLARS shortcuts at the desktop root
|
||||||
|
#
|
||||||
|
# These are the two shortcuts end users touch most frequently. Allowlisted
|
||||||
|
# in the sweep, and also force-dropped here (copied from the Shopfloor
|
||||||
|
# Tools\ versions we just created) so a sweep-and-forget user still ends
|
||||||
|
# up with them loose at the root.
|
||||||
|
# ============================================================================
|
||||||
|
function Update-DesktopRootPins {
|
||||||
|
foreach ($name in @('eDNC.lnk', 'NTLARS.lnk')) {
|
||||||
|
$src = Join-Path $shopfloorToolsDir $name
|
||||||
|
$dst = Join-Path $publicDesktop $name
|
||||||
|
if (Test-Path -LiteralPath $src) {
|
||||||
|
try {
|
||||||
|
Copy-Item -LiteralPath $src -Destination $dst -Force -ErrorAction Stop
|
||||||
|
Write-Host " root: $name"
|
||||||
|
} catch {
|
||||||
|
Write-Warning "Failed to drop $dst : $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Scheduled task registration (phase 1 re-run at every logon)
|
||||||
|
# ============================================================================
|
||||||
|
function Register-SweepScheduledTask {
|
||||||
|
param([string]$ScriptPath)
|
||||||
|
|
||||||
|
$taskName = 'Organize Public Desktop'
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $ScriptPath)) {
|
||||||
|
Write-Warning "Cannot register scheduled task - script not found at $ScriptPath"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$action = New-ScheduledTaskAction `
|
||||||
|
-Execute 'powershell.exe' `
|
||||||
|
-Argument "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$ScriptPath`""
|
||||||
|
|
||||||
|
$trigger = New-ScheduledTaskTrigger -AtLogOn
|
||||||
|
|
||||||
|
# Run as whichever user logs in, at Limited (standard) rights. Public
|
||||||
|
# desktop is writable by BUILTIN\Users so no elevation needed. Using
|
||||||
|
# the well-known Users group SID so this works on non-English Windows.
|
||||||
|
$principal = New-ScheduledTaskPrincipal `
|
||||||
|
-GroupId 'S-1-5-32-545' `
|
||||||
|
-RunLevel Limited
|
||||||
|
|
||||||
|
$settings = New-ScheduledTaskSettingsSet `
|
||||||
|
-AllowStartIfOnBatteries `
|
||||||
|
-DontStopIfGoingOnBatteries `
|
||||||
|
-StartWhenAvailable `
|
||||||
|
-ExecutionTimeLimit (New-TimeSpan -Minutes 5)
|
||||||
|
|
||||||
|
# -ErrorAction Stop is required because Register-ScheduledTask emits
|
||||||
|
# non-terminating errors on failure (Access Denied etc), which try/catch
|
||||||
|
# would otherwise miss and we'd falsely claim success.
|
||||||
|
Register-ScheduledTask `
|
||||||
|
-TaskName $taskName `
|
||||||
|
-Action $action `
|
||||||
|
-Trigger $trigger `
|
||||||
|
-Principal $principal `
|
||||||
|
-Settings $settings `
|
||||||
|
-Force `
|
||||||
|
-ErrorAction Stop | Out-Null
|
||||||
|
|
||||||
|
Write-Host "Scheduled task '$taskName' registered (runs at every logon)."
|
||||||
|
} catch {
|
||||||
|
Write-Warning "Failed to register scheduled task : $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Main
|
||||||
|
# ============================================================================
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "=== Phase 1: sweep loose shortcuts into category folders ==="
|
||||||
|
Invoke-DesktopSweep -DesktopPath $publicDesktop
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "=== Phase 2: populate Shopfloor Tools with app shortcuts ==="
|
||||||
|
Add-ShopfloorToolsApps
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "=== Phase 3: drop eDNC / NTLARS at desktop root ==="
|
||||||
|
Update-DesktopRootPins
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "=== Phase 4: register logon sweeper scheduled task ==="
|
||||||
|
Register-SweepScheduledTask -ScriptPath $scriptPath
|
||||||
|
|
||||||
|
exit 0
|
||||||
140
playbook/shopfloor-setup/Shopfloor/07-TaskbarLayout.ps1
Normal file
140
playbook/shopfloor-setup/Shopfloor/07-TaskbarLayout.ps1
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
# 07-TaskbarLayout.ps1 - Minimal taskbar pinner.
|
||||||
|
#
|
||||||
|
# Reads the shortcuts that 06-OrganizeDesktop.ps1 created in
|
||||||
|
# C:\Users\Public\Desktop\Shopfloor Tools\ and writes a LayoutModification.xml
|
||||||
|
# that pins them to the taskbar along with Microsoft Edge.
|
||||||
|
#
|
||||||
|
# This script does NOT create, move, or copy any shortcuts - all shortcut
|
||||||
|
# management lives in 06. 07 is the last-mile taskbar config only.
|
||||||
|
#
|
||||||
|
# Pin order (left to right): Edge, WJ Shopfloor, UDC, eDNC, Defect_Tracker.
|
||||||
|
# NTLARS is intentionally not pinned - per the original spec it lives on
|
||||||
|
# the desktop root only.
|
||||||
|
#
|
||||||
|
# LayoutModification.xml is written to the Default User profile shell
|
||||||
|
# directory, which means the pins apply on FIRST LOGON of any new user
|
||||||
|
# profile. Existing profiles don't re-read the Default User template, so
|
||||||
|
# they won't pick up the pins without a manual re-pin or profile delete.
|
||||||
|
# This is a Windows design limitation - syspin.exe style hacks are the
|
||||||
|
# only workaround for existing profiles, and they're unsupported.
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Continue'
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Admin check - writing to the Default User profile requires elevation.
|
||||||
|
# ============================================================================
|
||||||
|
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||||
|
if (-not $isAdmin) {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "ERROR: 07-TaskbarLayout.ps1 must run as Administrator." -ForegroundColor Red
|
||||||
|
Write-Host " Re-run from an elevated PowerShell." -ForegroundColor Red
|
||||||
|
Write-Host ""
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$publicDesktop = 'C:\Users\Public\Desktop'
|
||||||
|
$shopfloorToolsDir = Join-Path $publicDesktop 'Shopfloor Tools'
|
||||||
|
$defaultUserShell = 'C:\Users\Default\AppData\Local\Microsoft\Windows\Shell'
|
||||||
|
$layoutXmlPath = Join-Path $defaultUserShell 'LayoutModification.xml'
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Pin list: exact ordered list of shortcut names to pin. Each entry points
|
||||||
|
# at a .lnk file via a Windows environment-variable-expanded path. Edge is
|
||||||
|
# special-cased to its default Start Menu location since Windows maintains
|
||||||
|
# it; all other entries reference C:\Users\Public\Desktop\Shopfloor Tools\
|
||||||
|
# which 06-OrganizeDesktop.ps1 populates.
|
||||||
|
# ============================================================================
|
||||||
|
$pinSpec = @(
|
||||||
|
@{
|
||||||
|
Name = 'Microsoft Edge'
|
||||||
|
Path = '%ALLUSERSPROFILE%\Microsoft\Windows\Start Menu\Programs\Microsoft Edge.lnk'
|
||||||
|
# Resolved literal path used to check existence (can't Test-Path an
|
||||||
|
# unexpanded env var reliably on older PS versions)
|
||||||
|
Literal = 'C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Edge.lnk'
|
||||||
|
}
|
||||||
|
@{
|
||||||
|
Name = 'WJ Shopfloor'
|
||||||
|
Path = '%PUBLIC%\Desktop\Shopfloor Tools\WJ Shopfloor.lnk'
|
||||||
|
Literal = (Join-Path $shopfloorToolsDir 'WJ Shopfloor.lnk')
|
||||||
|
}
|
||||||
|
@{
|
||||||
|
Name = 'UDC'
|
||||||
|
Path = '%PUBLIC%\Desktop\Shopfloor Tools\UDC.lnk'
|
||||||
|
Literal = (Join-Path $shopfloorToolsDir 'UDC.lnk')
|
||||||
|
}
|
||||||
|
@{
|
||||||
|
Name = 'eDNC'
|
||||||
|
Path = '%PUBLIC%\Desktop\Shopfloor Tools\eDNC.lnk'
|
||||||
|
Literal = (Join-Path $shopfloorToolsDir 'eDNC.lnk')
|
||||||
|
}
|
||||||
|
@{
|
||||||
|
Name = 'Defect_Tracker'
|
||||||
|
Path = '%PUBLIC%\Desktop\Shopfloor Tools\Defect_Tracker.lnk'
|
||||||
|
Literal = (Join-Path $shopfloorToolsDir 'Defect_Tracker.lnk')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Build the pin list - skip any whose .lnk is missing
|
||||||
|
# ============================================================================
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Checking which Shopfloor Tools shortcuts exist..."
|
||||||
|
|
||||||
|
$pinPaths = @()
|
||||||
|
foreach ($pin in $pinSpec) {
|
||||||
|
if (Test-Path -LiteralPath $pin.Literal) {
|
||||||
|
Write-Host " pin: $($pin.Name) -> $($pin.Literal)"
|
||||||
|
$pinPaths += $pin.Path
|
||||||
|
} else {
|
||||||
|
Write-Host " skip: $($pin.Name) - not found at $($pin.Literal)" -ForegroundColor DarkGray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pinPaths.Count -eq 0) {
|
||||||
|
Write-Warning "No pins to apply (nothing found in Shopfloor Tools\). Did 06-OrganizeDesktop.ps1 run first?"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Write LayoutModification.xml
|
||||||
|
# ============================================================================
|
||||||
|
$pinXml = ($pinPaths | ForEach-Object {
|
||||||
|
" <taskbar:DesktopApp DesktopApplicationLinkPath=`"$_`"/>"
|
||||||
|
}) -join "`r`n"
|
||||||
|
|
||||||
|
$layoutXml = @"
|
||||||
|
<?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 PinListPlacement="Replace">
|
||||||
|
<defaultlayout:TaskbarLayout>
|
||||||
|
<taskbar:TaskbarPinList>
|
||||||
|
$pinXml
|
||||||
|
</taskbar:TaskbarPinList>
|
||||||
|
</defaultlayout:TaskbarLayout>
|
||||||
|
</CustomTaskbarLayoutCollection>
|
||||||
|
</LayoutModificationTemplate>
|
||||||
|
"@
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (-not (Test-Path -LiteralPath $defaultUserShell)) {
|
||||||
|
New-Item -ItemType Directory -Path $defaultUserShell -Force -ErrorAction Stop | Out-Null
|
||||||
|
}
|
||||||
|
# -ErrorAction Stop so Access Denied becomes a catchable terminating error
|
||||||
|
# instead of silently falling through to the success message.
|
||||||
|
Set-Content -LiteralPath $layoutXmlPath -Value $layoutXml -Encoding UTF8 -Force -ErrorAction Stop
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Wrote taskbar layout with $($pinPaths.Count) pin(s) to:"
|
||||||
|
Write-Host " $layoutXmlPath"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Pins will apply on first logon of any NEW user profile."
|
||||||
|
} catch {
|
||||||
|
Write-Warning "Failed to write $layoutXmlPath : $_"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
exit 0
|
||||||
254
playbook/shopfloor-setup/Shopfloor/08-EdgeDefaultBrowser.ps1
Normal file
254
playbook/shopfloor-setup/Shopfloor/08-EdgeDefaultBrowser.ps1
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
# 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'
|
||||||
|
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
|
||||||
Reference in New Issue
Block a user