Private/Core/ConvertTo-IDBridgePreviewRow.ps1
|
<# .SYNOPSIS Flatten the computed change lists into uniform preview rows for table review. .DESCRIPTION Called by Invoke-IDBridge -Preview after the plan phase. Turns every change list the pipeline computed (org units, deactivations, updates/renames/moves, creates, and group membership, per directory) into one flat row shape - Directory, Action, PersonID, Name, Account, Building, OrgUnit, Password, Changes - so the proposed run can be reviewed with Format-Table / Where-Object / Out-GridView without exporting anything. Rows are emitted per directory in the same order the apply phase would execute them (CreateOU, Deactivate, Update, Rename, Move, Create, GroupAdd, GroupRemove). Group changes emit one row per user+group pair. The Password column is filled for Create rows only, and only when -ShowPasswords is set - the create splats' SecureStrings are decoded at emit time and never logged. Every list parameter is optional so disabled directories or disabled group processing simply contribute no rows. .PARAMETER SourceData The enriched source records; used to resolve Name/Account/Building for rows whose change list item doesn't carry them (updates, renames, moves, groups). .PARAMETER ShowPasswords Decode the create splats' passwords into the Password column. Off by default. .PARAMETER ADUsersToCreate Output of Get-ADUsersToCreate. .PARAMETER ADUsersToUpdate Output of Get-ADUsersToUpdate (@{ UpdateList; RenameList; MoveList }). .PARAMETER ADUsersToDeactivate Output of Get-ADUsersToDeactivate. .PARAMETER ADUserGroupsToUpdate Output of Get-ADUserGroupsToUpdate (@{ Add; Remove }). .PARAMETER ADOrgUnitsToCreate Output of Get-ADOrgUnitsForProcessing (OU distinguished names). .PARAMETER GoogleUsersToCreate Output of Get-GoogleUsersToCreate. .PARAMETER GoogleUsersToUpdate Output of Get-GoogleUsersToUpdate. .PARAMETER GoogleUsersToDeactivate Output of Get-GoogleUsersToDeactivate. .PARAMETER GoogleUserGroupsToUpdate Output of Get-GoogleUserGroupsToUpdate (@{ Add; Remove }). .PARAMETER GoogleOrgUnitsToCreate Output of Get-GoogleOrgUnitsForProcessing (OU paths). .OUTPUTS [object[]] of @{ Directory; Action; PersonID; Name; Account; Building; OrgUnit; Password; Changes }. .EXAMPLE $rows = ConvertTo-IDBridgePreviewRow -SourceData $sourceData -ADUsersToCreate $ADUsersToCreate .NOTES Created by: Sam Cattanach Modified: 2026-08-21 #> function ConvertTo-IDBridgePreviewRow { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] $SourceData, [switch]$ShowPasswords, $ADUsersToCreate, $ADUsersToUpdate, $ADUsersToDeactivate, $ADUserGroupsToUpdate, $ADOrgUnitsToCreate, $GoogleUsersToCreate, $GoogleUsersToUpdate, $GoogleUsersToDeactivate, $GoogleUserGroupsToUpdate, $GoogleOrgUnitsToCreate ) #Source record lookup for rows whose list item carries only PersonID + splat $recordByID = @{} foreach ($record in $SourceData) { $recordByID["$($record.personID)"] = $record } #"First Last" from a source record, empty when the record can't be resolved $recordName = { param($record) if ($record) { "$($record.NameFirst) $($record.NameLast)".Trim() } else { '' } } #Compact "Key=value; Key=value" rendering of a splat, minus bookkeeping keys. #SecureStrings never appear in update splats, but render as (secure) defensively #so a password can never leak through the Changes column. $formatSplat = { param($Splat, $ExcludeKeys) $parts = foreach ($key in $Splat.Keys | Where-Object { $_ -notin $ExcludeKeys } | Sort-Object) { $value = $Splat[$key] if ($value -is [securestring]) { $value = '(secure)' } elseif ($value -is [hashtable]) { $value = ($value | ConvertTo-Json -Compress) } "$key=$value" } $parts -join '; ' } #Create-row password: decoded only when -ShowPasswords, empty otherwise $formatPassword = { param($Value) if (-not $ShowPasswords) { '' } elseif ($Value -is [securestring]) { ConvertFrom-SecureString -SecureString $Value -AsPlainText } else { "$Value" } } $rows = [System.Collections.Generic.List[object]]::new() #Rows follow the apply phase's execution order within each directory #region AD Rows foreach ($item in @($ADOrgUnitsToCreate)) { if ($null -eq $item) { continue } $rows.Add([PSCustomObject]@{ Directory = 'AD'; Action = 'CreateOU'; PersonID = ''; Name = ''; Account = '' Building = ''; OrgUnit = "$item"; Password = ''; Changes = '' }) } foreach ($item in @($ADUsersToDeactivate)) { if ($null -eq $item) { continue } $rows.Add([PSCustomObject]@{ Directory = 'AD'; Action = 'Deactivate'; PersonID = "$($item.PersonID)" Name = & $recordName $item; Account = $item.Username; Building = $item.Building OrgUnit = $item.ADOrganizationalUnitTrash; Password = ''; Changes = 'Disable account; move to trash OU' }) } foreach ($item in @($ADUsersToUpdate.UpdateList)) { if ($null -eq $item) { continue } $record = $recordByID["$($item.PersonID)"] $rows.Add([PSCustomObject]@{ Directory = 'AD'; Action = 'Update'; PersonID = "$($item.PersonID)" Name = & $recordName $record; Account = $record.Username; Building = $record.Building OrgUnit = ''; Password = '' Changes = & $formatSplat $item.Splat @('Identity', 'Division') }) } foreach ($item in @($ADUsersToUpdate.RenameList)) { if ($null -eq $item) { continue } $record = $recordByID["$($item.PersonID)"] $rows.Add([PSCustomObject]@{ Directory = 'AD'; Action = 'Rename'; PersonID = "$($item.PersonID)" Name = & $recordName $record; Account = $record.Username; Building = $record.Building OrgUnit = ''; Password = '' Changes = "CN '$($item.CN)' -> '$($item.NewName)'" }) } foreach ($item in @($ADUsersToUpdate.MoveList)) { if ($null -eq $item) { continue } $record = $recordByID["$($item.PersonID)"] $rows.Add([PSCustomObject]@{ Directory = 'AD'; Action = 'Move'; PersonID = "$($item.PersonID)" Name = & $recordName $record; Account = $record.Username; Building = $record.Building OrgUnit = $item.NewOrgUnit; Password = '' Changes = "Move to $($item.NewOrgUnit)" }) } foreach ($item in @($ADUsersToCreate)) { if ($null -eq $item) { continue } $rows.Add([PSCustomObject]@{ Directory = 'AD'; Action = 'Create'; PersonID = "$($item.PersonID)" Name = $item.Splat.DisplayName; Account = $item.Splat.SamAccountName; Building = $item.Splat.Office OrgUnit = $item.Splat.Path Password = & $formatPassword $item.Splat.AccountPassword Changes = '' }) } foreach ($listName in @('Add', 'Remove')) { $action = if ($listName -eq 'Add') { 'GroupAdd' } else { 'GroupRemove' } foreach ($item in @($ADUserGroupsToUpdate.$listName)) { if ($null -eq $item) { continue } $record = $recordByID["$($item.PersonID)"] foreach ($group in @($item.Groups)) { $rows.Add([PSCustomObject]@{ Directory = 'AD'; Action = $action; PersonID = "$($item.PersonID)" Name = & $recordName $record; Account = $record.Username; Building = $record.Building OrgUnit = ''; Password = ''; Changes = "$group" }) } } } #endregion AD Rows #region Google Rows foreach ($item in @($GoogleOrgUnitsToCreate)) { if ($null -eq $item) { continue } $rows.Add([PSCustomObject]@{ Directory = 'Google'; Action = 'CreateOU'; PersonID = ''; Name = ''; Account = '' Building = ''; OrgUnit = "$item"; Password = ''; Changes = '' }) } foreach ($item in @($GoogleUsersToDeactivate)) { if ($null -eq $item) { continue } $changes = 'Archive account; move to trash OU' if ($item.GoogleCurrentLicenses) { $skuNames = ($item.GoogleCurrentLicenses | ForEach-Object { if ($_.skuName) { $_.skuName } else { $_.skuId } }) -join ', ' $changes += "; removes licenses: $skuNames" } $rows.Add([PSCustomObject]@{ Directory = 'Google'; Action = 'Deactivate'; PersonID = "$($item.PersonID)" Name = & $recordName $item; Account = $item.UPN; Building = $item.Building OrgUnit = $item.GoogleOrganizationalUnitTrash; Password = ''; Changes = $changes }) } foreach ($item in @($GoogleUsersToUpdate)) { if ($null -eq $item) { continue } $record = $recordByID["$($item.PersonID)"] $rows.Add([PSCustomObject]@{ Directory = 'Google'; Action = 'Update'; PersonID = "$($item.PersonID)" Name = & $recordName $record; Account = $item.UPN; Building = $record.Building OrgUnit = ''; Password = '' Changes = & $formatSplat $item.Splat @('GoogleUserID') }) } foreach ($item in @($GoogleUsersToCreate)) { if ($null -eq $item) { continue } $rows.Add([PSCustomObject]@{ Directory = 'Google'; Action = 'Create' PersonID = "$($item.PersonID)" Name = "$($item.Splat.FirstName) $($item.Splat.LastName)" ; Account = $item.Splat.PrimaryEmail Building = $item.Splat.Building; OrgUnit = $item.Splat.OrgUnitPath Password = & $formatPassword $item.Splat.Password Changes = '' }) } foreach ($listName in @('Add', 'Remove')) { $action = if ($listName -eq 'Add') { 'GroupAdd' } else { 'GroupRemove' } foreach ($item in @($GoogleUserGroupsToUpdate.$listName)) { if ($null -eq $item) { continue } $record = $recordByID["$($item.PersonID)"] foreach ($group in @($item.Groups)) { $rows.Add([PSCustomObject]@{ Directory = 'Google'; Action = $action; PersonID = "$($item.PersonID)" Name = & $recordName $record; Account = $record.UPN; Building = $record.Building OrgUnit = ''; Password = ''; Changes = "$group" }) } } } #endregion Google Rows return $rows } |