246 lines
9.7 KiB
PowerShell
246 lines
9.7 KiB
PowerShell
# deprovision-kiosk.ps1 — remove the Vestibule kiosk session from this
|
|
# machine (Windows). The inverse of provision-kiosk.ps1, applied in
|
|
# reverse order:
|
|
#
|
|
# 1. AssignedAccess / Shell Launcher kiosk configuration
|
|
# 2. Automatic logon registry values (only if they point at the kiosk
|
|
# account — an unrelated autologon is never touched)
|
|
# 3. The Start Menu kiosk shortcut
|
|
# 4. Vestibule's merged policies.json — each directory is reset to its
|
|
# pre-Vestibule baseline (the backed-up operator file is reinstalled
|
|
# where one exists, Vestibule's generated file is deleted where one
|
|
# does not)
|
|
# 5. C:\ProgramData\Vestibule
|
|
# 6. Optionally the kiosk account and the Program Files install
|
|
#
|
|
# Exit codes:
|
|
# 0 success
|
|
# 10 success, reboot required to fully clear kiosk mode
|
|
# 2 unsupported platform / not elevated
|
|
# 5 removal failure
|
|
#
|
|
# Usage:
|
|
# powershell -ExecutionPolicy Bypass -File deprovision-kiosk.ps1 `
|
|
# -RemoveKioskAccount -RemoveInstall
|
|
|
|
param(
|
|
[string]$KioskUser = "VestibuleKiosk",
|
|
[string]$InstallRoot = "C:\Program Files\Vestibule",
|
|
[switch]$RemoveKioskAccount,
|
|
[switch]$RemoveInstall,
|
|
[switch]$Quiet,
|
|
[switch]$Check,
|
|
[switch]$Restart
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
$AUMID = "Vestibule.Kiosk"
|
|
|
|
function Fail([int]$code, [string]$msg) {
|
|
Write-Host ""
|
|
Write-Host "ERROR: $msg" -ForegroundColor Red
|
|
Write-Host " exiting with code $code"
|
|
exit $code
|
|
}
|
|
function Info($msg) { Write-Host $msg }
|
|
function Ok($msg) { Write-Host " [ok] $msg" -ForegroundColor Green }
|
|
function Step($msg) { Write-Host ""; Write-Host "==> $msg" -ForegroundColor Cyan }
|
|
|
|
if ($env:OS -ne "Windows_NT") {
|
|
Fail 2 "Windows-only; on Linux use scripts/deprovision-kiosk.sh"
|
|
}
|
|
$id = [Security.Principal.WindowsIdentity]::GetCurrent()
|
|
if (-not ([Security.Principal.WindowsPrincipal]$id).IsInRole(
|
|
[Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
|
Fail 2 "must run elevated"
|
|
}
|
|
|
|
$rebootNeeded = $false
|
|
|
|
# ─── Discover current state ───────────────────────────────────────────
|
|
|
|
$lnkPath = Join-Path $env:ProgramData "Microsoft\Windows\Start Menu\Programs\Vestibule Kiosk.lnk"
|
|
$wl = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
|
|
$autologonUser = (Get-ItemProperty $wl -ErrorAction SilentlyContinue).DefaultUserName
|
|
$autologonOn = (Get-ItemProperty $wl -ErrorAction SilentlyContinue).AutoAdminLogon -eq "1"
|
|
$weslPresent = [bool](Get-CimClass -Namespace "root\standardcimv2\embedded" `
|
|
-ClassName "WESL_UserSetting" -ErrorAction SilentlyContinue)
|
|
$accountPresent = [bool](Get-LocalUser -Name $KioskUser -ErrorAction SilentlyContinue)
|
|
|
|
Step "Current state"
|
|
Info "kiosk account : $(if ($accountPresent) {'present'} else {'absent'})"
|
|
Info "autologon : $(if ($autologonOn) {"on (user: $autologonUser)"} else {'off'})"
|
|
Info "Shell Launcher WMI : $(if ($weslPresent) {'present'} else {'absent'})"
|
|
Info "kiosk shortcut : $(if (Test-Path $lnkPath) {'present'} else {'absent'})"
|
|
|
|
if ($Check) {
|
|
Info "(check only — no changes made)"
|
|
exit 0
|
|
}
|
|
if (-not $Quiet) {
|
|
$answer = Read-Host "Proceed with removal? [y/N]"
|
|
if ($answer -notmatch '^[Yy]') { Info "aborted"; exit 0 }
|
|
}
|
|
|
|
# ─── 1. Lockdown removal ──────────────────────────────────────────────
|
|
|
|
Step "Remove kiosk lockdown"
|
|
|
|
if ($weslPresent) {
|
|
$wesl = [wmiclass]"\\.\root\standardcimv2\embedded:WESL_UserSetting"
|
|
$removed = $false
|
|
foreach ($call in @(
|
|
{ $wesl.RemoveCustomShell($KioskUser) },
|
|
{ $wesl.RemoveCustomShell() }
|
|
)) {
|
|
try {
|
|
$r = & $call
|
|
if ($r.ReturnValue -eq 0) { $removed = $true; break }
|
|
} catch { }
|
|
}
|
|
if ($removed) { Ok "Shell Launcher custom shell removed for '$KioskUser'" }
|
|
else { Info "Shell Launcher: no custom shell to remove (or already clean)" }
|
|
}
|
|
|
|
# AssignedAccess removal: call any Remove* method the bridge exposes on
|
|
# this build. Bridge method availability varies by build; on some builds
|
|
# the only clean removal is deleting the kiosk account (which orphans and
|
|
# neutralizes the config) — handled below, and reported honestly.
|
|
try {
|
|
$class = Get-CimClass -Namespace "root\cimv2\mdm\dmmap" -ClassName "MDM_AssignedAccess" `
|
|
-ErrorAction Stop
|
|
$removeMethods = @($class.CimClassMethods | Where-Object { $_.Name -like "Remove*" })
|
|
foreach ($m in $removeMethods) {
|
|
try {
|
|
$instances = @(Get-CimInstance -Namespace "root\cimv2\mdm\dmmap" `
|
|
-ClassName "MDM_AssignedAccess" -ErrorAction Stop)
|
|
foreach ($inst in $instances) {
|
|
$res = Invoke-CimMethod -InputObject $inst -MethodName $m.Name -ErrorAction Stop
|
|
if ($res.ReturnValue -eq 0) {
|
|
Ok "AssignedAccess cleared via $($m.Name)"
|
|
}
|
|
}
|
|
$rebootNeeded = $true
|
|
} catch {
|
|
Info "bridge method $($m.Name) present but call failed: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
} catch {
|
|
Info "MDM bridge not reachable — AssignedAccess config (if any) is cleared by removing the kiosk account below"
|
|
if ($accountPresent) { $rebootNeeded = $true }
|
|
}
|
|
|
|
# ─── 2. Automatic logon ───────────────────────────────────────────────
|
|
|
|
Step "Remove automatic logon"
|
|
if ($autologonOn -and $autologonUser -eq $KioskUser) {
|
|
Set-ItemProperty $wl -Name "AutoAdminLogon" -Value "0" -Type String
|
|
Remove-ItemProperty $wl -Name "DefaultUserName" -ErrorAction SilentlyContinue
|
|
Remove-ItemProperty $wl -Name "DefaultDomainName" -ErrorAction SilentlyContinue
|
|
Remove-ItemProperty $wl -Name "DefaultPassword" -ErrorAction SilentlyContinue
|
|
Ok "autologon disabled and credentials cleared"
|
|
} elseif ($autologonOn) {
|
|
Info "autologon is configured for '$autologonUser' (not ours) — left untouched"
|
|
} else {
|
|
Info "autologon already off"
|
|
}
|
|
|
|
# ─── 3. Shortcut ──────────────────────────────────────────────────────
|
|
|
|
Step "Remove kiosk shortcut"
|
|
if (Test-Path $lnkPath) {
|
|
Remove-Item $lnkPath -Force
|
|
Ok "removed $lnkPath"
|
|
} else {
|
|
Info "already absent"
|
|
}
|
|
|
|
# ─── 4. Policies ──────────────────────────────────────────────────────
|
|
|
|
Step "Reset browser policies to baseline"
|
|
# LibreWolf and Firefox share the distribution/policies.json mechanism;
|
|
# cover every install location for both browsers. Step-down per
|
|
# directory: backup present -> reinstall it; ours -> delete; else leave.
|
|
foreach ($browserExe in @(
|
|
"${env:ProgramFiles}\LibreWolf\librewolf.exe",
|
|
"${env:ProgramFiles(x86)}\LibreWolf\librewolf.exe",
|
|
"${env:LOCALAPPDATA}\Programs\LibreWolf\librewolf.exe",
|
|
"${env:ProgramFiles}\Mozilla Firefox\firefox.exe",
|
|
"${env:ProgramFiles}\Mozilla Firefox ESR\firefox.exe",
|
|
"${env:ProgramFiles(x86)}\Mozilla Firefox\firefox.exe",
|
|
"${env:ProgramFiles(x86)}\Mozilla Firefox ESR\firefox.exe",
|
|
"${env:LOCALAPPDATA}\Mozilla Firefox\firefox.exe")) {
|
|
if (-not (Test-Path $browserExe)) { continue }
|
|
$distDir = Join-Path (Split-Path -Parent $browserExe) "distribution"
|
|
$policiesPath = Join-Path $distDir "policies.json"
|
|
$bak = "$policiesPath.vestibule-bak"
|
|
if (Test-Path $bak) {
|
|
Move-Item $bak $policiesPath -Force
|
|
Ok "baseline policies.json reinstalled in $distDir (from vestibule-bak)"
|
|
} elseif (Test-Path $policiesPath) {
|
|
$content = Get-Content $policiesPath -Raw
|
|
if ($content -match "vestibule@vestibule\.kiosk") {
|
|
Remove-Item $policiesPath -Force
|
|
Ok "Vestibule-generated policies.json deleted in $distDir (no baseline on record)"
|
|
} else {
|
|
Info "policies.json in $distDir exists but is not ours — left untouched"
|
|
}
|
|
}
|
|
# librewolf.overrides.cfg is LibreWolf-only; harmless no-op for Firefox.
|
|
$cfg = Join-Path (Split-Path -Parent $browserExe) "librewolf.overrides.cfg"
|
|
if (Test-Path $cfg) { Remove-Item $cfg -Force; Ok "removed librewolf.overrides.cfg" }
|
|
}
|
|
|
|
# ─── 5. ProgramData ───────────────────────────────────────────────────
|
|
|
|
Step "Remove deployment config"
|
|
$pdDir = Join-Path $env:ProgramData "Vestibule"
|
|
if (Test-Path $pdDir) {
|
|
Remove-Item $pdDir -Recurse -Force
|
|
Ok "removed $pdDir"
|
|
} else {
|
|
Info "already absent"
|
|
}
|
|
|
|
# ─── 6. Optional: account + install tree ──────────────────────────────
|
|
|
|
if ($RemoveKioskAccount) {
|
|
Step "Remove kiosk account"
|
|
if ($accountPresent) {
|
|
# Ensure the account is not mid-logon (fast user switching) first.
|
|
try {
|
|
Remove-LocalUser -Name $KioskUser -ErrorAction Stop
|
|
Ok "removed local account '$KioskUser'"
|
|
$rebootNeeded = $true
|
|
} catch {
|
|
Fail 5 "could not remove account: $($_.Exception.Message)"
|
|
}
|
|
} else {
|
|
Info "account '$KioskUser' already absent"
|
|
}
|
|
}
|
|
|
|
if ($RemoveInstall) {
|
|
Step "Remove install tree"
|
|
if (Test-Path $InstallRoot) {
|
|
Remove-Item $InstallRoot -Recurse -Force
|
|
Ok "removed $InstallRoot"
|
|
} else {
|
|
Info "already absent"
|
|
}
|
|
}
|
|
|
|
# ─── Summary ──────────────────────────────────────────────────────────
|
|
|
|
Step "Deprovisioning complete"
|
|
Info "verify in Settings > Accounts > Other users that no kiosk entry remains"
|
|
if ($rebootNeeded) {
|
|
Info "reboot recommended to fully clear kiosk mode."
|
|
if ($Restart) {
|
|
shutdown.exe /r /t 10 /c "Vestibule kiosk removal"
|
|
exit 0
|
|
}
|
|
exit 10
|
|
}
|
|
exit 0
|