modules/HomeLab.UI/Public/Handlers/4-VpnClientHandler.ps1

<#
.SYNOPSIS
    VPN Client Menu Handler for HomeLab Setup
.DESCRIPTION
    Processes user selections in the VPN client menu using the new modular structure.
    Options include adding a computer to the VPN, connecting, disconnecting, and checking VPN connection status.
.PARAMETER ShowProgress
    If specified, shows a progress bar while loading the menu and performing operations.
.EXAMPLE
    Invoke-VpnClientMenu
.EXAMPLE
    Invoke-VpnClientMenu -ShowProgress
.NOTES
    Author: Jurie Smit
    Date: March 8, 2025
#>

function Invoke-VpnClientMenu {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false)]
        [switch]$ShowProgress
    )
    
    # Check if required functions exist
    $requiredFunctions = @(
        "Show-VpnClientMenu",
        "Get-Configuration",
        "Pause"
    )
    
    foreach ($function in $requiredFunctions) {
        if (-not (Get-Command -Name $function -ErrorAction SilentlyContinue)) {
            Write-Error "Required function '$function' not found. Make sure all required modules are imported."
            return
        }
    }
    
    # Check if logging is available
    $canLog = Get-Command -Name "Write-Log" -ErrorAction SilentlyContinue
    
    if ($canLog) {
        Write-Log -Message "Entering VPN Client Menu" -Level INFO
    }
    
    $selection = 0
    do {
        try {
            Show-VpnClientMenu -ShowProgress:$ShowProgress
        }
        catch {
            Write-Host "Error displaying VPN Client Menu: $_" -ForegroundColor Red
            if ($canLog) { Write-Log -Message "Error displaying VPN Client Menu: $_" -Level ERROR }
            break
        }
        
        $selection = Read-Host "Select an option"
        $config = Get-Configuration
        
        switch ($selection) {
            "1" {
                Write-Host "Adding computer to VPN..." -ForegroundColor Cyan
                if ($canLog) { Write-Log -Message "User selected: Add computer to VPN" -Level INFO }
                
                if ($ShowProgress) {
                    # Create a progress task for adding computer to VPN
                    $task = Start-ProgressTask -Activity "Adding Computer to VPN" -TotalSteps 3 -ScriptBlock {
                        # Step 1: Checking for required functions
                        $syncHash.Status = "Checking for required functions..."
                        $syncHash.CurrentStep = 1
                        
                        $useFunction = Get-Command VpnAddComputer -ErrorAction SilentlyContinue
                        
                        # Step 2: Preparing VPN configuration
                        $syncHash.Status = "Preparing VPN configuration..."
                        $syncHash.CurrentStep = 2
                        
                        # Step 3: Adding computer to VPN
                        $syncHash.Status = "Adding computer to VPN..."
                        $syncHash.CurrentStep = 3
                        
                        if ($useFunction) {
                            try {
                                VpnAddComputer
                                return @{
                                    Success = $true
                                    UsedFunction = $true
                                }
                            }
                            catch {
                                return @{
                                    Success = $false
                                    ErrorMessage = "Error adding computer to VPN: $_"
                                    UsedFunction = $true
                                }
                            }
                        }
                        else {
                            return @{
                                Success = $false;
                                UsedFunction = $false
                            }
                        }
                    }
                    
                    $result = $task.Complete()
                    
                    if ($result.Success) {
                        Write-Host "Computer added to VPN successfully." -ForegroundColor Green
                        if ($canLog) { Write-Log -Message "Computer added to VPN successfully" -Level INFO }
                    }
                    else {
                        if (-not $result.UsedFunction) {
                            Write-Host "Function VpnAddComputer not found. Make sure the required module is imported." -ForegroundColor Red
                            if ($canLog) { Write-Log -Message "Function VpnAddComputer not found" -Level ERROR }
                            
                            # Fallback to manual instructions
                            Write-Host "Manual steps to add computer to VPN:" -ForegroundColor Yellow
                            Write-Host "1. Extract the VPN client configuration ZIP file" -ForegroundColor White
                            Write-Host "2. Run the VPN client installer (usually in the WindowsAmd64 folder)" -ForegroundColor White
                            Write-Host "3. Follow the installation prompts" -ForegroundColor White
                            if ($canLog) { Write-Log -Message "Displayed manual VPN setup instructions" -Level INFO }
                        }
                        else {
                            Write-Host $result.ErrorMessage -ForegroundColor Red
                            if ($canLog) { Write-Log -Message $result.ErrorMessage -Level ERROR }
                        }
                    }
                }
                else {
                    # Original implementation without progress bar
                    # Assuming VpnAddComputer is defined in another module
                    if (Get-Command VpnAddComputer -ErrorAction SilentlyContinue) {
                        VpnAddComputer
                        if ($canLog) { Write-Log -Message "Called VpnAddComputer function" -Level INFO }
                    }
                    else {
                        Write-Host "Function VpnAddComputer not found. Make sure the required module is imported." -ForegroundColor Red
                        if ($canLog) { Write-Log -Message "Function VpnAddComputer not found" -Level ERROR }
                        
                        # Fallback to manual instructions
                        Write-Host "Manual steps to add computer to VPN:" -ForegroundColor Yellow
                        Write-Host "1. Extract the VPN client configuration ZIP file" -ForegroundColor White
                        Write-Host "2. Run the VPN client installer (usually in the WindowsAmd64 folder)" -ForegroundColor White
                        Write-Host "3. Follow the installation prompts" -ForegroundColor White
                        if ($canLog) { Write-Log -Message "Displayed manual VPN setup instructions" -Level INFO }
                    }
                }
                
                Pause
            }
            "2" {
                Write-Host "Connecting to VPN..." -ForegroundColor Cyan
                if ($canLog) { Write-Log -Message "User selected: Connect to VPN" -Level INFO }
                
                if ($ShowProgress) {
                    # Create a progress task for connecting to VPN
                    $task = Start-ProgressTask -Activity "Connecting to VPN" -TotalSteps 4 -ScriptBlock {
                        # Step 1: Checking for required functions
                        $syncHash.Status = "Checking for required functions..."
                        $syncHash.CurrentStep = 1
                        
                        $useFunction = Get-Command VpnConnectDisconnect -ErrorAction SilentlyContinue
                        
                        # Step 2: Finding VPN connection
                        $syncHash.Status = "Finding VPN connection..."
                        $syncHash.CurrentStep = 2
                        
                        $vpnName = "$($config.env)-$($config.project)-vpn"
                        $connections = Get-VpnConnection | Where-Object { $_.Name -like "*$($config.project)*" }
                        
                        # Step 3: Preparing connection
                        $syncHash.Status = "Preparing connection..."
                        $syncHash.CurrentStep = 3
                        
                        # Step 4: Connecting to VPN
                        $syncHash.Status = "Connecting to VPN..."
                        $syncHash.CurrentStep = 4
                        
                        if ($useFunction) {
                            try {
                                VpnConnectDisconnect -Connect
                                return @{
                                    Success = $true
                                    UsedFunction = $true
                                }
                            }
                            catch {
                                return @{
                                    Success = $false
                                    ErrorMessage = "Error connecting to VPN: $_"
                                    UsedFunction = $true
                                }
                            }
                        }
                        else {
                            if ($connections) {
                                $vpnName = $connections[0].Name
                                try {
                                    $connectResult = rasdial $vpnName 2>&1
                                    if ($LASTEXITCODE -eq 0) {
                                        return @{
                                            Success = $true
                                            UsedFunction = $false
                                            VpnName = $vpnName
                                        }
                                    }
                                    else {
                                        return @{
                                            Success = $false
                                            ErrorMessage = "Failed to connect to VPN: $connectResult"
                                            UsedFunction = $false
                                        }
                                    }
                                }
                                catch {
                                    return @{
                                        Success = $false
                                        ErrorMessage = "Error connecting to VPN: $_"
                                        UsedFunction = $false
                                    }
                                }
                            }
                            else {
                                return @{
                                    Success = $false
                                    ErrorMessage = "No VPN connections found for project $($config.project)."
                                    UsedFunction = $false
                                }
                            }
                        }
                    }
                    
                    $result = $task.Complete()
                    
                    if ($result.Success) {
                        if ($result.UsedFunction) {
                            Write-Host "Connected to VPN successfully." -ForegroundColor Green
                            if ($canLog) { Write-Log -Message "Connected to VPN successfully using VpnConnectDisconnect" -Level INFO }
                        }
                        else {
                            Write-Host "Connected to VPN '$($result.VpnName)' successfully." -ForegroundColor Green
                            if ($canLog) { Write-Log -Message "Connected to VPN '$($result.VpnName)' using rasdial" -Level INFO }
                        }
                    }
                    else {
                        if (-not $result.UsedFunction) {
                            Write-Host "Function VpnConnectDisconnect not found. Make sure the required module is imported." -ForegroundColor Red
                            if ($canLog) { Write-Log -Message "Function VpnConnectDisconnect not found" -Level ERROR }
                        }
                        Write-Host $result.ErrorMessage -ForegroundColor Red
                        if ($canLog) { Write-Log -Message $result.ErrorMessage -Level ERROR }
                    }
                }
                else {
                    # Original implementation without progress bar
                    # Assuming VpnConnectDisconnect is defined in another module
                    if (Get-Command VpnConnectDisconnect -ErrorAction SilentlyContinue) {
                        VpnConnectDisconnect -Connect
                        if ($canLog) { Write-Log -Message "Called VpnConnectDisconnect -Connect" -Level INFO }
                    }
                    else {
                        Write-Host "Function VpnConnectDisconnect not found. Make sure the required module is imported." -ForegroundColor Red
                        if ($canLog) { Write-Log -Message "Function VpnConnectDisconnect not found" -Level ERROR }
                        
                        # Fallback to direct PowerShell command
                        $vpnName = "$($config.env)-$($config.project)-vpn"
                        $connections = Get-VpnConnection | Where-Object { $_.Name -like "*$($config.project)*" }
                        
                        if ($connections) {
                            $vpnName = $connections[0].Name
                            Write-Host "Attempting to connect to VPN '$vpnName'..." -ForegroundColor Yellow
                            if ($canLog) { Write-Log -Message "Attempting to connect to VPN '$vpnName' using rasdial" -Level INFO }
                            rasdial $vpnName
                        }
                        else {
                            Write-Host "No VPN connections found for project $($config.project)." -ForegroundColor Red
                            if ($canLog) { Write-Log -Message "No VPN connections found for project $($config.project)" -Level ERROR }
                        }
                    }
                }
                
                Pause
            }
            "3" {
                Write-Host "Disconnecting from VPN..." -ForegroundColor Cyan
                if ($canLog) { Write-Log -Message "User selected: Disconnect from VPN" -Level INFO }
                
                if ($ShowProgress) {
                    # Create a progress task for disconnecting from VPN
                    $task = Start-ProgressTask -Activity "Disconnecting from VPN" -TotalSteps 3 -ScriptBlock {
                        # Step 1: Checking for required functions
                        $syncHash.Status = "Checking for required functions..."
                        $syncHash.CurrentStep = 1
                        
                        $useFunction = Get-Command VpnConnectDisconnect -ErrorAction SilentlyContinue
                        
                        # Step 2: Finding VPN connection
                        $syncHash.Status = "Finding VPN connection..."
                        $syncHash.CurrentStep = 2
                        
                        $vpnName = "$($config.env)-$($config.project)-vpn"
                        $connections = Get-VpnConnection | Where-Object { $_.Name -like "*$($config.project)*" }
                        
                        # Step 3: Disconnecting from VPN
                        $syncHash.Status = "Disconnecting from VPN..."
                        $syncHash.CurrentStep = 3
                        
                        if ($useFunction) {
                            try {
                                VpnConnectDisconnect -Disconnect
                                return @{
                                    Success = $true
                                    UsedFunction = $true
                                }
                            }
                            catch {
                                return @{
                                    Success = $false
                                    ErrorMessage = "Error disconnecting from VPN: $_"
                                    UsedFunction = $true
                                }
                            }
                        }
                        else {
                            if ($connections) {
                                $vpnName = $connections[0].Name
                                try {
                                    $disconnectResult = rasdial $vpnName /disconnect 2>&1
                                    return @{
                                        Success = $true
                                        UsedFunction = $false
                                        VpnName = $vpnName
                                    }
                                }
                                catch {
                                    return @{
                                        Success = $false
                                        ErrorMessage = "Error disconnecting from VPN: $_"
                                        UsedFunction = $false
                                    }
                                }
                            }
                            else {
                                return @{
                                    Success = $false
                                    ErrorMessage = "No VPN connections found for project $($config.project)."
                                    UsedFunction = $false
                                }
                            }
                        }
                    }
                    
                    $result = $task.Complete()
                    
                    if ($result.Success) {
                        if ($result.UsedFunction) {
                            Write-Host "Disconnected from VPN successfully." -ForegroundColor Green
                            if ($canLog) { Write-Log -Message "Disconnected from VPN successfully using VpnConnectDisconnect" -Level INFO }
                        }
                        else {
                            Write-Host "Disconnected from VPN '$($result.VpnName)' successfully." -ForegroundColor Green
                            if ($canLog) { Write-Log -Message "Disconnected from VPN '$($result.VpnName)' using rasdial" -Level INFO }
                        }
                    }
                    else {
                        if (-not $result.UsedFunction) {
                            Write-Host "Function VpnConnectDisconnect not found. Make sure the required module is imported." -ForegroundColor Red
                            if ($canLog) { Write-Log -Message "Function VpnConnectDisconnect not found" -Level ERROR }
                        }
                        Write-Host $result.ErrorMessage -ForegroundColor Red
                        if ($canLog) { Write-Log -Message $result.ErrorMessage -Level ERROR }
                    }
                }
                else {
                    # Original implementation without progress bar
                    # Assuming VpnConnectDisconnect is defined in another module
                    if (Get-Command VpnConnectDisconnect -ErrorAction SilentlyContinue) {
                        VpnConnectDisconnect -Disconnect
                        if ($canLog) { Write-Log -Message "Called VpnConnectDisconnect -Disconnect" -Level INFO }
                    }
                    else {
                        Write-Host "Function VpnConnectDisconnect not found. Make sure the required module is imported." -ForegroundColor Red
                        if ($canLog) { Write-Log -Message "Function VpnConnectDisconnect not found" -Level ERROR }
                        
                        # Fallback to direct PowerShell command
                        $vpnName = "$($config.env)-$($config.project)-vpn"
                        $connections = Get-VpnConnection | Where-Object { $_.Name -like "*$($config.project)*" }
                        
                        if ($connections) {
                            $vpnName = $connections[0].Name
                            Write-Host "Attempting to disconnect from VPN '$vpnName'..." -ForegroundColor Yellow
                            if ($canLog) { Write-Log -Message "Attempting to disconnect from VPN '$vpnName' using rasdial" -Level INFO }
                            rasdial $vpnName /disconnect
                        }
                        else {
                            Write-Host "No VPN connections found for project $($config.project)." -ForegroundColor Red
                            if ($canLog) { Write-Log -Message "No VPN connections found for project $($config.project)" -Level ERROR }
                        }
                    }
                }
                
                Pause
            }
            "4" {
                Write-Host "Checking VPN connection status..." -ForegroundColor Cyan
                if ($canLog) { Write-Log -Message "User selected: Check VPN connection status" -Level INFO }
                
                if ($ShowProgress) {
                    # Create a progress task for checking VPN status
                    $task = Start-ProgressTask -Activity "Checking VPN Connection Status" -TotalSteps 2 -ScriptBlock {
                        # Step 1: Finding VPN connections
                        $syncHash.Status = "Finding VPN connections..."
                        $syncHash.CurrentStep = 1
                        
                        # Step 2: Retrieving connection details
                        $syncHash.Status = "Retrieving connection details..."
                        $syncHash.CurrentStep = 2
                        
                        $connections = Get-VpnConnection | Where-Object { $_.Name -like "*$($config.project)*" }
                        
                        return @{
                            Connections = $connections
                        }
                    }
                    
                    $result = $task.Complete()
                    
                    if ($result.Connections -and $result.Connections.Count -gt 0) {
                        $result.Connections | Format-Table -Property Name, ServerAddress, ConnectionStatus, AuthenticationMethod
                        if ($canLog) { Write-Log -Message "Found $($result.Connections.Count) VPN connections" -Level INFO }
                    }
                    else {
                        Write-Host "No VPN connections found for project $($config.project)." -ForegroundColor Yellow
                        if ($canLog) { Write-Log -Message "No VPN connections found for project $($config.project)" -Level INFO }
                    }
                }
                else {
                    # Original implementation without progress bar
                    $connections = Get-VpnConnection | Where-Object { $_.Name -like "*$($config.project)*" }
                    if ($connections) {
                        $connections | Format-Table -Property Name, ServerAddress, ConnectionStatus, AuthenticationMethod
                        if ($canLog) { Write-Log -Message "Found $($connections.Count) VPN connections" -Level INFO }
                    }
                    else {
                        Write-Host "No VPN connections found for project $($config.project)." -ForegroundColor Yellow
                        if ($canLog) { Write-Log -Message "No VPN connections found for project $($config.project)" -Level INFO }
                    }
                }
                
                Pause
            }
            "0" {
                # Return to main menu
                if ($canLog) { Write-Log -Message "User exited VPN Client Menu" -Level INFO }
            }
            default {
                Write-Host "Invalid option. Please try again." -ForegroundColor Red
                if ($canLog) { Write-Log -Message "User selected invalid option: $selection" -Level Warning }
                Start-Sleep -Seconds 2
            }
        }
    } while ($selection -ne "0")
}