modules/HomeLab.Azure/Public/Deploy-Infrastructure.ps1

<#
.SYNOPSIS
    Deploys Azure infrastructure for HomeLab.
.DESCRIPTION
    Retrieves global configuration and deploys either the full infrastructure or a specific component
    (network, VPN gateway, or NAT gateway) using the corresponding Bicep templates.
    If the BackgroundMonitor switch is specified, deployment monitoring runs as background jobs and the function
    outputs immediately.
.PARAMETER ComponentsOnly
    Optional. Specifies a single component to deploy ("network", "vpngateway", or "natgateway").
    If not specified, full infrastructure is deployed.
.PARAMETER ResourceGroup
    Optional. The name of the resource group to deploy to. If not provided, it will be constructed from configuration.
.PARAMETER Force
    Optional. If specified, skips confirmation prompts during deployment.
.PARAMETER Monitor
    Optional. If specified, monitors the deployment until completion.
.PARAMETER BackgroundMonitor
    Optional. If specified, starts background monitoring jobs instead of blocking the console.
.EXAMPLE
    Deploy-Infrastructure -ComponentsOnly "network"
.EXAMPLE
    Deploy-Infrastructure -Force -BackgroundMonitor
.NOTES
    Author: Jurie Smit (Original)
    Updated: March 12, 2025 – Improved error handling and standardized return pattern.
#>

function Deploy-Infrastructure {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$false)]
        [ValidateSet("network", "vpngateway", "natgateway")]
        [string]$ComponentsOnly,
        
        [Parameter(Mandatory=$false)]
        [string]$ResourceGroup,
        
        [Parameter(Mandatory=$false)]
        [switch]$Force,
        
        [Parameter(Mandatory=$false)]
        [switch]$Monitor,
        
        [Parameter(Mandatory=$false)]
        [switch]$BackgroundMonitor
    )
    
    Write-Log -Message "==== Starting Deploy-Infrastructure ====" -Level Info

    # Ensure we're connected to Azure.
    if (-not (Connect-AzureAccount)) {
        Write-Log -Message "Failed to connect to Azure. Deployment aborted." -Level Error
        return $false
    }
    
    # Retrieve global configuration.
    $config = Get-Configuration
    $env      = $config.env
    $loc      = $config.loc
    $project  = $config.project
    $location = $config.location
    Write-Log -Message "Configuration: env=$env, loc=$loc, project=$project, location=$location" -Level Debug

    # Construct the resource group name if not provided.
    if (-not $ResourceGroup) {
        $ResourceGroup = "$env-$loc-rg-$project"
        Write-Log -Message "ResourceGroup not provided; using default: $ResourceGroup" -Level Debug
    }
    
    # Check if resource group exists; create if it doesn't.
    if (-not (Test-ResourceGroup -ResourceGroupName $ResourceGroup)) {
        Write-Log -Message "Resource group '$ResourceGroup' does not exist. Creating..." -Level Info
        try {
            az group create --name $ResourceGroup --location $location | Out-Null
            Write-Log -Message "Resource group '$ResourceGroup' created successfully." -Level Info
        }
        catch {
            Write-Log -Message "Failed to create resource group: $($_.Exception.Message)" -Level Error
            return $false
        }
    }
    else {
        Write-Log -Message "Resource group '$ResourceGroup' exists." -Level Info
    }
    
    # Get the templates path.
    $templatesPath = Join-Path -Path $PSScriptRoot -ChildPath "..\Templates"
    Write-Log -Message "Templates path: $templatesPath" -Level Debug
    
    Write-Log -Message "Starting deployment for resource group: $ResourceGroup" -Level Info
    
    # Define common deployment parameters, including new flags.
    $commonParams = @(
        "--resource-group", $ResourceGroup,
        "--parameters", "location=$location", "env=$env", "loc=$loc", "project=$project",
                        "enableNatGateway=$($config.natGateway.enabled)", "enableVpnGateway=$($config.vpn.enabled)"
    )
    Write-Log -Message "Common parameters: $($commonParams -join ' ')" -Level Debug
    
    if (-not $Force) {
        $commonParams += "--confirm-with-what-if"
        Write-Log -Message "Added confirmation parameter." -Level Debug
    }
    if ($Monitor -or $BackgroundMonitor) {
        $commonParams += "--no-wait"
        Write-Log -Message "Added no-wait parameter (monitoring enabled)." -Level Debug
    }
    
    # Dispatch to the appropriate component deployment function with improved error handling and consistent return pattern
    try {
        if ($ComponentsOnly -eq "network") {
            Write-Log -Message "Starting Network component deployment..." -Level Info
            $result = Deploy-NetworkComponent -ResourceGroup $ResourceGroup -location $location -env $env -loc $loc -project $project -commonParams $commonParams -templatesPath $templatesPath -Monitor:$Monitor -BackgroundMonitor:$BackgroundMonitor
            Write-Log -Message "Network component deployment completed with result: $result" -Level Info
            return $result
        }
        elseif ($ComponentsOnly -eq "vpngateway") {
            Write-Log -Message "Starting VPN Gateway component deployment..." -Level Info
            $result = Deploy-VPNGatewayComponent -ResourceGroup $ResourceGroup -location $location -env $env -loc $loc -project $project -commonParams $commonParams -templatesPath $templatesPath -Monitor:$Monitor -BackgroundMonitor:$BackgroundMonitor
            Write-Log -Message "VPN Gateway component deployment completed with result: $result" -Level Info
            return $result
        }
        elseif ($ComponentsOnly -eq "natgateway") {
            Write-Log -Message "Starting NAT Gateway component deployment..." -Level Info
            $result = Deploy-NATGatewayComponent -ResourceGroup $ResourceGroup -location $location -env $env -loc $loc -project $project -commonParams $commonParams -templatesPath $templatesPath -Monitor:$Monitor -BackgroundMonitor:$BackgroundMonitor
            Write-Log -Message "NAT Gateway component deployment completed with result: $result" -Level Info
            return $result
        }
        else {
            Write-Log -Message "Starting full infrastructure deployment..." -Level Info
            $result = Deploy-FullInfrastructure -ResourceGroup $ResourceGroup -location $location -env $env -loc $loc -project $project -commonParams $commonParams -templatesPath $templatesPath -Monitor:$Monitor -BackgroundMonitor:$BackgroundMonitor
            Write-Log -Message "Full infrastructure deployment completed with result: $result" -Level Info
            return $result
        }
    }
    catch {
        $errorMsg = $_.Exception.Message
        $lineNum = $_.InvocationInfo.ScriptLineNumber
        Write-Log -Message "Error in Deploy-Infrastructure at line $lineNum`: $errorMsg" -Level Error
        Write-ColorOutput -Text "Deployment failed: $errorMsg" -ForegroundColor Red
        return $false
    }
    finally {
        Write-Log -Message "==== Completed Deploy-Infrastructure ====" -Level Info
    }
}