Public/Start-NSPADManager.ps1

function Start-NSPADManager {
    <#
    .SYNOPSIS
        The AD Manager dashboard: RSAT, VPN group/OU scaffold, group nesting, auto-enrollment GPO,
        WMI filters, the VPN inventory export and GPO recipes (Huntress), with DRY RUN on by default.

    .DESCRIPTION
        Relaunches elevated if needed. Answers are kept in the AD work folder
        (Get-NSPToolWorkPath -Tool AD). -SeedAnswersJson merges a launcher's answers into them;
        with -SeedOnly that is all it does (what a generated shim calls first). The first start on a
        server also offers to move the zip-era AD-Manager's files into the work folder.

        Every change goes through Invoke-NSPStep: DRY RUN prints what it would do, D switches to APPLY.

    .PARAMETER SeedAnswersJson
        Answers to merge in (plain JSON, or an AD Answers hand-off).

    .PARAMETER SeedOnly
        Merge -SeedAnswersJson and return without opening the dashboard.

    .PARAMETER NoElevate
        Do not relaunch elevated.

    .EXAMPLE
        Start-NSPADManager

    .EXAMPLE
        Start-NSPADManager -SeedAnswersJson (Get-Content .\ExampleCo_AD_Answers.json -Raw)
    #>

    [CmdletBinding()]
    param(
        [string]$SeedAnswersJson,
        [switch]$SeedOnly,
        [switch]$NoElevate
    )

    Use-NSPADDependency -Console -Bootstrap

    if ($SeedOnly) {
        $null = Import-NSPToolSeedAnswers -Tool AD -SeedAnswersJson $SeedAnswersJson
        return
    }

    if (-not $NoElevate) {
        $manifest = (Join-Path $script:ModuleRoot 'NSP.ActiveDirectory.psd1').Replace("'", "''")
        if (Invoke-NSPElevated -Command "Import-Module '$manifest'; Start-NSPADManager" -NoExit) { return }
    }

    $script:ADAnswers = Import-NSPToolSeedAnswers -Tool AD -SeedAnswersJson $SeedAnswersJson

    # First start on this server: offer to bring over what the zip-era AD-Manager left behind.
    $marker = Join-Path (Get-NSPToolWorkPath -Tool AD -Create) '.legacy-checked'
    if (-not (Test-Path -LiteralPath $marker)) {
        try {
            $summary = Move-NSPToolLegacyData -Tool AD
            if ($summary.Found) { $script:ADAnswers = Get-NSPToolAnswers -Tool AD; Read-Host "`nPress Enter to continue" | Out-Null }
        } catch { Write-Warning "Old AD-Manager files could not be checked: $($_.Exception.Message)" }
        Set-Content -LiteralPath $marker -Value (Get-Date -Format 's')
    }

    $version = Get-NSPADModuleVersion
    $label = (Get-NSPToolVersionStatus -ToolKey 'NSP.ActiveDirectory' -Version $version).Label
    Set-NSPConsoleMaximized -ErrorAction SilentlyContinue
    Set-NSPDryRun -Enabled $true

    $choices = @(
        [pscustomobject]@{ Label = 'Install RSAT / management modules'; Value = 'Prereqs'; Help = 'ActiveDirectory and GroupPolicy PowerShell modules.' }
        [pscustomobject]@{ Label = 'Stand up the VPN group/OU scaffold'; Value = 'Scaffold'; Help = 'VPNGroups\ with the main VPN group, VPNGroups\FWRules\ with the VPNFW purpose groups.' }
        [pscustomobject]@{ Label = 'Nest a group into the shared VPNFW purpose groups'; Value = 'Nest'; Help = 'Repeatable, for group pairs added later.' }
        [pscustomobject]@{ Label = 'Push the certificate auto-enrollment GPO'; Value = 'Gpo' }
        [pscustomobject]@{ Label = 'Stand up the well-known WMI filters'; Value = 'Wmi' }
        [pscustomobject]@{ Label = 'Export VPN documentation inventory (read-only)'; Value = 'Inventory'; Help = 'Hand-off file for the Orchestrator VPN report.' }
        [pscustomobject]@{ Label = "Deploy a GPO recipe ($(@(Get-NSPADGpoRecipe).Name -join ', '))"; Value = 'Recipe'; Help = 'Share folder with report folders, the script, and the GPO linked at the domain root.' }
        [pscustomobject]@{ Key = 'D'; Label = 'Toggle DRY RUN / APPLY'; Value = 'DryRun' }
        [pscustomobject]@{ Key = 'L'; Label = 'Clean up old AD-Manager (zip) files'; Value = 'Legacy' }
    )

    while ($true) {
        Clear-NSPConsole
        $company = if ($script:ADAnswers -and $script:ADAnswers.PSObject.Properties['CompanyName']) { [string]$script:ADAnswers.CompanyName } else { $env:USERDNSDOMAIN }
        Write-NSPConsoleHeader -Title "AD Manager $label" -Subtitle $company
        if (Get-NSPDryRun) {
            Write-NSPConsoleLine ' MODE: DRY RUN - actions print what they would do and change nothing. Press D to switch to APPLY.' -Role Heading
        } else {
            Write-NSPConsoleLine ' MODE: APPLY - actions will make real changes. Press D to switch back to DRY RUN.' -Role Error
        }
        Write-Host ''
        $pick = Read-NSPMenu -Choices $choices -CancelLabel 'Quit'
        if ($null -eq $pick) { return }
        try {
            switch ($pick) {
                'Prereqs' { Invoke-NSPADMenuPrereqs }
                'Scaffold' { Invoke-NSPADMenuScaffold }
                'Nest' { Invoke-NSPADMenuNestGroup }
                'Gpo' { Invoke-NSPADMenuAutoEnrollGpo }
                'Wmi' { Invoke-NSPADMenuWmiFilters }
                'Inventory' { Invoke-NSPADMenuVpnInventory }
                'Recipe' { Invoke-NSPADMenuGpoRecipe }
                'Legacy' { Invoke-NSPADMenuLegacyCleanup }
                'DryRun' {
                    if (Get-NSPDryRun) {
                        Write-NSPConsoleLine "`nSwitching to APPLY mode - actions will make real changes." -Role Error
                        if ((Read-Host 'Type APPLY to confirm') -ceq 'APPLY') { Set-NSPDryRun -Enabled $false }
                        else { Write-NSPConsoleLine 'Kept DRY RUN.' -Role Muted }
                    } else {
                        Set-NSPDryRun -Enabled $true
                        Write-NSPConsoleLine "`nBack to DRY RUN." -Role Heading
                    }
                    Start-Sleep -Seconds 1
                }
            }
            Save-NSPToolAnswers -Tool AD -Answers $script:ADAnswers
        } catch {
            Write-NSPConsoleLine "`nUNEXPECTED ERROR: $($_.Exception.Message)" -Role Error
            Write-Host $_.ScriptStackTrace -ForegroundColor DarkGray
            Read-Host 'Press Enter to return to the menu' | Out-Null
        }
    }
}