PropertyBox/Write-SRxOperationTerm.ps1
|
<# .SYNOPSIS Jobs Phase 7 - isolated worker: create or delete a Job template ('operation' term) in the O365 Termstore under the Entra App Registration identity (PnP CSOM, app-only certificate), then update the local termSet.xml + JSON copies (Sync-SRxLocalTermStructure). Same bootstrap / result protocol as Write-SRxTermBatch.ps1: result file + "__JSON__" line = { Success; Conflict; Data; Error; TermId; Pid; Ended } Data = { Action; TermId; ParentTermId; Name; Props; SyncWarning } #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$RootPath, [Parameter(Mandatory)] [ValidateSet('New', 'Delete')] [string]$Action, [Parameter(Mandatory=$false)] [string]$TermId = '', [Parameter(Mandatory=$false)] [string]$ParentTermId = '', [Parameter(Mandatory=$false)] [AllowEmptyString()] [string]$Name = '', [Parameter(Mandatory=$false)] [string]$DesiredSetJson = '{}', [Parameter(Mandatory)] [string]$ResultPath ) $ErrorActionPreference = 'Stop' function Write-SRxWorkerResult { param([bool]$Success, $Data, [string]$ErrorText) $payload = [pscustomobject]@{ Success = $Success Conflict = $false Data = $Data Error = $ErrorText TermId = $TermId Pid = $PID Ended = (Get-Date).ToString('o') } $json = $payload | ConvertTo-Json -Depth 25 -Compress try { $dir = Split-Path -Parent $ResultPath if ($dir -and -not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null } Set-Content -LiteralPath $ResultPath -Value $json -Encoding UTF8 } catch { } Write-Host ("__JSON__" + $json) } function ConvertFrom-SRxJsonToHashtable { param([string]$Json) $ht = @{} if ([string]::IsNullOrWhiteSpace($Json)) { return $ht } $obj = $Json | ConvertFrom-Json foreach ($p in $obj.PSObject.Properties) { $ht[$p.Name] = [string]$p.Value } return $ht } try { $Name = ([string]$Name).Trim() $externalScript = Join-Path $RootPath 'loadmodule.ps1' . $externalScript if ($null -ne $LoadModule) { $LoadModule.Invoke("SRxCore") | Out-Null } Initialize-SRxEnv -LoadModule2 $LoadModule -RootPath $RootPath | Out-Null Connect-SRxSPOService if ($null -eq $global:SRxEnv -or $null -eq $global:SRxEnv.Tenancy) { throw "global:SRxEnv or global:SRxEnv.Tenancy is null after bootstrap." } try { Import-Module (Join-Path $PSScriptRoot 'SRxPropertyBox.psm1') -DisableNameChecking -Force -ErrorAction SilentlyContinue } catch { } $connection = Get-SRxConnection -siteUrl $global:SRxEnv.Tenancy.AdminUrl -Termstore -Supress if (-not $connection) { throw "Failed to establish app-only PnP connection to the Termstore." } $desired = ConvertFrom-SRxJsonToHashtable -Json $DesiredSetJson if ($Action -eq 'New') { if ([string]::IsNullOrEmpty($ParentTermId)) { throw 'ParentTermId is required' } if ([string]::IsNullOrEmpty($Name)) { throw 'Name is required' } if ([string]::IsNullOrEmpty($TermId)) { $TermId = [guid]::NewGuid().ToString() } $parent = Get-PnPTerm -Identity ([GUID]$ParentTermId) -Connection $connection -ErrorAction Stop Get-PnPProperty -ClientObject $parent -Property Id, Name -Connection $connection | Out-Null $new = $parent.CreateTerm($Name, 1033, [GUID]$TermId) foreach ($k in $desired.Keys) { $new.SetCustomProperty($k, [string]$desired[$k]) } Invoke-PnPQuery -Connection $connection -ErrorAction Stop } else { if ([string]::IsNullOrEmpty($TermId)) { throw 'TermId is required' } $term = Get-PnPTerm -Identity ([GUID]$TermId) -Connection $connection -ErrorAction Stop Get-PnPProperty -ClientObject $term -Property Id, Name -Connection $connection | Out-Null $term.DeleteObject() Invoke-PnPQuery -Connection $connection -ErrorAction Stop } # ---- local termSet.xml + JSON (off the UI thread, like the property batch) $syncWarning = $null try { if (Get-Command Sync-SRxLocalTermStructure -ErrorAction SilentlyContinue) { $sync = Sync-SRxLocalTermStructure -Action $Action -TermId $TermId -ParentTermId $ParentTermId -Name $Name -Raw $desired if (-not $sync.Success) { $syncWarning = "Local sync failed: $($sync.Error)" } } else { $syncWarning = "Sync-SRxLocalTermStructure not available in worker; local cache not updated." } } catch { $syncWarning = "Local sync threw: $($_.Exception.Message)" } $data = [pscustomobject]@{ Action = $Action TermId = $TermId ParentTermId = $ParentTermId Name = $Name Props = $desired SyncWarning = $syncWarning } Write-SRxWorkerResult -Success $true -Data $data -ErrorText $null exit 0 } catch { $msg = $_.Exception.Message if ($_.Exception.InnerException) { $msg += " | inner: " + $_.Exception.InnerException.Message } Write-SRxWorkerResult -Success $false -Data $null -ErrorText $msg exit 1 } |