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' ) |