Public/Start-NSPEntraPairingWizard.ps1

function Start-NSPEntraPairingWizard {
    <#
    .SYNOPSIS
        Discovers tenant/DC/forest, inventories users and opens the pairing review.
    .DESCRIPTION
        Uses an existing Graph connection with Preflight read scopes. Displays the discovered
        environment before collecting users. User writes and hard-match apply are separate commands; the suffix menu offers explicit confirmed forest additions.
        Optional address preparation is a preview through the AD specialist's API, after review.
    .EXAMPLE
        Start-NSPEntraPairingWizard -SourceAnchor $anchor
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param([Parameter(Mandatory)]$SourceAnchor, [string]$Server, [string]$Path,
        [switch]$AssumeNoRoleEligibility)
    $environment = Get-NSPEntraPairingEnvironment -Server $Server
    Import-NSPToolkitModule -Name 'NSP.Console' -MinimumVersion '0.1.1'
    Write-NSPConsoleLine -Message "Tenant: $($environment.TenantName) [$($environment.TenantId)] DC: $($environment.Server) Forest: $($environment.Forest)"
    if (-not (Read-NSPConfirm -Prompt 'Read users from this tenant and forest?')) { return }
    if (-not $PSCmdlet.ShouldProcess('pairing inventory and review queue', 'Read and review')) { return }
    Assert-NSPEntraPairingSession -Purpose Preflight -TenantId $environment.TenantId
    $inventory = Get-NSPEntraPairingInventory -ExpectedTenantId $environment.TenantId -Server $environment.Server -Forest -ReadRoles -AssumeNoRoleEligibility:$AssumeNoRoleEligibility
    $inventory | Add-Member -NotePropertyName ForestName -NotePropertyValue $environment.Forest -Force
    $records = @(Get-NSPEntraUserPairCandidate -Inventory $inventory -SourceAnchor $SourceAnchor)
    $queue = ConvertTo-NSPEntraPairingQueue -Candidate $records -TenantId $environment.TenantId -Forest $environment.Forest -Domain $inventory.Domains -SourceAnchor $SourceAnchor
    $review = Start-NSPEntraUserPairing -Queue $queue -Server $environment.Server -Forest -Path $Path
    $plans = @()
    $suffixReviews = @()
    while ($review.ReviewCompleted) {
        $action = Read-NSPMenu -Prompt 'Optional AD address preparation' -Choices @(
            [pscustomobject]@{ Label = 'Finish with the pairing queue'; Value = 'finish' }
            [pscustomobject]@{ Label = 'Review / explicitly add missing cloud domains as forest UPN suffixes'; Value = 'suffixes' }
            [pscustomobject]@{ Label = 'Preview AD UPN, mail and proxy address changes'; Value = 'addresses' }
        )
        if ($null -eq $action -or $action -eq 'finish') { break }
        if ($action -eq 'suffixes') {
            $suffixReviews += Start-NSPEntraPairingUpnSuffixReview -ExpectedTenantId $queue.tenantId -ExpectedForest $queue.forest -Server $environment.Server -Apply
            continue
        }
        if ($action -eq 'addresses') {
            $old = Read-NSPNonEmpty -Prompt 'Current AD UPN suffix (DNS name only)'
            $new = Read-NSPNonEmpty -Prompt 'Target verified cloud domain / AD UPN suffix (DNS name only)'
            $plans = @(Get-NSPEntraPairingAddressPlan -Queue $queue -Inventory $inventory -OldDomain $old -NewDomain $new)
            foreach ($plan in $plans) {
                Write-NSPConsoleLine -Message "AD $($plan.ObjectGUID): blocked=$($plan.Blocked) $(@($plan.Reasons) -join '; ')"
                Write-NSPConsoleLine -Message ($plan.Before | ConvertTo-Json -Compress)
                Write-NSPConsoleLine -Message ($plan.After | ConvertTo-Json -Compress)
            }
            break
        }
    }
    [pscustomobject]@{ Environment = $environment; Inventory = $inventory; Queue = $queue; Review = $review; AddressPlans = $plans; SuffixReviews = $suffixReviews }
}