source/Public/Show-RJInteractiveSetup.ps1
|
function Show-RJInteractiveSetup { <# .SYNOPSIS Launches an interactive menu-driven setup for configuring RealmJoin tenant. .DESCRIPTION This cmdlet provides an interactive menu system for configuring RealmJoin tenant settings. It guides users through: - Choosing between New Tenant Setup or Update Existing Tenant - Selecting optional features with detailed descriptions - Choosing permission mode (Full or Read-Only) - Reviewing and confirming selections - Executing the configuration The menu system uses arrow keys for navigation and provides clear visual feedback. .PARAMETER SkipBanner Skips the welcome banner display. .EXAMPLE Show-RJInteractiveSetup Launches the interactive setup wizard. .EXAMPLE Show-RJInteractiveSetup -SkipBanner Launches the interactive setup wizard without the banner. .NOTES This cmdlet requires appropriate Azure AD permissions to create service principals and assign app roles. #> [CmdletBinding()] param( [switch]$SkipBanner ) begin { # Check for PowerShell ISE compatibility if (Test-PowerShellISE) { Write-Warning "PowerShell ISE is not supported for interactive menus." Write-Host "" Write-Host "Please use one of the following alternatives:" -ForegroundColor White Write-Host " • Windows PowerShell Console (powershell.exe)" -ForegroundColor Green Write-Host " • PowerShell Core (pwsh.exe)" -ForegroundColor Green Write-Host " • Windows Terminal with PowerShell" -ForegroundColor Green Write-Host " • Visual Studio Code with PowerShell Extension" -ForegroundColor Green Write-Host "" Write-Host "You can also use the individual cmdlets directly:" -ForegroundColor White Write-Host " • New-RJTenant" -ForegroundColor Green Write-Host " • Update-RJTenant" -ForegroundColor Green Write-Host " • Get-RJCurrentConfiguration" -ForegroundColor Green Write-Host "" $script:SkipExecution = $true return } $script:SkipExecution = $false # Initialize required modules Write-Host "Initializing modules..." -ForegroundColor Gray if (-not (Initialize-RequiredModule)) { Write-Host "ERROR: Module initialization failed" -ForegroundColor Red return } Write-Host "" } process { # Skip execution if we're in an unsupported environment if ($script:SkipExecution) { return } # Display welcome banner if (-not $SkipBanner) { Show-RJInteractiveBanner } # Main menu loop $continue = $true while ($continue) { $mainMenuChoice = Show-RJMainMenu switch ($mainMenuChoice) { 'NewTenant' { Show-RJInteractiveNewTenant } 'UpdateTenant' { Show-RJInteractiveUpdateTenant } 'ShowFeatures' { Show-RJFeatureInfo Write-Host "`nPress any key to continue..." -ForegroundColor Gray $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") } 'Exit' { Write-Host "" $continue = $false } } } } end { Write-Verbose "Interactive setup completed" } } function Show-RJInteractiveBanner { Clear-Host Write-Host "RealmJoin Tenant Configuration v1.1.0" -ForegroundColor White Write-Host ("-" * 50) -ForegroundColor DarkGray Write-Host "" } function Show-RJMainMenu { $extraInstructions = @( "Option 1 creates new configuration with review before applying.", "Option 2 shows current state and allows targeted modifications." ) return Show-RJGenericMenu -Title "RealmJoin Configuration" -Prompt "Select an option:" -Options $MainMenuOptions -ExtraInstructions $extraInstructions } function Show-RJTenantAuthentication { Write-Host "Authenticating..." -ForegroundColor Gray try { $null = Invoke-RJOperationWithRetry -Operation { Invoke-GraphLogin } -OperationName "Microsoft Graph Authentication" -ShowProgress $tenantInfo = Invoke-RJOperationWithRetry -Operation { Get-TenantInfo } -OperationName "Retrieve Tenant Information" Write-Host "Connected: $($tenantInfo.DisplayName)" -ForegroundColor Green Write-Host "Tenant ID: $($tenantInfo.TenantId)" -ForegroundColor DarkGray Write-Host "" return $tenantInfo } catch { Write-Host "Authentication failed: $_" -ForegroundColor Red Write-Host "Press any key to return to main menu..." -ForegroundColor Gray $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") return $null } } function Show-RJInteractiveNewTenant { Clear-Host Write-Host "New Tenant Setup" -ForegroundColor White Write-Host ("-" * 50) -ForegroundColor DarkGray Write-Host "" # Step 1: Login with error handling $tenantInfo = Show-RJTenantAuthentication if ($null -eq $tenantInfo) { return } # Step 2: Check if tenant is already configured Write-Host "Checking tenant configuration..." -ForegroundColor Gray $isConfigured = Test-RJTenantAlreadyConfigured if ($isConfigured) { Write-Host "NOTICE: This tenant appears to already have RealmJoin configuration." -ForegroundColor Yellow Write-Host "" $choice = Show-RJGenericMenu -Title "Tenant Already Configured" -Prompt "This tenant appears to already have RealmJoin configuration.`nWhat would you like to do?" -Options $TenantConfiguredOptions -AllowEscape switch ($choice) { 'UpdateTenant' { Show-RJInteractiveUpdateTenant return } 'MainMenu' { return } { $null -eq $_ } { return } } } # Step 3: Select permission mode Write-Host "Permission Mode" -ForegroundColor Gray $permissionMode = Show-RJGenericMenu -Title "Permission Mode" -Prompt "Select permission mode:" -Options $PermissionModeOptions -AllowEscape if ($null -eq $permissionMode) { return } # Step 4: Select features Write-Host "`nFeature Selection" -ForegroundColor Gray $allFeatures = Get-RJFeatureDefinitionList $optionalFeatures = $allFeatures | Where-Object { -not $_.IsMandatory } $mandatoryFeatures = $allFeatures | Where-Object { $_.IsMandatory } $promptText = "Mandatory Features:`n" foreach ($feature in $mandatoryFeatures) { $promptText += " * $($feature.Name) - $($feature.Description)`n" } $promptText += "`nOptional Features:" $selectedFeatures = Show-RJGenericMultiSelectMenu -Title "Feature Selection" -Prompt $promptText -Items $optionalFeatures -DefaultSelected $DefaultSelectedFeatures if ($null -eq $selectedFeatures) { return } # Step 5: Review and confirm Write-Host "`nPlease wait..." -ForegroundColor Gray # Show analysis using existing function $allSelectedFeatures = @($mandatoryFeatures.Name) + @($selectedFeatures) $analysisResult = Get-RJTenantPermissionAnalysis -FeatureResolution (Resolve-RJFeature -SelectedFeatures $allSelectedFeatures -ReadOnly:($permissionMode -eq 'ReadOnly')) Clear-Host Write-Host "Configuration Review" -ForegroundColor White Write-Host ("=" * 60) -ForegroundColor DarkGray Write-Host "" Write-Host "Tenant: $($tenantInfo.DisplayName)" -ForegroundColor Green Write-Host "ID: $($tenantInfo.TenantId)" -ForegroundColor DarkGray Write-Host "Permission Mode: $(if ($permissionMode -eq 'ReadOnly') { 'Read-Only' } else { 'Read/Write' })" -ForegroundColor Green Write-Host "" Show-RJFeatureAnalysis -FeatureAnalysis $analysisResult.FeatureAnalysis -OrphanedServicePrincipals $analysisResult.OrphanedServicePrincipals $confirm = Show-RJConfirmationMenu -Message "Proceed with configuration?" if (-not $confirm) { return } Write-Host "" Write-Host "Applying configuration..." -ForegroundColor Gray Write-Host "" try { Invoke-RJOperationWithRetry -Operation { if ($permissionMode -eq 'ReadOnly') { New-RJTenant -Features $selectedFeatures -ReadOnly } else { New-RJTenant -Features $selectedFeatures } } -OperationName "Apply Configuration" -ShowProgress Write-Host "" Write-Host "Configuration completed successfully." -ForegroundColor Green } catch { Write-Host "" Write-Host "ERROR: $_" -ForegroundColor Red Write-Host "" Write-Host "New tenant setup failed. You can retry the setup process." -ForegroundColor Yellow } Write-Host "" Write-Host "Press any key to continue..." -ForegroundColor Gray $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") } function Show-RJInteractiveUpdateTenant { Clear-Host Write-Host "Modify Existing Configuration" -ForegroundColor White Write-Host ("-" * 50) -ForegroundColor DarkGray Write-Host "" # Step 1: Login with error handling $tenantInfo = Show-RJTenantAuthentication if ($null -eq $tenantInfo) { return } # Step 2: Analyze current configuration with error handling Write-Host "Analyzing current configuration..." -ForegroundColor Gray try { $currentConfig = Invoke-RJOperationWithRetry -Operation { Get-RJCurrentConfiguration } -OperationName "Analyze Configuration" -ShowProgress if ($currentConfig.ConfiguredFeatures.Count -eq 0 -and $currentConfig.LegacyApplications.Count -eq 0) { Clear-Host Write-Host "No existing RealmJoin configuration found." -ForegroundColor Yellow Write-Host "Use 'New Tenant Setup' option instead." -ForegroundColor Yellow Write-Host "" Write-Host "Press any key to continue..." -ForegroundColor Gray $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") return } Show-RJCurrentConfiguration -Configuration $currentConfig Write-Host "Press any key to continue..." -ForegroundColor Gray $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") $updateOptions = $UpdateMenuOptions.Clone() if ($currentConfig.LegacyApplications.Count -gt 0) { $legacyOption = [PSCustomObject]@{ Key = 'RemoveLegacy' Display = "[4] Remove Legacy Applications ($($currentConfig.LegacyApplications.Count) found)" Description = 'Clean up old RealmJoin apps' } $updateOptions = $updateOptions[0..2] + $legacyOption } $updateChoice = Show-RJGenericMenu -Title "Update Options" -Prompt "What would you like to update?" -Options $updateOptions -AllowEscape switch ($updateChoice) { 'AddFeatures' { $allFeatureDefinitions = Get-RJFeatureDefinitionList Show-RJUpdateTenantAddFeature -CurrentConfig $currentConfig -AllFeatureDefinitions $allFeatureDefinitions } 'RemoveFeatures' { $allFeatureDefinitions = Get-RJFeatureDefinitionList Show-RJUpdateTenantRemoveFeature -CurrentConfig $currentConfig -AllFeatureDefinitions $allFeatureDefinitions } 'ChangePermissions' { Show-RJUpdateTenantChangePermission -CurrentConfig $currentConfig } 'RemoveLegacy' { Show-RJUpdateTenantRemoveLegacy -CurrentConfig $currentConfig } { $null -eq $_ } { return } } } catch { Write-Host "" Write-Host "ERROR: $_" -ForegroundColor Red } Write-Host "" Write-Host "Press any key to continue..." -ForegroundColor Gray $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") } function Show-RJConfirmationMenu { param( [string]$Message = "Do you want to proceed?" ) Write-Host "" Write-Host ("=" * 60) -ForegroundColor DarkGray Write-Host "" Write-Host $Message -ForegroundColor White Write-Host "" Write-Host "[Y] Yes [N] No (return to main menu)" -ForegroundColor DarkGray Write-Host "" Write-Host "Your choice: " -ForegroundColor White -NoNewline # Handle user input while ($true) { $key = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") switch ($key.Character.ToString().ToUpper()) { 'Y' { Write-Host "Yes" -ForegroundColor Green return $true } 'N' { Write-Host "No" -ForegroundColor Red return $false } } } } function Show-RJUpdateTenantAddFeature { param( [Parameter(Mandatory)] $CurrentConfig, [Parameter(Mandatory)] $AllFeatureDefinitions ) $configuredNames = $CurrentConfig.ConfiguredFeatures.Name $availableFeatures = $AllFeatureDefinitions | Where-Object { $_.Name -notin $configuredNames -and -not $_.IsMandatory } if ($availableFeatures.Count -eq 0) { Clear-Host Write-Host "All available features are already configured." -ForegroundColor Yellow return } $featuresToAdd = Show-RJGenericMultiSelectMenu -Title "Add Features" -Prompt "Select features to add:" -Items $availableFeatures if ($null -eq $featuresToAdd) { return } # Add mandatory features that are not already configured $mandatoryFeatures = $AllFeatureDefinitions | Where-Object { $_.IsMandatory -and $_.Name -notin $configuredNames } if ($mandatoryFeatures) { $featuresToAdd = @($featuresToAdd) + @($mandatoryFeatures.Name) } if ($featuresToAdd -and $featuresToAdd.Count -gt 0) { $permissionMode = Show-RJGenericMenu -Title "Permission Mode" -Prompt "Select permission mode for adding features:" -Options $PermissionModeOptions -AllowEscape if ($null -eq $permissionMode) { return } Write-Host "`nPlease wait..." -ForegroundColor Gray # Show analysis for features to add using incremental parameters $analysisResult = Get-RJTenantPermissionAnalysis -FeatureResolution (Resolve-RJFeature -CurrentConfig $CurrentConfig -FeaturesToAdd $featuresToAdd -ReadOnly:($permissionMode -eq 'ReadOnly')) Clear-Host Write-Host "Add Features Review" -ForegroundColor White Write-Host ("=" * 60) -ForegroundColor DarkGray Write-Host "" Write-Host "Permission Mode: $(if ($permissionMode -eq 'ReadOnly') { 'Read-Only' } else { 'Read/Write' })" -ForegroundColor Green Write-Host "" # Show only the features being added $addedFeatureAnalysis = $analysisResult.FeatureAnalysis | Where-Object { $_.FeatureName -in $featuresToAdd } Show-RJFeatureAnalysis -FeatureAnalysis $addedFeatureAnalysis -OrphanedServicePrincipals @() $confirm = Show-RJConfirmationMenu -Message "Proceed with adding features?" if (-not $confirm) { return } Write-Host "" Write-Host "Adding selected features..." -ForegroundColor Gray try { Invoke-RJOperationWithRetry -Operation { if ($permissionMode -eq 'ReadOnly') { Update-RJTenant -AddFeatures $featuresToAdd -ReadOnly } else { Update-RJTenant -AddFeatures $featuresToAdd } } -OperationName "Add Features" -ShowProgress Write-Host "" Write-Host "Features added successfully." -ForegroundColor Green } catch { Write-Host "" Write-Host "ERROR: $_" -ForegroundColor Red } } } function Show-RJUpdateTenantRemoveFeature { param( [Parameter(Mandatory)] $CurrentConfig, [Parameter(Mandatory)] $AllFeatureDefinitions ) $removableConfiguredFeatures = $CurrentConfig.ConfiguredFeatures | Where-Object { -not $_.IsMandatory } if ($removableConfiguredFeatures.Count -eq 0) { Clear-Host Write-Host "Remove Features" -ForegroundColor White Write-Host ("-" * 50) -ForegroundColor DarkGray Write-Host "" Write-Host "No removable features found." -ForegroundColor Yellow Write-Host "Only optional features can be removed." -ForegroundColor Gray return } $removableFeatures = $AllFeatureDefinitions | Where-Object { $_.Name -in $removableConfiguredFeatures.Name } $selectedFeatures = Show-RJGenericMultiSelectMenu -Title "Remove Features" -Prompt "Select features to remove:`n`nWARNING: Removing features cannot be easily undone" -Items $removableFeatures -SelectedColor "Red" if ($null -eq $selectedFeatures) { return } if ($selectedFeatures -and $selectedFeatures.Count -gt 0) { Write-Host "Analyzing removal impact..." -ForegroundColor Gray $removalAnalysis = Get-RJTenantPermissionAnalysis -FeatureResolution (Resolve-RJFeature -CurrentConfig $CurrentConfig -FeaturesToRemove $selectedFeatures) $removalAnalysisToShow = $removalAnalysis.FeatureAnalysis | Where-Object { $_.FeatureName -in $selectedFeatures } Show-RJFeatureAnalysis -FeatureAnalysis $removalAnalysisToShow -OrphanedServicePrincipals $removalAnalysis.OrphanedServicePrincipals Write-Host "Press any key to continue..." -ForegroundColor Gray $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") Write-Host "" Write-Host "WARNING: Removing features cannot be easily undone" -ForegroundColor Red $confirm = Show-RJConfirmationMenu -Message "Proceed with removing selected features?" if (-not $confirm) { return } Write-Host "" Write-Host "Removing selected features..." -ForegroundColor Gray try { $currentPermissionMode = if ($CurrentConfig.ConfiguredFeatures | Where-Object { $_.PermissionMode -eq 'ReadOnly' }) { 'ReadOnly' } else { 'Full' } Invoke-RJOperationWithRetry -Operation { if ($currentPermissionMode -eq 'ReadOnly') { Update-RJTenant -RemoveFeatures $selectedFeatures -ReadOnly } else { Update-RJTenant -RemoveFeatures $selectedFeatures } } -OperationName "Remove Features" -ShowProgress Write-Host "" Write-Host "Features removed successfully:" -ForegroundColor Green foreach ($feature in $selectedFeatures) { Write-Host " * $feature" -ForegroundColor Green } } catch { Write-Host "" Write-Host "ERROR: $_" -ForegroundColor Red } } } function Show-RJUpdateTenantChangePermission { param( [Parameter(Mandatory)] $CurrentConfig ) $newPermissionMode = Show-RJGenericMenu -Title "Permission Mode" -Prompt "Select new permission mode:" -Options $PermissionModeOptions -AllowEscape if ($null -eq $newPermissionMode) { return } Write-Host "`nPlease wait..." -ForegroundColor Gray $allCurrentFeatures = $CurrentConfig.ConfiguredFeatures | ForEach-Object { $_.Name } $analysisResult = Get-RJTenantPermissionAnalysis -FeatureResolution (Resolve-RJFeature -CurrentConfig $CurrentConfig -SelectedFeatures $allCurrentFeatures -ReadOnly:($newPermissionMode -eq 'ReadOnly')) # Check if there are any actual changes to be made $hasChanges = $analysisResult.FeatureAnalysis | Where-Object { $_.FeatureChangeType -ne "NoChanges" } if (-not $hasChanges) { Write-Host "" Write-Host "No permission changes are required. The tenant is already configured with the selected permission mode." -ForegroundColor Yellow return } Show-RJFeatureAnalysis -FeatureAnalysis $analysisResult.FeatureAnalysis -OrphanedServicePrincipals $removalAnalysis.OrphanedServicePrincipals $confirm = Show-RJConfirmationMenu -Message "Proceed with permission changes?" if (-not $confirm) { return } Write-Host "" Write-Host "Updating permissions..." -ForegroundColor Gray Write-Host "" if ($newPermissionMode -eq 'ReadOnly') { Update-RJTenant -Features $allCurrentFeatures -ReadOnly } else { Update-RJTenant -Features $allCurrentFeatures } Write-Host "" Write-Host "Permissions updated successfully." -ForegroundColor Green } function Show-RJUpdateTenantRemoveLegacy { param( [Parameter(Mandatory)] $CurrentConfig ) Clear-Host Write-Host "Remove Legacy Applications" -ForegroundColor White Write-Host ("=" * 60) -ForegroundColor DarkGray Write-Host "" Write-Host "Legacy applications to remove:" -ForegroundColor Yellow Write-Host "" foreach ($app in $CurrentConfig.LegacyApplications) { Write-Host " * $($app.DisplayName)" -ForegroundColor Gray } $confirm = Show-RJConfirmationMenu -Message "Proceed with removing legacy applications?" if (-not $confirm) { return } Write-Host "" Write-Host "Removing legacy applications..." -ForegroundColor Gray $results = Remove-RJLegacyApplication if ($results.Success) { Write-Host "" Write-Host "Legacy applications removed successfully." -ForegroundColor Green } else { Write-Host "" Write-Host "Some legacy applications could not be removed." -ForegroundColor Yellow } } |