Public/Export-NSPEntraPairingQueue.ps1
|
function Export-NSPEntraPairingQueue { <# .SYNOPSIS Saves a queue as typed JSON, and optionally as a CSV for audit. Returns the JSON path. .DESCRIPTION Without -Path, the file goes under %LOCALAPPDATA%\NSP\UserPairing\<tenant ID>. A path inside a Git checkout is refused. The JSON is what Import-NSPEntraPairingQueue reads; the CSV is for people, and cannot be imported. Cells that a spreadsheet would run as a formula are prefixed with an apostrophe, because display names come from the directory. Both files hold personal data, so set appropriate ACLs on any folder you choose. .EXAMPLE Export-NSPEntraPairingQueue -Queue $queue -CsvPath 'D:\Secure\pairing-audit.csv' #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory)]$Queue, [string]$Path, [string]$CsvPath ) if (-not $Path) { $name = 'pairing-{0}.queue.json' -f [datetime]::UtcNow.ToString('yyyyMMdd-HHmmss-fff') $Path = Join-Path (Join-Path (Get-NSPEntraPairingStatePath) $Queue.tenantId) $name } Assert-NSPEntraPairingStatePath -Path $Path if ($CsvPath) { Assert-NSPEntraPairingStatePath -Path $CsvPath } $folder = Split-Path -Parent ([IO.Path]::GetFullPath($Path)) if (-not (Test-Path -LiteralPath $folder)) { New-Item -ItemType Directory -Path $folder -Force | Out-Null } $utf8 = New-Object Text.UTF8Encoding($false) # Replace in one step: the review saves after every decision, and a failed write must not cost # the only copy. $temporary = "$Path.tmp" [IO.File]::WriteAllText($temporary, (ConvertTo-Json -InputObject $Queue -Depth 12), $utf8) # -WhatIf:$false: a caller's WhatIf must not leave the temporary file behind. Callers decide # whether to export at all. Move-Item -LiteralPath $temporary -Destination $Path -Force -WhatIf:$false if ($CsvPath) { $cell = { param($Value) ConvertTo-NSPEntraPairingCsvCell $Value } $rows = foreach ($pair in $Queue.pairs) { [pscustomobject]@{ TenantId = $Queue.tenantId Forest = $Queue.forest AnchorStrategy = $Queue.sourceAnchor.strategy AnchorStatus = $Queue.sourceAnchor.status CloudObjectId = $pair.cloudObjectId CloudUserPrincipalName = & $cell $pair.cloudUpnAtReview CloudDisplayName = & $cell $pair.cloudDisplayNameAtReview Status = $pair.status Decision = $pair.decision AdObjectGuid = $pair.adObjectGuid AdSid = $pair.adSid AdDistinguishedName = & $cell $pair.adDistinguishedNameAtReview MatchEvidence = @($pair.matchEvidence) -join ';' DecidedAtUtc = $pair.decidedAtUtc DecidedBy = & $cell $pair.decidedBy Note = & $cell $pair.note ExecutionState = $pair.executionState } } $csvFolder = Split-Path -Parent ([IO.Path]::GetFullPath($CsvPath)) if (-not (Test-Path -LiteralPath $csvFolder)) { New-Item -ItemType Directory -Path $csvFolder -Force | Out-Null } [IO.File]::WriteAllText($CsvPath, ((@($rows) | ConvertTo-Csv -NoTypeInformation) -join "`r`n"), $utf8) } $Path } |