modules/HomeLab.Azure/Public/BackgroundMonitoring/Start-BackgroundMonitoring.ps1
<#
.SYNOPSIS Starts a background monitoring job for Azure resources. .DESCRIPTION Creates a background job to monitor the status of an Azure resource deployment without blocking the main PowerShell session. Stores job information for later retrieval. .PARAMETER ResourceGroup The name of the resource group containing the resource. .PARAMETER ResourceType The type of Azure resource to monitor (e.g., "vnet-gateway", "nat-gateway", "vnet"). .PARAMETER ResourceName The name of the resource to monitor. .PARAMETER DeploymentName Optional. The name of the deployment to monitor (if monitoring a deployment). .PARAMETER DesiredState Optional. The desired provisioning state to wait for. Default is "Succeeded". .PARAMETER PollIntervalSeconds Optional. The interval in seconds between status checks. Default is 30 seconds. .PARAMETER TimeoutMinutes Optional. Maximum time in minutes to monitor before timing out. Default is 60 minutes. .PARAMETER CustomScriptBlock Optional. A custom script block to execute for monitoring. If provided, this overrides the default monitoring behavior. .PARAMETER CustomParameters Optional. Parameters to pass to the custom script block. .EXAMPLE Start-BackgroundMonitoring -ResourceGroup "dev-eastus-rg-homelab" -ResourceType "vnet-gateway" -ResourceName "dev-eastus-vpng-homelab" Monitors a VPN Gateway using the built-in monitoring logic. .EXAMPLE Start-BackgroundMonitoring -ResourceGroup "dev-eastus-rg-homelab" -DeploymentName "network-deployment" -CustomScriptBlock { param($params) Monitor-AzDeployment $params } -CustomParameters @{ ResourceGroupName = "dev-eastus-rg-homelab"; DeploymentName = "network-deployment" } Monitors a deployment using a custom monitoring script. .NOTES Author: Jurie Smit Date: March 9, 2025 #> function Start-BackgroundMonitoring { [CmdletBinding(DefaultParameterSetName = "StandardMonitoring")] param( [Parameter(Mandatory=$true, ParameterSetName="StandardMonitoring")] [Parameter(Mandatory=$false, ParameterSetName="CustomMonitoring")] [string]$ResourceGroup, [Parameter(Mandatory=$true, ParameterSetName="StandardMonitoring")] [Parameter(Mandatory=$false, ParameterSetName="CustomMonitoring")] [ValidateSet("vnet-gateway", "nat-gateway", "vnet", "deployment")] [string]$ResourceType, [Parameter(Mandatory=$true, ParameterSetName="StandardMonitoring")] [Parameter(Mandatory=$false, ParameterSetName="CustomMonitoring")] [string]$ResourceName, [Parameter(Mandatory=$false)] [string]$DeploymentName, [Parameter(Mandatory=$false, ParameterSetName="StandardMonitoring")] [string]$DesiredState = "Succeeded", [Parameter(Mandatory=$false, ParameterSetName="StandardMonitoring")] [int]$PollIntervalSeconds = 30, [Parameter(Mandatory=$false, ParameterSetName="StandardMonitoring")] [int]$TimeoutMinutes = 60, [Parameter(Mandatory=$true, ParameterSetName="CustomMonitoring")] [scriptblock]$CustomScriptBlock, [Parameter(Mandatory=$false, ParameterSetName="CustomMonitoring")] [hashtable]$CustomParameters = @{} ) # Create a unique job ID $jobId = Get-Random -Minimum 1 -Maximum 1000 # Ensure the job storage directory exists $jobDir = Join-Path -Path $env:TEMP -ChildPath "HomeLab\Jobs" if (-not (Test-Path -Path $jobDir)) { New-Item -Path $jobDir -ItemType Directory -Force | Out-Null } # Create a job file path $jobPath = Join-Path -Path $jobDir -ChildPath "job_$jobId.xml" # Format the resource type for display $displayResourceType = $ResourceType switch ($ResourceType) { "vnet-gateway" { $displayResourceType = "VPN Gateway" } "nat-gateway" { $displayResourceType = "NAT Gateway" } "vnet" { $displayResourceType = "Virtual Network" } "deployment" { $displayResourceType = "Deployment" } } # If using custom monitoring, use the provided script block if ($PSCmdlet.ParameterSetName -eq "CustomMonitoring") { $scriptBlock = $CustomScriptBlock $scriptParameters = $CustomParameters # Ensure we have resource information for display if (-not $ResourceType -and $CustomParameters.ContainsKey("ResourceType")) { $ResourceType = $CustomParameters.ResourceType $displayResourceType = $ResourceType } if (-not $ResourceName -and $CustomParameters.ContainsKey("ResourceName")) { $ResourceName = $CustomParameters.ResourceName } if (-not $ResourceGroup -and $CustomParameters.ContainsKey("ResourceGroup")) { $ResourceGroup = $CustomParameters.ResourceGroup } if (-not $DeploymentName -and $CustomParameters.ContainsKey("DeploymentName")) { $DeploymentName = $CustomParameters.DeploymentName } } else { # Create the standard script block for the background job $scriptBlock = { param($ResourceGroup, $ResourceType, $ResourceName, $DesiredState, $PollIntervalSeconds, $TimeoutMinutes) # Set up command based on resource type switch ($ResourceType) { "vnet-gateway" { $statusCmd = "az network vnet-gateway show --resource-group $ResourceGroup --name $ResourceName --query provisioningState -o tsv" $displayType = "VPN Gateway" } "nat-gateway" { $statusCmd = "az network nat gateway show --resource-group $ResourceGroup --name $ResourceName --query provisioningState -o tsv" $displayType = "NAT Gateway" } "vnet" { $statusCmd = "az network vnet show --resource-group $ResourceGroup --name $ResourceName --query provisioningState -o tsv" $displayType = "Virtual Network" } "deployment" { $statusCmd = "az deployment group show --resource-group $ResourceGroup --name $ResourceName --query properties.provisioningState -o tsv" $displayType = "Deployment" } default { $statusCmd = "az resource show --resource-group $ResourceGroup --name $ResourceName --query properties.provisioningState -o tsv" $displayType = $ResourceType } } # Calculate timeout timestamp $startTime = Get-Date $timeout = $startTime.AddMinutes($TimeoutMinutes) # Create a log file in temp directory $logFile = Join-Path -Path $env:TEMP -ChildPath "$ResourceType-$ResourceName-monitor.log" # Initialize log "$(Get-Date) - Starting monitoring of $displayType '$ResourceName'" | Out-File -FilePath $logFile "$(Get-Date) - Target state: $DesiredState, Timeout: $TimeoutMinutes minutes" | Out-File -FilePath $logFile -Append $lastStatus = "" $completed = $false try { while ((Get-Date) -lt $timeout -and -not $completed) { # Execute the status command $currentStatus = Invoke-Expression $statusCmd 2>$null $elapsedTime = (Get-Date) - $startTime $formattedTime = "{0:hh\:mm\:ss}" -f $elapsedTime # Log status changes if ($currentStatus -ne $lastStatus) { "$(Get-Date) - Status changed to: $currentStatus (Elapsed: $formattedTime)" | Out-File -FilePath $logFile -Append $lastStatus = $currentStatus } # Check if deployment has completed or failed if ($currentStatus -eq $DesiredState) { "$(Get-Date) - ✓ Deployment completed successfully after $formattedTime" | Out-File -FilePath $logFile -Append $completed = $true return @{ Status = "Succeeded" ElapsedTime = $formattedTime LogFile = $logFile ResourceName = $ResourceName ResourceType = $displayType ResourceGroup = $ResourceGroup } } elseif ($currentStatus -eq "Failed") { "$(Get-Date) - ✗ Deployment failed after $formattedTime" | Out-File -FilePath $logFile -Append $completed = $true return @{ Status = "Failed" ElapsedTime = $formattedTime LogFile = $logFile ResourceName = $ResourceName ResourceType = $displayType ResourceGroup = $ResourceGroup } } # Wait for next poll Start-Sleep -Seconds $PollIntervalSeconds } # Timeout reached "$(Get-Date) - ⚠ Monitoring timeout reached after $TimeoutMinutes minutes" | Out-File -FilePath $logFile -Append return @{ Status = "Timeout" ElapsedTime = $formattedTime LogFile = $logFile ResourceName = $ResourceName ResourceType = $displayType ResourceGroup = $ResourceGroup } } catch { "$(Get-Date) - Error monitoring deployment: $_" | Out-File -FilePath $logFile -Append return @{ Status = "Error" ErrorMessage = $_ LogFile = $logFile ResourceName = $ResourceName ResourceType = $displayType ResourceGroup = $ResourceGroup } } } $scriptParameters = @($ResourceGroup, $ResourceType, $ResourceName, $DesiredState, $PollIntervalSeconds, $TimeoutMinutes) } # Start the background job $job = Start-Job -ScriptBlock $scriptBlock -ArgumentList $scriptParameters # Create job info object with enhanced metadata $jobInfo = [PSCustomObject]@{ JobId = $jobId Job = $job StartTime = [DateTime]::Now Command = $scriptBlock.ToString() ResourceGroupName = $ResourceGroup ResourceType = $displayResourceType ResourceName = $ResourceName DeploymentName = $DeploymentName Parameters = if ($PSCmdlet.ParameterSetName -eq "CustomMonitoring") { $CustomParameters } else { @{} } } # Save job info $jobInfo | Export-Clixml -Path $jobPath # Display job information Write-Host "`nStarted background monitoring job with ID: $jobId" -ForegroundColor Green Write-Host "Resource: $displayResourceType '$ResourceName'" -ForegroundColor Cyan if ($DeploymentName) { Write-Host "Deployment: $DeploymentName" -ForegroundColor Cyan } Write-Host "Resource Group: $ResourceGroup" -ForegroundColor Cyan Write-Host "Use 'Show-BackgroundMonitoringDetails' to check status" -ForegroundColor Yellow return $jobInfo } |