modules/HomeLab.Azure/Private/Start-ResourceMonitoring.ps1

<#
.SYNOPSIS
    Monitors Azure resource deployment status.
.DESCRIPTION
    Monitors the provisioning state of an Azure resource, either synchronously or in a background job.
.PARAMETER ResourceGroup
    The name of the resource group containing the resource.
.PARAMETER ResourceType
    The Azure resource type.
.PARAMETER ResourceName
    The name of the resource to monitor.
.PARAMETER PollIntervalSeconds
    The interval in seconds between status checks.
.PARAMETER TimeoutMinutes
    The maximum time in minutes to monitor before timing out.
.PARAMETER BackgroundJob
    If specified, monitoring runs in a background job.
.EXAMPLE
    Start-ResourceMonitoring -ResourceGroup "myRG" -ResourceType "Microsoft.Network/virtualNetworks" -ResourceName "myVNet" -BackgroundJob
.NOTES
    Author: Jurie Smit
    Date: March 9, 2025
#>

function Start-ResourceMonitoring {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]$ResourceGroup,
        
        [Parameter(Mandatory=$true)]
        [string]$ResourceType,
        
        [Parameter(Mandatory=$true)]
        [string]$ResourceName,
        
        [Parameter(Mandatory=$false)]
        [int]$PollIntervalSeconds = 30,
        
        [Parameter(Mandatory=$false)]
        [int]$TimeoutMinutes = 60,
        
        [Parameter(Mandatory=$false)]
        [switch]$BackgroundJob
    )
    
    if ($BackgroundJob) {
        # Create a log file for this monitoring session
        $timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
        $logFile = Join-Path -Path $env:TEMP -ChildPath "Monitor_${ResourceType}_${timestamp}.log"
        
        # Create a script block for the monitoring job
        $monitorScriptBlock = {
            param($ResourceGroup, $ResourceType, $ResourceName, $PollIntervalSeconds, $TimeoutMinutes, $LogFile)
            
            # Simple logging function for the background job
            function Write-MonitorLog {
                param(
                    [Parameter(Mandatory=$true)]
                    [string]$Message,
                    
                    [Parameter(Mandatory=$false)]
                    [string]$Level = "Info"
                )
                
                $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
                $logEntry = "[$timestamp] [$Level] $Message"
                Write-Output $logEntry
                $logEntry | Out-File -FilePath $LogFile -Append
            }
            
            Write-MonitorLog "Starting monitoring for $ResourceType '$ResourceName' in resource group '$ResourceGroup'"
            
            $startTime = Get-Date
            $timeout = (Get-Date).AddMinutes($TimeoutMinutes)
            $completed = $false
            $status = "Unknown"
            
            while (-not $completed -and (Get-Date) -lt $timeout) {
                try {
                    # Use az CLI to check resource status
                    $status = az resource show --resource-group $ResourceGroup --name $ResourceName --resource-type $ResourceType --query "properties.provisioningState" -o tsv 2>$null
                    
                    if ($status) {
                        Write-MonitorLog "Current status: $status"
                        
                        if ($status -eq "Succeeded") {
                            Write-MonitorLog "Resource provisioning completed successfully." -Level "Success"
                            $completed = $true
                        }
                        elseif ($status -eq "Failed") {
                            Write-MonitorLog "Resource provisioning failed." -Level "Error"
                            $completed = $true
                        }
                        elseif ($status -eq "Canceled") {
                            Write-MonitorLog "Resource provisioning was canceled." -Level "Warning"
                            $completed = $true
                        }
                        else {
                            # Still in progress, wait for the next poll
                            Write-MonitorLog "Resource provisioning in progress. Waiting $PollIntervalSeconds seconds..."
                            Start-Sleep -Seconds $PollIntervalSeconds
                        }
                    }
                    else {
                        Write-MonitorLog "Could not retrieve resource status. Resource may not exist yet." -Level "Warning"
                        Start-Sleep -Seconds $PollIntervalSeconds
                    }
                }
                catch {
                    Write-MonitorLog "Error checking resource status: $_" -Level "Error"
                    Start-Sleep -Seconds $PollIntervalSeconds
                }
            }
            
            if (-not $completed) {
                Write-MonitorLog "Monitoring timed out after $TimeoutMinutes minutes." -Level "Warning"
            }
            
            $duration = (Get-Date) - $startTime
            Write-MonitorLog "Monitoring completed. Total duration: $($duration.TotalMinutes.ToString('0.00')) minutes" -Level "Info"
            
            return @{
                ResourceGroup = $ResourceGroup
                ResourceType = $ResourceType
                ResourceName = $ResourceName
                Completed = $completed
                FinalStatus = $status
                Duration = $duration.TotalMinutes.ToString('0.00')
                LogFile = $LogFile
                StartTime = $startTime
                EndTime = Get-Date
            }
        }
        
        # Start the monitoring job
        $monitorJobName = "Monitor_${ResourceType}_$(Get-Random)"
        $monitorJob = Start-Job -Name $monitorJobName -ScriptBlock $monitorScriptBlock -ArgumentList $ResourceGroup, $ResourceType, $ResourceName, $PollIntervalSeconds, $TimeoutMinutes, $logFile
        
        Write-Log "Started background monitoring job '$monitorJobName' (ID: $($monitorJob.Id)) for $ResourceType '$ResourceName'" -Level Info
        Write-Log "Monitoring log file: $logFile" -Level Info
        
        return @{
            JobId = $monitorJob.Id
            JobName = $monitorJobName
            LogFile = $logFile
            ResourceGroup = $ResourceGroup
            ResourceType = $ResourceType
            ResourceName = $ResourceName
            StartTime = Get-Date
        }
    }
    else {
        # Synchronous monitoring
        Write-Log "Starting synchronous monitoring for $ResourceType '$ResourceName' in resource group '$ResourceGroup'" -Level Info
        
        $startTime = Get-Date
        $timeout = (Get-Date).AddMinutes($TimeoutMinutes)
        $completed = $false
        $status = "Unknown"
        
        while (-not $completed -and (Get-Date) -lt $timeout) {
            try {
                # Use az CLI to check resource status
                $status = az resource show --resource-group $ResourceGroup --name $ResourceName --resource-type $ResourceType --query "properties.provisioningState" -o tsv 2>$null
                
                if ($status) {
                    Write-Log "Current status: $status" -Level Info
                    
                    if ($status -eq "Succeeded") {
                        Write-Log "Resource provisioning completed successfully." -Level Success
                        $completed = $true
                    }
                    elseif ($status -eq "Failed") {
                        Write-Log "Resource provisioning failed." -Level Error
                        $completed = $true
                    }
                    elseif ($status -eq "Canceled") {
                        Write-Log "Resource provisioning was canceled." -Level Warning
                        $completed = $true
                    }
                    else {
                        # Still in progress, wait for the next poll
                        Write-Log "Resource provisioning in progress. Waiting $PollIntervalSeconds seconds..." -Level Info -NoNewLine
                        Start-Sleep -Seconds $PollIntervalSeconds
                        Write-Host "`r `r" -NoNewline
                    }
                }
                else {
                    Write-Log "Could not retrieve resource status. Resource may not exist yet." -Level Warning
                    Start-Sleep -Seconds $PollIntervalSeconds
                }
            }
            catch {
                Write-Log "Error checking resource status: $_" -Level Error
                Start-Sleep -Seconds $PollIntervalSeconds
            }
        }
        
        if (-not $completed) {
            Write-Log "Monitoring timed out after $TimeoutMinutes minutes." -Level Warning
        }
        
        $duration = (Get-Date) - $startTime
        Write-Log "Monitoring completed. Total duration: $($duration.TotalMinutes.ToString('0.00')) minutes" -Level Info
        
        return @{
            ResourceGroup = $ResourceGroup
            ResourceType = $ResourceType
            ResourceName = $ResourceName
            Completed = $completed
            FinalStatus = $status
            Duration = $duration.TotalMinutes.ToString('0.00')
            StartTime = $startTime
            EndTime = Get-Date
        }
    }
}