#Requires -Version 5.1 # PIM Manager 3.0.70 -- Install-PimHybridWorker.ps1, standalone (built by tools/setup/Build-PimSupportScripts.ps1; helpers inlined: ..\setup\_pimscriptdoc.ps1, ..\..\engine\_shared\invardiaupdate.verify.ps1) <# .SYNOPSIS §80.2 PIMHYBRIDWRK -- configure the worker machine itself: an Azure VM, or (§100.63(b)) the customer's own on-premises Windows server that is Azure Arc-enabled. Run ON the worker, elevated (run-command runs it as SYSTEM on both). .DESCRIPTION Installs and configures the PIM for Active Directory hybrid worker on the worker machine itself (an Azure VM or an Azure Arc-enabled on-premises server): the gMSAs, the PIM code for its update ring and the scheduled tasks. SELF-CONTAINED on purpose: it runs before any PIM code is on the machine, so it carries the little it needs. No gallery module; the in-box Windows ScheduledTasks and CimCmdlets modules are used (owner 2026-10-10: in-box Windows modules approved for the hybrid worker). Deliver it with Invoke-PimHybridWorkerRunCommand.ps1 (run-command, nothing inbound) or run it locally, elevated. -HostType Auto (default) | AzureVm | Arc. Auto = Arc when the Connected Machine agent (himds) is present. AzureVm: the identity comes from IMDS (169.254.169.254). Arc: from the Arc agent on localhost:40342, a 401 challenge answered by reading a key file only local Administrators and the local group 'Hybrid agent extension applications' may read -- Configure puts each worker gMSA into that group. -Phase Join (Azure VM) Offline domain join from the blob Initialize-PimHybridWorkerAd.ps1 -ProvisionOdj printed, then reboot. The blob file is deleted before the join returns; nothing else keeps it. An Arc server is the customer's own, already domain-joined: no Join phase. -Phase Configure (after the reboot) PowerShell 7 (MSI downloaded from github.com, signature checked); the gMSA installed and TESTED -- a worker that cannot use its gMSA fails HERE, not at 03:00; "log on as a batch job" for the gMSA only; C:\PIM locked down (only SYSTEM / Administrators write the code the gMSA runs -- the default C:\ ACL lets any signed-in user modify files below it, which on a Tier-0 box is an escalation path); the SIGNED release of this environment's RING from the Invardia update service -- its signature, ring and the archive's SHA-256 + size are checked before anything is unpacked, and nothing is installed when a check fails (the installation's licence + install key are read from the PIM database with the machine's managed identity: no SAS, no PAT); then the six scheduled tasks below (-Phase Layout). -Phase Update Ask for the signed release of the ring (checked the same way, by the installed version), install it when it differs, switch C:\PIM\app\current, apply the NEW version's -Phase Layout. Never moves without a ring answer; never moves backwards. -Phase Layout The run wrapper (C:\PIM\bin\Run-PimHybridWorker.ps1) + the scheduled tasks of THIS version. -Phase Remove (§100.63, Remove-PimHybridWorker.ps1 -Target Arc runs it through run-command) Unregister the six tasks, take the gMSA(s) out of 'Hybrid agent extension applications' and 'log on as a batch job', delete \app + \bin (the logs stay). The machine, its Arc agent, PowerShell 7 and the domain membership are NOT touched. Also removes the metadata-service firewall rule below. -Phase MetadataAccess (Azure VM) Re-applies only the outbound firewall rule 'PIM Hybrid Worker - metadata service access': the instance metadata service (169.254.169.254, TCP 80) answers only SYSTEM (the Azure VM agent and its extensions), PIM's gMSAs and the gMSA of each product registered in C:\ProgramData\HybridWorkerHost\guests\.json; every other account is blocked. Configure, Layout and every daily Update apply it too; a product that registers or unregisters as a guest runs C:\PIM\bin\Install-PimHybridWorker.ps1 -Phase MetadataAccess (as SYSTEM) right after. The step prints each account with its SID and warns when one cannot be resolved. Not on an Azure Arc server (its local Arc agent already limits who gets a token). The scheduled tasks (each runs Start-PimScheduler -UseManagedIdentity as the account named): 'PIM Hybrid Worker' every 5 min AS THE gMSA, instance hybrid: hybrid-ad-apply + hybrid-ad-groups (the safety net), + hybrid-ad-servers when no -ServerGmsaName is given 'PIM Hybrid AD Sync' continuous AS THE gMSA (at boot + re-checked every minute, no time limit): hybrid-ad-sync, the CRITICAL (non-server) groups, a pass every 5 s 'PIM Hybrid AD Sync Servers' continuous AS THE gMSA: hybrid-ad-sync-servers, the per-server groups 'PIM Hybrid AD Changes' continuous AS THE gMSA: hybrid-ad-changes (+ apply + groups the moment a definition changes) 'PIM Hybrid Servers' (only with -ServerGmsaName) every 5 min AS THE SERVER gMSA, instance hybridsrv: hybrid-ad-servers -- the tier split: server local admins are never written by Tier 0 'PIM Hybrid Worker Update' daily 04:30 as SYSTEM: this script -Phase Update Identity model: AD writes = the gMSA (the scheduled task's run-as account; PIM-HybridAd.ps1 verifies it and refuses SYSTEM). SQL + Graph (+ the source store with -UpdateSource Blob) = the machine's managed identity (VM: IMDS; Arc: the Arc agent). No password, secret or certificate is stored. .EXAMPLE .\Install-PimHybridWorker.ps1 -Phase Join -OdjBlob .\Install-PimHybridWorker.ps1 -Phase Configure -GmsaName 'CORP\gmsa-pimhw' -SqlServer .database.windows.net ` -SqlDatabase PimPlatform -TenantId -SourceContainerUrl https://.blob.core.windows.net/pim-src -Ring 1 # an Azure Arc-enabled on-premises server (already domain-joined): no Join phase .\Install-PimHybridWorker.ps1 -Phase Configure -HostType Arc -GmsaName 'CORP\gMSA-PIM-L1-T0' -ServerGmsaName 'CORP\gMSA-PIM-L2-T1' ` -SqlServer .database.windows.net -TenantId -SourceContainerUrl https://.blob.core.windows.net/pim-src -Ring 2 .NOTES Requirements: PowerShell: Windows PowerShell 5.1 (powershell.exe) -- one of PIM's three exceptions to PowerShell 7. Why: Azure run-command runs it as SYSTEM in Windows PowerShell on a fresh server, before PowerShell 7 is there; it installs PowerShell 7 itself, and the worker's scheduled tasks run in PowerShell 7. Role: local Administrator on the worker (run-command runs it as SYSTEM). No Microsoft Entra or Azure role for the person; the worker's machine identity reads the PIM code from the source store. Hosts: .blob.core.windows.net (the source store), github.com / api.github.com / objects.githubusercontent.com (PowerShell 7, once), the machine identity endpoint on the machine itself (Azure VM metadata service, or the Azure Arc agent on localhost). PERMISSIONS AND CHANGES (from Install-PimHybridWorker.ps1 manifest, PIM Manager 3.0.70; full page: https://invardia.com/docs/pim/scripts/Install-PimHybridWorker/) The person running it needs: - Local Administrator / SYSTEM on the worker (run-command runs it as SYSTEM) [other, the worker machine] -- installs PowerShell 7, the gMSAs, user rights, the C:\PIM folder and scheduled tasks What it creates, changes or grants: - update offline domain join from the blob, then a reboot (the worker machine (-Phase Join, Azure VM)) -- the Azure VM joins the domain without line of sight Undo: unjoin the machine - create PowerShell 7 (Microsoft-signed MSI from github.com); the gMSA(s) installed and tested; 'log on as a batch job' for them; C:\PIM (inheritance removed: SYSTEM + Administrators write, the gMSAs read); the PIM code for the ring; the run wrapper; six scheduled tasks (PIM Hybrid Worker, PIM Hybrid AD Sync, PIM Hybrid AD Sync Servers, PIM Hybrid AD Changes, PIM Hybrid Servers with -ServerGmsaName, PIM Hybrid Worker Update) (the worker machine (-Phase Configure)) -- the worker itself; no password, secret or certificate is stored Undo: delete the 'PIM Hybrid *' scheduled tasks and C:\PIM; remove the gMSAs from 'log on as a batch job' - update the gMSA(s) added to the local group 'Hybrid agent extension applications' (never Administrators) (the worker machine (Azure Arc)) -- the tasks run as the gMSAs and must answer the Arc agent's key-file challenge for the machine identity Undo: remove them from that group - update the machine account password is changed once (nltest /sc_change_pwd) (the worker machine (Azure VM, -Phase Configure)) -- makes the offline-join blob worthless Undo: none needed - create one outbound Windows Firewall rule, 'PIM Hybrid Worker - metadata service access': the instance metadata service (169.254.169.254, TCP 80) answers only SYSTEM (the Azure VM agent and its extensions), the worker's gMSAs and the gMSA of each product registered on the machine (C:\ProgramData\HybridWorkerHost\guests); every other account is blocked. Replaced on each run; the accounts are printed with their SIDs, and a warning names any account that cannot be resolved. Not on an Azure Arc server (the worker machine (Azure VM: -Phase Configure, Layout, Update and MetadataAccess)) -- only the accounts that need the machine's managed identities can ask for their tokens Undo: -Phase Remove, or delete the rule - remove the six scheduled tasks, the gMSA(s) from 'Hybrid agent extension applications' (Arc) and from 'log on as a batch job', the metadata-service firewall rule, C:\PIM\app and C:\PIM\bin (logs kept); the machine, its agent, PowerShell 7 and the domain membership stay (the worker machine (-Phase Remove)) -- retiring the worker (Remove-PimHybridWorker.ps1) Undo: -Phase Configure -WhatIf: Prints what the phase would do on this host (join, gMSAs, Arc group, the code, the six tasks and the metadata-service rule with the accounts it lets through) and changes nothing. Undo: Delete the 'PIM Hybrid *' scheduled tasks, the firewall rule 'PIM Hybrid Worker - metadata service access' and C:\PIM; on Arc remove the gMSAs from 'Hybrid agent extension applications'. .LINK https://invardia.com/docs/pim/scripts/Install-PimHybridWorker/ #> [CmdletBinding(SupportsShouldProcess)] param( # Guests (PIM 80.2a, framework 12.26): READ ONLY -- lists the other products registered on this host # MetadataAccess: re-applies only the metadata-service rule -- a guest runs it after it registers / unregisters [Parameter(Mandatory)][ValidateSet('Join', 'Configure', 'Update', 'Layout', 'Remove', 'Guests', 'MetadataAccess')][string]$Phase, [string]$OdjBlob, [string]$GmsaName, # 2.4.467 tier split (operator 2026-09-30): server onboarding runs as its OWN gMSA (DOMAIN\gMSA-PIM-L2-T1), in its own # task 'PIM Hybrid Servers'; blank = the main gMSA also onboards servers (the pre-2.4.467 layout) [string]$ServerGmsaName = '', [string]$SqlServer, [string]$SqlDatabase = 'PimPlatform', [string]$TenantId, [string]$SourceContainerUrl, [string]$Ring = '', [string]$Version = '', [string]$AdServer = '', [string]$Root = 'C:\PIM', [int]$IntervalMinutes = 5, [switch]$NoReboot, # Every tick runs -WhatIf: the jobs PLAN and log (\logs) and change nothing in AD. Re-run -Phase Configure without it to go live. [switch]$PlanOnly, # §100.63(b): where the managed identity comes from. Auto = Arc when the Connected Machine agent (himds) is installed. [ValidateSet('Auto', 'AzureVm', 'Arc')][string]$HostType = 'Auto', # SEC-95 (SECAUDIT-1): where the worker takes its code -- the FIRST install and every daily update. Invardia (default) = # the signed manifest, verified by the installed version (Resolve-PimHybridWorkerUpdate.ps1) or, on a first install, by # this installer (Resolve-FirstInstallSignedTarget); Blob = the source container's channel.json + archive, which carry # NO signature -- a lab choice only. A worker configured before this setting existed counts as Invardia. [ValidateSet('Invardia', 'Blob')][string]$UpdateSource = 'Invardia' ) $ErrorActionPreference = 'Stop' # 12.7 run frame: "