# Register-MapSfldShare.ps1 - stage Map-SfldShare.ps1 and register the # 'GE Map SFLD Share' scheduled task that keeps S: alive for every # interactive BUILTIN\Users member (ShopFloor, SupportUser, ...). # # Replaces the HKLM\Run entry of the same name. Run fired once per logon and # had no way to repair S: mid-shift; a task can fire at logon, on an # interval, on unlock / console connect and when the network comes up. # Principal is the Users GROUP with RunLevel Limited, so it runs in each # logged-on member's own session with a non-elevated token: the mapping # lands where Explorer and the apps look for it. param( [int]$IntervalMinutes = 3, [string]$InstallRoot = 'C:\Program Files\GE\SfldShare' ) $ErrorActionPreference = 'Continue' $taskName = 'GE Map SFLD Share' $runKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run' $runValue = 'GE Map SFLD Share' $legacyTask = 'GE Shopfloor Map S: Drive' $mapScript = Join-Path $InstallRoot 'Map-SfldShare.ps1' $logDir = 'C:\Logs\SFLD' $logFile = Join-Path $logDir 'register-mapshare.log' if (-not (Test-Path $logDir)) { New-Item -Path $logDir -ItemType Directory -Force | Out-Null } function Write-RegLog { param([string]$Message) $line = '[{0}] [INFO] {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Message Add-Content -Path $logFile -Value $line -ErrorAction SilentlyContinue Write-Host $line } Write-RegLog '=== Register-MapSfldShare start ===' # Self-contained PC types never get S: (Display kiosk: local accounts, no # SFLD creds, mapping could only fail). Gate here as well as at the call # site - see the 579C144 note in the previous version. $selfContainedTypes = @('Display', 'gea-shopfloor-display') $pcType = '' if (Test-Path -LiteralPath 'C:\Enrollment\pc-type.txt') { $pcType = (Get-Content -LiteralPath 'C:\Enrollment\pc-type.txt' -First 1 -EA SilentlyContinue) if ($pcType) { $pcType = $pcType.Trim() } } if ($pcType -and ($selfContainedTypes -contains $pcType)) { Write-RegLog "PC type '$pcType' is self-contained - not registering the S: mapper." Remove-ItemProperty -Path $runKey -Name $runValue -Force -EA SilentlyContinue if (Get-ScheduledTask -TaskName $taskName -EA SilentlyContinue) { Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -EA SilentlyContinue } exit 0 } # --- stage the script ---------------------------------------------------- $src = Join-Path $PSScriptRoot 'lib\Map-SfldShare.ps1' if (-not (Test-Path $src)) { Write-RegLog "Map-SfldShare.ps1 not found at $src - cannot register"; exit 1 } if (-not (Test-Path $InstallRoot)) { New-Item -Path $InstallRoot -ItemType Directory -Force | Out-Null } Copy-Item -Path $src -Destination $mapScript -Force Write-RegLog "Staged $src -> $mapScript" # --- retire the older mechanisms ----------------------------------------- if (Get-ItemProperty -Path $runKey -Name $runValue -EA SilentlyContinue) { Remove-ItemProperty -Path $runKey -Name $runValue -Force -EA SilentlyContinue Write-RegLog "Removed HKLM\Run '$runValue' (superseded by the scheduled task)" } if (Get-ScheduledTask -TaskName $legacyTask -EA SilentlyContinue) { Unregister-ScheduledTask -TaskName $legacyTask -Confirm:$false -EA SilentlyContinue Write-RegLog "Removed legacy task '$legacyTask'" } # --- task ------------------------------------------------------------------ try { $ps = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe" $action = New-ScheduledTaskAction -Execute $ps ` -Argument ('-NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File "{0}"' -f $mapScript) $ns = 'Root/Microsoft/Windows/TaskScheduler' $triggers = New-Object System.Collections.ArrayList # 1. logon (any user) [void]$triggers.Add((New-ScheduledTaskTrigger -AtLogOn)) # 2. interval, forever $rep = New-ScheduledTaskTrigger -Once -At (Get-Date).Date -RepetitionInterval (New-TimeSpan -Minutes $IntervalMinutes) [void]$triggers.Add($rep) # 3. console connect (1) + session unlock (8) $ssCls = Get-CimClass -ClassName MSFT_TaskSessionStateChangeTrigger -Namespace $ns foreach ($state in 1, 8) { $t = New-CimInstance -CimClass $ssCls -ClientOnly $t.StateChange = $state $t.Delay = 'PT10S' [void]$triggers.Add($t) } # 4. network profile connected (NetworkProfile/Operational 10000) - wifi # roam / cable back / VPN up. Short delay so DNS is usable. $evCls = Get-CimClass -ClassName MSFT_TaskEventTrigger -Namespace $ns $ev = New-CimInstance -CimClass $evCls -ClientOnly $ev.Subscription = '' $ev.Delay = 'PT15S' [void]$triggers.Add($ev) $principal = New-ScheduledTaskPrincipal -GroupId 'S-1-5-32-545' -RunLevel Limited $settings = New-ScheduledTaskSettingsSet ` -MultipleInstances IgnoreNew ` -ExecutionTimeLimit (New-TimeSpan -Minutes 5) ` -StartWhenAvailable ` -AllowStartIfOnBatteries ` -DontStopIfGoingOnBatteries ` -Hidden Register-ScheduledTask -TaskName $taskName ` -Action $action -Trigger $triggers.ToArray() -Principal $principal -Settings $settings ` -Force -ErrorAction Stop | Out-Null Write-RegLog "Registered task '$taskName' (BUILTIN\Users, Limited; logon + every $IntervalMinutes min + unlock/connect + network-up)" } catch { Write-RegLog "FAILED to register task: $_" exit 1 } # --- stamp ----------------------------------------------------------------- # Written only after staging + registration both succeeded. The GE-Enforce # manifest entry detects on MapperHash == SHA256 of Map-SfldShare.ps1, so a # new script version (new hash) or a failed registration (no stamp) re-fires # this registrar on the next cycle. try { $hash = (Get-FileHash -Path $mapScript -Algorithm SHA256).Hash $stampKey = 'HKLM:\SOFTWARE\GE\SfldShare' if (-not (Test-Path $stampKey)) { New-Item -Path $stampKey -Force | Out-Null } Set-ItemProperty -Path $stampKey -Name MapperHash -Value $hash -Type String Set-ItemProperty -Path $stampKey -Name RegisteredAt -Value (Get-Date -Format 's') -Type String Set-ItemProperty -Path $stampKey -Name TaskName -Value $taskName -Type String Write-RegLog "Stamped $stampKey MapperHash=$hash" } catch { Write-RegLog "Stamp write failed (registrar will re-run next cycle): $_" } Write-RegLog '=== Register-MapSfldShare end ===' exit 0