Public/Select-Office365Modules.ps1
|
function Select-Office365Modules { <# .SYNOPSIS Interactive module selection menu for Office 365 modules. .DESCRIPTION Provides a text-based interactive menu to select/deselect Office 365 modules. Navigate with Up/Down arrow keys, toggle selection with Space, commit with Enter. After selection, installs new modules and uninstalls deselected modules. .PARAMETER AllowPrerelease Allow installation of prerelease modules. .EXAMPLE Select-Office365Modules Opens interactive module selection menu. .EXAMPLE Select-Office365Modules -AllowPrerelease Opens interactive module selection menu with prerelease support. #> [CmdletBinding()] param( [switch]$AllowPrerelease ) # Check if running as administrator $local:IsAdmin = Test-IsAdministrator if (-not $local:IsAdmin) { Write-Warning 'Script not running with elevated privileges; module installation/uninstallation may fail' $continue = Read-Host "Continue anyway? (y/N)" if ($continue -notmatch '^[Yy]') { return } } $script:myOffice365Services['AllowPrerelease'] = if ($PSBoundParameters.ContainsKey('AllowPrerelease')) { $AllowPrerelease.IsPresent } else { [bool]$script:myOffice365Services['AllowPrerelease'] } # Get module information $local:ModuleInfo = Get-Office365ModuleInfo $local:CurrentSelection = @{} $local:SelectedIndex = 0 $local:MaxIndex = $local:ModuleInfo.Count - 1 # Initialize current selection based on installed modules $local:AllInstalled = Get-Module -ListAvailable -ErrorAction SilentlyContinue foreach ($module in $local:ModuleInfo) { $installedModule = Get-InstalledRepoModule -Name $module.Module -Repo $module.Repo -AllInstalled $local:AllInstalled $local:CurrentSelection[$module.Module] = $null -ne $installedModule } # Display single-column menu function Show-ModuleMenu { param($ModuleInfo, $CurrentSelection, $SelectedIndex) Write-Host 'Module Selection' Write-Host ('-' * 50) Write-Host ('Active Scope: {0,-12}' -f $script:myOffice365Services['Scope']) Write-Host '' for ($i = 0; $i -lt $ModuleInfo.Count; $i++) { $module = $ModuleInfo[$i] $isSelected = $CurrentSelection[$module.Module] $isReadOnly = $module.ReplacedBy -and -not $isSelected $checkbox = if ($isReadOnly) { ' - ' } elseif ($isSelected) { '[x]' } else { '[ ]' } $prefix = if ($i -eq $SelectedIndex) { '>' } else { ' ' } $replacedBySuffix = if ($module.ReplacedBy) { ' (Replaced by: {0})' -f $module.ReplacedBy } else { '' } $line = '{0} {1} {2}{3}' -f $prefix, $checkbox, $module.Description, $replacedBySuffix if ($i -eq $SelectedIndex) { Write-Host $line -ForegroundColor White } else { Write-Host $line } } $local:selectedCount = ($CurrentSelection.Values | Where-Object { $_ } | Measure-Object).Count Write-Host '' Write-Host ('Selected: {0}/{1}' -f $local:selectedCount, $ModuleInfo.Count) Write-Host '' Write-Host 'Up/Down: navigate Space: toggle S: scope Enter: confirm Esc: cancel' } # Compute menu line count for in-place redraw (9 fixed lines + 1 per module) $local:menuLineCount = 9 + $local:ModuleInfo.Count $local:menuTopRow = -1 # Main menu loop $exitMenu = $false $committed = $false while (-not $exitMenu) { if ($local:menuTopRow -ge 0) { $Host.UI.RawUI.CursorPosition = [System.Management.Automation.Host.Coordinates]::new(0, $local:menuTopRow) } Show-ModuleMenu -ModuleInfo $local:ModuleInfo -CurrentSelection $local:CurrentSelection -SelectedIndex $local:SelectedIndex if ($local:menuTopRow -lt 0) { $local:menuTopRow = $Host.UI.RawUI.CursorPosition.Y - $local:menuLineCount } $key = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown') switch ($key.VirtualKeyCode) { 38 { # Up arrow $local:next = $local:SelectedIndex - 1 while ($local:next -ge 0 -and $local:ModuleInfo[$local:next].ReplacedBy -and -not $local:CurrentSelection[$local:ModuleInfo[$local:next].Module]) { $local:next-- } if ($local:next -ge 0) { $local:SelectedIndex = $local:next } } 40 { # Down arrow $local:next = $local:SelectedIndex + 1 while ($local:next -le $local:MaxIndex -and $local:ModuleInfo[$local:next].ReplacedBy -and -not $local:CurrentSelection[$local:ModuleInfo[$local:next].Module]) { $local:next++ } if ($local:next -le $local:MaxIndex) { $local:SelectedIndex = $local:next } } 32 { # Spacebar — toggle selection $currentModuleInfo = $local:ModuleInfo[$local:SelectedIndex] # Deprecated modules (ReplacedBy) can only be deselected when installed; cannot be installed fresh if ($currentModuleInfo.ReplacedBy -and -not $local:CurrentSelection[$currentModuleInfo.Module]) { # Not installed and deprecated — do nothing } else { $local:CurrentSelection[$currentModuleInfo.Module] = -not $local:CurrentSelection[$currentModuleInfo.Module] } } 83 { # S — toggle scope if ($script:myOffice365Services['Scope'] -eq 'AllUsers') { $script:myOffice365Services['Scope'] = 'CurrentUser' } else { $script:myOffice365Services['Scope'] = 'AllUsers' } Save-Office365ServicesPreferences } 13 { # Enter — commit $exitMenu = $true $committed = $true } 27 { # Escape — cancel $exitMenu = $true $committed = $false } } } if (-not $committed) { Write-Host 'Operation cancelled.' return } # Process changes Write-Host 'Processing module changes...' Write-Host '' $modulesToInstall = @() $modulesToUninstall = @() $local:AllInstalled = Get-Module -ListAvailable -ErrorAction SilentlyContinue foreach ($module in $local:ModuleInfo) { $moduleName = $module.Module $shouldBeInstalled = $local:CurrentSelection[$moduleName] $installedModule = Get-InstalledRepoModule -Name $moduleName -Repo $module.Repo -AllInstalled $local:AllInstalled $isCurrentlyInstalled = $null -ne $installedModule if ($shouldBeInstalled -and -not $isCurrentlyInstalled) { $modulesToInstall += $module } elseif (-not $shouldBeInstalled -and $isCurrentlyInstalled) { $modulesToUninstall += @{ Module = $module; InstalledModule = $installedModule } } } # Install new modules foreach ($module in $modulesToInstall) { Write-Host ('Installing {0}...' -f $module.Description) try { Install-myModule -Name $module.Module -AllowPrerelease:([bool]$script:myOffice365Services['AllowPrerelease']) -AllowClobber Write-Host (' Installed {0}' -f $module.Module) } catch { Write-Error ('Failed to install {0}: {1}' -f $module.Module, $_.Exception.Message) } } # Uninstall removed modules foreach ($moduleInfo in $modulesToUninstall) { $module = $moduleInfo.Module Write-Host ('Uninstalling {0}...' -f $module.Description) try { $allVersions = Get-Module -Name $module.Module -ListAvailable | Where-Object { $_.RepositorySourceLocation -and ([System.Uri]($_.RepositorySourceLocation)).Authority -ieq ([System.Uri]($module.Repo)).Authority } foreach ($version in $allVersions) { Uninstall-myModule -Name $version.Name -Version $version.Version -IsPrerelease:$version.IsPrerelease } Write-Host (' Uninstalled {0}' -f $module.Module) } catch { Write-Error ('Failed to uninstall {0}: {1}' -f $module.Module, $_.Exception.Message) } } if ($modulesToInstall.Count -eq 0 -and $modulesToUninstall.Count -eq 0) { Write-Host 'No changes were made.' } else { Write-Host '' Write-Host ('Done. Installed: {0}, Uninstalled: {1}' -f $modulesToInstall.Count, $modulesToUninstall.Count) } } |