- gea-shopfloor-nocollections/02-MachineNumberACLs.ps1: gut to no-op matching the collections variant. SYSTEM Apply task no longer needs per-user ACLs on the eDNC reg key or UDC ProgramData dir. - Configure-PC.ps1 item 6 (Machine number logon prompt toggle): Stop duplicating Register-ScheduledTask logic inline. Call the shared Register-CheckMachineNumberTask.ps1 registrar so both the Prompt user-task and Apply SYSTEM-task are installed with matching SDDL config. Existence check now treats EITHER the new "Prompt Machine Number" task OR the legacy "Check Machine Number" task as "ON" so old bays still register correctly. Toggle OFF unregisters all three names (Prompt + Apply + legacy) for clean removal.
505 lines
21 KiB
PowerShell
505 lines
21 KiB
PowerShell
# Configure-PC.ps1 - Interactive 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.
|
|
#
|
|
# 2. Auto-startup items + machine-number logon prompt - toggle which apps
|
|
# start automatically for all users, and optionally register a logon
|
|
# task that prompts STANDARD users for the machine number if it's
|
|
# still 9999 when they log in (for the case where SupportUser skips
|
|
# it here and the end user needs to set it themselves).
|
|
#
|
|
# Startup .lnk files go in the AllUsers Startup folder:
|
|
# C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\
|
|
#
|
|
# Run via Configure-PC.bat on the SupportUser desktop.
|
|
|
|
param(
|
|
# When set, only show the machine number section (skip startup items).
|
|
# Used by sync_intune -AsTask after completion -- startup items are
|
|
# already auto-applied by 06-OrganizeDesktop from the PC profile.
|
|
[switch]$MachineNumberOnly
|
|
)
|
|
|
|
$ErrorActionPreference = 'Continue'
|
|
|
|
# --- Transcript logging ---
|
|
$logDir = 'C:\Logs\SFLD'
|
|
if (-not (Test-Path $logDir)) {
|
|
try { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } catch { $logDir = $env:TEMP }
|
|
}
|
|
$transcriptPath = Join-Path $logDir 'Configure-PC.log'
|
|
try { Start-Transcript -Path $transcriptPath -Append -Force | Out-Null } catch {}
|
|
Write-Host "Transcript: $transcriptPath"
|
|
Write-Host "Started: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
|
|
Write-Host "Running as: $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)"
|
|
|
|
# Load site config + PC profile (resolves pc-type.txt + pc-subtype.txt
|
|
# into a profile from site-config.json's pcProfiles section)
|
|
. "$PSScriptRoot\lib\Get-PCProfile.ps1"
|
|
. "$PSScriptRoot\lib\Update-MachineNumber.ps1"
|
|
|
|
$startupDir = 'C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup'
|
|
$publicDesktop = 'C:\Users\Public\Desktop'
|
|
$overridesPath = 'C:\ProgramData\GE\Shopfloor\startup-overrides.json'
|
|
|
|
# Persist user-toggled startup overrides so the logon-triggered sweep
|
|
# (06-OrganizeDesktop.ps1 Phase 3) doesn't re-create .lnks the tech
|
|
# explicitly removed. Labels in $suppressed are the ones currently OFF.
|
|
function Get-SuppressedStartup {
|
|
if (-not (Test-Path -LiteralPath $overridesPath)) { return @() }
|
|
try {
|
|
$data = Get-Content -LiteralPath $overridesPath -Raw | ConvertFrom-Json
|
|
if ($data.suppressed) { return @($data.suppressed) }
|
|
} catch {
|
|
Write-Warning " Failed to parse $overridesPath : $_"
|
|
}
|
|
return @()
|
|
}
|
|
|
|
function Set-SuppressedStartup {
|
|
param([string[]]$Labels)
|
|
$dir = Split-Path -Parent $overridesPath
|
|
if (-not (Test-Path $dir)) {
|
|
New-Item -ItemType Directory -Path $dir -Force | Out-Null
|
|
}
|
|
$payload = @{ suppressed = @($Labels | Sort-Object -Unique) }
|
|
$payload | ConvertTo-Json | Set-Content -LiteralPath $overridesPath -Encoding UTF8
|
|
}
|
|
|
|
# ============================================================================
|
|
# Helpers
|
|
# ============================================================================
|
|
|
|
function Get-UrlFromFile {
|
|
param([string]$BaseName)
|
|
$candidates = @(
|
|
(Join-Path $publicDesktop "$BaseName.url"),
|
|
(Join-Path (Join-Path $publicDesktop 'Web Links') "$BaseName.url")
|
|
)
|
|
foreach ($c in $candidates) {
|
|
if (-not (Test-Path -LiteralPath $c)) { continue }
|
|
try {
|
|
$content = Get-Content -LiteralPath $c -ErrorAction Stop
|
|
$urlLine = $content | Where-Object { $_ -match '^URL=' } | Select-Object -First 1
|
|
if ($urlLine) { return ($urlLine -replace '^URL=', '').Trim() }
|
|
} catch {}
|
|
}
|
|
return $null
|
|
}
|
|
|
|
function New-StartupLnk {
|
|
param(
|
|
[string]$Name,
|
|
[string]$Target,
|
|
[string]$Arguments = '',
|
|
[string]$WorkingDirectory = ''
|
|
)
|
|
$path = Join-Path $startupDir "$Name.lnk"
|
|
$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.Save()
|
|
return $true
|
|
} catch {
|
|
Write-Warning "Failed to create startup shortcut '$Name': $_"
|
|
return $false
|
|
} finally {
|
|
try { [System.Runtime.InteropServices.Marshal]::ReleaseComObject($wsh) | Out-Null } catch {}
|
|
}
|
|
}
|
|
|
|
# ============================================================================
|
|
# Machine number - read current state
|
|
# ============================================================================
|
|
|
|
$currentMN = Get-CurrentMachineNumber
|
|
$currentUdc = $currentMN.Udc
|
|
$currentEdnc = $currentMN.Ednc
|
|
|
|
$needsMachineNumber = ($currentUdc -eq '9999' -or $currentEdnc -eq '9999')
|
|
|
|
# ============================================================================
|
|
# Edge path (for URL-based startup items)
|
|
# ============================================================================
|
|
|
|
$edgePath = @(
|
|
'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe',
|
|
'C:\Program Files\Microsoft\Edge\Application\msedge.exe'
|
|
) | Where-Object { Test-Path -LiteralPath $_ } | Select-Object -First 1
|
|
|
|
# ============================================================================
|
|
# Startup item definitions
|
|
# Resolved from: pcProfile.startupItems > siteConfig.startupItems > hardcoded
|
|
# ============================================================================
|
|
|
|
$cfgItems = Get-ProfileValue 'startupItems'
|
|
|
|
if ($null -ne $cfgItems -and $cfgItems.Count -gt 0) {
|
|
$items = @()
|
|
$num = 0
|
|
foreach ($si in $cfgItems) {
|
|
$num++
|
|
switch ($si.type) {
|
|
'exe' {
|
|
$target = $si.target
|
|
$items += @{
|
|
Num = $num
|
|
Label = $si.label
|
|
Detail = (Split-Path -Leaf $target)
|
|
Available = (Test-Path $target)
|
|
CreateLnk = [scriptblock]::Create("return (New-StartupLnk -Name '$($si.label)' -Target '$target')")
|
|
}
|
|
}
|
|
'existing' {
|
|
$srcLnk = $si.sourceLnk
|
|
$label = $si.label
|
|
$items += @{
|
|
Num = $num
|
|
Label = $label
|
|
Detail = 'shortcut'
|
|
Available = $true
|
|
CreateLnk = [scriptblock]::Create(@"
|
|
`$src = @(
|
|
(Join-Path `$publicDesktop '$srcLnk'),
|
|
(Join-Path (Join-Path `$publicDesktop 'Shopfloor Tools') '$srcLnk')
|
|
) | Where-Object { Test-Path -LiteralPath `$_ } | Select-Object -First 1
|
|
if (`$src) {
|
|
`$dst = Join-Path `$startupDir '$label.lnk'
|
|
try { Copy-Item -LiteralPath `$src -Destination `$dst -Force; return `$true }
|
|
catch { Write-Warning "Failed to copy $label`: `$_"; return `$false }
|
|
} else {
|
|
Write-Warning "$srcLnk not found on desktop"
|
|
return `$false
|
|
}
|
|
"@)
|
|
}
|
|
}
|
|
'url' {
|
|
$urlKey = $si.urlKey
|
|
$label = $si.label
|
|
$fallback = if ($urlKey -and $siteConfig.urls) { $siteConfig.urls.$urlKey } else { '' }
|
|
$items += @{
|
|
Num = $num
|
|
Label = $label
|
|
Detail = 'opens in Edge'
|
|
Available = [bool]$edgePath
|
|
CreateLnk = [scriptblock]::Create(@"
|
|
`$fallback = '$fallback'
|
|
`$url = Get-UrlFromFile '$label'
|
|
if (-not `$url) {
|
|
Write-Host " $label .url not found on desktop, using known URL."
|
|
`$url = `$fallback
|
|
}
|
|
Write-Host " URL: `$url"
|
|
return (New-StartupLnk -Name '$label' -Target `$edgePath -Arguments "--new-window ```"`$url```"")
|
|
"@)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
$items = @(
|
|
@{
|
|
Num = 1
|
|
Label = 'UDC'
|
|
Detail = 'UDC.exe'
|
|
Available = (Test-Path 'C:\Program Files\UDC\UDC.exe')
|
|
CreateLnk = {
|
|
return (New-StartupLnk -Name 'UDC' -Target 'C:\Program Files\UDC\UDC.exe')
|
|
}
|
|
}
|
|
@{
|
|
Num = 2
|
|
Label = 'eDNC'
|
|
Detail = 'DncMain.exe'
|
|
Available = (Test-Path 'C:\Program Files (x86)\Dnc\bin\DncMain.exe')
|
|
CreateLnk = {
|
|
return (New-StartupLnk -Name 'eDNC' -Target 'C:\Program Files (x86)\Dnc\bin\DncMain.exe')
|
|
}
|
|
}
|
|
@{
|
|
Num = 3
|
|
Label = 'Defect Tracker'
|
|
Detail = 'ClickOnce app'
|
|
Available = $true
|
|
CreateLnk = {
|
|
$src = @(
|
|
(Join-Path $publicDesktop 'Defect_Tracker.lnk'),
|
|
(Join-Path (Join-Path $publicDesktop 'Shopfloor Tools') 'Defect_Tracker.lnk')
|
|
) | Where-Object { Test-Path -LiteralPath $_ } | Select-Object -First 1
|
|
if ($src) {
|
|
$dst = Join-Path $startupDir 'Defect Tracker.lnk'
|
|
try { Copy-Item -LiteralPath $src -Destination $dst -Force; return $true }
|
|
catch { Write-Warning "Failed to copy Defect Tracker: $_"; return $false }
|
|
} else {
|
|
Write-Warning "Defect_Tracker.lnk not found on desktop"
|
|
return $false
|
|
}
|
|
}
|
|
}
|
|
@{
|
|
Num = 4
|
|
Label = 'WJ Shopfloor'
|
|
Detail = 'HostExplorer session'
|
|
Available = $true
|
|
CreateLnk = {
|
|
$src = @(
|
|
(Join-Path $publicDesktop 'WJ Shopfloor.lnk'),
|
|
(Join-Path (Join-Path $publicDesktop 'Shopfloor Tools') 'WJ Shopfloor.lnk')
|
|
) | Where-Object { Test-Path -LiteralPath $_ } | Select-Object -First 1
|
|
if ($src) {
|
|
$dst = Join-Path $startupDir 'WJ Shopfloor.lnk'
|
|
try { Copy-Item -LiteralPath $src -Destination $dst -Force; return $true }
|
|
catch { Write-Warning "Failed to copy WJ Shopfloor: $_"; return $false }
|
|
} else {
|
|
Write-Warning "WJ Shopfloor.lnk not found on desktop"
|
|
return $false
|
|
}
|
|
}
|
|
}
|
|
@{
|
|
Num = 5
|
|
Label = 'Plant Apps'
|
|
Detail = 'opens in Edge'
|
|
Available = [bool]$edgePath
|
|
CreateLnk = {
|
|
$fallback = 'https://mes-wjefferson.apps.lr.geaerospace.net/run/?app_name=Plant%20Applications'
|
|
$url = Get-UrlFromFile 'Plant Apps'
|
|
if (-not $url) {
|
|
Write-Host " Plant Apps .url not found on desktop, using known URL."
|
|
$url = $fallback
|
|
}
|
|
Write-Host " URL: $url"
|
|
return (New-StartupLnk -Name 'Plant Apps' -Target $edgePath -Arguments "--new-window `"$url`"")
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
# Machine-number logon tasks (item 6 toggle controls both)
|
|
# 2026-05-24: split into user-context Prompt + SYSTEM-context Apply.
|
|
# 'Check Machine Number' is the legacy single-task name kept for
|
|
# backward-detection on bays imaged before the split.
|
|
$machineNumPromptTask = 'Prompt Machine Number'
|
|
$machineNumApplyTask = 'Apply Machine Number'
|
|
$machineNumLegacyTask = 'Check Machine Number'
|
|
|
|
# ============================================================================
|
|
# Interactive UI
|
|
# ============================================================================
|
|
|
|
Clear-Host
|
|
Write-Host ""
|
|
Write-Host "========================================" -ForegroundColor Cyan
|
|
Write-Host " Configure PC" -ForegroundColor Cyan
|
|
Write-Host "========================================" -ForegroundColor Cyan
|
|
|
|
# --- Section 1: Machine number ---
|
|
Write-Host ""
|
|
Write-Host " MACHINE NUMBER" -ForegroundColor Yellow
|
|
Write-Host " ----------------------------------------"
|
|
|
|
if ($needsMachineNumber) {
|
|
if ($currentUdc) { Write-Host " UDC : $currentUdc" -ForegroundColor Red }
|
|
if ($currentEdnc) { Write-Host " eDNC : $currentEdnc" -ForegroundColor Red }
|
|
Write-Host ""
|
|
$newNum = Read-Host " Enter new machine number (digits only, Enter to skip)"
|
|
$newNum = $newNum.Trim()
|
|
|
|
if ($newNum -and $newNum -match '^\d+$') {
|
|
$site = if ($siteConfig) { $siteConfig.siteName } else { 'West Jefferson' }
|
|
$mnResult = Update-MachineNumber -NewNumber $newNum -Site $site
|
|
|
|
if ($mnResult.UdcUpdated) {
|
|
Write-Host " UDC : $currentUdc -> $newNum" -ForegroundColor Green
|
|
$currentUdc = $newNum
|
|
}
|
|
if ($mnResult.EdncUpdated) {
|
|
Write-Host " eDNC : $currentEdnc -> $newNum" -ForegroundColor Green
|
|
$currentEdnc = $newNum
|
|
}
|
|
foreach ($err in $mnResult.Errors) { Write-Warning " $err" }
|
|
if ($mnResult.UdcUpdated) { Write-Host " UDC.exe relaunched." }
|
|
|
|
$needsMachineNumber = ($currentUdc -eq '9999' -or $currentEdnc -eq '9999')
|
|
} elseif ($newNum) {
|
|
Write-Host " Invalid (digits only). Skipped." -ForegroundColor Yellow
|
|
} else {
|
|
Write-Host " Skipped (still 9999)." -ForegroundColor DarkGray
|
|
}
|
|
} else {
|
|
if ($currentUdc) { Write-Host " UDC : $currentUdc" -ForegroundColor Green }
|
|
if ($currentEdnc) { Write-Host " eDNC : $currentEdnc" -ForegroundColor Green }
|
|
}
|
|
|
|
# --- Section 2: Startup items + machine-number logon task ---
|
|
# Skipped when -MachineNumberOnly (called from sync_intune -AsTask after
|
|
# completion -- startup items already auto-applied by 06-OrganizeDesktop).
|
|
if (-not $MachineNumberOnly) {
|
|
Write-Host ""
|
|
Write-Host " AUTO-STARTUP ITEMS" -ForegroundColor Yellow
|
|
Write-Host " ----------------------------------------"
|
|
Write-Host " Toggle which items start at user logon:"
|
|
Write-Host ""
|
|
|
|
$existingStartup = @(Get-ChildItem -LiteralPath $startupDir -Filter '*.lnk' -File -ErrorAction SilentlyContinue | Select-Object -ExpandProperty BaseName)
|
|
|
|
foreach ($item in $items) {
|
|
$on = if ($existingStartup -contains $item.Label) { '[ON]' } else { '[ ]' }
|
|
$avail = if ($item.Available) { '' } else { ' (not installed)' }
|
|
Write-Host " $($item.Num). $on $($item.Label) - $($item.Detail)$avail"
|
|
}
|
|
|
|
# Item 6: machine number logon prompt. "ON" if EITHER the new Prompt task OR
|
|
# the legacy Check Machine Number task is registered.
|
|
$machineNumTaskExists = [bool](
|
|
(Get-ScheduledTask -TaskName $machineNumPromptTask -ErrorAction SilentlyContinue) -or
|
|
(Get-ScheduledTask -TaskName $machineNumLegacyTask -ErrorAction SilentlyContinue)
|
|
)
|
|
$mnOn = if ($machineNumTaskExists) { '[ON]' } else { '[ ]' }
|
|
Write-Host " 6. $mnOn Prompt standard user for machine number if 9999"
|
|
|
|
Write-Host ""
|
|
Write-Host " Enter numbers to toggle (e.g. 1,2,6), Enter to keep current:"
|
|
$selection = Read-Host " Selection"
|
|
$selection = $selection.Trim()
|
|
|
|
if ($selection) {
|
|
$selected = $selection -split '[,\s]+' | Where-Object { $_ -match '^\d+$' } | ForEach-Object { [int]$_ }
|
|
|
|
# Process startup items 1-5
|
|
$suppressed = @(Get-SuppressedStartup)
|
|
$suppressedChanged = $false
|
|
|
|
foreach ($item in $items) {
|
|
if ($selected -notcontains $item.Num) { continue }
|
|
|
|
$existingLnk = Join-Path $startupDir "$($item.Label).lnk"
|
|
if (Test-Path -LiteralPath $existingLnk) {
|
|
# Toggling OFF: remove the .lnk AND record the suppression so
|
|
# the logon sweep in 06-OrganizeDesktop doesn't re-create it.
|
|
try {
|
|
Remove-Item -LiteralPath $existingLnk -Force
|
|
Write-Host " $($item.Label): REMOVED from startup" -ForegroundColor Yellow
|
|
} catch { Write-Warning " Failed to remove $($item.Label): $_" }
|
|
|
|
if ($suppressed -notcontains $item.Label) {
|
|
$suppressed += $item.Label
|
|
$suppressedChanged = $true
|
|
}
|
|
} else {
|
|
# Toggling ON: drop any suppression first so the sweep won't
|
|
# fight us next logon, then create the .lnk.
|
|
if ($suppressed -contains $item.Label) {
|
|
$suppressed = @($suppressed | Where-Object { $_ -ne $item.Label })
|
|
$suppressedChanged = $true
|
|
}
|
|
|
|
if (-not $item.Available) {
|
|
Write-Host " $($item.Label): not installed, cannot add to startup" -ForegroundColor DarkGray
|
|
continue
|
|
}
|
|
|
|
$result = & $item.CreateLnk
|
|
if ($result) {
|
|
Write-Host " $($item.Label): ADDED to startup" -ForegroundColor Green
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($suppressedChanged) {
|
|
try {
|
|
Set-SuppressedStartup -Labels $suppressed
|
|
Write-Host " (saved override state to $overridesPath)" -ForegroundColor DarkGray
|
|
} catch {
|
|
Write-Warning " Failed to write override state: $_"
|
|
}
|
|
}
|
|
|
|
# Process item 6: machine number logon task
|
|
if ($selected -contains 6) {
|
|
if ($machineNumTaskExists) {
|
|
# Toggle OFF - remove Prompt + Apply (new design) AND the legacy
|
|
# Check Machine Number task name (in case this bay was imaged
|
|
# before the split and never re-imaged).
|
|
$removed = @()
|
|
foreach ($t in @($machineNumPromptTask, $machineNumApplyTask, $machineNumLegacyTask)) {
|
|
try {
|
|
if (Get-ScheduledTask -TaskName $t -ErrorAction SilentlyContinue) {
|
|
Unregister-ScheduledTask -TaskName $t -Confirm:$false -ErrorAction Stop
|
|
$removed += $t
|
|
}
|
|
} catch { Write-Warning " Failed to remove '$t': $_" }
|
|
}
|
|
if ($removed) {
|
|
Write-Host " Machine number logon prompt: REMOVED ($($removed -join ', '))" -ForegroundColor Yellow
|
|
}
|
|
$machineNumTaskExists = $false
|
|
} else {
|
|
# Toggle ON - register logon task
|
|
# Defer task registration to the shared registrar so this code
|
|
# path always matches the imaging-time path. Registrar installs
|
|
# BOTH the user-context "Prompt Machine Number" task and the
|
|
# SYSTEM-context "Apply Machine Number" task, sets the SDDL on
|
|
# Apply so Limited users can schtasks /run it, and cleans up
|
|
# any legacy "Check Machine Number" task name.
|
|
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
$registrar = Join-Path $scriptDir 'Register-CheckMachineNumberTask.ps1'
|
|
if (-not (Test-Path -LiteralPath $registrar)) {
|
|
$registrar = 'C:\Enrollment\shopfloor-setup\Shopfloor\Register-CheckMachineNumberTask.ps1'
|
|
}
|
|
if (Test-Path -LiteralPath $registrar) {
|
|
try {
|
|
& $registrar
|
|
Write-Host " Machine number logon prompt: ENABLED" -ForegroundColor Green
|
|
Write-Host " (Prompt user-task + Apply SYSTEM-task registered;" -ForegroundColor DarkGray
|
|
Write-Host " will auto-disable after machine number is set)" -ForegroundColor DarkGray
|
|
$machineNumTaskExists = $true
|
|
} catch {
|
|
Write-Warning " Register-CheckMachineNumberTask failed: $_"
|
|
}
|
|
} else {
|
|
Write-Warning " Register-CheckMachineNumberTask.ps1 not found at $registrar"
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
Write-Host " No changes." -ForegroundColor DarkGray
|
|
}
|
|
|
|
} # end if (-not $MachineNumberOnly)
|
|
|
|
# --- Summary ---
|
|
Write-Host ""
|
|
Write-Host " CURRENT STATE" -ForegroundColor Cyan
|
|
Write-Host " ----------------------------------------"
|
|
$final = @(Get-ChildItem -LiteralPath $startupDir -Filter '*.lnk' -File -ErrorAction SilentlyContinue | Select-Object -ExpandProperty BaseName)
|
|
Write-Host " Startup items:"
|
|
if ($final.Count -eq 0) {
|
|
Write-Host " (none)"
|
|
} else {
|
|
foreach ($f in $final) { Write-Host " - $f" }
|
|
}
|
|
$mnState = if ($machineNumTaskExists) { 'ON (prompts at logon if 9999)' } else { 'OFF' }
|
|
Write-Host " Machine number logon prompt: $mnState"
|
|
|
|
Write-Host ""
|
|
Write-Host "========================================" -ForegroundColor Cyan
|
|
Write-Host " Done" -ForegroundColor Cyan
|
|
Write-Host "========================================" -ForegroundColor Cyan
|
|
Write-Host ""
|
|
Write-Host "Completed: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
|
|
try { Stop-Transcript | Out-Null } catch {}
|
|
|
|
Write-Host "Press any key to close..."
|
|
try { [void][Console]::ReadKey($true) } catch { [void](Read-Host) }
|