Public/Test-InPlaceUpgradeReadiness.ps1
|
function Test-InPlaceUpgradeReadiness { <# .SYNOPSIS Run the read-only preflight for an in-place upgrade of one Azure VM .DESCRIPTION Collects control-plane facts from ARM (power state, OS disk type, security type), in-guest facts through a short Run Command probe (build, edition, installation type, language, free space, pending reboot, domain role, cluster service, activation channel) and the availability of the hidden upgrade media image in the VM's region, then applies every readiness rule and returns one object with a Decision (Eligible, NotEligible, AlreadyAtTarget) and the full list of checks. Nothing is created, attached, tagged or started. Running it on a production VM is safe; the Run Command probe takes about a minute. .PARAMETER VM The VM object from Get-AzVM. Accepts pipeline input. .PARAMETER ResourceGroupName Resource group of the VM when -Name is used instead of -VM. .PARAMETER Name Name of the VM when -ResourceGroupName is used instead of -VM. .PARAMETER Target The target key from the matrix, for example WS2025. Defaults to the VM's UpgradeTarget tag; one of the two must be present. .PARAMETER MinimumFreeSpaceGB Free space required on C: before an upgrade would be started. .PARAMETER SkipMediaCheck Do not look up the upgrade media image in the VM's region. Saves one call when only the guest is of interest; the engine check then reports a warning instead of a pass. .EXAMPLE # Preflight one VM and show the checks $r = Test-InPlaceUpgradeReadiness -ResourceGroupName rg-apps-prod-weu -Name vm-app-prod-weu-01 -Target WS2025 $r.Decision $r.Checks | Format-Table Name, Result, Detail -AutoSize .EXAMPLE # Preflight everything that is approved, summarised Get-AzVM -ResourceGroupName rg-apps-prod-weu | Test-InPlaceUpgradeReadiness -Target WS2025 | Select-Object VMName, SourceName, Target, Engine, Decision, Failures, Warnings .EXAMPLE # Only the failed checks across a resource group Get-AzVM -ResourceGroupName rg-apps-prod-weu | Test-InPlaceUpgradeReadiness -Target WS2025 | ForEach-Object { $vm = $_.VMName; $_.Checks | Where-Object Result -eq 'Fail' | Select-Object @{ n = 'VM'; e = { $vm } }, Name, Detail } .INPUTS Microsoft.Azure.Commands.Compute.Models.PSVirtualMachine .OUTPUTS AzureInPlaceUpgrade.Readiness .NOTES Author: Simon Vedder (simonvedder.com) Version: 0.1.0 Created: 2026-09-04 LastModified: 2026-09-04 RequiredPermissions: Microsoft.Compute/virtualMachines/read, Microsoft.Compute/virtualMachines/instanceView/read, Microsoft.Compute/virtualMachines/runCommand/action, Microsoft.Compute/locations/publishers/artifacttypes/offers/skus/versions/read (Reader is not enough because of runCommand/action; Virtual Machine Contributor is far more than needed - use a custom role with these four actions) Prerequisites: PowerShell 7.2+, Az.Compute; a healthy Azure Guest Agent in the VM; an established Azure context .LINK https://github.com/simon-vedder/azure-vm-inplace-upgrade #> [CmdletBinding(DefaultParameterSetName = 'ByObject')] [OutputType('AzureInPlaceUpgrade.Readiness')] param( [Parameter(Mandatory, ParameterSetName = 'ByObject', ValueFromPipeline)] $VM, [Parameter(Mandatory, ParameterSetName = 'ByName')] [string]$ResourceGroupName, [Parameter(Mandatory, ParameterSetName = 'ByName')] [string]$Name, [Parameter(Mandatory)] [string]$Target, [Parameter()] [ValidateRange(10, 200)] [int]$MinimumFreeSpaceGB = 30, [Parameter()] [switch]$SkipMediaCheck ) process { if ($PSCmdlet.ParameterSetName -eq 'ByName') { $VM = Get-AzVM -ResourceGroupName $ResourceGroupName -Name $Name -ErrorAction Stop } $targetObject = Get-InPlaceUpgradeTarget -Name $Target Write-Verbose "[$($VM.Name)] Reading power state." $powerState = Get-VMPowerState -ResourceGroupName $VM.ResourceGroupName -VMName $VM.Name $armFacts = Get-VMArmFacts -VM $VM -PowerState $powerState $guestFacts = $null $guestError = $null if ($powerState -eq 'running') { Write-Verbose "[$($VM.Name)] Probing the guest through Run Command." $probe = Get-GuestFacts -ResourceGroupName $VM.ResourceGroupName -VMName $VM.Name $guestFacts = $probe.Facts $guestError = $probe.Error } $media = $null $mediaEngine = Get-PropertyOrDefault -InputObject $targetObject.Engines -Name 'MediaDisk' if (-not $SkipMediaCheck -and $mediaEngine) { Write-Verbose "[$($VM.Name)] Looking up upgrade media $($mediaEngine.sku) in $($armFacts.Location)." $media = Get-MediaImageVersion -Location $armFacts.Location -Publisher $mediaEngine.publisher -Offer $mediaEngine.offer -Sku $mediaEngine.sku } Resolve-InPlaceUpgradeReadiness -Target $targetObject -ArmFacts $armFacts -GuestFacts $guestFacts ` -GuestError $guestError -MinimumFreeSpaceGB $MinimumFreeSpaceGB -Media $media } } |