S: drive mapping via HKLM\Run, autologon-count non-intervention, Phase 4 no-scripts handling
- Register-MapSfldShare.ps1: swap scheduled task for HKLM\Run entry. Task with -GroupId runs in session 0 with no HKCU, so /persistent:yes fails and the drive mapping isn't visible to Explorer. Run key fires at Explorer startup in the interactive user's session with full token + HKCU. Unregisters legacy 'GE Shopfloor Map S: Drive' task for PCs already imaged. - Run-ShopfloorSetup.ps1: stop bumping AutoLogonCount (99 at start, 4 at end). Windows decrements per-logon and at 0 clears AutoAdminLogon + DefaultPassword, which nukes the lockdown-configured ShopFloor autologon. Re-enable-wired-NICs task now gates on Autologon_Remediation.log 'Autologon set for ShopFloor' instead of SFLD creds, so wired stays off through the whole Intune+DSC+lockdown chain. - Monitor-IntuneProgress.ps1: Phase 4 treats 'no custom scripts' as COMPLETE when DSC install is done (was WAITING, which stalled the state machine on PC types without scripts). Push retrigger out to 15min when entering lockdown-wait so a stale 5min retrigger doesn't fire mid-Remediation. Removed the AutoLogonCount delete in Invoke-SetupComplete since we no longer set it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -26,11 +26,11 @@ Write-Host " Transcript: $transcriptPath"
|
|||||||
Write-Host "================================================================"
|
Write-Host "================================================================"
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
|
|
||||||
# Bump AutoLogonCount HIGH at the start so reboots during setup (e.g. VC++ 2008
|
# AutoLogonCount is NOT set here. Previously we bumped it to 99/4, but
|
||||||
# triggering an immediate ExitWindowsEx) don't exhaust autologin attempts before
|
# Windows decrements it per-logon and at 0 clears AutoAdminLogon -- which
|
||||||
# the dispatcher can complete. The end-of-script reset puts it back to 2 once
|
# nukes the lockdown-configured ShopFloor autologon later in the chain.
|
||||||
# everything succeeds.
|
# The unattend XML's <AutoLogon><LogonCount> handles SupportUser logons;
|
||||||
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v AutoLogonCount /t REG_DWORD /d 99 /f | Out-Null
|
# the lockdown's Autologon.exe handles ShopFloor. We stay out of it.
|
||||||
|
|
||||||
# Cancel any pending reboot so it doesn't interrupt setup
|
# Cancel any pending reboot so it doesn't interrupt setup
|
||||||
cmd /c "shutdown /a 2>nul" *>$null
|
cmd /c "shutdown /a 2>nul" *>$null
|
||||||
@@ -237,32 +237,28 @@ if (Test-Path -LiteralPath $monitorScript) {
|
|||||||
# Set auto-logon to expire after 4 more logins (2 needed for sync_intune
|
# Set auto-logon to expire after 4 more logins (2 needed for sync_intune
|
||||||
# pre-reboot + post-reboot, plus 2 margin for unexpected reboots from
|
# pre-reboot + post-reboot, plus 2 margin for unexpected reboots from
|
||||||
# Windows Update, PPKG file operations, or script crashes).
|
# Windows Update, PPKG file operations, or script crashes).
|
||||||
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v AutoLogonCount /t REG_DWORD /d 4 /f | Out-Null
|
# (AutoLogonCount intentionally not set -- see comment at top of script)
|
||||||
Write-Host "Auto-logon set to 4 remaining logins."
|
|
||||||
|
|
||||||
# --- Register cross-PC-type enforcers (Acrobat, etc.) ---
|
# --- Register cross-PC-type enforcers (Acrobat, etc.) ---
|
||||||
# These run on every logon regardless of PC type, mounting the SFLD share
|
# These run on every logon regardless of PC type, mounting the SFLD share
|
||||||
# for version-pinned app enforcement. Initial install already handled by
|
# for version-pinned app enforcement. Initial install already handled by
|
||||||
# preinstall flow; enforcers only kick in when detection fails.
|
# preinstall flow; enforcers only kick in when detection fails.
|
||||||
# --- Re-enable wired NICs once SFLD creds arrive (Phase 5) ---
|
# --- Re-enable wired NICs once lockdown completes (Phase 6) ---
|
||||||
# migrate-to-wifi.ps1 disables wired NICs so the PPKG runs over WiFi.
|
# migrate-to-wifi.ps1 disables wired NICs so the PPKG runs over WiFi.
|
||||||
# After Phase 5 (SFLD creds populated), WiFi duty is done and the tech
|
# Keep them disabled through the entire Intune sync + DSC + lockdown
|
||||||
# needs wired back for production ethernet. Monitor-IntuneProgress runs
|
# chain so nothing interrupts the WiFi-based enrollment. Only re-enable
|
||||||
# as Limited and can't call Enable-NetAdapter (needs admin). This SYSTEM
|
# after lockdown lands (Autologon_Remediation.log confirms ShopFloor
|
||||||
# task fires at logon, waits for the SFLD cred marker, re-enables wired
|
# autologon set). Monitor-IntuneProgress runs as Limited and can't call
|
||||||
# NICs, and self-deletes. If creds haven't landed yet, the task exits
|
# Enable-NetAdapter (needs admin). This SYSTEM task fires at logon,
|
||||||
# quickly and the repetition interval retries every 5 minutes.
|
# polls for lockdown completion, re-enables wired NICs, and self-deletes.
|
||||||
$reEnableTask = 'GE Re-enable Wired NICs'
|
$reEnableTask = 'GE Re-enable Wired NICs'
|
||||||
try {
|
try {
|
||||||
$script = @'
|
$script = @'
|
||||||
$credsBase = 'HKLM:\SOFTWARE\GE\SFLD\Credentials'
|
$imeLogs = 'C:\ProgramData\Microsoft\IntuneManagementExtension\Logs'
|
||||||
if (-not (Test-Path $credsBase)) { exit 0 }
|
$remLog = Join-Path $imeLogs 'Autologon_Remediation.log'
|
||||||
$hasCreds = $false
|
if (-not (Test-Path $remLog)) { exit 0 }
|
||||||
Get-ChildItem -Path $credsBase -ErrorAction SilentlyContinue | ForEach-Object {
|
$content = Get-Content $remLog -Raw -ErrorAction SilentlyContinue
|
||||||
$p = Get-ItemProperty -Path $_.PSPath -ErrorAction SilentlyContinue
|
if ($content -notmatch 'Autologon set for ShopFloor') { exit 0 }
|
||||||
if ($p -and $p.TargetHost -and $p.Username -and $p.Password) { $hasCreds = $true }
|
|
||||||
}
|
|
||||||
if (-not $hasCreds) { exit 0 }
|
|
||||||
Get-NetAdapter -Physical -ErrorAction SilentlyContinue |
|
Get-NetAdapter -Physical -ErrorAction SilentlyContinue |
|
||||||
Where-Object { $_.InterfaceDescription -notmatch 'Wi-?Fi|Wireless|WLAN|802\.11' } |
|
Where-Object { $_.InterfaceDescription -notmatch 'Wi-?Fi|Wireless|WLAN|802\.11' } |
|
||||||
Enable-NetAdapter -Confirm:$false -ErrorAction SilentlyContinue
|
Enable-NetAdapter -Confirm:$false -ErrorAction SilentlyContinue
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
# Register-MapSfldShare.ps1 - Stage Map-SfldShare.ps1 + register a logon
|
# Register-MapSfldShare.ps1 - Stage Map-SfldShare.ps1 + register an
|
||||||
# task that maps S: for any user in BUILTIN\Users (SupportUser, ShopFloor,
|
# HKLM\Run entry that maps S: for any interactive user (SupportUser,
|
||||||
# any future end-user accounts).
|
# ShopFloor, any future end-user accounts).
|
||||||
|
#
|
||||||
|
# Why HKLM\Run instead of a scheduled task: Run fires at Explorer
|
||||||
|
# startup in the logged-in user's interactive session with their full
|
||||||
|
# token + HKCU mounted. No principal/LogonType/group-SID plumbing, no
|
||||||
|
# "task fires in session 0 but drive not visible to Explorer" class of
|
||||||
|
# bugs. Works for every BUILTIN\Users member with no extra logic.
|
||||||
#
|
#
|
||||||
# Why not the vendor's ConsumeCredentials.ps1: it calls
|
# Why not the vendor's ConsumeCredentials.ps1: it calls
|
||||||
# New-StoredCredential -Persist LocalMachine (needs admin) before net use.
|
# New-StoredCredential -Persist LocalMachine (needs admin) before net
|
||||||
# ShopFloor is non-admin, so the cred-store fails and net use has no auth.
|
# use. ShopFloor is non-admin, so the cred-store fails and net use has
|
||||||
# Our Map-SfldShare.ps1 reads HKLM creds directly and passes them inline
|
# no auth. Our Map-SfldShare.ps1 reads HKLM creds directly and passes
|
||||||
# to net use /user: -- no Credential Manager needed, works as Limited.
|
# them inline to net use /user: -- no Credential Manager needed.
|
||||||
|
|
||||||
$ErrorActionPreference = 'Continue'
|
$ErrorActionPreference = 'Continue'
|
||||||
|
|
||||||
@@ -14,6 +20,10 @@ $installRoot = 'C:\Program Files\GE\SfldShare'
|
|||||||
$mapScript = Join-Path $installRoot 'Map-SfldShare.ps1'
|
$mapScript = Join-Path $installRoot 'Map-SfldShare.ps1'
|
||||||
$logDir = 'C:\Logs\SFLD'
|
$logDir = 'C:\Logs\SFLD'
|
||||||
$logFile = Join-Path $logDir 'register-mapshare.log'
|
$logFile = Join-Path $logDir 'register-mapshare.log'
|
||||||
|
$runKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'
|
||||||
|
$runValue = 'GE Map SFLD Share'
|
||||||
|
$legacyTask = 'GE Shopfloor Map S: Drive'
|
||||||
|
|
||||||
if (-not (Test-Path $logDir)) { New-Item -Path $logDir -ItemType Directory -Force | Out-Null }
|
if (-not (Test-Path $logDir)) { New-Item -Path $logDir -ItemType Directory -Force | Out-Null }
|
||||||
|
|
||||||
function Write-RegLog {
|
function Write-RegLog {
|
||||||
@@ -38,39 +48,30 @@ if (Test-Path $src) {
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Remove the legacy scheduled task if it exists (left behind by older
|
||||||
|
# imaging runs that used the scheduled-task approach).
|
||||||
|
if (Get-ScheduledTask -TaskName $legacyTask -ErrorAction SilentlyContinue) {
|
||||||
|
try {
|
||||||
|
Unregister-ScheduledTask -TaskName $legacyTask -Confirm:$false -ErrorAction Stop
|
||||||
|
Write-RegLog "Removed legacy scheduled task '$legacyTask'"
|
||||||
|
} catch {
|
||||||
|
Write-RegLog "Failed to remove legacy task '$legacyTask': $_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Register HKLM\Run entry. Runs at Explorer startup for every
|
||||||
|
# interactive user in that user's session.
|
||||||
try {
|
try {
|
||||||
$action = New-ScheduledTaskAction `
|
$command = '"{0}" -NoProfile -ExecutionPolicy Bypass -File "{1}"' -f `
|
||||||
-Execute 'powershell.exe' `
|
"$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe", $mapScript
|
||||||
-Argument "-NoProfile -ExecutionPolicy Bypass -File `"$mapScript`""
|
|
||||||
|
|
||||||
$trigger = New-ScheduledTaskTrigger -AtLogOn
|
if (-not (Test-Path $runKey)) {
|
||||||
|
New-Item -Path $runKey -Force | Out-Null
|
||||||
# BUILTIN\Users + Limited: any logged-in user triggers it; action
|
}
|
||||||
# runs in that user's session so net use lands the drive in the
|
New-ItemProperty -Path $runKey -Name $runValue -Value $command -PropertyType String -Force | Out-Null
|
||||||
# right place.
|
Write-RegLog "Set $runKey\$runValue = $command"
|
||||||
$principal = New-ScheduledTaskPrincipal -GroupId 'S-1-5-32-545' -RunLevel Limited
|
|
||||||
|
|
||||||
$settings = New-ScheduledTaskSettingsSet `
|
|
||||||
-AllowStartIfOnBatteries `
|
|
||||||
-DontStopIfGoingOnBatteries `
|
|
||||||
-StartWhenAvailable `
|
|
||||||
-ExecutionTimeLimit (New-TimeSpan -Minutes 5)
|
|
||||||
|
|
||||||
Write-RegLog "Registering 'GE Shopfloor Map S: Drive' (logon trigger, BUILTIN\Users -> $vendorScript)"
|
|
||||||
|
|
||||||
Register-ScheduledTask `
|
|
||||||
-TaskName 'GE Shopfloor Map S: Drive' `
|
|
||||||
-Action $action `
|
|
||||||
-Trigger $trigger `
|
|
||||||
-Principal $principal `
|
|
||||||
-Settings $settings `
|
|
||||||
-Force `
|
|
||||||
-Description 'Map SFLD share drives on any user logon using HKLM creds (parallel to the principal-restricted vendor task) so ShopFloor and other end-user accounts get S: mapped' `
|
|
||||||
-ErrorAction Stop | Out-Null
|
|
||||||
|
|
||||||
Write-RegLog 'Scheduled task registered'
|
|
||||||
} catch {
|
} catch {
|
||||||
Write-RegLog "FAILED to register task: $_"
|
Write-RegLog "FAILED to register Run key: $_"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -601,13 +601,19 @@ function Format-Snapshot {
|
|||||||
@{ Ok = $Snap.Phase3.InstallComplete; Failed = $false }
|
@{ Ok = $Snap.Phase3.InstallComplete; Failed = $false }
|
||||||
)
|
)
|
||||||
$p4HasFailed = $false; $p4AllDone = $true; $p4AnyStarted = $false
|
$p4HasFailed = $false; $p4AllDone = $true; $p4AnyStarted = $false
|
||||||
if ($Snap.Phase4 -and $Snap.Phase4.Count -gt 0) {
|
$p4HasScripts = ($Snap.Phase4 -and $Snap.Phase4.Count -gt 0)
|
||||||
|
if ($p4HasScripts) {
|
||||||
foreach ($s in $Snap.Phase4) {
|
foreach ($s in $Snap.Phase4) {
|
||||||
if ($s.Status -eq 'failed') { $p4HasFailed = $true }
|
if ($s.Status -eq 'failed') { $p4HasFailed = $true }
|
||||||
if ($s.Status -ne 'done') { $p4AllDone = $false }
|
if ($s.Status -ne 'done') { $p4AllDone = $false }
|
||||||
if ($s.Status -ne 'pending') { $p4AnyStarted = $true }
|
if ($s.Status -ne 'pending') { $p4AnyStarted = $true }
|
||||||
}
|
}
|
||||||
} else { $p4AllDone = $false }
|
} else {
|
||||||
|
# No scripts discovered. If DSC install is already complete,
|
||||||
|
# there are simply no custom scripts for this image type --
|
||||||
|
# that's COMPLETE, not WAITING.
|
||||||
|
$p4AllDone = $Snap.Phase3.InstallComplete
|
||||||
|
}
|
||||||
$p4Status = if ($p4HasFailed) { 'FAILED' } elseif ($p4AllDone) { 'COMPLETE' } elseif ($p4AnyStarted) { 'IN PROGRESS' } else { 'WAITING' }
|
$p4Status = if ($p4HasFailed) { 'FAILED' } elseif ($p4AllDone) { 'COMPLETE' } elseif ($p4AnyStarted) { 'IN PROGRESS' } else { 'WAITING' }
|
||||||
|
|
||||||
$p5Done = ($Snap.Phase5.ConsumeCredsTask -and $Snap.Phase5.CredsPopulated)
|
$p5Done = ($Snap.Phase5.ConsumeCredsTask -and $Snap.Phase5.CredsPopulated)
|
||||||
@@ -736,15 +742,6 @@ function Invoke-SetupComplete {
|
|||||||
try { & $ConfigureScript -MachineNumberOnly } catch { Write-Warning "Configure-PC failed: $_" }
|
try { & $ConfigureScript -MachineNumberOnly } catch { Write-Warning "Configure-PC failed: $_" }
|
||||||
}
|
}
|
||||||
|
|
||||||
# Delete AutoLogonCount so it can't deplete and nuke ShopFloor's
|
|
||||||
# autologon. Run-ShopfloorSetup set it to 4 for the SupportUser
|
|
||||||
# imaging chain; Windows decrements per-logon and at 0 clears
|
|
||||||
# AutoAdminLogon + DefaultPassword, breaking the lockdown-set
|
|
||||||
# ShopFloor autologon. Removing the value entirely leaves the
|
|
||||||
# lockdown's Autologon.exe-configured autologon intact forever.
|
|
||||||
& reg.exe delete 'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' /v AutoLogonCount /f 2>$null | Out-Null
|
|
||||||
Write-Host "Cleared AutoLogonCount (ShopFloor autologon will persist)."
|
|
||||||
|
|
||||||
# Reboot so Winlogon's new DefaultUserName=ShopFloor kicks in -
|
# Reboot so Winlogon's new DefaultUserName=ShopFloor kicks in -
|
||||||
# autologon only fires at the logon boundary. Next boot brings up
|
# autologon only fires at the logon boundary. Next boot brings up
|
||||||
# a clean ShopFloor session; this task will fire again for that
|
# a clean ShopFloor session; this task will fire again for that
|
||||||
@@ -925,6 +922,12 @@ try {
|
|||||||
}
|
}
|
||||||
$currentInterval = if ($waitingForLockdownOnly) { 15 } else { $RetriggerMinutes }
|
$currentInterval = if ($waitingForLockdownOnly) { 15 } else { $RetriggerMinutes }
|
||||||
|
|
||||||
|
# If we just entered lockdown-wait and the existing countdown is
|
||||||
|
# shorter than 15 min, push it out immediately so we don't fire
|
||||||
|
# a stale 5-min retrigger mid-Remediation.
|
||||||
|
$minNext = $lastSync.AddMinutes($currentInterval)
|
||||||
|
if ($minNext -gt $nextRetrigger) { $nextRetrigger = $minNext }
|
||||||
|
|
||||||
if ((Get-Date) -ge $nextRetrigger) {
|
if ((Get-Date) -ge $nextRetrigger) {
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
Write-Host "Re-triggering Intune sync..." -ForegroundColor Cyan
|
Write-Host "Re-triggering Intune sync..." -ForegroundColor Cyan
|
||||||
|
|||||||
Reference in New Issue
Block a user