Scf.Platform.psm1

#Requires -Version 5.1

<#
.SYNOPSIS
    Smart Cloud Framework (SCF) Platform Module
.DESCRIPTION
    Framework abstraction layer for Azure operations with configuration-driven service management.
    All operations are scoped to the framework, leveraging deployment patterns to eliminate complexity.
.NOTES
    Version: 2.0.0
    Author: SCF Team
    Framework Doctrine: Short Commands -> SCF Cmdlets + SCF Config -> Azure API/Cmdlets
#>


# Module variables
$script:PlatformContext = $null
$script:FrameworkConfig = $null

#region Framework Configuration

<#
.SYNOPSIS
    Loads the framework configuration from file or creates default structure.

.DESCRIPTION
    Loads the SCF framework configuration that defines platforms, regions, services,
    and deployment patterns. This configuration drives all framework operations.

.EXAMPLE
    Initialize-ScfFramework
#>

function Initialize-ScfFramework {
    [CmdletBinding()]
    param()

    try {
        $configPath = Join-Path $PSScriptRoot "scf-framework-config.json"
        
        if (Test-Path $configPath) {
            Write-Verbose "Loading existing framework configuration from: $configPath"
            $script:FrameworkConfig = Get-Content $configPath | ConvertFrom-Json
        }
        else {
            Write-Verbose "Creating default framework configuration"
            $script:FrameworkConfig = @{
                platforms = @{
                    "framework-tenant-id" = @{
                        regions = @{
                            "USE" = "eastus2"
                            "EUW" = "westeurope"
                            "USW" = "westus2"
                            "APS" = "australiasoutheast"
                        }
                        services = @{
                            "BST" = @{
                                type = "bastion"
                                resourceGroup = "framework-bastion-rg"
                                deploymentPattern = "standard"
                                description = "Bastion Service"
                            }
                            "NTR" = @{
                                type = "network"
                                resourceGroup = "framework-network-rg"
                                deploymentPattern = "standard"
                                description = "Network Service"
                            }
                            "APP" = @{
                                type = "application"
                                resourceGroup = "framework-app-rg"
                                deploymentPattern = "standard"
                                description = "Application Service"
                            }
                        }
                    }
                }
            }
            
            # Save default configuration
            $script:FrameworkConfig | ConvertTo-Json -Depth 10 | Set-Content $configPath
            Write-Host "Default framework configuration created at: $configPath" -ForegroundColor Green
        }
        
        Write-Verbose "Framework configuration loaded successfully"
        return $script:FrameworkConfig
    }
    catch {
        Write-Error "Failed to initialize framework configuration: $($_.Exception.Message)"
        throw
    }
}

#endregion

#region Connect-ScfPlatform

<#
.SYNOPSIS
    Connects to the SCF framework platform.

.DESCRIPTION
    Connects to the SCF framework platform using the provided platform ID.
    Subscription management is abstracted away by the framework configuration.

.PARAMETER PlatformId
    The framework platform identifier.

.EXAMPLE
    Connect-ScfPlatform -PlatformId 'framework-tenant-id'
#>

function Connect-ScfPlatform {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false)]
        [string]$PlatformId
    )

    try {
        # Initialize framework configuration
        Initialize-ScfFramework
        
        # If no PlatformId provided, handle platform selection
        if (-not $PlatformId) {
            # Get available platforms from the configuration
            $availablePlatforms = @()
            
            # Handle PSCustomObject (from JSON) - get property names
            if ($script:FrameworkConfig.platforms -is [PSCustomObject]) {
                $availablePlatforms = $script:FrameworkConfig.platforms.PSObject.Properties.Name
            }
            # Handle hashtable (default configuration)
            elseif ($script:FrameworkConfig.platforms -is [System.Collections.IDictionary]) {
                $availablePlatforms = $script:FrameworkConfig.platforms.Keys
            }
            # Fallback - try to get keys as strings
            else {
                $availablePlatforms = $script:FrameworkConfig.platforms | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name
            }
            
            Write-Verbose "Available platforms: $($availablePlatforms -join ', ')"
            
            if ($availablePlatforms.Count -eq 0) {
                throw "No platforms configured in framework configuration"
            }
            elseif ($availablePlatforms.Count -eq 1) {
                $PlatformId = $availablePlatforms | Select-Object -First 1
                Write-Host "Auto-selected platform: $PlatformId" -ForegroundColor Green
            }
            else {
                Write-Host "Multiple platforms available in configuration:" -ForegroundColor Yellow
                for ($i = 0; $i -lt $availablePlatforms.Count; $i++) {
                    Write-Host " $($i + 1). $($availablePlatforms[$i])" -ForegroundColor White
                }
                
                do {
                    $selection = Read-Host "`nSelect platform (1-$($availablePlatforms.Count)) or enter platform ID"
                    
                    # Check if user entered a number
                    if ($selection -match '^\d+$') {
                        $index = [int]$selection - 1
                        if ($index -ge 0 -and $index -lt $availablePlatforms.Count) {
                            $PlatformId = $availablePlatforms[$index]
                            break
                        }
                    }
                    # Check if user entered a platform ID directly
                    elseif ($availablePlatforms -contains $selection) {
                        $PlatformId = $selection
                        break
                    }
                    
                    Write-Host "Invalid selection. Please try again." -ForegroundColor Red
                } while ($true)
            }
        }
        
        Write-Verbose "Connecting to SCF framework platform: $PlatformId"
        
        # Validate platform exists in configuration
        if (-not $script:FrameworkConfig.platforms.$PlatformId) {
            throw "Platform '$PlatformId' not found in framework configuration"
        }
        
        # Connect to Azure (subscription abstracted)
        Write-Verbose "Authenticating with Azure platform..."
        
        try {
            # Interactive user authentication (browser-based)
            Write-Verbose "Using interactive user authentication..."
            
            $connectParams = @{
                TenantId = $PlatformId
            }
            
            $connection = Connect-AzAccount @connectParams -ErrorAction Stop
            
            # Get tenant information
            $tenantInfo = Get-AzTenant -ErrorAction SilentlyContinue
            
            if ($tenantInfo -and $tenantInfo.Count -gt 0) {
                $currentTenant = $tenantInfo | Where-Object { $_.TenantId -eq $PlatformId } | Select-Object -First 1
                if (-not $currentTenant) {
                    $currentTenant = $tenantInfo | Select-Object -First 1
                }
                
                $displayName = if ($currentTenant.DisplayName) { $currentTenant.DisplayName } else { "SCF Framework" }
                $platformCode = if ($displayName -ne "SCF Framework" -and $displayName.Length -ge 3) { 
                    ($displayName -replace '\s+', '').Substring(0, [Math]::Min(3, ($displayName -replace '\s+', '').Length)).ToUpper() 
                } else { 
                    "SCF" 
                }
                
                $platformInfo = [PSCustomObject]@{
                    Platform = $displayName
                    PlatformCode = $platformCode
                    PlatformId = $PlatformId
                    PlatformType = $script:FrameworkConfig.platforms.$PlatformId.platform
                    PlatformName = $script:FrameworkConfig.platforms.$PlatformId.platformName
                    TenantId = $currentTenant.TenantId
                    Domain = if ($currentTenant.DefaultDomain) { $currentTenant.DefaultDomain } else { "Unknown" }
                }
            }
            else {
                $platformInfo = [PSCustomObject]@{
                    Platform = "SCF Framework"
                    PlatformCode = "SCF"
                    PlatformId = $PlatformId
                    TenantId = $PlatformId
                    Domain = "Unknown"
                }
            }
            
            # Update platform context
            $script:PlatformContext = [PSCustomObject]@{
                PlatformId = $PlatformId
                Credential = $null
                IsServicePrincipal = $false
                ConnectedAt = Get-Date
                ConnectionId = [System.Guid]::NewGuid().ToString()
                TenantInfo = $tenantInfo
                AzureConnection = $connection
                FrameworkConfig = $script:FrameworkConfig.platforms.$PlatformId
            }
            
            Write-Output $platformInfo
            Write-Verbose "Successfully connected to SCF framework platform"
            
            # Show helpful suggestions for next steps
            Write-Host ""
            Write-Host "=== What's Next? ===" -ForegroundColor Cyan
            Write-Host "Available Commands:" -ForegroundColor Green
            Write-Host " • Get-ScfServices -Region USE # List available services" -ForegroundColor White
            Write-Host " • Connect-ScfService -Name BST -Region USE -Credential `$credential" -ForegroundColor White
            Write-Host " • Disconnect-ScfPlatform # Disconnect when done" -ForegroundColor White
            Write-Host ""
            Write-Host "Quick Start Examples:" -ForegroundColor Green
            Write-Host " # List services in different regions" -ForegroundColor Gray
            Write-Host " Get-ScfServices -Region USE" -ForegroundColor Cyan
            Write-Host " Get-ScfServices -Region EUW" -ForegroundColor Cyan
            Write-Host ""
            Write-Host " # Connect to Bastion service (choose authentication method)" -ForegroundColor Gray
            Write-Host " Connect-ScfService -Name BST -Region USE" -ForegroundColor Cyan
            Write-Host ""
            Write-Host " # Connect to Network service (choose authentication method)" -ForegroundColor Gray
            Write-Host " Connect-ScfService -Name NTR -Region USE" -ForegroundColor Cyan
            Write-Host ""
            
            return $script:PlatformContext
        }
        catch {
            Write-Error "Authentication failed: $($_.Exception.Message)"
            throw
        }
    }
    catch {
        Write-Error "Failed to connect to SCF framework platform: $($_.Exception.Message)"
        throw
    }
}

#endregion

#region Connect-ScfService

<#
.SYNOPSIS
    Connects to a framework service in a specific region.

.DESCRIPTION
    Connects to a framework service using the configuration-driven approach.
    Service details are retrieved from framework configuration.

.PARAMETER Name
    The service name (e.g., BST, NTR, APP).

.PARAMETER Region
    The framework region code (e.g., USE, EUW, USW).

.PARAMETER Credential
    The credential for service connection.

.EXAMPLE
    Connect-ScfService -Name BST -Region USE -Credential $credential
#>

function Connect-ScfService {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name,
        
        [Parameter(Mandatory = $true)]
        [string]$Region,
        
        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]$Credential
    )

    # Check if we're connected to framework
    if (-not $script:PlatformContext) {
        throw "Not connected to SCF framework. Please run Connect-ScfPlatform first."
    }

    try {
        Write-Host "Connecting to SCF service: $Name in region: $Region" -ForegroundColor Green
        
        # Get framework configuration
        $frameworkConfig = $script:PlatformContext.FrameworkConfig
        
        # Validate service exists
        if (-not $frameworkConfig.services.$Name) {
            throw "Service '$Name' not found in framework configuration"
        }
        
        # Validate region exists
        if (-not $frameworkConfig.regions.$Region) {
            throw "Region '$Region' not found in framework configuration"
        }
        
        $service = $frameworkConfig.services.$Name
        $azureRegion = $frameworkConfig.regions.$Region
        
        Write-Host "Service Details:" -ForegroundColor Cyan
        Write-Host " Name: $Name" -ForegroundColor White
        Write-Host " Type: $($service.type)" -ForegroundColor White
        Write-Host " Region: $Region ($azureRegion)" -ForegroundColor White
        Write-Host " Resource Group: $($service.resourceGroup)" -ForegroundColor White
        Write-Host " Description: $($service.description)" -ForegroundColor White
        
        # Handle different service types
        switch ($service.type.ToLower()) {
            "bastion" {
                Write-Host "`nConnecting to Bastion service..." -ForegroundColor Green
                Write-Host "Note: Bastion is a gateway service. You'll need VM credentials to connect to target VMs." -ForegroundColor Cyan
                
                # No credential prompt needed - SSH/RDP will handle authentication
                Write-Host "`nNote: VM authentication will be handled when you connect via SSH/RDP" -ForegroundColor Cyan
                
                return Connect-ScfBastionService -Name $Name -Region $Region -AzureRegion $azureRegion -Service $service
            }
            "network" {
                Write-Host "`nConnecting to Network service..." -ForegroundColor Green
                Write-Host "Note: Network service provides VM management and direct connections." -ForegroundColor Cyan
                
                # Prompt for credential if not provided
                if (-not $Credential) {
                    Write-Host "`nVM Authentication Options:" -ForegroundColor Yellow
                    Write-Host " Most enterprise environments use Azure/AD credentials for VM access." -ForegroundColor White
                    Write-Host " Some deployments use unique local admin accounts per VM." -ForegroundColor White
                    Write-Host ""
                    Write-Host " Choose authentication method:" -ForegroundColor Cyan
                    Write-Host " 1. Use Azure/AD credentials (recommended for enterprise)" -ForegroundColor Green
                    Write-Host " 2. Use local VM credentials (for emergency access)" -ForegroundColor Yellow
                    Write-Host ""
                    
                    do {
                        $choice = Read-Host "Select option (1 or 2)"
                        if ($choice -eq "1") {
                            Write-Host "Using Azure/AD credentials for VM access" -ForegroundColor Green
                            Write-Host " You'll use your current Azure login for VM connections." -ForegroundColor White
                            $Credential = $null  # No local credential needed
                            break
                        }
                        elseif ($choice -eq "2") {
                            Write-Host "Using local VM credentials (emergency access)" -ForegroundColor Yellow
                            Write-Host " Note: Each VM may have different local admin credentials." -ForegroundColor White
                            Write-Host " Common usernames: admin, azureuser, or custom username" -ForegroundColor Gray
                            Write-Host ""
                            $Credential = Get-Credential -Message "Enter local VM credentials for Network service VMs"
                            break
                        }
                        else {
                            Write-Host "Invalid choice. Please enter 1 or 2." -ForegroundColor Red
                        }
                    } while ($true)
                }
                
                return Connect-ScfNetworkService -Name $Name -Region $Region -AzureRegion $azureRegion -Service $service -Credential $Credential
            }
            "application" {
                Write-Host "`nConnecting to Application service..." -ForegroundColor Green
                Write-Host "Note: Application service provides web app and container access." -ForegroundColor Cyan
                
                # Prompt for credential if not provided
                if (-not $Credential) {
                    Write-Host "`nApplication Credentials Required:" -ForegroundColor Yellow
                    Write-Host " You may need credentials for web apps or containers." -ForegroundColor White
                    Write-Host " This varies by application deployment." -ForegroundColor Gray
                    Write-Host ""
                    $Credential = Get-Credential -Message "Enter application credentials (if required)"
                }
                
                return Connect-ScfApplicationService -Name $Name -Region $Region -AzureRegion $azureRegion -Service $service -Credential $Credential
            }
            default {
                throw "Unsupported service type: $($service.type)"
            }
        }
    }
    catch {
        Write-Error "Failed to connect to SCF service: $($_.Exception.Message)"
        throw
    }
}

#endregion

#region Service-Specific Connection Functions

function Connect-ScfBastionService {
    [CmdletBinding()]
    param(
        [string]$Name,
        [string]$Region,
        [string]$AzureRegion,
        [object]$Service
    )
    
    try {
        Write-Host "Discovering Bastion hosts in $AzureRegion..." -ForegroundColor Yellow
        
        # Find Bastion hosts in the region
        $bastions = Get-AzBastion -ErrorAction SilentlyContinue | Where-Object { $_.Location -eq $AzureRegion }
        
        if (-not $bastions) {
            throw "No Bastion hosts found in region: $AzureRegion"
        }
        
        # Use the first Bastion found (framework assumption)
        $bastion = $bastions | Select-Object -First 1
        
        Write-Host "Found Bastion host: $($bastion.Name)" -ForegroundColor Green
        Write-Host "Resource Group: $($bastion.ResourceGroupName)" -ForegroundColor Cyan
        
        # Get Bastion URL
        $bastionUrl = if ($bastion.DnsName) {
            "https://$($bastion.DnsName)"
        } else {
            throw "Bastion host does not have DNS name configured"
        }
        
        Write-Host "`nDiscovering VMs accessible through Bastion..." -ForegroundColor Yellow
        
        # Discover VMs in the region that can be accessed through this Bastion
        $vms = Get-AzVM -ErrorAction SilentlyContinue | Where-Object { 
            $_.Location -eq $AzureRegion -and 
            $_.ResourceGroupName -eq $bastion.ResourceGroupName
        }
        
        # Auto-add discovered VMs to configuration (simplified for now)
        if ($vms) {
            Write-Host "`nAuto-discovery complete!" -ForegroundColor Green
            Write-Host "Found $($vms.Count) VM(s) in resource group: $($bastion.ResourceGroupName)" -ForegroundColor Cyan
            Write-Host "Note: Auto-configuration will be added in a future update" -ForegroundColor Yellow
        }
        
        if ($vms) {
            Write-Host "`nAvailable Resources in ${AzureRegion}:" -ForegroundColor Green
            Write-Host "=" * 50 -ForegroundColor Gray
            
            foreach ($vm in $vms) {
                $powerState = (Get-AzVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name -Status -ErrorAction SilentlyContinue).Statuses[1].DisplayStatus
                $statusColor = if ($powerState -eq "VM running") { "Green" } else { "Yellow" }
                
                Write-Host "`nVM: $($vm.Name)" -ForegroundColor White
                Write-Host " Status: $powerState" -ForegroundColor $statusColor
                Write-Host " Size: $($vm.HardwareProfile.VmSize)" -ForegroundColor Cyan
                Write-Host " OS: $($vm.StorageProfile.OsDisk.OsType)" -ForegroundColor Cyan
                Write-Host " Resource Group: $($vm.ResourceGroupName)" -ForegroundColor Gray
                Write-Host " Command: Connect-ScfVM -Name '$($vm.Name)' -Region '$Region'" -ForegroundColor Green
            }
            
            Write-Host "`n" + "=" * 50 -ForegroundColor Gray
            Write-Host "Quick Actions:" -ForegroundColor Yellow
            Write-Host " • Connect to any VM: Use the Connect-ScfVM command above" -ForegroundColor White
            Write-Host " • Manual access: $bastionUrl" -ForegroundColor Gray
        } else {
            Write-Host "`nNo VMs found in region ${AzureRegion}" -ForegroundColor Yellow
            Write-Host "Check if VMs exist in resource group: $($bastion.ResourceGroupName)" -ForegroundColor Gray
        }
        
        Write-Host "`nBastion service discovery complete!" -ForegroundColor Green
        
        return [PSCustomObject]@{
            ServiceName = $Name
            Region = $Region
            AzureRegion = $AzureRegion
            ServiceType = "Bastion"
            BastionName = $bastion.Name
            ResourceGroup = $bastion.ResourceGroupName
            Url = $bastionUrl
            VMs = $vms
            VMCount = if ($vms) { $vms.Count } else { 0 }
            ConnectedAt = Get-Date
            Status = "Discovered"
        }
    }
    catch {
        Write-Error "Failed to connect to Bastion service: $($_.Exception.Message)"
        throw
    }
}

function Connect-ScfNetworkService {
    [CmdletBinding()]
    param(
        [string]$Name,
        [string]$Region,
        [string]$AzureRegion,
        [object]$Service,
        [PSCredential]$Credential
    )
    
    try {
        Write-Host "Discovering Network service resources in $AzureRegion..." -ForegroundColor Yellow
        
        # Find VMs in the framework resource group
        $vms = Get-AzVM -ResourceGroupName $Service.resourceGroup -ErrorAction SilentlyContinue | Where-Object { $_.Location -eq $AzureRegion }
        
        if (-not $vms) {
            Write-Host "No VMs found in Network service resource group: $($Service.resourceGroup)" -ForegroundColor Yellow
            Write-Host "Available VMs in region:" -ForegroundColor Cyan
            
            # List all VMs in the region for reference
            $allVMs = Get-AzVM -ErrorAction SilentlyContinue | Where-Object { $_.Location -eq $AzureRegion }
            if ($allVMs) {
                foreach ($vm in $allVMs) {
                    $statusColor = if ($vm.PowerState -eq "VM running") { "Green" } else { "Red" }
                    Write-Host " $($vm.Name) ($($vm.ResourceGroupName)) - $($vm.PowerState)" -ForegroundColor $statusColor
                }
            }
            else {
                Write-Host " No VMs found in region: $AzureRegion" -ForegroundColor Gray
            }
        }
        else {
            Write-Host "Found VMs in Network service:" -ForegroundColor Green
            foreach ($vm in $vms) {
                $statusColor = if ($vm.PowerState -eq "VM running") { "Green" } else { "Red" }
                Write-Host " $($vm.Name) - $($vm.PowerState)" -ForegroundColor $statusColor
            }
        }
        
        Write-Host "`nNetwork service connection established!" -ForegroundColor Green
        Write-Host "Use Azure Bastion or direct connection methods to access VMs" -ForegroundColor Cyan
        
        return [PSCustomObject]@{
            ServiceName = $Name
            Region = $Region
            AzureRegion = $AzureRegion
            ServiceType = "Network"
            ResourceGroup = $Service.resourceGroup
            VMs = $vms
            ConnectedAt = Get-Date
            Status = "Connected"
        }
    }
    catch {
        Write-Error "Failed to connect to Network service: $($_.Exception.Message)"
        throw
    }
}

function Connect-ScfApplicationService {
    [CmdletBinding()]
    param(
        [string]$Name,
        [string]$Region,
        [string]$AzureRegion,
        [object]$Service,
        [PSCredential]$Credential
    )
    
    try {
        Write-Host "Discovering Application service resources in $AzureRegion..." -ForegroundColor Yellow
        
        # Find web apps or containers in the framework resource group
        $webApps = Get-AzWebApp -ResourceGroupName $Service.resourceGroup -ErrorAction SilentlyContinue | Where-Object { $_.Location -eq $AzureRegion }
        
        if ($webApps) {
            Write-Host "Found Web Applications:" -ForegroundColor Green
            foreach ($app in $webApps) {
                Write-Host " $($app.Name) - $($app.State)" -ForegroundColor Cyan
                if ($app.DefaultHostName) {
                    Write-Host " URL: https://$($app.DefaultHostName)" -ForegroundColor White
                }
            }
        }
        else {
            Write-Host "No web applications found in Application service" -ForegroundColor Yellow
        }
        
        Write-Host "`nApplication service connection established!" -ForegroundColor Green
        Write-Host "Access applications via their URLs or use Azure Portal" -ForegroundColor Cyan
        
        return [PSCustomObject]@{
            ServiceName = $Name
            Region = $Region
            AzureRegion = $AzureRegion
            ServiceType = "Application"
            ResourceGroup = $Service.resourceGroup
            WebApps = $webApps
            ConnectedAt = Get-Date
            Status = "Connected"
        }
    }
    catch {
        Write-Error "Failed to connect to Application service: $($_.Exception.Message)"
        throw
    }
}

#endregion

#region Get-ScfServices

<#
.SYNOPSIS
    Lists available framework services in a region.

.DESCRIPTION
    Lists all available framework services in the specified region based on
    framework configuration. This replaces general VM discovery with
    framework-scoped service discovery.

.PARAMETER Region
    The framework region code (e.g., USE, EUW, USW).

.EXAMPLE
    Get-ScfServices -Region USE
#>

function Get-ScfServices {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Region
    )

    # Check if we're connected to framework
    if (-not $script:PlatformContext) {
        throw "Not connected to SCF framework. Please run Connect-ScfPlatform first."
    }

    try {
        Write-Host "=== Framework Services in $Region ===" -ForegroundColor Green
        Write-Host ""
        
        # Get framework configuration
        $frameworkConfig = $script:PlatformContext.FrameworkConfig
        
        # Validate region exists
        if (-not $frameworkConfig.regions.$Region) {
            throw "Region '$Region' not found in framework configuration"
        }
        
        $azureRegion = $frameworkConfig.regions.$Region
        Write-Host "Azure Region: $azureRegion" -ForegroundColor Cyan
        Write-Host ""
        
        $services = @()
        
        # List all available services in the framework
        foreach ($serviceName in $frameworkConfig.services.Keys) {
            $service = $frameworkConfig.services.$serviceName
            
            $serviceInfo = [PSCustomObject]@{
                Name = $serviceName
                Type = $service.type
                Description = $service.description
                ResourceGroup = $service.resourceGroup
                Region = $Region
                AzureRegion = $azureRegion
            }
            
            $services += $serviceInfo
            
            Write-Host "Service: $serviceName" -ForegroundColor Yellow
            Write-Host " Type: $($service.type)" -ForegroundColor White
            Write-Host " Description: $($service.description)" -ForegroundColor White
            Write-Host " Resource Group: $($service.resourceGroup)" -ForegroundColor White
            Write-Host ""
        }
        
        if ($services.Count -eq 0) {
            Write-Host "No services configured in framework for region: $Region" -ForegroundColor Yellow
        }
        else {
            Write-Host "Summary:" -ForegroundColor Cyan
            Write-Host "Total services available: $($services.Count)" -ForegroundColor Green
            
            Write-Host "`nTo connect to a service, use:" -ForegroundColor Green
            Write-Host "Connect-ScfService -Name 'ServiceName' -Region '$Region' -Credential `$credential" -ForegroundColor Cyan
        }
        
        return $services
    }
    catch {
        Write-Error "Failed to get framework services: $($_.Exception.Message)"
        throw
    }
}

#endregion

#region Disconnect-ScfPlatform

<#
.SYNOPSIS
    Disconnects from the SCF framework platform.

.DESCRIPTION
    Disconnects from the SCF framework platform and clears the connection context.

.EXAMPLE
    Disconnect-ScfPlatform
#>

function Disconnect-ScfPlatform {
    [CmdletBinding()]
    param()

    try {
        if ($script:PlatformContext) {
            Write-Host "Disconnecting from SCF framework platform..." -ForegroundColor Yellow
            
            # Disconnect from Azure
            Disconnect-AzAccount -ErrorAction SilentlyContinue
            
            # Clear platform context
            $script:PlatformContext = $null
            
            Write-Host "Successfully disconnected from SCF framework platform" -ForegroundColor Green
        }
        else {
            Write-Host "Not connected to SCF framework platform" -ForegroundColor Yellow
        }
    }
    catch {
        Write-Error "Failed to disconnect from SCF framework platform: $($_.Exception.Message)"
        throw
    }
}

#endregion

#region Connect-ScfVM

<#
.SYNOPSIS
    Automatically connects to a VM through the Bastion service.

.DESCRIPTION
    Automatically discovers and connects to a VM through the configured Bastion service.
    This eliminates the need for manual URL navigation or complex Azure CLI commands.

.PARAMETER Name
    The name of the VM to connect to.

.PARAMETER Region
    The region where the VM is located.

.PARAMETER Credential
    Optional credentials for the VM. If not provided, will use stored credentials from config.

.EXAMPLE
    Connect-ScfVM -Name "colt-dev-use02-vm" -Region USE
#>

function Connect-ScfVM {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name,
        
        [Parameter(Mandatory = $true)]
        [string]$Region,
        
        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]$Credential
    )

    # Check if we're connected to framework
    if (-not $script:PlatformContext) {
        throw "Not connected to SCF framework. Please run Connect-ScfPlatform first."
    }

    try {
        Write-Host "Connecting to VM: $Name in region: $Region" -ForegroundColor Green
        
        # Check and install required Azure CLI extensions
        Write-Host "Checking Azure CLI dependencies..." -ForegroundColor Yellow
        $requiredExtensions = @("ssh", "bastion")
        
        foreach ($ext in $requiredExtensions) {
            $installed = az extension list --query "[?name=='$ext'].name" --output tsv
            if (-not $installed) {
                Write-Host "Installing required extension: $ext" -ForegroundColor Yellow
                az extension add -n $ext --yes | Out-Null
                Write-Host "Extension $ext installed successfully" -ForegroundColor Green
            } else {
                Write-Host "Extension $ext already installed" -ForegroundColor Green
            }
        }
        
        # Get framework configuration
        $frameworkConfig = $script:PlatformContext.FrameworkConfig
        
        # Validate region exists
        if (-not $frameworkConfig.regions.$Region) {
            throw "Region '$Region' not found in framework configuration"
        }
        
        $azureRegion = $frameworkConfig.regions.$Region
        
        # Find the VM in the framework
        $targetVM = $null
        $vmService = $null
        
        # Look for VM in configured services
        foreach ($serviceName in $frameworkConfig.services.Keys) {
            $service = $frameworkConfig.services.$serviceName
            if ($service.type -eq "virtualMachine" -or $service.type -eq "vm") {
                # Check if this service contains our target VM
                $vms = Get-AzVM -ResourceGroupName $service.resourceGroup -ErrorAction SilentlyContinue | 
                       Where-Object { $_.Location -eq $azureRegion -and $_.Name -eq $Name }
                
                if ($vms) {
                    $targetVM = $vms[0]
                    $vmService = $service
                    break
                }
            }
        }
        
        # If VM not found in configured services, try to find it in the region
        if (-not $targetVM) {
            Write-Host "VM not found in configured services. Searching in region..." -ForegroundColor Yellow
            
            # Search for VM in the region
            $allVMs = Get-AzVM -ErrorAction SilentlyContinue | Where-Object { 
                $_.Location -eq $azureRegion -and $_.Name -eq $Name 
            }
            
            if ($allVMs) {
                $targetVM = $allVMs[0]
                Write-Host "Found VM in region: $($targetVM.ResourceGroupName)" -ForegroundColor Green
                
                # Create a default service object for the VM
                $vmService = @{
                    resourceGroup = $targetVM.ResourceGroupName
                    credentials = $null
                }
            }
        }
        
        if (-not $targetVM) {
            throw "VM '$Name' not found in region '$Region'"
        }
        
        Write-Host "Found VM: $($targetVM.Name)" -ForegroundColor Green
        Write-Host "Resource Group: $($targetVM.ResourceGroupName)" -ForegroundColor Cyan
        Write-Host "Location: $($targetVM.Location)" -ForegroundColor Cyan
        Write-Host "Size: $($targetVM.HardwareProfile.VmSize)" -ForegroundColor Cyan
        Write-Host "OS: $($targetVM.StorageProfile.OsDisk.OsType)" -ForegroundColor Cyan
        
        # Check VM power state
        $vmStatus = Get-AzVM -ResourceGroupName $targetVM.ResourceGroupName -Name $targetVM.Name -Status -ErrorAction SilentlyContinue
        $powerState = $vmStatus.Statuses[1].DisplayStatus
        
        if ($powerState -ne "VM running") {
            Write-Host "VM is not running. Current state: $powerState" -ForegroundColor Yellow
            Write-Host "Starting VM..." -ForegroundColor Yellow
            
            Start-AzVM -ResourceGroupName $targetVM.ResourceGroupName -Name $targetVM.Name
            Start-Sleep -Seconds 30
            
            # Check status again
            $vmStatus = Get-AzVM -ResourceGroupName $targetVM.ResourceGroupName -Name $targetVM.Name -Status -ErrorAction SilentlyContinue
            $powerState = $vmStatus.Statuses[1].DisplayStatus
            
            if ($powerState -ne "VM running") {
                throw "Failed to start VM. Current state: $powerState"
            }
            
            Write-Host "VM is now running!" -ForegroundColor Green
        }
        
        # Get or prompt for credentials
        if (-not $Credential) {
            if ($vmService.credentials -and $vmService.credentials.username) {
                # Use stored credentials from config
                $username = $vmService.credentials.username
                $password = $vmService.credentials.password
                
                if ($password) {
                    $securePassword = ConvertTo-SecureString $password -AsPlainText -Force
                    $Credential = New-Object System.Management.Automation.PSCredential($username, $securePassword)
                    Write-Host "Using stored credentials for user: $username" -ForegroundColor Green
                }
                else {
                    Write-Host "Password not stored in config. Please provide credentials." -ForegroundColor Yellow
                    $Credential = Get-Credential -Message "Enter credentials for VM: $Name"
                }
            }
            else {
                Write-Host "No stored credentials found. Please provide credentials." -ForegroundColor Yellow
                $Credential = Get-Credential -Message "Enter credentials for VM: $Name"
            }
        }
        
        # Find Bastion host for this VM
        $bastions = Get-AzBastion -ErrorAction SilentlyContinue | Where-Object { $_.Location -eq $azureRegion }
        
        if (-not $bastions) {
            throw "No Bastion hosts found in region: $azureRegion"
        }
        
        $bastion = $bastions | Select-Object -First 1
        Write-Host "Using Bastion: $($bastion.Name)" -ForegroundColor Green
        
        # Build the Bastion SSH command
        $subscriptionId = (Get-AzContext).Subscription.Id
        $vmResourceId = "/subscriptions/$subscriptionId/resourceGroups/$($targetVM.ResourceGroupName)/providers/Microsoft.Compute/virtualMachines/$($targetVM.Name)"
        
        Write-Host "`nConnecting to VM through Bastion..." -ForegroundColor Green
        Write-Host "This will open an SSH session to your VM." -ForegroundColor Cyan
        
        # Execute the Bastion SSH command
        $bastionCommand = @(
            "az", "network", "bastion", "ssh",
            "--name", $bastion.Name,
            "--resource-group", $bastion.ResourceGroupName,
            "--target-resource-id", $vmResourceId,
            "--auth-type", "password",
            "--username", $Credential.UserName
        )
        
        Write-Host "Executing: $($bastionCommand -join ' ')" -ForegroundColor Gray
        
        # Start the SSH session
        Start-Process -FilePath "az" -ArgumentList $bastionCommand[1..($bastionCommand.Length-1)] -NoNewWindow
        
        Write-Host "`nSSH session initiated!" -ForegroundColor Green
        Write-Host "If the SSH session doesn't open automatically, you can:" -ForegroundColor Cyan
        Write-Host "1. Check your terminal for the SSH prompt" -ForegroundColor White
        Write-Host "2. Use the Bastion URL: https://$($bastion.DnsName)" -ForegroundColor White
        Write-Host "3. Run the command manually: $($bastionCommand -join ' ')" -ForegroundColor Gray
        
        return [PSCustomObject]@{
            VMName = $targetVM.Name
            ResourceGroup = $targetVM.ResourceGroupName
            Region = $Region
            AzureRegion = $azureRegion
            PowerState = $powerState
            Size = $targetVM.HardwareProfile.VmSize
            OS = $targetVM.StorageProfile.OsDisk.OsType
            BastionName = $bastion.Name
            ConnectedAt = Get-Date
            Status = "Connected"
            CredentialUsername = $Credential.UserName
        }
    }
    catch {
        Write-Error "Failed to connect to VM: $($_.Exception.Message)"
        throw
    }
}

#endregion

# Export functions
Export-ModuleMember -Function @(
    'Initialize-ScfFramework',
    'Connect-ScfPlatform',
    'Connect-ScfService',
    'Get-ScfServices',
    'Get-ScfVMs',
    'Connect-ScfVM',
    'Disconnect-ScfPlatform'
)