<# .SYNOPSIS Provision a PC for GE-Enforce + shopdb: write its identity (so it knows its PC type and bay), deploy the shopdb client kit, and register the enforcement scheduled task. Site-neutral and imaging-path independent - run it from a PXE step, an OOBE provisioning package (ppkg), Intune, or by hand. This is the "set up the PC to determine its PC type and such" step. There is no auto-detection: the caller supplies the type (and, where relevant, the bay machine number and CMM version), and this script writes the files/registry the GE-Enforce engine reads. This script does NOT contain the GE-Enforce engine (Install-FromManifest.ps1) or dispatcher - those are the GE-Enforce framework's. Point at your copy with -EngineSource, or ensure it is already present under -InstallRoot\lib. .PARAMETER PCType The imaging PC type = the manifest scope this PC runs. One of the scope names (e.g. gea-shopfloor-cmm, gea-shopfloor-collections) or a legacy alias the engine maps. Written to C:\Enrollment\pc-type.txt. REQUIRED. .PARAMETER MachineNumber The bay machine number (for TargetMachineNumbers gates). Written to machine-number.txt as a fallback; the DNC registry MachineNo wins if present. .PARAMETER CmmVersion CMM bays only: the resolved PC-DMIS version (2016/2019/2026) for _CmmVersion gating. Written to C:\Enrollment\cmm\version.txt. Omit for non-CMM PCs. .PARAMETER CmmId CMM bays only: the bay id. Written to C:\Enrollment\cmm\cmmid.txt. .PARAMETER ShareRoot The GE-Enforce share root (UNC), written into site-config.json as shopfloorShareRoot. .PARAMETER Site Site name, written into site-config.json. .PARAMETER ShopdbUrl shopdb base URL (scheme + host) for the fetch/report client. Written to HKLM:\SOFTWARE\GE\ShopDB\BaseUrl. .PARAMETER ShopdbToken A geenforce.fetch (+ geenforce.report) managed service token. Written to HKLM:\SOFTWARE\GE\ShopDB\ApiToken. (In many sites Azure DSC provisions this instead - omit here if so.) .PARAMETER EngineSource Optional path (folder or share) containing the GE-Enforce engine to copy in: expects GE-Enforce.ps1 and lib\Install-FromManifest.ps1. If omitted, the engine is assumed already present under -InstallRoot. .PARAMETER InstallRoot Where the client kit + engine live on the PC. Default C:\ProgramData\GE-Enforce. .PARAMETER TaskName Scheduled task name. Default 'GE-Enforce'. .PARAMETER IntervalMinutes How often the enforcement task repeats. Default 5. .PARAMETER NoTask Provision identity + kit only; do not register the scheduled task. .EXAMPLE .\Install-GEEnforce.ps1 -PCType gea-shopfloor-cmm -MachineNumber 0615 ` -CmmVersion 2019 -ShareRoot \\server\share\dt\shopfloor -Site "Main Plant" ` -ShopdbUrl https://shopdb.example.net -ShopdbToken shopdb_pat_xxx ` -EngineSource \\server\share\dt\shopfloor\common #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$PCType, [string]$MachineNumber, [string]$CmmVersion, [string]$CmmId, [string]$ShareRoot, [string]$Site, [string]$ShopdbUrl, [string]$ShopdbToken, [string]$EngineSource, [string]$InstallRoot = 'C:\ProgramData\GE-Enforce', [string]$EnrollmentRoot = 'C:\Enrollment', [string]$TaskName = 'GE-Enforce', [int]$IntervalMinutes = 5, [switch]$NoTask ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' function Write-TextFile { param([string]$Path, [string]$Value) $dir = Split-Path -Parent $Path if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null } Set-Content -LiteralPath $Path -Value $Value -Encoding ascii -NoNewline Write-Host " wrote $Path" } try { Write-Host "GE-Enforce provisioning: PCType=$PCType" # --- 1. Identity: what the PC is (what the engine reads) ----------------- Write-TextFile (Join-Path $EnrollmentRoot 'pc-type.txt') $PCType if ($MachineNumber) { Write-TextFile (Join-Path $EnrollmentRoot 'machine-number.txt') $MachineNumber } if ($CmmVersion) { Write-TextFile (Join-Path $EnrollmentRoot 'cmm\version.txt') $CmmVersion } if ($CmmId) { Write-TextFile (Join-Path $EnrollmentRoot 'cmm\cmmid.txt') $CmmId } $siteConfig = @{} if ($ShareRoot) { $siteConfig['shopfloorShareRoot'] = $ShareRoot } if ($Site) { $siteConfig['site'] = $Site } if ($siteConfig.Count) { $siteConfigPath = Join-Path $EnrollmentRoot 'site-config.json' $dir = Split-Path -Parent $siteConfigPath if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null } ($siteConfig | ConvertTo-Json) | Set-Content -LiteralPath $siteConfigPath -Encoding ascii Write-Host " wrote $siteConfigPath" } # --- 2. shopdb client registry config ------------------------------------ if ($ShopdbUrl -or $ShopdbToken) { $regPath = 'HKLM:\SOFTWARE\GE\ShopDB' if (-not (Test-Path $regPath)) { New-Item -Path $regPath -Force | Out-Null } if ($ShopdbUrl) { Set-ItemProperty -Path $regPath -Name BaseUrl -Value $ShopdbUrl } if ($ShopdbToken) { Set-ItemProperty -Path $regPath -Name ApiToken -Value $ShopdbToken } Write-Host " wrote HKLM:\SOFTWARE\GE\ShopDB" } # --- 3. Deploy the client kit (ships alongside this script) -------------- if (-not (Test-Path $InstallRoot)) { New-Item -ItemType Directory -Path $InstallRoot -Force | Out-Null } foreach ($file in 'ShopdbEnforceClient.psm1', 'Invoke-ShopdbEnforce.ps1') { $src = Join-Path $PSScriptRoot $file if (Test-Path $src) { Copy-Item -Path $src -Destination (Join-Path $InstallRoot $file) -Force Write-Host " deployed $file" } else { Write-Warning "client kit file not found next to this script: $file" } } # --- 4. Engine (referenced, not vendored) -------------------------------- if ($EngineSource) { Copy-Item -Path (Join-Path $EngineSource 'GE-Enforce.ps1') ` -Destination (Join-Path $InstallRoot 'GE-Enforce.ps1') -Force -ErrorAction SilentlyContinue $libDir = Join-Path $InstallRoot 'lib' if (-not (Test-Path $libDir)) { New-Item -ItemType Directory -Path $libDir -Force | Out-Null } Copy-Item -Path (Join-Path $EngineSource 'lib\Install-FromManifest.ps1') ` -Destination (Join-Path $libDir 'Install-FromManifest.ps1') -Force -ErrorAction SilentlyContinue Write-Host " copied engine from $EngineSource" } $enginePath = Join-Path $InstallRoot 'lib\Install-FromManifest.ps1' if (-not (Test-Path $enginePath)) { Write-Warning "engine not present at $enginePath - provide -EngineSource or place it there before enforcement runs." } # --- 5. Scheduled task: run the enforcement client as SYSTEM ------------- if (-not $NoTask) { $runner = Join-Path $InstallRoot 'Invoke-ShopdbEnforce.ps1' $arguments = "-NoProfile -ExecutionPolicy Bypass -File `"$runner`" -Scope `"$PCType`" -EnginePath `"$enginePath`"" $action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument $arguments $triggers = @( (New-ScheduledTaskTrigger -AtLogOn), (New-ScheduledTaskTrigger -Once -At (Get-Date) ` -RepetitionInterval (New-TimeSpan -Minutes $IntervalMinutes)) ) $principal = New-ScheduledTaskPrincipal -UserId 'NT AUTHORITY\SYSTEM' -RunLevel Highest $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries ` -DontStopIfGoingOnBatteries -StartWhenAvailable Register-ScheduledTask -TaskName $TaskName -Action $action -Trigger $triggers ` -Principal $principal -Settings $settings -Force | Out-Null Write-Host " registered task '$TaskName' (at logon + every $IntervalMinutes min)" } Write-Host "GE-Enforce provisioning complete." exit 0 } catch { Write-Error "GE-Enforce provisioning FAILED: $($_.Exception.Message)" exit 1 }