Private/Interactive/Show-TBMainMenu.ps1
|
function Get-TBReconnectScenarioFromStatus { <# .SYNOPSIS Infers the current connection scenario from granted scopes. #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory = $true)] [pscustomobject]$Status ) $scopes = @() if ($Status.Scopes) { $scopes = @($Status.Scopes) } if ($scopes -contains 'Application.ReadWrite.All') { return 'Setup' } if (($scopes -contains 'ConfigurationMonitoring.Read.All') -and -not ($scopes -contains 'ConfigurationMonitoring.ReadWrite.All')) { return 'ReadOnly' } return 'Manage' } function Get-TBMainMenuCapabilityState { <# .SYNOPSIS Computes feature availability from current Graph scopes. #> [CmdletBinding()] [OutputType([PSCustomObject])] param( [Parameter(Mandatory = $true)] [pscustomobject]$Status ) $scopes = @() if ($Status.Scopes) { $scopes = @($Status.Scopes) } $hasReadWrite = $scopes -contains 'ConfigurationMonitoring.ReadWrite.All' $hasReadOnly = $scopes -contains 'ConfigurationMonitoring.Read.All' $hasMonitoringAccess = $hasReadWrite -or $hasReadOnly $hasSetupAccess = $scopes -contains 'Application.ReadWrite.All' return [PSCustomObject]@{ HasMonitoringAccess = $hasMonitoringAccess HasSetupAccess = $hasSetupAccess HasMetadataScopes = [bool]$Status.DirectoryMetadataEnabled } } function Write-TBMainMenuCapabilitySummary { <# .SYNOPSIS Shows a "what can I do now" summary above main menu options. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [pscustomobject]$Status, [Parameter(Mandatory = $true)] [pscustomobject]$Capabilities ) $hasMonitoring = $Capabilities.HasMonitoringAccess $hasSetup = $Capabilities.HasSetupAccess if ($hasMonitoring -or $hasSetup) { Write-Host ' Available:' -ForegroundColor Cyan -NoNewline $parts = @() if ($hasMonitoring) { $parts += 'Monitors, Baselines, Snapshots, Drift, Reports' } if ($hasSetup) { $parts += 'Setup' } Write-Host (' {0}' -f ($parts -join ', ')) -ForegroundColor Cyan } else { Write-Host ' Available: Connection Status only' -ForegroundColor Yellow } if (-not $Capabilities.HasSetupAccess) { Write-Host ' Setup actions are locked (requires Application.ReadWrite.All).' -ForegroundColor DarkGray } if (-not $Capabilities.HasMonitoringAccess) { Write-Host ' Workload actions are locked (requires ConfigurationMonitoring.Read.All or ReadWrite.All).' -ForegroundColor DarkGray } Write-Host '' } function Show-TBLockedSectionMessage { <# .SYNOPSIS Displays a standard locked-section message. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$SectionName, [Parameter(Mandatory = $true)] [string]$RequiredScopeHint ) Write-Host '' Write-Host (' {0} is currently locked.' -f $SectionName) -ForegroundColor Red Write-Host (' Required: {0}' -f $RequiredScopeHint) -ForegroundColor Yellow Read-Host -Prompt ' Press Enter to continue' | Out-Null } function Get-TBMenuSectionTitle { <# .SYNOPSIS Returns the display title for a menu section, with lock suffix when needed. #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory = $true)] [string]$BaseTitle, [Parameter(Mandatory = $true)] [bool]$IsEnabled ) if ($IsEnabled) { return $BaseTitle } return ('{0} [Locked]' -f $BaseTitle) } function Show-TBConnectionStatusPanel { <# .SYNOPSIS Renders connection status details and optional metadata-consent action. #> [CmdletBinding()] param() $showTechnicalDetails = $false while ($true) { Clear-Host Write-TBMenuHeader -Mode Rich Write-Host '' Write-Host ' -- Connection Status --' -ForegroundColor Cyan Write-Host '' try { $status = Get-TBConnectionStatus } catch { Write-Host (' Error: {0}' -f $_.Exception.Message) -ForegroundColor Red Read-Host -Prompt ' Press Enter to return to the main menu' | Out-Null return } if ($status.Connected) { $identityLabel = Format-TBTenantIdentity -ConnectionStatus $status Write-Host ' Connected: Yes' -ForegroundColor Green Write-Host (' Organization: {0}' -f $identityLabel) -ForegroundColor White Write-Host (' Account: {0}' -f $status.Account) -ForegroundColor White Write-Host (' Connected At: {0}' -f $status.ConnectedAt) -ForegroundColor White if ($status.Environment -and $status.Environment -ne 'Global') { $envLabel = switch ($status.Environment) { 'USGov' { 'USGov (GCC High)' } 'USGovDoD' { 'USGovDoD (DoD)' } 'China' { 'China (21Vianet)' } default { $status.Environment } } Write-Host (' Environment: {0}' -f $envLabel) -ForegroundColor Yellow } if ($showTechnicalDetails) { Write-Host '' Write-Host ' Technical details' -ForegroundColor DarkGray Write-Host (' Tenant ID: {0}' -f $status.TenantId) -ForegroundColor DarkGray if ($status.PrimaryDomain) { Write-Host (' Primary Domain: {0}' -f $status.PrimaryDomain) -ForegroundColor DarkGray } if ($status.TenantDisplayName) { Write-Host (' Tenant Name: {0}' -f $status.TenantDisplayName) -ForegroundColor DarkGray } if ($status.Scopes -and $status.Scopes.Count -gt 0) { Write-Host (' Scopes: {0}' -f ($status.Scopes -join ', ')) -ForegroundColor DarkGray } else { Write-Host ' Scopes: (none reported)' -ForegroundColor DarkGray } } else { Write-Host '' Write-Host ' Technical details are hidden. Press T to show tenant ID and scopes.' -ForegroundColor DarkGray } } else { Write-Host ' Connected: No' -ForegroundColor Red Write-Host ' Sign in to establish a Microsoft Graph connection.' -ForegroundColor Yellow } Write-Host '' if (-not $status.Connected) { Write-Host ' [S] Sign in now' -ForegroundColor Cyan } elseif (-not $status.DirectoryMetadataEnabled) { Write-Host ' [D] Enable organization metadata (primary domain/display name)' -ForegroundColor Cyan } if ($showTechnicalDetails) { Write-Host ' [T] Hide technical details' -ForegroundColor Cyan } else { Write-Host ' [T] Show technical details' -ForegroundColor Cyan } Write-Host ' [Enter] Back to main menu' -ForegroundColor Cyan $choice = Read-Host -Prompt ' Choose an action' if ([string]::IsNullOrWhiteSpace($choice)) { return } if ($choice -match '^[Tt]') { $showTechnicalDetails = -not $showTechnicalDetails continue } if (($choice -match '^[Ss]') -and -not $status.Connected) { $signInParams = @{} if ($status.Environment) { $signInParams['Environment'] = $status.Environment } try { Connect-TBTenant @signInParams | Out-Null } catch { Write-Host (' Sign-in failed: {0}' -f $_.Exception.Message) -ForegroundColor Red Read-Host -Prompt ' Press Enter to continue' | Out-Null } continue } if (($choice -match '^[Dd]') -and $status.Connected -and -not $status.DirectoryMetadataEnabled) { $scenario = Get-TBReconnectScenarioFromStatus -Status $status $connectParams = @{ Scenario = $scenario IncludeDirectoryMetadata = $true } if ($status.TenantId) { $connectParams['TenantId'] = $status.TenantId } if ($status.Environment) { $connectParams['Environment'] = $status.Environment } try { Connect-TBTenant @connectParams | Out-Null Write-Host ' Metadata consent completed and connection refreshed.' -ForegroundColor Green Read-Host -Prompt ' Press Enter to continue' | Out-Null } catch { Write-Host (' Metadata consent failed: {0}' -f $_.Exception.Message) -ForegroundColor Red Read-Host -Prompt ' Press Enter to continue' | Out-Null } continue } Write-Host '' Write-Host ' Invalid option for current state.' -ForegroundColor Yellow Read-Host -Prompt ' Press Enter to continue' | Out-Null } } function Show-TBMainMenuClassic { <# .SYNOPSIS Classic main menu for non-interactive hosts. .DESCRIPTION Original numbered-prompt main menu loop. Used when arrow-key support is not available. #> [CmdletBinding()] param() while ($true) { Clear-Host Write-TBMenuHeader -Mode Rich $status = Get-TBConnectionStatus $capabilities = Get-TBMainMenuCapabilityState -Status $status Write-TBMainMenuCapabilitySummary -Status $status -Capabilities $capabilities $setupTitle = Get-TBMenuSectionTitle -BaseTitle 'Setup and Permissions' -IsEnabled $capabilities.HasSetupAccess $monitorTitle = Get-TBMenuSectionTitle -BaseTitle 'Monitor Management' -IsEnabled $capabilities.HasMonitoringAccess $baselineTitle = Get-TBMenuSectionTitle -BaseTitle 'Baseline Management' -IsEnabled $capabilities.HasMonitoringAccess $snapshotTitle = Get-TBMenuSectionTitle -BaseTitle 'Snapshot Management' -IsEnabled $capabilities.HasMonitoringAccess $driftTitle = Get-TBMenuSectionTitle -BaseTitle 'Drift Detection' -IsEnabled $capabilities.HasMonitoringAccess $reportTitle = Get-TBMenuSectionTitle -BaseTitle 'Reports and Documentation' -IsEnabled $capabilities.HasMonitoringAccess $options = @( 'Connection Status' $setupTitle $monitorTitle $baselineTitle $snapshotTitle $driftTitle $reportTitle ) $choice = Show-TBMenu -Title 'Main Menu' -Options $options -IncludeQuit if ($choice -eq 'Quit') { return } switch ($choice) { 0 { Show-TBConnectionStatusPanel } 1 { if (-not $capabilities.HasSetupAccess) { Show-TBLockedSectionMessage -SectionName 'Setup and Permissions' -RequiredScopeHint 'Application.ReadWrite.All (use Connect-TBTenant -Scenario Setup)' continue } Show-TBSetupMenu } 2 { if (-not $capabilities.HasMonitoringAccess) { Show-TBLockedSectionMessage -SectionName 'Monitor Management' -RequiredScopeHint 'ConfigurationMonitoring.Read.All or ConfigurationMonitoring.ReadWrite.All' continue } Show-TBMonitorMenu } 3 { if (-not $capabilities.HasMonitoringAccess) { Show-TBLockedSectionMessage -SectionName 'Baseline Management' -RequiredScopeHint 'ConfigurationMonitoring.Read.All or ConfigurationMonitoring.ReadWrite.All' continue } Show-TBBaselineMenu } 4 { if (-not $capabilities.HasMonitoringAccess) { Show-TBLockedSectionMessage -SectionName 'Snapshot Management' -RequiredScopeHint 'ConfigurationMonitoring.Read.All or ConfigurationMonitoring.ReadWrite.All' continue } Show-TBSnapshotMenu } 5 { if (-not $capabilities.HasMonitoringAccess) { Show-TBLockedSectionMessage -SectionName 'Drift Detection' -RequiredScopeHint 'ConfigurationMonitoring.Read.All or ConfigurationMonitoring.ReadWrite.All' continue } Show-TBDriftMenu } 6 { if (-not $capabilities.HasMonitoringAccess) { Show-TBLockedSectionMessage -SectionName 'Reports and Documentation' -RequiredScopeHint 'ConfigurationMonitoring.Read.All or ConfigurationMonitoring.ReadWrite.All' continue } Show-TBReportMenu } } } } function Show-TBMainMenu { <# .SYNOPSIS Displays the top-level interactive menu. .DESCRIPTION Main navigation hub that routes to the various management submenus and shows connection status. On PS 7+ with interactive console support, uses an accordion-style menu. Falls back to classic numbered menus on non-interactive hosts. #> [CmdletBinding()] param() if (-not (Test-TBArrowKeySupport)) { Show-TBMainMenuClassic return } $lastExpanded = -1 while ($true) { Clear-Host Write-TBMenuHeader -Mode Rich $status = Get-TBConnectionStatus $capabilities = Get-TBMainMenuCapabilityState -Status $status Write-TBMainMenuCapabilitySummary -Status $status -Capabilities $capabilities $setupTitle = Get-TBMenuSectionTitle -BaseTitle 'Setup and Permissions' -IsEnabled $capabilities.HasSetupAccess $monitorTitle = Get-TBMenuSectionTitle -BaseTitle 'Monitor Management' -IsEnabled $capabilities.HasMonitoringAccess $baselineTitle = Get-TBMenuSectionTitle -BaseTitle 'Baseline Management' -IsEnabled $capabilities.HasMonitoringAccess $snapshotTitle = Get-TBMenuSectionTitle -BaseTitle 'Snapshot Management' -IsEnabled $capabilities.HasMonitoringAccess $driftTitle = Get-TBMenuSectionTitle -BaseTitle 'Drift Detection' -IsEnabled $capabilities.HasMonitoringAccess $reportTitle = Get-TBMenuSectionTitle -BaseTitle 'Reports and Documentation' -IsEnabled $capabilities.HasMonitoringAccess $sections = @( @{ Title = 'Connection Status' Children = @() IsDirect = $true } @{ Title = $setupTitle Children = @( 'Install UTCM service principal' 'Check service principal status' 'Grant workload permissions' ) IsDirect = $false } @{ Title = $monitorTitle Children = @( 'Create new monitor' 'List monitors' 'View monitor details' 'Update monitor' 'Delete monitor' 'View monitor results' ) IsDirect = $false } @{ Title = $baselineTitle Children = @( 'View baseline from monitor' 'Export baseline' 'Import baseline' ) IsDirect = $false } @{ Title = $snapshotTitle Children = @( 'Create snapshot (selected types)' 'Create snapshot (entire workload)' 'Create snapshot (all workloads)' 'List snapshot jobs' 'View snapshot details' 'Export snapshot' 'Delete snapshot' ) IsDirect = $false } @{ Title = $driftTitle Children = @( 'View all drifts' 'View drifts by monitor' 'Drift summary' 'View drift details' ) IsDirect = $false } @{ Title = $reportTitle Children = @( 'Generate drift report' 'Generate dashboard' 'Generate documentation' ) IsDirect = $false } ) $result = Show-TBMenuArrowAccordion -Sections $sections -InitialExpanded $lastExpanded if ($result -eq 'Quit') { return } $lastExpanded = $result.Section Clear-Host switch ($result.Section) { 0 { Show-TBConnectionStatusPanel } 1 { if (-not $capabilities.HasSetupAccess) { Show-TBLockedSectionMessage -SectionName 'Setup and Permissions' -RequiredScopeHint 'Application.ReadWrite.All (use Connect-TBTenant -Scenario Setup)' continue } Show-TBSetupMenu -DirectAction $result.Item } 2 { if (-not $capabilities.HasMonitoringAccess) { Show-TBLockedSectionMessage -SectionName 'Monitor Management' -RequiredScopeHint 'ConfigurationMonitoring.Read.All or ConfigurationMonitoring.ReadWrite.All' continue } Show-TBMonitorMenu -DirectAction $result.Item } 3 { if (-not $capabilities.HasMonitoringAccess) { Show-TBLockedSectionMessage -SectionName 'Baseline Management' -RequiredScopeHint 'ConfigurationMonitoring.Read.All or ConfigurationMonitoring.ReadWrite.All' continue } Show-TBBaselineMenu -DirectAction $result.Item } 4 { if (-not $capabilities.HasMonitoringAccess) { Show-TBLockedSectionMessage -SectionName 'Snapshot Management' -RequiredScopeHint 'ConfigurationMonitoring.Read.All or ConfigurationMonitoring.ReadWrite.All' continue } Show-TBSnapshotMenu -DirectAction $result.Item } 5 { if (-not $capabilities.HasMonitoringAccess) { Show-TBLockedSectionMessage -SectionName 'Drift Detection' -RequiredScopeHint 'ConfigurationMonitoring.Read.All or ConfigurationMonitoring.ReadWrite.All' continue } Show-TBDriftMenu -DirectAction $result.Item } 6 { if (-not $capabilities.HasMonitoringAccess) { Show-TBLockedSectionMessage -SectionName 'Reports and Documentation' -RequiredScopeHint 'ConfigurationMonitoring.Read.All or ConfigurationMonitoring.ReadWrite.All' continue } Show-TBReportMenu -DirectAction $result.Item } } } } |