#Requires -Version 7.0 # PIM Manager 3.0.70 -- Publish-PimActivatorRemediation.ps1, standalone (built by tools/setup/Build-PimSupportScripts.ps1; helpers inlined: _pimactivatorbuild.ps1, ..\setup\_pimscriptdoc.ps1, ..\setup\_pimloopbackanswer.ps1) <# .SYNOPSIS Builds the PIM Activator Intune Remediation (Detect + Remediate) for your tenant and uploads it to Intune -- creates it, or updates it in place when a Remediation with the same name exists -- and assigns it to the one group you name (nobody otherwise). .DESCRIPTION Documentation (purpose, who may run it, every change and permission, -WhatIf, undo): https://invardia.com/docs/pim/scripts/Publish-PimActivatorRemediation/ -WhatIf signs in, reads Intune and prints every change it would make; it uploads, assigns and writes nothing (the pair it would upload is built in the temp folder so you can read it). ONE file, ONE command: nothing else to download. 97.2 (owner 2026-10-08: "this guide is wrong as it refers to 3 files, but the cmdlets show is missing 2 of them"): the script builds Detect-PimActivator.ps1 + Remediate-PimActivator.ps1 ITSELF from the parameters (the same text the PIM Manager's Operations > PIM Activator page builds for the same settings), writes them next to itself (-OutDir), then uploads them. Always: run as SYSTEM, 64-bit PowerShell, no signature check. -DetectScript / -RemediateScript upload your own pair instead (an override). WHO GETS IT (owner 2026-10-08: "customer must define who (group) to deploy to - not everyone" / "confirm that the cmdlets doesn't add an assignment by default"): -AssignToGroupId assigns the Remediation to THAT group (it replaces the Remediation's assignments) and reads the assignment back. Without it NOTHING is assigned -- a new Remediation reaches nobody until you assign a group; an existing one keeps the assignments it has. There is no "all users" / "all devices" option. NO MODULES (owner 2026-10-07: "neither pim, si or invardia must have dependencies"): plain REST. You sign in in the browser (Edge) as an Intune administrator; the script asks Microsoft Graph for DeviceManagementScripts.ReadWrite.All only. -AccessToken takes a token you already have instead (automation). .EXAMPLE # PROD, assigned to the group of admins who use the Activator (the command the PIM Manager page shows): .\Publish-PimActivatorRemediation.ps1 -TenantId -Channel Released -ClientId ` -CatalogName 'Contoso' -AssignToGroupId .EXAMPLE # Upload a pair you edited yourself, no assignment yet: .\Publish-PimActivatorRemediation.ps1 -TenantId -Name 'PIM Activator settings (TEST)' ` -DetectScript .\Detect-PimActivator.ps1 -RemediateScript .\Remediate-PimActivator.ps1 .EXAMPLE # Preview: what it would upload and assign -- changes nothing: .\Publish-PimActivatorRemediation.ps1 -TenantId -ClientId -AssignToGroupId -WhatIf .NOTES Requirements: PowerShell: 7.0 or later (pwsh). Windows PowerShell 5.1 is not supported. (The Detect / Remediate pair it builds runs in Windows PowerShell 5.1 on the devices, where Intune runs it.) Role: Intune Administrator (Microsoft Entra). Hosts: login.microsoftonline.com, graph.microsoft.com PERMISSIONS AND CHANGES (from Publish-PimActivatorRemediation.ps1 manifest, PIM Manager 3.0.70; full page: https://invardia.com/docs/pim/scripts/Publish-PimActivatorRemediation/) The person running it needs: - Intune Administrator [entraRole, tenant] -- creates / updates a Remediation (device health script) and assigns it. Global Administrator also works. - Delegated sign-in: DeviceManagementScripts.ReadWrite.All [other, Microsoft Graph, for the first-party app Microsoft Graph Command Line Tools] -- the browser sign-in asks for this one delegated permission only; it acts within your Intune role. Automation: -AccessToken with the same permission. What it creates, changes or grants: - create Detect-PimActivator.ps1 and Remediate-PimActivator.ps1 in -OutDir (default: next to the script), built from the parameters (this computer) -- the pair that is uploaded, kept so you can read exactly what the devices run Undo: delete the two files - create Remediation (deviceHealthScript) -Name, default 'PIM Activator settings (PROD)' / '(TEST)': the Detect + Remediate scripts, run as SYSTEM, 64-bit PowerShell, no signature check; updated in place when one with that exact name exists (Microsoft Intune) -- it writes the PIM Activator browser policies (force-install + tenant catalog + settings) on the devices it is assigned to, every day Undo: Intune admin center > Devices > Scripts and remediations > the Remediation > Delete - grant assignment to group -AssignToGroupId (user or device group); REPLACES the Remediation's assignments (Remediation -Name): Remediation assignment: runs on the group's devices daily at -DailyTime, or every -EveryHours hours @ only that group -- never all users or all devices; without -AssignToGroupId nothing is assigned -- who gets the PIM Activator settings Undo: Intune > the Remediation > Properties > Assignments > remove the group -WhatIf: Signs in, reads Intune, builds the pair in the temp folder (so you can read it) and prints a 'What if:' line for the Remediation it would create or update (with the scripts' checksums) and the group assignment it would make. Nothing is uploaded, assigned or written next to the script. Undo: Delete the Remediation in the Intune admin center (Devices > Scripts and remediations). Devices keep the policies it already wrote until you remove them (Deploy-PimActivatorClient.ps1 -Uninstall on a device, or a Remediation that removes them). A transcript of the run is written to \pim-manager-logs\ (its path is printed). .LINK https://invardia.com/docs/pim/scripts/Publish-PimActivatorRemediation/ #> [CmdletBinding(SupportsShouldProcess)] param( # The tenant to sign in to and to write into the extension's catalog (its id). [string]$TenantId = 'organizations', # Released (PROD) or Test -- which extension the Remediation configures. [ValidateSet('Released', 'Test')][string]$Channel = 'Released', # The PIM Activator app registration of that channel (its Application (client) id) -- step 1 of the page. [string]$ClientId, # The settings step of the page: the name admins see, the activation defaults, the optional filters and limits. [string]$CatalogName = 'PIM', [string]$DefaultJustification = 'Change in infrastructure', [ValidateRange(1, 24)][int]$DefaultDurationHours = 8, [string]$Prefix = '', [string]$EntraPrefix = '', [string]$AzurePrefix = '', [ValidateRange(0, 100)][int]$AutoActivateMaxGroups = -1, [ValidateRange(0, 100)][int]$BulkActivateConfirmThreshold = -1, # Optional end-of-day time 'HH:MM' (device local time): every activation ends no later than this today. [ValidatePattern('^(([01][0-9]|2[0-3]):[0-5][0-9])?$')][string]$ActivationEndTime = '', [string[]]$Browsers = @('Edge', 'Chrome'), # Optional: more tenants for the popup's tenant switcher (multi-tenant), a JSON array of # { name, tenantId, clientId, ... } -- this tenant stays the first entry. Empty = this tenant only. [string]$AdditionalTenantsJson = '', # The Remediation's name in Intune. Default 'PIM Activator settings (PROD)' / '(TEST)'. [string]$Name, # Overrides: upload these two files instead of building them. [string]$DetectScript, [string]$RemediateScript, # Where the built pair is written (default: next to this script). [string]$OutDir, # A Microsoft Graph access token with DeviceManagementScripts.ReadWrite.All -- skips the browser sign-in. [string]$AccessToken, [string]$Description = '', # Assign to this Entra group (user or device group). REPLACES the Remediation's assignments. Not given = NOTHING is # assigned (nobody gets it until you assign a group). [ValidatePattern('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$')][string]$AssignToGroupId, [ValidatePattern('^\d{2}:\d{2}$')][string]$DailyTime = '09:00', # With an assignment: run every N hours instead of daily (e.g. 1 = every hour). [ValidateRange(1, 23)][int]$EveryHours ) $ErrorActionPreference = 'Stop' [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 # The pure builder (catalog + the filled pair) and the shipped Detect / Remediate text. Loaded with the literal form below # so Build-PimSupportScripts inlines it (with the two scripts embedded) into the standalone download. # ===== BEGIN inlined helper: _PimActivatorBuild.ps1 ===== <# 97.2 -- the builder behind Operations > PIM Activator in the PIM Manager (operator 2026-10-07: "Easy Setup of Intune (remediation jobs) ... Edge for Business ... the entra apps ... PROD and TEST ... build of the catalog ... scripts for servers ... make it optional to which group or all devices or all users"). PURE: no network, no Graph -- the Manager calls it with what it knows, and the offline suite (tests\Test-PimActivatorBuild.ps1) proves it. What it builds: * the Intune Remediation pair (Detect / Remediate) with the SETTINGS block filled in -- channel, browsers, the tenant catalog, the two limits -- identically in both files (the README's rule), from the shipped scripts; * the Edge management service ExtensionSettings JSON ("Import JSON" on the Managed extensions tab) for the chosen channels, and the ExtensionInstallSources value; * the tenant catalog defaults from this environment (tenant id, the Activator app id, defaults); * the exact, self-contained commands: the apps (PROD + TEST; no group -- the app does not decide who gets anything, 100.90, 2026-10-11), the Remediation build + upload for the DEPLOYMENT group the customer chose (or nobody yet -- never all users / all devices; 2026-10-08), the server / no-Intune install. #> $script:PimActivatorChannels = [ordered]@{ Released = [ordered]@{ id = 'eheocihmlppcophaeakmdenhgcookkab'; updateUrl = 'https://knudsenmorten.github.io/PIM4EntraPS/updates.xml'; label = 'PROD'; appName = 'PIM Activator' } Test = [ordered]@{ id = 'glldnbmjpdkjemcnficagdhgienfdpoo'; updateUrl = 'https://knudsenmorten.github.io/PIM4EntraPS/updates-test.xml'; label = 'TEST'; appName = 'PIM Activator (TEST)' } } $script:PimActivatorMinimumVersion = '1.6.136' function Get-PimActivatorInstallSourcePattern { <# PURE. 97.2 (owner 2026-10-08: "step 4 url is wrong"): the ExtensionInstallSources pattern for an update URL = its FOLDER + '*' (https:////updates.xml -> https:////*) -- exactly where the page tells Edge to install from, never a whole site. Follows the update URL wherever it is hosted. #> param([Parameter(Mandatory)][string]$UpdateUrl) $u = $null if (-not [uri]::TryCreate("$UpdateUrl".Trim(), [UriKind]::Absolute, [ref]$u) -or $u.Scheme -ne 'https') { throw "The update URL '$UpdateUrl' is not an https URL." } $path = $u.AbsolutePath; $dir = $path.Substring(0, $path.LastIndexOf('/') + 1) "https://$($u.Authority)$dir*" } function Get-PimActivatorChannel { <# PURE. Released | Test -> @{ id; updateUrl; label; appName }. Throws on anything else. #> param([Parameter(Mandatory)][string]$Channel) foreach ($k in $script:PimActivatorChannels.Keys) { if ($k -ieq "$Channel".Trim()) { return $script:PimActivatorChannels[$k] } } throw "Unknown PIM Activator channel '$Channel' (Released or Test)." } function Get-PimActivatorEdgePolicy { <# PURE. The Edge management service policy for the chosen extensions -- the exact shape of the operator's working internal policy "Extension, Force-install PIM Activator (TEST + PROD)" (admin center, 2026-10-07): ONE Cloud policy with BOTH settings, ExtensionSettings {"*":{}, "":{installation_mode, blocked_permissions, runtime_blocked_hosts, runtime_allowed_hosts, minimum_version_required, override_update_url, update_url, toolbar_state}, ...} ExtensionInstallForcelist ";" per extension The service does NOT merge extension lists across policies, so both channels go in one policy when both are wanted. -Extensions: @( @{ channel = 'Released'|'Test'; minimumVersion; toolbarState = default_hidden|default_shown| force_shown; blockedHosts = @(); allowedHosts = @(); blockedPermissions = @(); overrideUpdateUrl = $true; installationMode = force_installed|normal_installed } ). -Channels is the short form (defaults for each). Returns @{ installSources; minimumVersion; extensionSettingsJson; extensionSettings (object); forcelist (array); forcelistText; extensions (the normalised input) }. #> param([string[]]$Channels = @('Released'), [object[]]$Extensions = @()) $list = @($Extensions | Where-Object { $_ }) if (-not $list.Count) { $list = @($Channels | Where-Object { $_ } | Select-Object -Unique | ForEach-Object { @{ channel = $_ } }) } $get = { param($o, $n) if ($o -is [System.Collections.IDictionary]) { if ($o.Contains($n)) { $o[$n] } } elseif ($null -ne $o -and $o.PSObject.Properties[$n]) { $o.$n } } $hosts = { param($v) @(@($v) | ForEach-Object { "$_" -split '[\r\n,]+' } | ForEach-Object { $_.Trim() } | Where-Object { $_ } | Select-Object -Unique) } $settings = [ordered]@{ '*' = [ordered]@{} } $force = New-Object System.Collections.Generic.List[string] $norm = New-Object System.Collections.Generic.List[object] $seen = @{} foreach ($e in $list) { $ch = Get-PimActivatorChannel -Channel "$(& $get $e 'channel')" if ($seen[$ch.id]) { continue }; $seen[$ch.id] = $true $min = "$(& $get $e 'minimumVersion')".Trim(); if (-not $min) { $min = $script:PimActivatorMinimumVersion } if ($min -notmatch '^\d+(\.\d+){0,3}$') { throw "Minimum version '$min' for $($ch.label) is not a version (e.g. 1.6.136)." } $tb = "$(& $get $e 'toolbarState')".Trim(); if (-not $tb) { $tb = 'default_hidden' } if ($tb -notin @('default_hidden', 'default_shown', 'force_shown')) { throw "Toolbar state '$tb' is not one of default_hidden, default_shown, force_shown." } $mode = "$(& $get $e 'installationMode')".Trim(); if (-not $mode) { $mode = 'force_installed' } if ($mode -notin @('force_installed', 'normal_installed')) { throw "Installation setting '$mode' is not force_installed or normal_installed." } $ovr = & $get $e 'overrideUpdateUrl'; $ovr = if ($null -eq $ovr -or "$ovr" -eq '') { $true } else { [bool]$ovr -and "$ovr" -ne 'false' } # @(): a script block returning an empty or one-item array unrolls it ($null / a bare string) -- the policy needs [] $blocked = @(& $hosts (& $get $e 'blockedHosts')); $allowed = @(& $hosts (& $get $e 'allowedHosts')) $perms = @(@(& $get $e 'blockedPermissions') | ForEach-Object { "$_".Trim() } | Where-Object { $_ } | Select-Object -Unique) $settings[$ch.id] = [ordered]@{ installation_mode = $mode; blocked_permissions = $perms; runtime_blocked_hosts = $blocked; runtime_allowed_hosts = $allowed minimum_version_required = $min; override_update_url = [bool]$ovr; update_url = $ch.updateUrl; toolbar_state = $tb } $force.Add("$($ch.id);$($ch.updateUrl)") $norm.Add([ordered]@{ channel = $(if ($ch.label -eq 'TEST') { 'Test' } else { 'Released' }); id = $ch.id; name = $ch.appName; updateUrl = $ch.updateUrl minimumVersion = $min; toolbarState = $tb; installationMode = $mode; overrideUpdateUrl = [bool]$ovr blockedHosts = $blocked; allowedHosts = $allowed; blockedPermissions = $perms }) } # Compact, like the admin center's own export (and so empty arrays stay [] -- ConvertTo-Json keeps them as [] only # with -InputObject on the whole object). $json = ConvertTo-Json -InputObject $settings -Depth 6 -Compress $json = $json -replace '"\*":\{\}', '"*":{}' -replace '"\*":\[\]', '"*":{}' # ExtensionInstallSources: one pattern row per distinct update-URL folder of the chosen extensions (owner 2026-10-08). $src = @($norm | ForEach-Object { Get-PimActivatorInstallSourcePattern -UpdateUrl $_.updateUrl } | Select-Object -Unique) return [ordered]@{ installSources = ($src -join "`n"); installSourcesList = @($src); minimumVersion = $script:PimActivatorMinimumVersion extensionSettingsJson = $json; extensionSettings = $settings; forcelist = $force.ToArray(); forcelistText = ($force.ToArray() -join ', ') extensions = $norm.ToArray() } } function Get-PimActivatorPublishedVersion { <# PURE. The version an update manifest (updates.xml / updates-test.xml, Google update2 format) publishes for an extension id; '' when the text holds none. #> param([string]$UpdateXml, [Parameter(Mandatory)][string]$ExtensionId) if (-not "$UpdateXml".Trim()) { return '' } $rx = "(?s)]*>.*?]*\bversion=['""]([0-9.]+)['""]" $m = [regex]::Match($UpdateXml, $rx) if ($m.Success) { return $m.Groups[1].Value } return '' } function New-PimActivatorCatalog { <# PURE. The tenant catalog (the JSON the extension reads) for THIS environment. -ClientId = the Activator app of the channel. Optional prefixes narrow what the popup lists. Returns the JSON text (an array of one tenant). #> param([Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][string]$TenantId, [Parameter(Mandatory)][string]$ClientId, [string]$DefaultJustification = 'Change in infrastructure', [int]$DefaultDurationHours = 8, [string]$Prefix = '', [string]$EntraPrefix = '', [string]$AzurePrefix = '', # 97.2 (owner 2026-10-08: "the custom json structure for multi-tenant (optional). so the admins can select from # different tenants"): more tenants for the popup's tenant switcher, a JSON array (ConvertTo-PimActivatorExtraTenants). [string]$AdditionalTenantsJson = '') $guid = '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$' if ("$TenantId" -notmatch $guid) { throw "TenantId is not a GUID: '$TenantId'" } if ("$ClientId" -notmatch $guid) { throw "ClientId (the PIM Activator app) is not a GUID: '$ClientId'" } if ($DefaultDurationHours -lt 1 -or $DefaultDurationHours -gt 24) { throw 'DefaultDurationHours must be 1-24.' } $t = [ordered]@{ name = "$Name".Trim(); tenantId = "$TenantId".ToLowerInvariant(); clientId = "$ClientId".ToLowerInvariant() defaultJustification = "$DefaultJustification"; defaultDurationHours = $DefaultDurationHours } if ("$Prefix".Trim()) { $t['prefix'] = "$Prefix".Trim() } if ("$EntraPrefix".Trim()) { $t['entraPrefix'] = "$EntraPrefix".Trim() } if ("$AzurePrefix".Trim()) { $t['azurePrefix'] = "$AzurePrefix".Trim() } $extra = @(ConvertTo-PimActivatorExtraTenants -Json $AdditionalTenantsJson -ExcludeTenantId $t.tenantId) return (ConvertTo-Json -InputObject (@($t) + $extra) -Depth 4) } function ConvertTo-PimActivatorExtraTenants { <# PURE. The OPTIONAL extra tenants of the catalog (managed-schema.json tenantCatalog: an array of { name, tenantId, clientId, defaultJustification?, defaultDurationHours?, prefix?, entraPrefix?, azurePrefix?, groupNameFilter?, entraGroupRegex?, azureGroupRegex?, bulkActivateConfirmThreshold? }). Empty = none (one tenant, as before). Throws on anything the extension would not read: not an array, a missing name / tenant id / client id, a key it does not know, a value out of range, the same tenant twice. An entry for this environment's own tenant (-ExcludeTenantId) is skipped: that tenant is always the first entry already. Returns the normalised entries (ordered, ids lower case). #> param([string]$Json, [string]$ExcludeTenantId = '') if (-not "$Json".Trim()) { return @() } $guid = '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$' if (-not "$Json".TrimStart().StartsWith('[')) { throw 'The extra tenants must be a JSON array: [ { "name": ..., "tenantId": ..., "clientId": ... } ].' } try { $arr = ConvertFrom-Json -InputObject "$Json" -ErrorAction Stop } catch { throw "The extra tenants are not valid JSON: $($_.Exception.Message)" } # Windows PowerShell 5.1 can answer broken text with $null instead of an error -- that is not "no tenants" if ($null -eq $arr -and "$Json".Trim() -notmatch '^\[\s*\]$') { throw 'The extra tenants are not valid JSON (nothing could be read from it).' } $known = @('name', 'tenantId', 'clientId', 'defaultJustification', 'defaultDurationHours', 'prefix', 'entraPrefix', 'azurePrefix', 'groupNameFilter', 'entraGroupRegex', 'azureGroupRegex', 'bulkActivateConfirmThreshold') $seen = @{}; if ("$ExcludeTenantId".Trim()) { $seen["$ExcludeTenantId".ToLowerInvariant()] = 'this environment' } $out = New-Object System.Collections.Generic.List[object] $i = 0 foreach ($e in @($arr)) { $i++ if ($null -eq $e -or $e -isnot [pscustomobject]) { throw "Extra tenant #${i} is not an object." } $bad = @($e.PSObject.Properties.Name | Where-Object { $_ -cnotin $known }) if ($bad.Count) { throw "Extra tenant #${i}: unknown key(s) $($bad -join ', ') (allowed: $($known -join ', '))." } $n = "$($e.name)".Trim(); $tid = "$($e.tenantId)".Trim(); $cid = "$($e.clientId)".Trim() if (-not $n) { throw "Extra tenant #${i}: 'name' is required." } if ($tid -notmatch $guid) { throw "Extra tenant #$i ($n): 'tenantId' must be a GUID." } if ($cid -notmatch $guid) { throw "Extra tenant #$i ($n): 'clientId' (its PIM Activator app) must be a GUID." } $k = $tid.ToLowerInvariant() # this environment's own tenant is always the FIRST entry (built from the Settings step): an entry for it here is # the sample's first row -- skipped, not doubled if ("$ExcludeTenantId".Trim() -and $k -eq "$ExcludeTenantId".Trim().ToLowerInvariant()) { continue } if ($seen.ContainsKey($k)) { throw "Extra tenant #$i ($n): tenant $k is already in the catalog ($($seen[$k]))." } $seen[$k] = $n $o = [ordered]@{ name = $n; tenantId = $k; clientId = $cid.ToLowerInvariant() } foreach ($p in $known | Select-Object -Skip 3) { if (-not $e.PSObject.Properties[$p] -or $null -eq $e.$p -or "$($e.$p)" -eq '') { continue } $v = $e.$p if ($p -eq 'defaultDurationHours') { if ("$v" -notmatch '^\d+$' -or [int]$v -lt 1 -or [int]$v -gt 24) { throw "Extra tenant #$i ($n): defaultDurationHours must be 1-24." }; $v = [int]$v } elseif ($p -eq 'bulkActivateConfirmThreshold') { if ("$v" -notmatch '^\d+$' -or [int]$v -lt 1 -or [int]$v -gt 100) { throw "Extra tenant #$i ($n): bulkActivateConfirmThreshold must be 1-100." }; $v = [int]$v } elseif ($p -in @('entraPrefix', 'azurePrefix') -and $v -is [array]) { $v = @($v | ForEach-Object { "$_" }) } else { $v = "$v" } $o[$p] = $v } $out.Add($o) } return $out.ToArray() # unrolled on purpose: callers collect with @() } function Set-PimActivatorRemediationSettings { <# PURE. The shipped Detect / Remediate script text with its SETTINGS block filled in. Only these five assignments are replaced; everything else stays byte-for-byte. Throws when a setting line is not found (a changed script must not ship with a half-filled block) or the catalog is not a JSON array. #> param([Parameter(Mandatory)][string]$Text, [Parameter(Mandatory)][ValidateSet('Released', 'Test')][string]$Channel, [string[]]$Browsers = @('Edge', 'Chrome'), [Parameter(Mandatory)][string]$CatalogJson, [Nullable[int]]$AutoActivateMaxGroups = $null, [Nullable[int]]$BulkActivateConfirmThreshold = $null, [string]$ActivationEndTime = '') # ACT-ENDTIME (owner 2026-10-31): '' = no end time; else 'HH:MM' 00:00-23:59 $endT = "$ActivationEndTime".Trim() if ($endT -and $endT -notmatch '^([01][0-9]|2[0-3]):[0-5][0-9]$') { throw "ActivationEndTime must be 'HH:MM' between 00:00 and 23:59 (got '$endT')." } $b = @($Browsers | Where-Object { $_ -in @('Edge', 'Chrome') } | Select-Object -Unique) if (-not $b.Count) { throw 'Browsers must hold Edge and/or Chrome.' } try { $cat = $CatalogJson | ConvertFrom-Json } catch { throw "The tenant catalog is not valid JSON: $($_.Exception.Message)" } if (-not ($CatalogJson.TrimStart().StartsWith('['))) { throw 'The tenant catalog must be a JSON array of tenants.' } if ($CatalogJson -match "(?m)^'@") { throw "The tenant catalog may not contain a line starting with '@." } $nl = if ($Text -match "`r`n") { "`r`n" } else { "`n" } $rules = [ordered]@{ Channel = @('(?m)^\$Channel = .*$', ('$Channel = ''{0}''' -f $Channel)) Browsers = @('(?m)^\$Browsers = .*$', ('$Browsers = @({0})' -f ((@($b) | ForEach-Object { "'$_'" }) -join ', '))) Catalog = @("(?s)^\`$TenantCatalog = @'\r?\n.*?\r?\n'@", ("`$TenantCatalog = @'" + $nl + (($CatalogJson.Trim()) -replace "`r?`n", $nl) + $nl + "'@")) AutoMax = @('(?m)^\$AutoActivateMaxGroups = .*$', ('$AutoActivateMaxGroups = {0}' -f $(if ($null -ne $AutoActivateMaxGroups) { [int]$AutoActivateMaxGroups } else { '$null' }))) BulkConf = @('(?m)^\$BulkActivateConfirmThreshold = .*$', ('$BulkActivateConfirmThreshold = {0}' -f $(if ($null -ne $BulkActivateConfirmThreshold) { [int]$BulkActivateConfirmThreshold } else { '$null' }))) EndTime = @('(?m)^\$ActivationEndTime = .*$', ('$ActivationEndTime = {0}' -f $(if ($endT) { "'$endT'" } else { '$null' }))) } $out = $Text foreach ($k in $rules.Keys) { $rx = [regex]::new($rules[$k][0], [System.Text.RegularExpressions.RegexOptions]::Multiline) if (-not $rx.IsMatch($out)) { throw "The script has no '$k' setting line -- refusing to build a half-filled Remediation." } $rep = $rules[$k][1] $out = $rx.Replace($out, { param($m) $rep }, 1) } return $out } # --------------------------------------------------------------------------------------------------------------------- # 97.2 (owner 2026-10-08: "remove these buttons and leave only the cmdlets to run ... customer must define who (group) to # deploy to - not everyone"; then "give option to dropdown group ... the cmdlets doesn't add an assignment by default"; and # "this guide is wrong as it refers to 3 files, but the cmdlets show is missing 2 of them"). The page shows ONLY commands. # Each command is SELF-CONTAINED (one download from invardia.com/support/pim, nothing else to fetch). The Remediation # command names WHO Intune deploys it to: the customer's chosen group (-AssignToGroupId), or nobody yet when no group is # chosen. Never all users / all devices. # 100.90 (owner 2026-10-11: "we dont use it to assign or login ... strip out the user assignement part"): the group is # DEPLOYMENT targeting only (Intune, Edge for Business). The app commands carry no group: the app is how the extension # signs in, every user of the tenant can, and what a person can activate is decided by their PIM eligibility. # --------------------------------------------------------------------------------------------------------------------- # The shipped Detect / Remediate scripts. In the repository they are read from intune-remediation\ next to this file; the # standalone Publish-PimActivatorRemediation.ps1 that Build-PimSupportScripts makes carries them EMBEDDED (the builder # fills the table on the next line), so the published script needs no other file. $script:PimActivatorBuildDir = $PSScriptRoot $script:PimActivatorEmbeddedTemplates = @{ 'Detect-PimActivator.ps1' = 'IyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgIFBJTSBBY3RpdmF0b3IgZm9yIE1pY3Jvc29mdCBFZGdlIGFuZCBHb29nbGUgQ2hyb21lIC0tIHNldHRpbmdzICsgInJlcGFpciBvbiByZXF1ZXN0IiAgIEludHVuZSBSZW1lZGlhdGlvbjogREVURUNUSU9ODQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIw0KIyAgV0hBVCBJVCBET0VTDQojICAgIENoZWNrcyB0aGlzIGRldmljZSAtLSBpdCBvbmx5IFJFQURTLCBub3RoaW5nIGlzIGNoYW5nZWQ6DQojICAgICAgMS4gdGhlIFBJTSBBY3RpdmF0b3Igc2V0dGluZ3MgYXJlIGV4YWN0bHkgYXMgaW4gdGhlIFNFVFRJTkdTIGJsb2NrIGJlbG93LCBmb3IgZXZlcnkgYnJvd3NlciBpbiAkQnJvd3NlcnMNCiMgICAgICAgICAgICBFZGdlOiAgICBIS0xNXFNPRlRXQVJFXFBvbGljaWVzXE1pY3Jvc29mdFxFZGdlXDNyZHBhcnR5XGV4dGVuc2lvbnNcPGV4dGVuc2lvbiBpZD5ccG9saWN5DQojICAgICAgICAgICAgQ2hyb21lOiAgSEtMTVxTT0ZUV0FSRVxQb2xpY2llc1xHb29nbGVcQ2hyb21lXDNyZHBhcnR5XGV4dGVuc2lvbnNcPGV4dGVuc2lvbiBpZD5ccG9saWN5DQojICAgICAgICAgICAgKHRlbmFudENhdGFsb2cgZm9yIHRoZSBleHRlbnNpb24gb2YgJENoYW5uZWw7IGF1dG9BY3RpdmF0ZU1heEdyb3VwcyArIGJ1bGtBY3RpdmF0ZUNvbmZpcm1UaHJlc2hvbGQgKyBhY3RpdmF0aW9uRW5kVGltZSB3aXRoIHRoZQ0KIyAgICAgICAgICAgICBTQU1FIHZhbHVlcyBmb3IgQk9USCBQSU0gQWN0aXZhdG9yIGV4dGVuc2lvbnMgLS0gcHJvZHVjdGlvbiBhbmQgdGVzdCBiZWhhdmUgdGhlIHNhbWUpDQojICAgICAgMi4gbm9ib2R5IGhhcyBhc2tlZCBmb3IgYSByZXBhaXIgLS0gdGhlIFJlcGFpclN0dWNrIGZsYWcgaXMgbm90IHNldCwgbmVpdGhlciBieSBhIHVzZXINCiMgICAgICAgICAgICBIS0NVXFNvZnR3YXJlXFBJTTRFbnRyYVBTXFBpbUFjdGl2YXRvciAgUmVwYWlyU3R1Y2sgPSAxICAgKGFueSBzaWduZWQtaW4gdXNlcikNCiMgICAgICAgICBub3IgYnkgYW4gYWRtaW4gZm9yIHRoZSB3aG9sZSBkZXZpY2UNCiMgICAgICAgICAgICBIS0xNXFNPRlRXQVJFXFBJTTRFbnRyYVBTXFBpbUFjdGl2YXRvciAgUmVwYWlyU3R1Y2sgPSAxDQojDQojICBSRVNVTFQgQ09ERVMgKEludHVuZSBhY3RzIG9uIHRoZXNlKQ0KIyAgICBleGl0IDAgPSBPSyAtLSBub3RoaW5nIHRvIGRvDQojICAgIGV4aXQgMSA9IG5vdCBPSyAtLSBhIHNldHRpbmcgaXMgbWlzc2luZyBvciBkaWZmZXJlbnQsIG9yIGEgcmVwYWlyIHdhcyByZXF1ZXN0ZWQgKHRoZSBvdXRwdXQgc2F5cyB3aGljaCk7DQojICAgICAgICAgICAgIEludHVuZSB0aGVuIHJ1bnMgUmVtZWRpYXRlLVBpbUFjdGl2YXRvci5wczENCiMNCiMgIElNUE9SVEFOVA0KIyAgICBLZWVwIHRoZSBTRVRUSU5HUyBibG9jayBJREVOVElDQUwgdG8gdGhlIG9uZSBpbiBSZW1lZGlhdGUtUGltQWN0aXZhdG9yLnBzMS4gSWYgdGhleSBkaWZmZXIsIHRoaXMgc2NyaXB0IHJlcG9ydHMNCiMgICAgIm5vdCBPSyIgYWZ0ZXIgZXZlcnkgZml4IGFuZCB0aGUgUmVtZWRpYXRpb24gcnVucyBhZ2FpbiBhbmQgYWdhaW4uDQojICAgIEV2ZXJ5IHNldHRpbmcsIHRoZSByZXBhaXIsIGFuZCBob3cgdG8gc2V0IHVwIHRoZSBSZW1lZGlhdGlvbiBhcmUgZXhwbGFpbmVkIGluIFJlbWVkaWF0ZS1QaW1BY3RpdmF0b3IucHMxLg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0gU0VUVElOR1MgLS0gZWRpdCB0aGVzZSA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KIyBXaGljaCBleHRlbnNpb246ICdSZWxlYXNlZCcgPSBwcm9kdWN0aW9uIChpZCBlaGVvY2lobWxwcGNvcGhhZWFrbWRlbmhnY29va2thYikNCiMgICAgICAgICAgICAgICAgICAnVGVzdCcgICAgID0gdGVzdCBidWlsZCAgIChpZCBnbGxkbmJtanBka2plbWNuZmljYWdkaGdpZW5mZHBvbykNCiRDaGFubmVsID0gJ1JlbGVhc2VkJw0KDQojIFdoaWNoIGJyb3dzZXJzIGdldCB0aGUgc2V0dGluZ3MgKGFuZCB0aGUgcmVwYWlyKTogJ0VkZ2UnLCAnQ2hyb21lJywgb3IgYm90aC4gU2FtZSBleHRlbnNpb24gaWQgaW4gYm90aC4NCiRCcm93c2VycyA9IEAoJ0VkZ2UnLCAnQ2hyb21lJykNCg0KIyBUaGUgdGVuYW50cyB1c2VycyBjYW4gc2lnbiBpbiB0byAtLSBldmVyeSBmaWVsZCBpcyBleHBsYWluZWQgaW4gUmVtZWRpYXRlLVBpbUFjdGl2YXRvci5wczEuDQokVGVuYW50Q2F0YWxvZyA9IEAnDQpbDQogIHsgIm5hbWUiOiAiQ29udG9zbyIsICJ0ZW5hbnRJZCI6ICIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDAiLCAiY2xpZW50SWQiOiAiMDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAwIiwNCiAgICAiZGVmYXVsdEp1c3RpZmljYXRpb24iOiAiQ2hhbmdlIGluIGluZnJhc3RydWN0dXJlIiwgImRlZmF1bHREdXJhdGlvbkhvdXJzIjogOCB9DQpdDQonQA0KDQojIEF1dG8tYWN0aXZhdGUgY2FwOiAgICRudWxsID0gbm8gbGltaXQgfCAwID0gYXV0by1hY3RpdmF0aW9uIE9GRiB8IDEtMTAwID0gbWF4IGdyb3VwcyBhIHVzZXIgbWF5IHRpY2sgYXMgImF1dG8iDQokQXV0b0FjdGl2YXRlTWF4R3JvdXBzID0gJG51bGwNCg0KIyBDb25maXJtIHRocmVzaG9sZDogICAkbnVsbCA9IGRlZmF1bHQgKDUpIHwgMS0xMDAgPSB0aGUgQWN0aXZhdGUgYnV0dG9uIGFza3MgZm9yIGEgMm5kIGNsaWNrIGF0IHRoaXMgbWFueSByb2xlcw0KJEJ1bGtBY3RpdmF0ZUNvbmZpcm1UaHJlc2hvbGQgPSAkbnVsbA0KDQojIEVuZC1vZi1kYXkgdGltZTogICAgICRudWxsID0gbm9uZSB8ICdISDpNTScgPSBhY3RpdmF0aW9ucyBlbmQgbm8gbGF0ZXIgdGhhbiB0aGlzIGxvY2FsIHRpbWUgdG9kYXkNCiRBY3RpdmF0aW9uRW5kVGltZSA9ICRudWxsDQoNCiMgUmVwYWlyIG9uIHJlcXVlc3QsIHdoZW4gdGhlIGJyb3dzZXIgaXMgb3BlbjogICAkZmFsc2UgPSB3YWl0IHVudGlsIGl0IGlzIGNsb3NlZCB8ICR0cnVlID0gY2xvc2UgRWRnZS9DaHJvbWUgICAodXNlZCBieSBSZW1lZGlhdGUpDQokQ2xvc2VFZGdlID0gJGZhbHNlDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCiMgLS0tIG5vdGhpbmcgdG8gZWRpdCBiZWxvdyB0aGlzIGxpbmUgLS0tDQojIFRoZSBjaGFubmVsIHBpY2tzIE9OTFkgd2hpY2ggZXh0ZW5zaW9uIHRoaXMgaXMgKGl0cyBpZCk7IGV2ZXJ5dGhpbmcgaXQgY2hlY2tzIGlzIHRoZSBzYW1lIGZvciBib3RoLg0KJEV4dGVuc2lvbklkID0gaWYgKCRDaGFubmVsIC1lcSAnVGVzdCcpIHsgJ2dsbGRuYm1qcGRramVtY25maWNhZ2RoZ2llbmZkcG9vJyB9IGVsc2UgeyAnZWhlb2NpaG1scHBjb3BoYWVha21kZW5oZ2Nvb2trYWInIH0NCiRBbGxFeHRlbnNpb25zID0gW29yZGVyZWRdQHsgJ2VoZW9jaWhtbHBwY29waGFlYWttZGVuaGdjb29ra2FiJyA9ICdwcm9kdWN0aW9uJzsgJ2dsbGRuYm1qcGRramVtY25maWNhZ2RoZ2llbmZkcG9vJyA9ICd0ZXN0JyB9DQokUG9saWN5Um9vdHMgPSBAeyBFZGdlID0gJ1NPRlRXQVJFXFBvbGljaWVzXE1pY3Jvc29mdFxFZGdlJzsgQ2hyb21lID0gJ1NPRlRXQVJFXFBvbGljaWVzXEdvb2dsZVxDaHJvbWUnIH0NCiRTZWxlY3RlZCAgICA9IEAoJEJyb3dzZXJzIHwgRm9yRWFjaC1PYmplY3QgeyAiJF8iLlRyaW0oKSB9IHwgV2hlcmUtT2JqZWN0IHsgJF8gfSB8IFNlbGVjdC1PYmplY3QgLVVuaXF1ZSkNCiMgVGhlIHNhbWUgY29tcGFjdCBsaXN0IGZvcm0gdGhlIFJlbWVkaWF0ZSBzY3JpcHQgd3JpdGVzIChzZWUgdGhlIG5vdGUgdGhlcmUgYWJvdXQgV2luZG93cyBQb3dlclNoZWxsIDUuMSkuDQokQ2F0YWxvZyAgICAgPSBDb252ZXJ0VG8tSnNvbiAtSW5wdXRPYmplY3QgQCgkVGVuYW50Q2F0YWxvZyB8IENvbnZlcnRGcm9tLUpzb24gfCBGb3JFYWNoLU9iamVjdCB7ICRfIH0pIC1EZXB0aCAxMCAtQ29tcHJlc3MNCiRGbGFnS2V5cyAgICA9IEAoJ1JlZ2lzdHJ5OjpIS0VZX0xPQ0FMX01BQ0hJTkVcU09GVFdBUkVcUElNNEVudHJhUFNcUGltQWN0aXZhdG9yJykgKw0KICAgICAgICAgICAgICAgQChHZXQtQ2hpbGRJdGVtIC1QYXRoICdSZWdpc3RyeTo6SEtFWV9VU0VSUycgLUVycm9yQWN0aW9uIFNpbGVudGx5Q29udGludWUgfA0KICAgICAgICAgICAgICAgICBXaGVyZS1PYmplY3QgeyAkXy5QU0NoaWxkTmFtZSAtbGlrZSAnUy0xLSonIC1hbmQgJF8uUFNDaGlsZE5hbWUgLW5vdGxpa2UgJypfQ2xhc3NlcycgfSB8DQogICAgICAgICAgICAgICAgIEZvckVhY2gtT2JqZWN0IHsgIlJlZ2lzdHJ5OjokKCRfLk5hbWUpXFNvZnR3YXJlXFBJTTRFbnRyYVBTXFBpbUFjdGl2YXRvciIgfSkNCg0KdHJ5IHsNCiAgICAkcHJvYmxlbXMgPSBAKCkNCiAgICAkdW5rbm93biA9IEAoJFNlbGVjdGVkIHwgV2hlcmUtT2JqZWN0IHsgLW5vdCAkUG9saWN5Um9vdHMuQ29udGFpbnNLZXkoJF8pIH0pDQogICAgaWYgKC1ub3QgJFNlbGVjdGVkLkNvdW50IC1vciAkdW5rbm93bi5Db3VudCkgeyAkcHJvYmxlbXMgKz0gImAkQnJvd3NlcnMgbXVzdCBiZSAnRWRnZScsICdDaHJvbWUnIG9yIGJvdGggKGdvdCAnJCgkQnJvd3NlcnMgLWpvaW4gIicsICciKScpIiB9DQogICAgaWYgKCRudWxsIC1uZSAkQWN0aXZhdGlvbkVuZFRpbWUgLWFuZCAiJEFjdGl2YXRpb25FbmRUaW1lIiAtbm90bWF0Y2ggJ14oWzAxXVswLTldfDJbMC0zXSk6WzAtNV1bMC05XSQnKSB7ICRwcm9ibGVtcyArPSAiYCRBY3RpdmF0aW9uRW5kVGltZSBtdXN0IGJlICdISDpNTScgYmV0d2VlbiAwMDowMCBhbmQgMjM6NTkgKGdvdCAnJEFjdGl2YXRpb25FbmRUaW1lJykiIH0NCiAgICAjIDEuIHRoZSBzZXR0aW5ncywgcGVyIGJyb3dzZXIgKDY0LWJpdCByZWdpc3RyeSwgYXMgdGhlIGJyb3dzZXJzIHJlYWQgaXQpDQogICAgZm9yZWFjaCAoJGIgaW4gQCgkU2VsZWN0ZWQgfCBXaGVyZS1PYmplY3QgeyAkUG9saWN5Um9vdHMuQ29udGFpbnNLZXkoJF8pIH0pKSB7DQogICAgICAgICRoa2xtID0gW01pY3Jvc29mdC5XaW4zMi5SZWdpc3RyeUtleV06Ok9wZW5CYXNlS2V5KCdMb2NhbE1hY2hpbmUnLCAnUmVnaXN0cnk2NCcpDQogICAgICAgICRrZXkgPSAkaGtsbS5PcGVuU3ViS2V5KCIkKCRQb2xpY3lSb290c1skYl0pXDNyZHBhcnR5XGV4dGVuc2lvbnNcJEV4dGVuc2lvbklkXHBvbGljeSIpDQogICAgICAgIGlmICgtbm90ICRrZXkpIHsgJHByb2JsZW1zICs9ICIkYiBoYXMgbm8gUElNIEFjdGl2YXRvciBzZXR0aW5ncyBvbiB0aGlzIGRldmljZSB5ZXQiIH0NCiAgICAgICAgZWxzZSB7DQogICAgICAgICAgICBpZiAoIiQoJGtleS5HZXRWYWx1ZSgndGVuYW50Q2F0YWxvZycpKSIgLW5lICRDYXRhbG9nKSB7ICRwcm9ibGVtcyArPSAiJGJgOiB0aGUgdGVuYW50IGNhdGFsb2cgZGlmZmVycyIgfQ0KICAgICAgICAgICAgJGtleS5DbG9zZSgpDQogICAgICAgIH0NCiAgICAgICAgIyB0aGUgbGltaXRzOiB0aGUgU0FNRSB2YWx1ZXMgZm9yIGJvdGggUElNIEFjdGl2YXRvciBleHRlbnNpb25zIChwcm9kdWN0aW9uIGFuZCB0ZXN0IGJlaGF2ZSB0aGUgc2FtZSkNCiAgICAgICAgZm9yZWFjaCAoJGlkIGluICRBbGxFeHRlbnNpb25zLktleXMpIHsNCiAgICAgICAgICAgICR3aGljaCA9IGlmICgkaWQgLWVxICRFeHRlbnNpb25JZCkgeyAnJyB9IGVsc2UgeyAiIGZvciB0aGUgJCgkQWxsRXh0ZW5zaW9uc1skaWRdKSBleHRlbnNpb24gdG9vIChwcm9kdWN0aW9uIGFuZCB0ZXN0IGdldCB0aGUgc2FtZSBsaW1pdHMgLS0gaWYgYW5vdGhlciBQSU0gQWN0aXZhdG9yIFJlbWVkaWF0aW9uIHNldHMgb3RoZXIgdmFsdWVzLCBnaXZlIGJvdGggdGhlIHNhbWUpIiB9DQogICAgICAgICAgICAka2V5ID0gJGhrbG0uT3BlblN1YktleSgiJCgkUG9saWN5Um9vdHNbJGJdKVwzcmRwYXJ0eVxleHRlbnNpb25zXCRpZFxwb2xpY3kiKQ0KICAgICAgICAgICAgaWYgKC1ub3QgJGtleSkgeyBpZiAoJGlkIC1uZSAkRXh0ZW5zaW9uSWQpIHsgJHByb2JsZW1zICs9ICIkYmA6IHRoZSBsaW1pdHMgYXJlIG5vdCBzZXQkd2hpY2giIH07IGNvbnRpbnVlIH0NCiAgICAgICAgICAgIGlmICgiJCgka2V5LkdldFZhbHVlKCdhdXRvQWN0aXZhdGVNYXhHcm91cHMnKSkiIC1uZSAiJEF1dG9BY3RpdmF0ZU1heEdyb3VwcyIpIHsgJHByb2JsZW1zICs9ICIkYmA6IHRoZSBhdXRvLWFjdGl2YXRlIGNhcCBkaWZmZXJzIChkZXZpY2UgJyQoJGtleS5HZXRWYWx1ZSgnYXV0b0FjdGl2YXRlTWF4R3JvdXBzJykpJywgd2FudGVkICckQXV0b0FjdGl2YXRlTWF4R3JvdXBzJykkd2hpY2giIH0NCiAgICAgICAgICAgIGlmICgiJCgka2V5LkdldFZhbHVlKCdidWxrQWN0aXZhdGVDb25maXJtVGhyZXNob2xkJykpIiAtbmUgIiRCdWxrQWN0aXZhdGVDb25maXJtVGhyZXNob2xkIikgeyAkcHJvYmxlbXMgKz0gIiRiYDogdGhlIGNvbmZpcm0gdGhyZXNob2xkIGRpZmZlcnMgKGRldmljZSAnJCgka2V5LkdldFZhbHVlKCdidWxrQWN0aXZhdGVDb25maXJtVGhyZXNob2xkJykpJywgd2FudGVkICckQnVsa0FjdGl2YXRlQ29uZmlybVRocmVzaG9sZCcpJHdoaWNoIiB9DQogICAgICAgICAgICBpZiAoIiQoJGtleS5HZXRWYWx1ZSgnYWN0aXZhdGlvbkVuZFRpbWUnKSkiIC1uZSAiJEFjdGl2YXRpb25FbmRUaW1lIikgeyAkcHJvYmxlbXMgKz0gIiRiYDogdGhlIGVuZC1vZi1kYXkgdGltZSBkaWZmZXJzIChkZXZpY2UgJyQoJGtleS5HZXRWYWx1ZSgnYWN0aXZhdGlvbkVuZFRpbWUnKSknLCB3YW50ZWQgJyRBY3RpdmF0aW9uRW5kVGltZScpJHdoaWNoIiB9DQogICAgICAgICAgICAka2V5LkNsb3NlKCkNCiAgICAgICAgfQ0KICAgICAgICAkaGtsbS5DbG9zZSgpDQogICAgfQ0KICAgICMgMi4gYSByZXBhaXIgcmVxdWVzdA0KICAgIGZvcmVhY2ggKCRrIGluICRGbGFnS2V5cykgew0KICAgICAgICBpZiAoKEdldC1JdGVtUHJvcGVydHkgLVBhdGggJGsgLU5hbWUgUmVwYWlyU3R1Y2sgLUVycm9yQWN0aW9uIFNpbGVudGx5Q29udGludWUpLlJlcGFpclN0dWNrIC1lcSAxKSB7ICRwcm9ibGVtcyArPSAiYSByZXBhaXIgd2FzIHJlcXVlc3RlZCAoJCgkayAtcmVwbGFjZSAnXlJlZ2lzdHJ5OjonLCAnJykpIjsgYnJlYWsgfQ0KICAgIH0NCiAgICBpZiAoJHByb2JsZW1zKSB7IFdyaXRlLU91dHB1dCAiTm90IE9LOiAkKCRwcm9ibGVtcyAtam9pbiAnOyAnKS4iOyBleGl0IDEgfQ0KICAgIFdyaXRlLU91dHB1dCAiT0s6IHRoZSBQSU0gQWN0aXZhdG9yIHNldHRpbmdzIGFyZSBjdXJyZW50ICgkKCRTZWxlY3RlZCAtam9pbiAnICsgJykpIGFuZCBubyByZXBhaXIgaXMgcmVxdWVzdGVkLiINCiAgICBleGl0IDANCn0gY2F0Y2ggew0KICAgIFdyaXRlLU91dHB1dCAiTm90IE9LOiBjb3VsZCBub3QgY2hlY2sgdGhlIFBJTSBBY3RpdmF0b3Igc2V0dGluZ3M6ICQoJF8uRXhjZXB0aW9uLk1lc3NhZ2UpIg0KICAgIGV4aXQgMQ0KfQ0K'; 'Remediate-PimActivator.ps1' = 'IyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgIFBJTSBBY3RpdmF0b3IgZm9yIE1pY3Jvc29mdCBFZGdlIGFuZCBHb29nbGUgQ2hyb21lIC0tIHNldHRpbmdzICsgInJlcGFpciBvbiByZXF1ZXN0IiAgIEludHVuZSBSZW1lZGlhdGlvbjogUkVNRURJQVRJT04NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQojDQojICBXSEFUIElUIERPRVMgLS0gdHdvIGpvYnMsIGluIHRoaXMgb3JkZXINCiMNCiMgICAxLiBTRVRUSU5HUyAoZXZlcnkgcnVuKS4gV3JpdGVzIHRoZSBQSU0gQWN0aXZhdG9yIGV4dGVuc2lvbidzIHNldHRpbmdzIG9uIHRoaXMgZGV2aWNlLCBzbyB1c2VycyBnZXQgdGhlIHJpZ2h0DQojICAgICAgdGVuYW50cywgZGVmYXVsdHMgYW5kIGxpbWl0cyB3aXRob3V0IGFueSBzZXR1cCAtLSBmb3IgZXZlcnkgYnJvd3NlciBpbiAkQnJvd3NlcnMgKEVkZ2UsIENocm9tZSBvciBib3RoKS4gT25seQ0KIyAgICAgIHRoZXNlIHJlZ2lzdHJ5IHZhbHVlcyBhcmUgd3JpdHRlbiAodGhlIGJyb3dzZXIgaGFuZHMgdGhlbSB0byB0aGUgZXh0ZW5zaW9uIGFzICJtYW5hZ2VkIHN0b3JhZ2UiKToNCiMNCiMgICAgICAgIEVkZ2U6ICAgIEhLTE1cU09GVFdBUkVcUG9saWNpZXNcTWljcm9zb2Z0XEVkZ2VcM3JkcGFydHlcZXh0ZW5zaW9uc1w8ZXh0ZW5zaW9uIGlkPlxwb2xpY3kNCiMgICAgICAgIENocm9tZTogIEhLTE1cU09GVFdBUkVcUG9saWNpZXNcR29vZ2xlXENocm9tZVwzcmRwYXJ0eVxleHRlbnNpb25zXDxleHRlbnNpb24gaWQ+XHBvbGljeQ0KIyAgICAgICAgICAgIHRlbmFudENhdGFsb2cgICAgICAgICAgICAgICAgICBSRUdfU1ogICAgIHRoZSB0ZW5hbnRzIChKU09OKSAgICAgICAgICAgICAgICAgICAgICAgYWx3YXlzIHdyaXR0ZW4NCiMgICAgICAgICAgICBhdXRvQWN0aXZhdGVNYXhHcm91cHMgICAgICAgICAgUkVHX0RXT1JEICB0aGUgYXV0by1hY3RpdmF0ZSBjYXAgICAgICAgICAgICAgICAgICAgIHdyaXR0ZW4sIG9yIHJlbW92ZWQgd2hlbiAkbnVsbA0KIyAgICAgICAgICAgIGJ1bGtBY3RpdmF0ZUNvbmZpcm1UaHJlc2hvbGQgICBSRUdfRFdPUkQgIHRoZSAiY2xpY2sgYWdhaW4gdG8gY29uZmlybSIgdGhyZXNob2xkICAgd3JpdHRlbiwgb3IgcmVtb3ZlZCB3aGVuICRudWxsDQojICAgICAgICAgICAgYWN0aXZhdGlvbkVuZFRpbWUgICAgICAgICAgICAgIFJFR19TWiAgICAgdGhlIGVuZC1vZi1kYXkgdGltZSAnSEg6TU0nICAgICAgICAgICAgICB3cml0dGVuLCBvciByZW1vdmVkIHdoZW4gJG51bGwNCiMNCiMgICAgICBQUk9EVUNUSU9OIEFORCBURVNUIEJFSEFWRSBUSEUgU0FNRS4gVGhlIHRlbmFudCBjYXRhbG9nIGdvZXMgdG8gdGhlIGV4dGVuc2lvbiBvZiAkQ2hhbm5lbCAoaXQgbmFtZXMgdGhlIGFwcCB0aGF0DQojICAgICAgZXh0ZW5zaW9uIHNpZ25zIGluIHdpdGgpLiBUaGUgdHdvIExJTUlUUyBnbyB0byBCT1RIIFBJTSBBY3RpdmF0b3IgZXh0ZW5zaW9ucyAocHJvZHVjdGlvbiBhbmQgdGVzdCkgd2l0aCB0aGUgc2FtZQ0KIyAgICAgIHZhbHVlcyAtLSBhIGRldmljZSBjYW4gbmV2ZXIgcnVuIHRoZSB0ZXN0IGV4dGVuc2lvbiB3aXRoIG9uZSBsaW1pdCBhbmQgcHJvZHVjdGlvbiB3aXRoIGFub3RoZXIsIHNvIGFwcHJvdmluZw0KIyAgICAgIHRlc3QgZm9yIHByb2R1Y3Rpb24gY2hhbmdlcyBub3RoaW5nIGluIGhvdyBpdCBiZWhhdmVzLg0KIyAgICAgIFRoZSBleHRlbnNpb24gaWQgaXMgdGhlIHNhbWUgaW4gYm90aCBicm93c2VycyAob25lIHNpZ25lZCBwYWNrYWdlKS4gRWFjaCBrZXkgYmVsb25ncyB0byB0aGUgZXh0ZW5zaW9uIGFsb25lLCBzbyBpdA0KIyAgICAgIG5ldmVyIGNvbmZsaWN0cyB3aXRoIGFueSBvdGhlciBleHRlbnNpb24gcG9saWN5LiBUaGlzIHBhcnQgbmV2ZXIgdG91Y2hlcyB0aGUgYnJvd3NlcnMgdGhlbXNlbHZlcyBhbmQgd29ya3Mgd2hpbGUNCiMgICAgICB0aGV5IGFyZSBvcGVuLiBBIGJyb3dzZXIgbGVmdCBvdXQgb2YgJEJyb3dzZXJzIGlzIG5vdCB0b3VjaGVkIChpdHMgdmFsdWVzIHN0YXkgYXMgdGhleSBhcmUpLg0KIw0KIyAgIDIuIFJFUEFJUiAob25seSB3aGVuIHNvbWVib2R5IGFza2VkIGZvciBpdCkuIElmIHRoZSBleHRlbnNpb24gaXMgc3R1Y2sgKGUuZy4gYW4gb2xkIHZlcnNpb24gdGhhdCBkb2VzIG5vdCB1cGRhdGUpLA0KIyAgICAgIGEgdXNlciBvciBhbiBhZG1pbiBzZXRzIHRoZSByZXBhaXIgZmxhZzoNCiMNCiMgICAgICAgIHVzZXIsIG5vIGFkbWluIHJpZ2h0cyBuZWVkZWQgKHJ1biBpbiB0aGUgdXNlcidzIG93biBzZXNzaW9uLCBlLmcuIGZyb20gYSBDb21tYW5kIFByb21wdCk6DQojICAgICAgICAgICAgcmVnIGFkZCBIS0NVXFNvZnR3YXJlXFBJTTRFbnRyYVBTXFBpbUFjdGl2YXRvciAvdiBSZXBhaXJTdHVjayAvdCBSRUdfRFdPUkQgL2QgMSAvZg0KIyAgICAgICAgYWRtaW4sIGZvciB0aGUgd2hvbGUgZGV2aWNlOg0KIyAgICAgICAgICAgIHJlZyBhZGQgSEtMTVxTT0ZUV0FSRVxQSU00RW50cmFQU1xQaW1BY3RpdmF0b3IgL3YgUmVwYWlyU3R1Y2sgL3QgUkVHX0RXT1JEIC9kIDEgL2YNCiMNCiMgICAgICBPbiB0aGUgbmV4dCBydW4gdGhpcyBzY3JpcHQgdGhlbiwgZm9yIGV2ZXJ5IEVkZ2UgLyBDaHJvbWUgcHJvZmlsZSAodGhlIGJyb3dzZXJzIGluICRCcm93c2Vycykgb2YgZXZlcnkgdXNlcjoNCiMgICAgICAgIC0gY3V0cyBQSU0gQWN0aXZhdG9yJ3MgZW50cmllcyBvdXQgb2YgdGhlIHByb2ZpbGUncyAiUHJlZmVyZW5jZXMiIGFuZCAiU2VjdXJlIFByZWZlcmVuY2VzIiBmaWxlcw0KIyAgICAgICAgICAoZWFjaCBmaWxlIGlzIGNvcGllZCB0byAiPGZpbGU+LmJhay08ZGF0ZS10aW1lPiIgZmlyc3QpLCBhbmQNCiMgICAgICAgIC0gZGVsZXRlcyBQSU0gQWN0aXZhdG9yJ3MgaW5zdGFsbGVkIHByb2dyYW0gZmlsZXMgKHRoZSAiRXh0ZW5zaW9uc1w8ZXh0ZW5zaW9uIGlkPiIgZm9sZGVyKSwNCiMgICAgICBhbmQgZmluYWxseSBERUxFVEVTIHRoZSBmbGFnLiBUaGUgbmV4dCB0aW1lIHRoZSBicm93c2VyIHN0YXJ0cywgdGhlIHBvbGljeSB0aGF0IGZvcmNlLWluc3RhbGxzIHRoZSBleHRlbnNpb24NCiMgICAgICAoRWRnZTogdGhlIEVkZ2UgbWFuYWdlbWVudCBzZXJ2aWNlOyBDaHJvbWU6IGl0cyBFeHRlbnNpb25JbnN0YWxsRm9yY2VsaXN0IHBvbGljeSkgaW5zdGFsbHMgdGhlIGN1cnJlbnQgdmVyc2lvbiBmcmVzaC4NCiMNCiMgICAgICBIT1cgVEhFIEZJTEVTIEFSRSBDSEFOR0VEIC0tIGFuZCB3aHkgaXQgaXMgZG9uZSB0aGlzIHdheTogdGhlIGVudHJpZXMgYXJlIGN1dCBvdXQgYXMgVEVYVCAob25seSB0aGUNCiMgICAgICAiPGV4dGVuc2lvbiBpZD4iOiB7IC4uLiB9IGJsb2NrcyBhcmUgcmVtb3ZlZDsgZXZlcnkgb3RoZXIgYnl0ZSBvZiB0aGUgZmlsZSBzdGF5cyBleGFjdGx5IGFzIEVkZ2Ugd3JvdGUgaXQpLg0KIyAgICAgIFRoZSBmaWxlcyBhcmUgTk9UIHJlYWQgYW5kIHJlLXdyaXR0ZW4gd2l0aCBQb3dlclNoZWxsJ3MgSlNPTiBjb252ZXJ0ZXI6IG9uIDIwMjYtMDYtMDkgdGhhdCBtZXRob2QgY29ycnVwdGVkIGENCiMgICAgICBQcmVmZXJlbmNlcyBmaWxlIHVuZGVyIFdpbmRvd3MgUG93ZXJTaGVsbCA1LjEgYW5kIEVkZ2UgcmVzZXQgdGhlIHdob2xlIHByb2ZpbGUgKGJvb2ttYXJrcywgaGlzdG9yeSwgdGFicykuDQojICAgICAgQSBmaWxlIHRoYXQgZG9lcyBub3QgbG9vayBsaWtlIEVkZ2UncyBKU09OIGJlZm9yZSBvciBhZnRlciB0aGUgY3V0IGlzIGxlZnQgdW50b3VjaGVkLg0KIw0KIyAgICAgIFdoYXQgdGhlIHJlcGFpciBkb2VzIE5PVCB0b3VjaDogYm9va21hcmtzLCBwYXNzd29yZHMsIGhpc3RvcnksIGNvb2tpZXMsIG90aGVyIGV4dGVuc2lvbnMsIHN5bmMuDQojICAgICAgV2hhdCB0aGUgdXNlciB3aWxsIG5vdGljZTogUElNIEFjdGl2YXRvciBpcyByZWluc3RhbGxlZCwgc28gdGhlIHVzZXIgc2lnbnMgaW4gdG8gaXQgYWdhaW4sIGFuZCBpdHMgb3duIHNhdmVkDQojICAgICAgZGF0YSAoZmF2b3JpdGVzLCAiYXV0byIgdGlja3MsIGFuIGltcG9ydGVkIGNhdGFsb2cpIG1heSBiZSBnb25lIC0tIEVkZ2UgdHJlYXRzIGl0IGFzIGEgbmV3IGluc3RhbGwuDQojDQojICAgICAgVGhlIGJyb3dzZXIgbXVzdCBiZSBDTE9TRUQgZm9yIHRoZSByZXBhaXI6IGl0IHJld3JpdGVzIHRoZXNlIGZpbGVzIHdoZW4gaXQgZXhpdHMsIHdoaWNoIHdvdWxkIHVuZG8gaXQuIFNvIHdoaWxlDQojICAgICAgRWRnZSBvciBDaHJvbWUgKHRob3NlIGluICRCcm93c2VycykgaXMgcnVubmluZyB0aGUgcmVwYWlyIGRvZXMgbm90aGluZywga2VlcHMgdGhlIGZsYWcgYW5kIHNheXMgc28gLS0gdGhlIG5leHQgcnVuDQojICAgICAgdHJpZXMgYWdhaW4gKGUuZy4gZXZlcnkgaG91cikuIFNldCAkQ2xvc2VFZGdlID0gJHRydWUgdG8gY2xvc2UgdGhlIGJyb3dzZXJzIGluc3RlYWQgLS0gaXQgY292ZXJzIEVkZ2UgQU5EIENocm9tZQ0KIyAgICAgICh0aGUgbmFtZSBpcyBrZXB0IHNvIGV4aXN0aW5nIGNvcGllcyBrZWVwIHdvcmtpbmcpOyBvcGVuIHRhYnMgY29tZSBiYWNrIHdoZW4gdGhlIHVzZXIgcmVvcGVucyB0aGUgYnJvd3Nlci4NCiMgICAgICBOb3RlOiBhIGJyb3dzZXIgc2V0IHRvIGtlZXAgcnVubmluZyBpbiB0aGUgYmFja2dyb3VuZCAoIlN0YXJ0dXAgYm9vc3QiIC8gIkNvbnRpbnVlIHJ1bm5pbmcgYmFja2dyb3VuZA0KIyAgICAgIGV4dGVuc2lvbnMgYW5kIGFwcHMgd2hlbiAuLi4gaXMgY2xvc2VkIikgY291bnRzIGFzIHJ1bm5pbmcuDQojDQojICBXSEFUIElUIERPRVMgTk9UIERPDQojICAgIEl0IGRvZXMgTk9UIGluc3RhbGwgdGhlIGV4dGVuc2lvbiAtLSB0aGUgYnJvd3NlciBwb2xpY3kgZG9lczogZm9yIEVkZ2UgdGhlIE1pY3Jvc29mdCBFZGdlIG1hbmFnZW1lbnQgc2VydmljZSAoYQ0KIyAgICBDbG91ZCBwb2xpY3kgdGhhdCBmb3JjZXMgdGhlIGV4dGVuc2lvbiB3aXRoIGl0cyB1cGRhdGUgVVJMIC0tIHNlZSBSRUFETUUubWQsIHN0ZXAgMSk7IGZvciBDaHJvbWUgdGhlDQojICAgIEV4dGVuc2lvbkluc3RhbGxGb3JjZWxpc3QgcG9saWN5IHdpdGggdGhlIHNhbWUgaWQgKyB1cGRhdGUgVVJMLg0KIw0KIyAgSE9XIFRPIFVTRSBJVA0KIyAgICAxLiBDb3B5IHRoaXMgc2NyaXB0IEFORCBEZXRlY3QtUGltQWN0aXZhdG9yLnBzMS4NCiMgICAgMi4gRWRpdCB0aGUgU0VUVElOR1MgYmxvY2sgYmVsb3cgLS0gdGhlIFNBTUUgdmFsdWVzIGluIGJvdGggc2NyaXB0cyAodGhlIERldGVjdCBzY3JpcHQgY29tcGFyZXMgYWdhaW5zdCB0aGVtKS4NCiMgICAgMy4gSW50dW5lIGFkbWluIGNlbnRlciAtPiBEZXZpY2VzIC0+IFNjcmlwdHMgYW5kIHJlbWVkaWF0aW9ucyAtPiBDcmVhdGU6DQojICAgICAgICAgRGV0ZWN0aW9uIHNjcmlwdCBmaWxlICAgPSBEZXRlY3QtUGltQWN0aXZhdG9yLnBzMQ0KIyAgICAgICAgIFJlbWVkaWF0aW9uIHNjcmlwdCBmaWxlID0gdGhpcyBmaWxlDQojICAgICAgICAgUnVuIHRoaXMgc2NyaXB0IHVzaW5nIHRoZSBsb2dnZWQtb24gY3JlZGVudGlhbHMgPSBObyAgICAocnVucyBhcyBTWVNURU0gLS0gbmVlZGVkIHRvIHdyaXRlIHVuZGVyDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSEtMTVxTT0ZUV0FSRVxQb2xpY2llcyBhbmQgdG8gcmVwYWlyIGV2ZXJ5IHByb2ZpbGUpDQojICAgICAgICAgRW5mb3JjZSBzY3JpcHQgc2lnbmF0dXJlIGNoZWNrICAgICAgICAgICAgICAgICAgPSBObyAgICAodW5sZXNzIHlvdSBzaWduIHRoZSBzY3JpcHRzKQ0KIyAgICAgICAgIFJ1biBzY3JpcHQgaW4gNjQtYml0IFBvd2VyU2hlbGwgICAgICAgICAgICAgICAgID0gWWVzDQojICAgICAgIEFzc2lnbiBpdCB0byB0aGUgc2FtZSBncm91cCBhcyB0aGUgRWRnZSBwb2xpY3kgdGhhdCBpbnN0YWxscyB0aGUgZXh0ZW5zaW9uIChhIHVzZXIgZ3JvdXAgd29ya3M6IGl0IHJ1bnMgb24NCiMgICAgICAgdGhhdCB1c2VyJ3MgZGV2aWNlcyksIGFuZCBzY2hlZHVsZSBpdCAtLSBldmVyeSBob3VyIGlzIGEgZ29vZCBjaG9pY2UgKGEgcmVxdWVzdGVkIHJlcGFpciB0aGVuIGhhcHBlbnMgd2l0aGluDQojICAgICAgIHRoZSBob3VyIG9uY2UgRWRnZSBpcyBjbG9zZWQpLiBPciB1cGxvYWQgaXQgd2l0aCAuLlxQdWJsaXNoLVBpbUFjdGl2YXRvclJlbWVkaWF0aW9uLnBzMS4NCiMgICAgNC4gRWRnZSBwaWNrcyB1cCBuZXcgc2V0dGluZ3Mgb24gaXRzIG5leHQgcG9saWN5IHJlZnJlc2g7IGVkZ2U6Ly9wb2xpY3kgLT4gIlJlbG9hZCBwb2xpY2llcyIgc2hvd3MgdGhlbSBhdCBvbmNlLg0KIw0KIyAgT05FIFJFTUVESUFUSU9OIFBFUiBDSEFOTkVMIC0tIFdJVEggVEhFIFNBTUUgTElNSVRTDQojICAgIFByb2R1Y3Rpb24gYW5kIHRlc3QgYXJlIHR3byBleHRlbnNpb25zICh0d28gaWRzKTogb25lIFJlbWVkaWF0aW9uIGVhY2gsIGUuZy4gIlBJTSBBY3RpdmF0b3Igc2V0dGluZ3MgKFBST0QpIg0KIyAgICAoJENoYW5uZWwgPSAnUmVsZWFzZWQnKSBhbmQgIlBJTSBBY3RpdmF0b3Igc2V0dGluZ3MgKFRFU1QpIiAoJENoYW5uZWwgPSAnVGVzdCcpLiBCb3RoIHdyaXRlIHRoZSB0d28gbGltaXRzIHRvIEJPVEgNCiMgICAgZXh0ZW5zaW9ucywgc28gZ2l2ZSBib3RoIFJlbWVkaWF0aW9ucyB0aGUgU0FNRSAkQXV0b0FjdGl2YXRlTWF4R3JvdXBzIGFuZCAkQnVsa0FjdGl2YXRlQ29uZmlybVRocmVzaG9sZDogaWYgdGhleQ0KIyAgICBkaWZmZXIsIGVhY2ggcnVuIHJlcG9ydHMgIm5vdCBPSyIgYW5kIHRoZSBvdGhlciBvbmUgY2hhbmdlcyBpdCBiYWNrLiBHcm91cHMgdGhhdCBuZWVkIERJRkZFUkVOVCB2YWx1ZXMgbmVlZCB0aGVpcg0KIyAgICBvd24gUmVtZWRpYXRpb25zIGFuZCBzaG91bGQgbm90IG92ZXJsYXAgKGEgZGV2aWNlIGluIGJvdGggcnVucyBib3RoOyB0aGUgbGFzdCBvbmUgd2lucykuDQojICAgIFRoZSByZXBhaXIgY2xlYXJzIEJPVEggUElNIEFjdGl2YXRvciBleHRlbnNpb25zIChwcm9kdWN0aW9uIGFuZCB0ZXN0KSBvbiBhIGZsYWdnZWQgZGV2aWNlIC0tIGEgaGVhbHRoeSBvbmUgaXMNCiMgICAgc2ltcGx5IHJlaW5zdGFsbGVkIGJ5IGl0cyBFZGdlIHBvbGljeS4NCiMNCiMgIFJFU1VMVCBDT0RFUyAoc2hvd24gaW4gSW50dW5lKQ0KIyAgICBleGl0IDAgPSBzZXR0aW5ncyB3cml0dGVuLCBhbmQgdGhlIHJlcGFpciBkb25lIGlmIG9uZSB3YXMgcmVxdWVzdGVkDQojICAgIGV4aXQgMSA9IHNldHRpbmdzIGNvdWxkIG5vdCBiZSB3cml0dGVuLCBPUiBhIHJlcXVlc3RlZCByZXBhaXIgaXMgc3RpbGwgd2FpdGluZyBmb3IgRWRnZSB0byBiZSBjbG9zZWQNCiMgICAgICAgICAgICAgKHRoZSBvdXRwdXQgc2F5cyB3aGljaDsgdGhlIG5leHQgcnVuIHRyaWVzIGFnYWluKQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0gU0VUVElOR1MgLS0gZWRpdCB0aGVzZSA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KIyBXaGljaCBleHRlbnNpb246ICdSZWxlYXNlZCcgPSBwcm9kdWN0aW9uIChpZCBlaGVvY2lobWxwcGNvcGhhZWFrbWRlbmhnY29va2thYikNCiMgICAgICAgICAgICAgICAgICAnVGVzdCcgICAgID0gdGVzdCBidWlsZCAgIChpZCBnbGxkbmJtanBka2plbWNuZmljYWdkaGdpZW5mZHBvbykNCiRDaGFubmVsID0gJ1JlbGVhc2VkJw0KDQojIFdoaWNoIGJyb3dzZXJzIGdldCB0aGUgc2V0dGluZ3MgKGFuZCB0aGUgcmVwYWlyKTogJ0VkZ2UnLCAnQ2hyb21lJywgb3IgYm90aC4gU2FtZSBleHRlbnNpb24gaWQgaW4gYm90aC4NCiRCcm93c2VycyA9IEAoJ0VkZ2UnLCAnQ2hyb21lJykNCg0KIyBUaGUgdGVuYW50cyB1c2VycyBjYW4gc2lnbiBpbiB0byAtLSBvbmUgeyAuLi4gfSBwZXIgdGVuYW50LCBzZXBhcmF0ZWQgYnkgY29tbWFzLiBUaGUgZmlyc3Qgb25lIGlzIHRoZSBkZWZhdWx0Lg0KIyAgIFJlcXVpcmVkIGZpZWxkcw0KIyAgICAgIm5hbWUiICAgICAgICAgICAgICAgICBuYW1lIHNob3duIGluIHRoZSB0ZW5hbnQgc3dpdGNoZXINCiMgICAgICJ0ZW5hbnRJZCIgICAgICAgICAgICAgdGhlIEVudHJhIHRlbmFudCBpZCAoR1VJRCkNCiMgICAgICJjbGllbnRJZCIgICAgICAgICAgICAgdGhlIGFwcGxpY2F0aW9uIGlkIG9mIHRoZSAiUElNIEFjdGl2YXRvciIgYXBwIHJlZ2lzdHJhdGlvbiBpbiBUSEFUIHRlbmFudCAoR1VJRCkNCiMgICBPcHRpb25hbCBmaWVsZHMNCiMgICAgICJkZWZhdWx0SnVzdGlmaWNhdGlvbiIgcHJlLWZpbGxlZCBqdXN0aWZpY2F0aW9uIG9uIHRoZSBBY3RpdmF0ZSBmb3JtICAgKGRlZmF1bHQgIkNoYW5nZSBpbiBpbmZyYXN0cnVjdHVyZSIpDQojICAgICAiZGVmYXVsdER1cmF0aW9uSG91cnMiIHByZS1maWxsZWQgZHVyYXRpb24gaW4gaG91cnMsIDEtMjQgICAgICAgICAgICAgIChkZWZhdWx0IDg7IGVhY2ggcm9sZSdzIFBJTSBwb2xpY3kgY2FuIGNhcCBpdCkNCiMgICAgICJwcmVmaXgiICAgICAgICAgICAgICAgb25seSBsaXN0IGdyb3VwcyB3aG9zZSBuYW1lIHN0YXJ0cyB3aXRoIHRoaXMsIGUuZy4gIlBJTS0iIChvciBhIGxpc3Q6IFsiUElNLSIsIkFETS0iXSkNCiMgICAgICJlbnRyYVByZWZpeCIgICAgICAgICAgbmFtZSB0ZXh0IHRoYXQgcHV0cyBhIGdyb3VwIGluIHRoZSAiRW50cmEiIHNlY3Rpb24sICBlLmcuIFsiUElNLUVudHJhIiwiUElNLUFBRCJdDQojICAgICAiYXp1cmVQcmVmaXgiICAgICAgICAgIG5hbWUgdGV4dCB0aGF0IHB1dHMgYSBncm91cCBpbiB0aGUgIkF6dXJlIiBzZWN0aW9uLCAgZS5nLiBbIlBJTS1BenVyZSIsIlBJTS1BelJlcyJdDQojICAgICAiZ3JvdXBOYW1lRmlsdGVyIiAgICAgIGFkdmFuY2VkOiBhIHJlZ3VsYXIgZXhwcmVzc2lvbiBpbnN0ZWFkIG9mICJwcmVmaXgiICAgICAgKHdpbnMgb3ZlciAicHJlZml4IikNCiMgICAgICJlbnRyYUdyb3VwUmVnZXgiICAgICAgYWR2YW5jZWQ6IGEgcmVndWxhciBleHByZXNzaW9uIGluc3RlYWQgb2YgImVudHJhUHJlZml4IiAoZGVmYXVsdCAiRW50cmEiKQ0KIyAgICAgImF6dXJlR3JvdXBSZWdleCIgICAgICBhZHZhbmNlZDogYSByZWd1bGFyIGV4cHJlc3Npb24gaW5zdGVhZCBvZiAiYXp1cmVQcmVmaXgiIChkZWZhdWx0ICJBelJlc3xBenVyZSIpDQojICAgICAiYnVsa0FjdGl2YXRlQ29uZmlybVRocmVzaG9sZCIgIHRoaXMgdGVuYW50J3Mgb3duIGNvbmZpcm0gdGhyZXNob2xkIChvdmVycmlkZXMgdGhlIGRldmljZS13aWRlIG9uZSBiZWxvdykNCiMgICBHcm91cHMgdGhhdCBtYXRjaCBuZWl0aGVyIHRoZSBFbnRyYSBub3IgdGhlIEF6dXJlIHRleHQgYXJlIGxpc3RlZCB1bmRlciAiUElNIGZvciBHcm91cHMiLg0KJFRlbmFudENhdGFsb2cgPSBAJw0KWw0KICB7ICJuYW1lIjogIkNvbnRvc28iLCAidGVuYW50SWQiOiAiMDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAwIiwgImNsaWVudElkIjogIjAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDAwMCIsDQogICAgImRlZmF1bHRKdXN0aWZpY2F0aW9uIjogIkNoYW5nZSBpbiBpbmZyYXN0cnVjdHVyZSIsICJkZWZhdWx0RHVyYXRpb25Ib3VycyI6IDggfQ0KXQ0KJ0ANCg0KIyBBdXRvLWFjdGl2YXRlIGNhcDogaG93IG1hbnkgZ3JvdXBzIGEgdXNlciBtYXkgdGljayBhcyAiYXV0byIgKGFjdGl2YXRlZCBhdXRvbWF0aWNhbGx5IHdoZW4gdGhlIHBvcHVwIG9wZW5zKS4NCiMgICAkbnVsbCAgPSBubyBsaW1pdCAodGhlIHZhbHVlIGlzIHJlbW92ZWQgZnJvbSB0aGUgZGV2aWNlKQ0KIyAgIDAgICAgICA9IGF1dG8tYWN0aXZhdGlvbiBpcyB0dXJuZWQgT0ZGIG9uIHRoaXMgZGV2aWNlDQojICAgMS0xMDAgID0gYXQgbW9zdCB0aGlzIG1hbnkgZ3JvdXBzLiBUaWNraW5nIG9uZSBtb3JlIGlzIHJlZnVzZWQgd2l0aCBhIG1lc3NhZ2U7IGlmIG1vcmUgYXJlIGFscmVhZHkgdGlja2VkLA0KIyAgICAgICAgICAgIG9ubHkgdGhlIGZpcnN0IE4gYXJlIGFjdGl2YXRlZCBhbmQgdGhlIHJlc3QgYXJlIG1hcmtlZCAiTm90IGF1dG8tYWN0aXZhdGVkIi4NCiMgICBOZWVkcyBQSU0gQWN0aXZhdG9yIDEuNi4xMzUgb3IgbGF0ZXIgKHByb2R1Y3Rpb24gZmlyc3QgaGFkIGl0IGluIDEuNi4xMzU7IG9sZGVyIHZlcnNpb25zIGlnbm9yZSBpdCkuIEVuZm9yY2VkDQojICAgcmVsaWFibHkgZnJvbSAxLjYuMTQwOiBhIHNsb3cgZGV2aWNlIG5vIGxvbmdlciBza2lwcyB0aGUgbGltaXQsIGFuZCB1bi10aWNraW5nIGFuZCByZS10aWNraW5nIG5vIGxvbmdlciBnZXRzIHBhc3QgaXQuDQokQXV0b0FjdGl2YXRlTWF4R3JvdXBzID0gJG51bGwNCg0KIyBDb25maXJtIHRocmVzaG9sZDogd2hlbiBhIHVzZXIgc2VsZWN0cyBhdCBsZWFzdCB0aGlzIG1hbnkgcm9sZXMsIHRoZSBBY3RpdmF0ZSBidXR0b24gYXNrcyBmb3IgYSBzZWNvbmQgY2xpY2suDQojICAgJG51bGwgID0gdGhlIGJ1aWx0LWluIGRlZmF1bHQgKDUpIC0tIHRoZSB2YWx1ZSBpcyByZW1vdmVkIGZyb20gdGhlIGRldmljZQ0KIyAgIDEtMTAwICA9IHlvdXIgdGhyZXNob2xkLiBBIHRlbmFudCdzIG93biAiYnVsa0FjdGl2YXRlQ29uZmlybVRocmVzaG9sZCIgaW4gdGhlIGNhdGFsb2cgd2lucyBvdmVyIHRoaXMuDQokQnVsa0FjdGl2YXRlQ29uZmlybVRocmVzaG9sZCA9ICRudWxsDQoNCiMgRW5kLW9mLWRheSB0aW1lOiBldmVyeSBhY3RpdmF0aW9uIGVuZHMgbm8gbGF0ZXIgdGhhbiB0aGlzIGxvY2FsIHRpbWUgdG9kYXksIGUuZy4gJzE2OjAwJyAoMjQtaG91ciAnSEg6TU0nKS4NCiMgICAkbnVsbCAgID0gbm8gZW5kIHRpbWUgKHRoZSB2YWx1ZSBpcyByZW1vdmVkIGZyb20gdGhlIGRldmljZSkNCiMgICAnSEg6TU0nID0gYWN0aXZhdGlvbnMgZW5kIGF0IHRoaXMgdGltZSwgb3IgYWZ0ZXIgdGhlaXIgbm9ybWFsIGR1cmF0aW9uIGlmIHRoYXQgaXMgc29vbmVyLiBPbmUgc3RhcnRlZCBhZnRlciB0aGlzDQojICAgICAgICAgICAgIHRpbWUgZ2V0cyBpdHMgbm9ybWFsIGR1cmF0aW9uLiBBIHVzZXIgbWF5IGNob29zZSBhbiBlYXJsaWVyIHRpbWUgZm9yIHRoZW1zZWx2ZXMsIG5ldmVyIGEgbGF0ZXIgb25lLg0KJEFjdGl2YXRpb25FbmRUaW1lID0gJG51bGwNCg0KIyBSZXBhaXIgb24gcmVxdWVzdDogd2hhdCB0byBkbyB3aGVuIHRoZSBSZXBhaXJTdHVjayBmbGFnIGlzIHNldCBidXQgdGhlIGJyb3dzZXIgKEVkZ2UgLyBDaHJvbWUpIGlzIG9wZW4uDQojICAgJGZhbHNlID0gd2FpdCAtLSBjaGFuZ2Ugbm90aGluZywga2VlcCB0aGUgZmxhZywgdHJ5IGFnYWluIG9uIHRoZSBuZXh0IHJ1biAocmVjb21tZW5kZWQpDQojICAgJHRydWUgID0gY2xvc2UgdGhlIGJyb3dzZXJzIGFuZCByZXBhaXIgbm93DQokQ2xvc2VFZGdlID0gJGZhbHNlDQoNCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCiMgLS0tIG5vdGhpbmcgdG8gZWRpdCBiZWxvdyB0aGlzIGxpbmUgLS0tDQojIFRoZSBjaGFubmVsIHBpY2tzIE9OTFkgd2hpY2ggZXh0ZW5zaW9uIHRoaXMgaXMgKGl0cyBpZCk7IGV2ZXJ5dGhpbmcgaXQgZG9lcyBpcyB0aGUgc2FtZSBmb3IgYm90aC4NCiRFeHRlbnNpb25JZCAgPSBpZiAoJENoYW5uZWwgLWVxICdUZXN0JykgeyAnZ2xsZG5ibWpwZGtqZW1jbmZpY2FnZGhnaWVuZmRwb28nIH0gZWxzZSB7ICdlaGVvY2lobWxwcGNvcGhhZWFrbWRlbmhnY29va2thYicgfQ0KJEFsbEV4dGVuc2lvbklkcyA9IEAoJ2VoZW9jaWhtbHBwY29waGFlYWttZGVuaGdjb29ra2FiJywgJ2dsbGRuYm1qcGRramVtY25maWNhZ2RoZ2llbmZkcG9vJykgICAjIHByb2R1Y3Rpb24sIHRlc3QNCiMgUGVyIGJyb3dzZXI6IGl0cyBwb2xpY3kgcm9vdCwgaXRzIHByb2Nlc3MgbmFtZSBhbmQgd2hlcmUgZWFjaCB1c2VyJ3MgcHJvZmlsZXMgYXJlICh0aGUgU0FNRSBleHRlbnNpb24gaWQgaW4gYm90aCkuDQokQnJvd3NlckluZm8gID0gQHsNCiAgICBFZGdlICAgPSBAeyBSb290ID0gJ1NPRlRXQVJFXFBvbGljaWVzXE1pY3Jvc29mdFxFZGdlJzsgUHJvY2VzcyA9ICdtc2VkZ2UnOyBVc2VyRGF0YSA9ICdBcHBEYXRhXExvY2FsXE1pY3Jvc29mdFxFZGdlXFVzZXIgRGF0YScgfQ0KICAgIENocm9tZSA9IEB7IFJvb3QgPSAnU09GVFdBUkVcUG9saWNpZXNcR29vZ2xlXENocm9tZSc7ICBQcm9jZXNzID0gJ2Nocm9tZSc7IFVzZXJEYXRhID0gJ0FwcERhdGFcTG9jYWxcR29vZ2xlXENocm9tZVxVc2VyIERhdGEnIH0NCn0NCiRTZWxlY3RlZCAgICAgPSBAKCRCcm93c2VycyB8IEZvckVhY2gtT2JqZWN0IHsgIiRfIi5UcmltKCkgfSB8IFdoZXJlLU9iamVjdCB7ICRfIH0gfCBTZWxlY3QtT2JqZWN0IC1VbmlxdWUpDQokdW5rbm93biAgICAgID0gQCgkU2VsZWN0ZWQgfCBXaGVyZS1PYmplY3QgeyAtbm90ICRCcm93c2VySW5mby5Db250YWluc0tleSgkXykgfSkNCmlmICgtbm90ICRTZWxlY3RlZC5Db3VudCAtb3IgJHVua25vd24uQ291bnQpIHsgV3JpdGUtT3V0cHV0ICJGQUlMRUQ6IGAkQnJvd3NlcnMgbXVzdCBiZSAnRWRnZScsICdDaHJvbWUnIG9yIGJvdGggKGdvdCAnJCgkQnJvd3NlcnMgLWpvaW4gIicsICciKScpLiI7IGV4aXQgMSB9DQppZiAoJG51bGwgLW5lICRBY3RpdmF0aW9uRW5kVGltZSAtYW5kICIkQWN0aXZhdGlvbkVuZFRpbWUiIC1ub3RtYXRjaCAnXihbMDFdWzAtOV18MlswLTNdKTpbMC01XVswLTldJCcpIHsgV3JpdGUtT3V0cHV0ICJGQUlMRUQ6IGAkQWN0aXZhdGlvbkVuZFRpbWUgbXVzdCBiZSAnSEg6TU0nIGJldHdlZW4gMDA6MDAgYW5kIDIzOjU5IChnb3QgJyRBY3RpdmF0aW9uRW5kVGltZScpLiI7IGV4aXQgMSB9DQojIFRoZSBjYXRhbG9nIGlzIHN0b3JlZCBjb21wYWN0IGFuZCBhbHdheXMgYXMgYSBsaXN0IFsgLi4uIF0uIFRoZSAifCBGb3JFYWNoLU9iamVjdCB7ICRfIH0iIGtlZXBzIGl0IGEgcGxhaW4gbGlzdCBvbg0KIyBXaW5kb3dzIFBvd2VyU2hlbGwgNS4xICh3aGljaCBJbnR1bmUgdXNlcykgLS0gd2l0aG91dCBpdCA1LjEgc3RvcmVzIHsidmFsdWUiOlsuLi5dLCJDb3VudCI6MX0gYW5kIHNpZ24taW4gYnJlYWtzLg0KJENhdGFsb2cgICAgICA9IENvbnZlcnRUby1Kc29uIC1JbnB1dE9iamVjdCBAKCRUZW5hbnRDYXRhbG9nIHwgQ29udmVydEZyb20tSnNvbiB8IEZvckVhY2gtT2JqZWN0IHsgJF8gfSkgLURlcHRoIDEwIC1Db21wcmVzcw0KIyBXaGVyZSB0aGUgcmVwYWlyIGZsYWcgY2FuIGJlOiB0aGUgZGV2aWNlIChIS0xNKSBhbmQgZXZlcnkgc2lnbmVkLWluIHVzZXIgKHRoZWlyIEhLQ1UgPSBIS0VZX1VTRVJTXDx1c2VyIFNJRD4pLg0KJEZsYWdLZXlzICAgICA9IEAoJ1JlZ2lzdHJ5OjpIS0VZX0xPQ0FMX01BQ0hJTkVcU09GVFdBUkVcUElNNEVudHJhUFNcUGltQWN0aXZhdG9yJykgKw0KICAgICAgICAgICAgICAgIEAoR2V0LUNoaWxkSXRlbSAtUGF0aCAnUmVnaXN0cnk6OkhLRVlfVVNFUlMnIC1FcnJvckFjdGlvbiBTaWxlbnRseUNvbnRpbnVlIHwNCiAgICAgICAgICAgICAgICAgIFdoZXJlLU9iamVjdCB7ICRfLlBTQ2hpbGROYW1lIC1saWtlICdTLTEtKicgLWFuZCAkXy5QU0NoaWxkTmFtZSAtbm90bGlrZSAnKl9DbGFzc2VzJyB9IHwNCiAgICAgICAgICAgICAgICAgIEZvckVhY2gtT2JqZWN0IHsgIlJlZ2lzdHJ5OjokKCRfLk5hbWUpXFNvZnR3YXJlXFBJTTRFbnRyYVBTXFBpbUFjdGl2YXRvciIgfSkNCg0KIyAtLS0tIDEuIFNFVFRJTkdTIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQp0cnkgew0KICAgIGZvcmVhY2ggKCRiIGluICRTZWxlY3RlZCkgew0KICAgICAgICAjIFRoZSA2NC1iaXQgcmVnaXN0cnkgb24gcHVycG9zZTogSW50dW5lIHJ1bnMgc2NyaXB0cyAzMi1iaXQgdW5sZXNzIHRvbGQgb3RoZXJ3aXNlLCBhbmQgbmVpdGhlciBicm93c2VyIHJlYWRzIHRoZQ0KICAgICAgICAjIDMyLWJpdCBjb3B5IChXT1c2NDMyTm9kZSkuDQogICAgICAgICRoa2xtID0gW01pY3Jvc29mdC5XaW4zMi5SZWdpc3RyeUtleV06Ok9wZW5CYXNlS2V5KCdMb2NhbE1hY2hpbmUnLCAnUmVnaXN0cnk2NCcpDQogICAgICAgICMgdGhlIHRlbmFudCBjYXRhbG9nOiBUSElTIGNoYW5uZWwncyBleHRlbnNpb24gKHRoZSBjYXRhbG9nIG5hbWVzIHRoZSBhcHAgaXQgc2lnbnMgaW4gd2l0aCkNCiAgICAgICAgJGtleVBhdGggPSAiJCgkQnJvd3NlckluZm9bJGJdLlJvb3QpXDNyZHBhcnR5XGV4dGVuc2lvbnNcJEV4dGVuc2lvbklkXHBvbGljeSINCiAgICAgICAgJGtleSA9ICRoa2xtLkNyZWF0ZVN1YktleSgka2V5UGF0aCwgJHRydWUpDQogICAgICAgICRrZXkuU2V0VmFsdWUoJ3RlbmFudENhdGFsb2cnLCAkQ2F0YWxvZywgJ1N0cmluZycpDQogICAgICAgICRrZXkuQ2xvc2UoKQ0KICAgICAgICAjIHRoZSBsaW1pdHM6IHRoZSBTQU1FIHZhbHVlcyBmb3IgQk9USCBQSU0gQWN0aXZhdG9yIGV4dGVuc2lvbnMgKHByb2R1Y3Rpb24gYW5kIHRlc3QgYmVoYXZlIHRoZSBzYW1lKQ0KICAgICAgICBmb3JlYWNoICgkaWQgaW4gJEFsbEV4dGVuc2lvbklkcykgew0KICAgICAgICAgICAgJGtleSA9ICRoa2xtLkNyZWF0ZVN1YktleSgiJCgkQnJvd3NlckluZm9bJGJdLlJvb3QpXDNyZHBhcnR5XGV4dGVuc2lvbnNcJGlkXHBvbGljeSIsICR0cnVlKQ0KICAgICAgICAgICAgaWYgKCRudWxsIC1uZSAkQXV0b0FjdGl2YXRlTWF4R3JvdXBzKSAgICAgICAgeyAka2V5LlNldFZhbHVlKCdhdXRvQWN0aXZhdGVNYXhHcm91cHMnLCBbaW50XSRBdXRvQWN0aXZhdGVNYXhHcm91cHMsICdEV29yZCcpIH0NCiAgICAgICAgICAgIGVsc2UgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHsgJGtleS5EZWxldGVWYWx1ZSgnYXV0b0FjdGl2YXRlTWF4R3JvdXBzJywgJGZhbHNlKSB9DQogICAgICAgICAgICBpZiAoJG51bGwgLW5lICRCdWxrQWN0aXZhdGVDb25maXJtVGhyZXNob2xkKSB7ICRrZXkuU2V0VmFsdWUoJ2J1bGtBY3RpdmF0ZUNvbmZpcm1UaHJlc2hvbGQnLCBbaW50XSRCdWxrQWN0aXZhdGVDb25maXJtVGhyZXNob2xkLCAnRFdvcmQnKSB9DQogICAgICAgICAgICBlbHNlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7ICRrZXkuRGVsZXRlVmFsdWUoJ2J1bGtBY3RpdmF0ZUNvbmZpcm1UaHJlc2hvbGQnLCAkZmFsc2UpIH0NCiAgICAgICAgICAgIGlmICgkbnVsbCAtbmUgJEFjdGl2YXRpb25FbmRUaW1lKSAgICAgICAgICAgIHsgJGtleS5TZXRWYWx1ZSgnYWN0aXZhdGlvbkVuZFRpbWUnLCBbc3RyaW5nXSRBY3RpdmF0aW9uRW5kVGltZSwgJ1N0cmluZycpIH0NCiAgICAgICAgICAgIGVsc2UgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHsgJGtleS5EZWxldGVWYWx1ZSgnYWN0aXZhdGlvbkVuZFRpbWUnLCAkZmFsc2UpIH0NCiAgICAgICAgICAgICRrZXkuQ2xvc2UoKQ0KICAgICAgICB9DQogICAgICAgICRoa2xtLkNsb3NlKCkNCiAgICB9DQogICAgV3JpdGUtT3V0cHV0ICgiU2V0dGluZ3Mgd3JpdHRlbiAoezB9OyB7NH0pOiB7MX0gdGVuYW50KHMpLCBjYXAgPSB7Mn0sIGNvbmZpcm0gdGhyZXNob2xkID0gezN9LCBlbmQgdGltZSA9IHs1fSAodGhlIGxpbWl0cyBmb3IgcHJvZHVjdGlvbiBBTkQgdGVzdCkuIiAtZiAkQ2hhbm5lbCwNCiAgICAgICAgQCgkVGVuYW50Q2F0YWxvZyB8IENvbnZlcnRGcm9tLUpzb24gfCBGb3JFYWNoLU9iamVjdCB7ICRfIH0pLkNvdW50LA0KICAgICAgICAkKGlmICgkbnVsbCAtZXEgJEF1dG9BY3RpdmF0ZU1heEdyb3VwcykgeyAnbm9uZScgfSBlbHNlIHsgJEF1dG9BY3RpdmF0ZU1heEdyb3VwcyB9KSwNCiAgICAgICAgJChpZiAoJG51bGwgLWVxICRCdWxrQWN0aXZhdGVDb25maXJtVGhyZXNob2xkKSB7ICdkZWZhdWx0JyB9IGVsc2UgeyAkQnVsa0FjdGl2YXRlQ29uZmlybVRocmVzaG9sZCB9KSwNCiAgICAgICAgKCRTZWxlY3RlZCAtam9pbiAnICsgJyksDQogICAgICAgICQoaWYgKCRudWxsIC1lcSAkQWN0aXZhdGlvbkVuZFRpbWUpIHsgJ25vbmUnIH0gZWxzZSB7ICRBY3RpdmF0aW9uRW5kVGltZSB9KSkNCn0gY2F0Y2ggew0KICAgIFdyaXRlLU91dHB1dCAiRkFJTEVEIHRvIHdyaXRlIHRoZSBQSU0gQWN0aXZhdG9yIHNldHRpbmdzOiAkKCRfLkV4Y2VwdGlvbi5NZXNzYWdlKSINCiAgICBleGl0IDENCn0NCg0KIyAtLS0tIDIuIFJFUEFJUiwgb25seSB3aGVuIHRoZSBmbGFnIGlzIHNldCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQokZmxhZ2dlZCA9IEAoJEZsYWdLZXlzIHwgV2hlcmUtT2JqZWN0IHsgKEdldC1JdGVtUHJvcGVydHkgLVBhdGggJF8gLU5hbWUgUmVwYWlyU3R1Y2sgLUVycm9yQWN0aW9uIFNpbGVudGx5Q29udGludWUpLlJlcGFpclN0dWNrIC1lcSAxIH0pDQppZiAoLW5vdCAkZmxhZ2dlZCkgeyBleGl0IDAgfQ0KDQokcHJvY05hbWVzID0gQCgkU2VsZWN0ZWQgfCBGb3JFYWNoLU9iamVjdCB7ICRCcm93c2VySW5mb1skX10uUHJvY2VzcyB9KQ0KJHJ1bm5pbmcgPSBAKEdldC1Qcm9jZXNzIC1OYW1lICRwcm9jTmFtZXMgLUVycm9yQWN0aW9uIFNpbGVudGx5Q29udGludWUpDQppZiAoJHJ1bm5pbmcgLWFuZCAtbm90ICRDbG9zZUVkZ2UpIHsNCiAgICAkb3BlbiA9IChAKCRydW5uaW5nIHwgRm9yRWFjaC1PYmplY3QgeyBpZiAoJF8uUHJvY2Vzc05hbWUgLWVxICdjaHJvbWUnKSB7ICdDaHJvbWUnIH0gZWxzZSB7ICdFZGdlJyB9IH0gfCBTZWxlY3QtT2JqZWN0IC1VbmlxdWUpKSAtam9pbiAnIGFuZCAnDQogICAgV3JpdGUtT3V0cHV0ICJSZXBhaXIgcmVxdWVzdGVkLCBidXQgJG9wZW4gaXMgb3BlbiAtLSBub3RoaW5nIGNoYW5nZWQgaW4gdGhlIGJyb3dzZXIsIHRoZSByZXF1ZXN0IGlzIGtlcHQgYW5kIHRoZSBuZXh0IHJ1biB0cmllcyBhZ2Fpbi4iDQogICAgZXhpdCAxDQp9DQppZiAoJHJ1bm5pbmcpIHsgJHJ1bm5pbmcgfCBTdG9wLVByb2Nlc3MgLUZvcmNlIC1FcnJvckFjdGlvbiBTaWxlbnRseUNvbnRpbnVlOyBTdGFydC1TbGVlcCAtU2Vjb25kcyAzIH0NCg0KIyBDdXQgZXZlcnkgICI8a2V5PiI6IDx2YWx1ZT4gIG91dCBvZiBKU09OIFRFWFQgKHZhbHVlID0geyB9LCBbIF0sIGEgc3RyaW5nIG9yIGEgcGxhaW4gdmFsdWUpIHBsdXMgb25lIG5laWdoYm91cmluZw0KIyBjb21tYSwgc28gdGhlIEpTT04gc3RheXMgdmFsaWQuIFRleHQgb25seSAtLSB0aGUgcmVzdCBvZiB0aGUgZmlsZSBpcyBrZXB0IGJ5dGUgZm9yIGJ5dGUgKHNlZSB0aGUgaGVhZGVyIGZvciB3aHkpLg0KIyBUaGUgc2FtZSBtZXRob2QgVXBkYXRlLVBpbUFjdGl2YXRvci1FeHRlbnNpb24ucHMxIHVzZXMgc2luY2UgdGhlIDIwMjYtMDYtMDkgaW5jaWRlbnQuDQpmdW5jdGlvbiBSZW1vdmUtUGFKc29uS2V5KFtzdHJpbmddJFRleHQsIFtzdHJpbmddJEtleSkgew0KICAgICRuZWVkbGUgPSAnIicgKyAkS2V5ICsgJyI6Jw0KICAgIHdoaWxlICgkdHJ1ZSkgew0KICAgICAgICAkcyA9ICRUZXh0LkluZGV4T2YoJG5lZWRsZSk7IGlmICgkcyAtbHQgMCkgeyBicmVhayB9DQogICAgICAgICR2ID0gJHMgKyAkbmVlZGxlLkxlbmd0aA0KICAgICAgICB3aGlsZSAoJHYgLWx0ICRUZXh0Lkxlbmd0aCAtYW5kIFtjaGFyXTo6SXNXaGl0ZVNwYWNlKCRUZXh0WyR2XSkpIHsgJHYrKyB9DQogICAgICAgIGlmICgkdiAtZ2UgJFRleHQuTGVuZ3RoKSB7IGJyZWFrIH0NCiAgICAgICAgJGMwID0gJFRleHRbJHZdDQogICAgICAgIGlmICgkYzAgLWVxICd7JyAtb3IgJGMwIC1lcSAnWycpIHsNCiAgICAgICAgICAgICRvcGVuID0gJGMwOyAkY2xvc2UgPSBpZiAoJG9wZW4gLWVxICd7JykgeyAnfScgfSBlbHNlIHsgJ10nIH07ICRkZXB0aCA9IDE7ICRpID0gJHYgKyAxDQogICAgICAgICAgICB3aGlsZSAoJGRlcHRoIC1ndCAwIC1hbmQgJGkgLWx0ICRUZXh0Lkxlbmd0aCkgew0KICAgICAgICAgICAgICAgICRjID0gJFRleHRbJGldDQogICAgICAgICAgICAgICAgaWYgKCRjIC1lcSAnIicpIHsgJGkrKzsgd2hpbGUgKCRpIC1sdCAkVGV4dC5MZW5ndGggLWFuZCAkVGV4dFskaV0gLW5lICciJykgeyBpZiAoJFRleHRbJGldIC1lcSAnXCcpIHsgJGkrKyB9OyAkaSsrIH0gfQ0KICAgICAgICAgICAgICAgIGVsc2VpZiAoJGMgLWVxICRvcGVuKSB7ICRkZXB0aCsrIH0gZWxzZWlmICgkYyAtZXEgJGNsb3NlKSB7ICRkZXB0aC0tIH0NCiAgICAgICAgICAgICAgICAkaSsrDQogICAgICAgICAgICB9DQogICAgICAgICAgICAkZW5kID0gJGkNCiAgICAgICAgfSBlbHNlaWYgKCRjMCAtZXEgJyInKSB7DQogICAgICAgICAgICAkaSA9ICR2ICsgMTsgd2hpbGUgKCRpIC1sdCAkVGV4dC5MZW5ndGggLWFuZCAkVGV4dFskaV0gLW5lICciJykgeyBpZiAoJFRleHRbJGldIC1lcSAnXCcpIHsgJGkrKyB9OyAkaSsrIH07ICRlbmQgPSAkaSArIDENCiAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAgICRpID0gJHY7IHdoaWxlICgkaSAtbHQgJFRleHQuTGVuZ3RoIC1hbmQgJFRleHRbJGldIC1ub3RpbiBAKCcsJywgJ30nLCAnXScpKSB7ICRpKysgfTsgJGVuZCA9ICRpDQogICAgICAgIH0NCiAgICAgICAgJGNzID0gJHM7ICRjZSA9ICRlbmQNCiAgICAgICAgJGogPSAkY3MgLSAxOyB3aGlsZSAoJGogLWdlIDAgLWFuZCBbY2hhcl06OklzV2hpdGVTcGFjZSgkVGV4dFskal0pKSB7ICRqLS0gfQ0KICAgICAgICBpZiAoJGogLWdlIDAgLWFuZCAkVGV4dFskal0gLWVxICcsJykgeyAkY3MgPSAkaiB9DQogICAgICAgIGVsc2UgeyAkayA9ICRjZTsgd2hpbGUgKCRrIC1sdCAkVGV4dC5MZW5ndGggLWFuZCBbY2hhcl06OklzV2hpdGVTcGFjZSgkVGV4dFska10pKSB7ICRrKysgfTsgaWYgKCRrIC1sdCAkVGV4dC5MZW5ndGggLWFuZCAkVGV4dFska10gLWVxICcsJykgeyAkY2UgPSAkayArIDEgfSB9DQogICAgICAgICRUZXh0ID0gJFRleHQuU3Vic3RyaW5nKDAsICRjcykgKyAkVGV4dC5TdWJzdHJpbmcoJGNlKQ0KICAgIH0NCiAgICByZXR1cm4gJFRleHQNCn0NCiMgVHJ1ZSB3aGVuIHRoZSB0ZXh0IGlzIE9ORSBiYWxhbmNlZCB7IC4uLiB9IG9iamVjdCAoYnJhY2VzL2JyYWNrZXRzIGNvdW50ZWQgb3V0c2lkZSBzdHJpbmdzKSAtLSBhIGNoZWFwIGNoZWNrIHRoYXQgdGhlDQojIGN1dCBsZWZ0IGEgd2VsbC1mb3JtZWQgZmlsZSwgZG9uZSB3aXRob3V0IHJlLXNlcmlhbGlzaW5nIGFueXRoaW5nLg0KZnVuY3Rpb24gVGVzdC1QYUpzb25CYWxhbmNlZChbc3RyaW5nXSRUZXh0KSB7DQogICAgJHQgPSAkVGV4dC5UcmltKCk7IGlmICgtbm90ICgkdC5TdGFydHNXaXRoKCd7JykgLWFuZCAkdC5FbmRzV2l0aCgnfScpKSkgeyByZXR1cm4gJGZhbHNlIH0NCiAgICAkZGVwdGggPSAwOyAkaSA9IDANCiAgICB3aGlsZSAoJGkgLWx0ICR0Lkxlbmd0aCkgew0KICAgICAgICAkYyA9ICR0WyRpXQ0KICAgICAgICBpZiAoJGMgLWVxICciJykgeyAkaSsrOyB3aGlsZSAoJGkgLWx0ICR0Lkxlbmd0aCAtYW5kICR0WyRpXSAtbmUgJyInKSB7IGlmICgkdFskaV0gLWVxICdcJykgeyAkaSsrIH07ICRpKysgfSB9DQogICAgICAgIGVsc2VpZiAoJGMgLWVxICd7JyAtb3IgJGMgLWVxICdbJykgeyAkZGVwdGgrKyB9DQogICAgICAgIGVsc2VpZiAoJGMgLWVxICd9JyAtb3IgJGMgLWVxICddJykgeyAkZGVwdGgtLTsgaWYgKCRkZXB0aCAtbHQgMCkgeyByZXR1cm4gJGZhbHNlIH07IGlmICgkZGVwdGggLWVxIDAgLWFuZCAkaSAtbmUgJHQuTGVuZ3RoIC0gMSkgeyByZXR1cm4gJGZhbHNlIH0gfQ0KICAgICAgICAkaSsrDQogICAgfQ0KICAgIHJldHVybiAoJGRlcHRoIC1lcSAwKQ0KfQ0KDQokc3RhbXAgPSBHZXQtRGF0ZSAtRm9ybWF0ICd5eXl5TU1kZC1ISG1tc3MnDQokY2xlYXJlZCA9IDANCiMgRXZlcnkgdXNlcidzIEVkZ2UgLyBDaHJvbWUgcHJvZmlsZXMgLS0gdGhlIHNjcmlwdCBydW5zIGFzIFNZU1RFTSwgc28gaXQgd2Fsa3MgQzpcVXNlcnMgcmF0aGVyIHRoYW4gaXRzIG93biBwcm9maWxlLg0KJHVzZXJEYXRhRGlycyA9IGZvcmVhY2ggKCRiIGluICRTZWxlY3RlZCkgeyBHZXQtQ2hpbGRJdGVtIC1QYXRoICIkZW52OlN5c3RlbURyaXZlXFVzZXJzXCpcJCgkQnJvd3NlckluZm9bJGJdLlVzZXJEYXRhKSIgLURpcmVjdG9yeSAtRXJyb3JBY3Rpb24gU2lsZW50bHlDb250aW51ZSB9DQpmb3JlYWNoICgkdXNlckRhdGEgaW4gQCgkdXNlckRhdGFEaXJzKSkgew0KICAgIGZvcmVhY2ggKCRicm93c2VyUHJvZmlsZSBpbiBHZXQtQ2hpbGRJdGVtIC1MaXRlcmFsUGF0aCAkdXNlckRhdGEuRnVsbE5hbWUgLURpcmVjdG9yeSB8IFdoZXJlLU9iamVjdCB7ICRfLk5hbWUgLWVxICdEZWZhdWx0JyAtb3IgJF8uTmFtZSAtbGlrZSAnUHJvZmlsZSAqJyB9KSB7DQogICAgICAgIGZvcmVhY2ggKCRpZCBpbiAkQWxsRXh0ZW5zaW9uSWRzKSB7DQogICAgICAgICAgICAjIFBJTSBBY3RpdmF0b3IncyBpbnN0YWxsZWQgcHJvZ3JhbSBmaWxlcyAtLSBFZGdlIGRvd25sb2FkcyB0aGUgY3VycmVudCB2ZXJzaW9uIGFnYWluIG9uIGl0cyBuZXh0IHN0YXJ0Lg0KICAgICAgICAgICAgJGluc3RhbGxlZCA9IEpvaW4tUGF0aCAkYnJvd3NlclByb2ZpbGUuRnVsbE5hbWUgIkV4dGVuc2lvbnNcJGlkIg0KICAgICAgICAgICAgaWYgKFRlc3QtUGF0aCAtTGl0ZXJhbFBhdGggJGluc3RhbGxlZCkgeyBSZW1vdmUtSXRlbSAtTGl0ZXJhbFBhdGggJGluc3RhbGxlZCAtUmVjdXJzZSAtRm9yY2UgLUVycm9yQWN0aW9uIFNpbGVudGx5Q29udGludWUgfQ0KICAgICAgICB9DQogICAgICAgIGZvcmVhY2ggKCRmaWxlIGluICdQcmVmZXJlbmNlcycsICdTZWN1cmUgUHJlZmVyZW5jZXMnKSB7DQogICAgICAgICAgICAkcGF0aCA9IEpvaW4tUGF0aCAkYnJvd3NlclByb2ZpbGUuRnVsbE5hbWUgJGZpbGUNCiAgICAgICAgICAgIGlmICgtbm90IChUZXN0LVBhdGggLUxpdGVyYWxQYXRoICRwYXRoKSkgeyBjb250aW51ZSB9DQogICAgICAgICAgICAkdXRmOCA9IE5ldy1PYmplY3QgU3lzdGVtLlRleHQuVVRGOEVuY29kaW5nKCRmYWxzZSkNCiAgICAgICAgICAgICR0ZXh0ID0gW0lPLkZpbGVdOjpSZWFkQWxsVGV4dCgkcGF0aCwgJHV0ZjgpDQogICAgICAgICAgICBpZiAoLW5vdCAkdGV4dC5UcmltU3RhcnQoKS5TdGFydHNXaXRoKCd7JykpIHsgY29udGludWUgfSAgICMgbm90IEVkZ2UncyBKU09OIC0tIGxlYXZlIGl0IGFsb25lDQogICAgICAgICAgICAkbmV3ID0gJHRleHQNCiAgICAgICAgICAgIGZvcmVhY2ggKCRpZCBpbiAkQWxsRXh0ZW5zaW9uSWRzKSB7ICRuZXcgPSBSZW1vdmUtUGFKc29uS2V5IC1UZXh0ICRuZXcgLUtleSAkaWQgfQ0KICAgICAgICAgICAgaWYgKCRuZXcgLWVxICR0ZXh0KSB7IGNvbnRpbnVlIH0gICAgICAgICAgICAgICAgICAgICAgICAgICMgUElNIEFjdGl2YXRvciBub3QgaW4gdGhpcyBmaWxlDQogICAgICAgICAgICAjIHNhZmV0eSBjaGVjazogdGhlIHJlc3VsdCBtdXN0IHN0aWxsIGJlIG9uZSBKU09OIG9iamVjdCAoYmFsYW5jZWQgYnJhY2VzLCBvdXRzaWRlIHN0cmluZ3MpDQogICAgICAgICAgICBpZiAoLW5vdCAoVGVzdC1QYUpzb25CYWxhbmNlZCAkbmV3KSkgeyBXcml0ZS1PdXRwdXQgIlNraXBwZWQgKHNhZmV0eSBjaGVjayk6ICQoJGJyb3dzZXJQcm9maWxlLkZ1bGxOYW1lKVwkZmlsZSB3YXMgbGVmdCB1bmNoYW5nZWQiOyBjb250aW51ZSB9DQogICAgICAgICAgICBDb3B5LUl0ZW0gLUxpdGVyYWxQYXRoICRwYXRoIC1EZXN0aW5hdGlvbiAiJHBhdGguYmFrLSRzdGFtcCIgLUZvcmNlDQogICAgICAgICAgICBbSU8uRmlsZV06OldyaXRlQWxsVGV4dCgkcGF0aCwgJG5ldywgJHV0ZjgpDQogICAgICAgICAgICAkY2xlYXJlZCsrDQogICAgICAgICAgICBXcml0ZS1PdXRwdXQgIlJlcGFpcmVkOiBjdXQgUElNIEFjdGl2YXRvciBvdXQgb2YgJCgkYnJvd3NlclByb2ZpbGUuRnVsbE5hbWUpXCRmaWxlIChiYWNrdXA6ICRmaWxlLmJhay0kc3RhbXApIg0KICAgICAgICB9DQogICAgfQ0KfQ0KIyBUaGUgcmVxdWVzdCBpcyBkb25lIC0tIGRlbGV0ZSB0aGUgZmxhZyBldmVyeXdoZXJlIGl0IHdhcyBzZXQuDQpmb3JlYWNoICgkayBpbiAkZmxhZ2dlZCkgeyBSZW1vdmUtSXRlbVByb3BlcnR5IC1QYXRoICRrIC1OYW1lIFJlcGFpclN0dWNrIC1FcnJvckFjdGlvbiBTaWxlbnRseUNvbnRpbnVlIH0NCldyaXRlLU91dHB1dCAiUmVwYWlyIGRvbmUgKCQoJFNlbGVjdGVkIC1qb2luICcgKyAnKSk6ICRjbGVhcmVkIGZpbGUocykgY2xlYXJlZCwgUmVwYWlyU3R1Y2sgZmxhZyBkZWxldGVkLiBUaGUgYnJvd3NlciBpbnN0YWxscyB0aGUgY3VycmVudCBQSU0gQWN0aXZhdG9yIG9uIGl0cyBuZXh0IHN0YXJ0LiINCmV4aXQgMA0K' } # embedded by Build-PimSupportScripts: intune-remediation/Detect-PimActivator.ps1, intune-remediation/Remediate-PimActivator.ps1 function Get-PimActivatorRemediationTemplate { <# The shipped text of Detect-PimActivator.ps1 / Remediate-PimActivator.ps1: the embedded copy when this runs as the standalone download, else the file in intune-remediation\. Decoded like [IO.File]::ReadAllText (a BOM is dropped), so both paths give the same text. Throws when neither is there. #> param([Parameter(Mandatory)][ValidateSet('Detect-PimActivator.ps1', 'Remediate-PimActivator.ps1')][string]$Name) $b64 = $script:PimActivatorEmbeddedTemplates[$Name] if ($b64) { $ms = New-Object IO.MemoryStream (, [Convert]::FromBase64String($b64)) $sr = New-Object IO.StreamReader($ms, (New-Object Text.UTF8Encoding($false)), $true) try { return $sr.ReadToEnd() } finally { $sr.Dispose() } } foreach ($d in @($script:PimActivatorBuildDir, $PSScriptRoot) | Where-Object { $_ } | Select-Object -Unique) { $p = Join-Path $d "intune-remediation\$Name" if (Test-Path -LiteralPath $p) { return [IO.File]::ReadAllText($p) } } throw "The shipped $Name was not found (embedded or in intune-remediation\). Download the standalone script again from https://invardia.com/support/pim/Publish-PimActivatorRemediation.ps1." } function New-PimActivatorRemediationPair { <# The filled Detect + Remediate pair for one environment -- the SAME text the Manager's Operations > PIM Activator builds and the standalone Publish-PimActivatorRemediation.ps1 builds from its parameters (one function, so the two cannot differ). Returns @{ channel; catalog; detect; remediate }. #> param([Parameter(Mandatory)][string]$TenantId, [Parameter(Mandatory)][string]$ClientId, [string]$Name = 'PIM', [ValidateSet('Released', 'Test')][string]$Channel = 'Released', [string[]]$Browsers = @('Edge', 'Chrome'), [string]$DefaultJustification = 'Change in infrastructure', [int]$DefaultDurationHours = 8, [string]$Prefix = '', [string]$EntraPrefix = '', [string]$AzurePrefix = '', [Nullable[int]]$AutoActivateMaxGroups = $null, [Nullable[int]]$BulkActivateConfirmThreshold = $null, [string]$AdditionalTenantsJson = '', [string]$ActivationEndTime = '') $cat = New-PimActivatorCatalog -Name $(if ("$Name".Trim()) { $Name } else { 'PIM' }) -TenantId $TenantId -ClientId $ClientId -DefaultJustification $DefaultJustification ` -DefaultDurationHours $DefaultDurationHours -Prefix $Prefix -EntraPrefix $EntraPrefix -AzurePrefix $AzurePrefix -AdditionalTenantsJson $AdditionalTenantsJson $det = Set-PimActivatorRemediationSettings -Text (Get-PimActivatorRemediationTemplate -Name 'Detect-PimActivator.ps1') -Channel $Channel -Browsers $Browsers -CatalogJson $cat -AutoActivateMaxGroups $AutoActivateMaxGroups -BulkActivateConfirmThreshold $BulkActivateConfirmThreshold -ActivationEndTime $ActivationEndTime $rem = Set-PimActivatorRemediationSettings -Text (Get-PimActivatorRemediationTemplate -Name 'Remediate-PimActivator.ps1') -Channel $Channel -Browsers $Browsers -CatalogJson $cat -AutoActivateMaxGroups $AutoActivateMaxGroups -BulkActivateConfirmThreshold $BulkActivateConfirmThreshold -ActivationEndTime $ActivationEndTime [ordered]@{ channel = $Channel; catalog = $cat; detect = $det; remediate = $rem } } function ConvertTo-PimActivatorTargets { <# PURE. pim.Settings['ActivatorTargets'] normalised: @{ Released = @{ groupId; displayName } | $null; Test = ... }. The DEPLOYMENT group per channel (Intune Remediation assignment, Edge for Business policy) -- not who may sign in. A value without a GUID group id is no target (never "everyone"). #> param($Value) $guid = '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$' $get = { param($o, $n) if ($o -is [System.Collections.IDictionary]) { if ($o.Contains($n)) { $o[$n] } } elseif ($null -ne $o -and $o.PSObject.Properties[$n]) { $o.$n } } $out = [ordered]@{ Released = $null; Test = $null } foreach ($c in 'Released', 'Test') { $t = & $get $Value $c $gid = "$(& $get $t 'groupId')".Trim() if ($gid -match $guid) { $out[$c] = [ordered]@{ groupId = $gid.ToLowerInvariant(); displayName = ("$(& $get $t 'displayName')" -replace '[\x00-\x1F\x7F]', ' ').Trim() } } } $out } function ConvertTo-PimActivatorPsLiteral { # PURE. A PowerShell single-quoted literal ('' doubles a quote). param([string]$Text) "'" + ("$Text" -replace "'", "''") + "'" } function Get-PimActivatorWho { # PURE. What the Remediation / the Edge policy is deployed to, in words: the group, or nobody yet. param($Target, [string]$Channel = 'Released') $label = if ($Channel -eq 'Test') { 'TEST' } else { 'PROD' } if ($Target -and "$($Target.groupId)") { $n = if ("$($Target.displayName)".Trim()) { "$($Target.displayName)" } else { "$($Target.groupId)" } return "$label -- $n" } "$label -- no group chosen yet (nobody gets it until you assign a group)" } function Get-PimActivatorCommands { <# PURE. The exact, self-contained command lines for this environment. Each needs only its own script from invardia.com/support/pim -- no other file. -Targets = the ActivatorTargets setting (the DEPLOYMENT group per channel): it goes into the Remediation command only; a channel without a group gets that command WITHOUT -AssignToGroupId (the script then assigns it to nobody and says so). The app commands never carry a group (100.90). -Settings = the Settings step (name, defaultJustification, defaultDurationHours, prefix, entraPrefix, azurePrefix, autoActivateMaxGroups, bulkActivateConfirmThreshold, browsers) for the Remediation + server commands. -TestSharesProdApp: there is no TEST app because the PROD app carries the TEST redirects -> the TEST command updates the PROD app (-Channel Both). Returns @{ appProd; appTest; remediationProd; remediationTest; remediation (= -Channel); server (= -Channel); whoProd; whoTest; who (= -Channel) }. A Remediation command is '' while its channel has no app id. #> param([Parameter(Mandatory)][string]$TenantId, [ValidateSet('Released', 'Test')][string]$Channel = 'Released', $Targets = $null, $Settings = $null, [string]$ClientId = '', [string]$TestClientId = '', [string]$DailyTime = '', [int]$EveryHours = 0, [switch]$TestSharesProdApp) $guid = '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$' $tg = ConvertTo-PimActivatorTargets $Targets $get = { param($o, $n) if ($o -is [System.Collections.IDictionary]) { if ($o.Contains($n)) { $o[$n] } } elseif ($null -ne $o -and $o.PSObject.Properties[$n]) { $o.$n } } $asg = { param($c) if ($tg[$c]) { " -AssignToGroupId $($tg[$c].groupId)" } else { '' } } $appProd = ".\Deploy-PimActivatorBackend.ps1 -TenantId $TenantId -Channel Released -DisplayName 'PIM Activator'" $appTest = if ($TestSharesProdApp) { ".\Deploy-PimActivatorBackend.ps1 -TenantId $TenantId -Channel Both -DisplayName 'PIM Activator'" } else { ".\Deploy-PimActivatorBackend.ps1 -TenantId $TenantId -Channel Test -ExtensionId $($script:PimActivatorChannels.Test.id) -DisplayName 'PIM Activator (TEST)'" } # the settings shared by the Remediation + the server install (step 2) $name = "$(& $get $Settings 'name')".Trim(); if (-not $name) { $name = 'PIM' } $just = "$(& $get $Settings 'defaultJustification')".Trim(); if (-not $just) { $just = 'Change in infrastructure' } $hrs = "$(& $get $Settings 'defaultDurationHours')"; $hrs = if ($hrs -match '^\d+$' -and [int]$hrs -ge 1 -and [int]$hrs -le 24) { [int]$hrs } else { 8 } $br = @(@(& $get $Settings 'browsers') | Where-Object { "$_" -in @('Edge', 'Chrome') } | Select-Object -Unique); if (-not $br.Count) { $br = @('Edge', 'Chrome') } # the filters ($pfx) and the limits ($lim): the Remediation AND the server install carry both (owner 2026-10-22: the # install without Intune "must support the new features like max 3 activations") $pfx = ''; $lim = '' foreach ($k in 'Prefix', 'EntraPrefix', 'AzurePrefix') { $v = "$(& $get $Settings ($k.Substring(0,1).ToLower() + $k.Substring(1)))".Trim(); if ($v) { $pfx += " -$k $(ConvertTo-PimActivatorPsLiteral $v)" } } foreach ($k in 'AutoActivateMaxGroups', 'BulkActivateConfirmThreshold') { $v = "$(& $get $Settings ($k.Substring(0,1).ToLower() + $k.Substring(1)))".Trim(); if ($v -match '^\d+$') { $lim += " -$k $v" } } # ACT-ENDTIME (owner 2026-10-31): the end-of-day time travels the same way ('HH:MM', 00:00-23:59) $endV = "$(& $get $Settings 'activationEndTime')".Trim(); if ($endV -match '^([01][0-9]|2[0-3]):[0-5][0-9]$') { $lim += " -ActivationEndTime $endV" } $opt = $pfx + $lim # the optional extra tenants (multi-tenant catalog), validated, compact, as one PowerShell literal $xt = @(ConvertTo-PimActivatorExtraTenants -Json "$(& $get $Settings 'additionalTenants')" -ExcludeTenantId $TenantId) $xtArg = if ($xt.Count) { ' -AdditionalTenantsJson ' + (ConvertTo-PimActivatorPsLiteral (ConvertTo-Json -InputObject $xt -Depth 4 -Compress)) } else { '' } $opt += $xtArg $sched = if ($EveryHours -ge 1 -and $EveryHours -le 23) { " -EveryHours $EveryHours" } elseif ("$DailyTime" -match '^\d{2}:\d{2}$' -and "$DailyTime" -ne '09:00') { " -DailyTime $DailyTime" } else { '' } $cidFor = { param($c) $x = if ($c -eq 'Test' -and "$TestClientId" -match $guid) { $TestClientId } elseif ("$ClientId" -match $guid) { $ClientId } else { '' }; "$x".ToLowerInvariant() } $rem = { param($c) $cid = & $cidFor $c if (-not $cid) { return '' } ".\Publish-PimActivatorRemediation.ps1 -TenantId $TenantId -Channel $c -ClientId $cid -CatalogName $(ConvertTo-PimActivatorPsLiteral $name) -DefaultJustification $(ConvertTo-PimActivatorPsLiteral $just) -DefaultDurationHours $hrs -Browsers $($br -join ',')$opt" + (& $asg $c) + $sched } $srv = { param($c) $cid = & $cidFor $c $line = ".\Deploy-PimActivatorClient.ps1 -Scope Machine -Browser Both -Channel $c" if ($cid) { $line += " -TenantId $TenantId -ClientId $cid -TenantName $(ConvertTo-PimActivatorPsLiteral $name) -DefaultJustification $(ConvertTo-PimActivatorPsLiteral $just) -DefaultDurationHours $hrs$pfx$xtArg" } $line + $lim } [ordered]@{ appProd = $appProd; appTest = $appTest remediationProd = (& $rem 'Released'); remediationTest = (& $rem 'Test'); remediation = (& $rem $Channel) server = (& $srv $Channel) whoProd = (Get-PimActivatorWho -Target $tg['Released'] -Channel Released); whoTest = (Get-PimActivatorWho -Target $tg['Test'] -Channel Test) who = (Get-PimActivatorWho -Target $tg[$Channel] -Channel $Channel) } } # ===== END inlined helper: _PimActivatorBuild.ps1 ===== # 12.7 run frame: "