Nobody logs on
No RDP session, no ISO mounted by hand, no answer file written on the machine. Setup runs unattended from a media disk.
PowerShell module · Bicep orchestrator
Take an Azure VM from Windows Server 2016, 2019 or 2022 to 2025 without an RDP session. Read-only preflight, an OS disk snapshot and unattended Setup in one workflow.
Install-Module AzureInPlaceUpgrade -AllowPrerelease# 1. Read-only preflight — a short Run Command probe, nothing changes Test-InPlaceUpgradeReadiness -ResourceGroupName rg-apps -Name vm-app-01 -Target WS2025 Decision Eligible · 19 checks, 0 blocking # 2. Name a VM and a target: snapshot, media disk, Setup as SYSTEM $s = Start-InPlaceUpgrade -ResourceGroupName rg-apps -Name vm-app-01 -Target WS2025 Setup started · returns in minutes, $s carries the resume values # 3. Every 20 minutes until Completed or Failed — locally or from Automation Complete-InPlaceUpgrade -ResourceGroupName rg-apps -Name vm-app-01 -Target WS2025 -Snapshot $s.Snapshot vm-app-01 Completed · Windows Server 2025, build 26100
Quick start
From a workstation with PowerShell 7.2 and the Az modules. Nothing is deployed into your subscription and no tags are involved. You name the machine and the target, and the preflight refuses anything it does not like.
Needs the upgrade operator actions on the VM, or Virtual Machine Contributor · Prerequisites
Install-Module AzureInPlaceUpgrade -AllowPrerelease
Connect-AzAccountNineteen read-only checks inside the guest. Nothing is created, attached or started.
Test-InPlaceUpgradeReadiness -ResourceGroupName rg-apps-prod-weu -Name vm-app-prod-weu-01 -Target WS2025Snapshot, media disk, unattended Setup, and wait for the guest to report the target build. -WhatIf works everywhere, and -ReuseSnapshot makes a rerun cheap.
Invoke-InPlaceUpgrade -ResourceGroupName rg-apps-prod-weu -Name vm-app-prod-weu-01 -Target WS2025An Azure Automation job dies after three hours, so start and check are separate commands. Start returns in minutes and hands you the line that finishes the job.
$s = Start-InPlaceUpgrade -ResourceGroupName rg-apps-prod-weu -Name vm-app-prod-weu-01 -Target WS2025
$s.ResumeCommandWhat it does
No RDP session, no ISO mounted by hand, no answer file written on the machine. Setup runs unattended from a media disk.
Disk space, pending reboots, build and edition, agent health. The preflight is read-only and refuses rather than repairs.
The OS disk is snapshotted before Setup starts, and a rerun can reuse it instead of paying for a second one.
Because an Automation job dies after three hours and an upgrade does not. Progress is read from the guest, not assumed.
Running one machine takes a name. Rings, parallelism and schedules exist only once you deploy the orchestrator.
One row per attempt in Log Analytics, with the workbook that shows which ring is where.
Proof
Shipped as targets.json. A tag value that is not a key there never reaches an Azure image. Same edition, same installation type, 64-bit, en-US, Standard and Datacenter, Server and Server Core. Windows Server Azure Edition is out of scope.
| Target | Source | MediaDisk engine | FeatureUpdate engine |
|---|---|---|---|
WS2025 | Windows Server 2022 | Verified — Desktop Experience and Core | Verified once |
WS2025 | Windows Server 2019 | Verified — through Azure Automation | Documented |
WS2025 | Windows Server 2016 | Verified | Not available |
WS2025 | Windows Server 2012 R2 | Documented by Microsoft | Not available |
WS2022 | Windows Server 2016, 2019 | Documented by Microsoft | Not available |
WS2019 | Windows Server 2012 R2, 2016 | Documented by Microsoft | Not available |
"Verified" means the path ran end to end in this project's lab on a Marketplace VM; runs, timings and result objects are in docs/verification.md. The media disk finishes in 37 to 43 minutes; the feature update took two to three hours on the same image.

FAQ
The module never does. Windows Setup reboots the VM several times on its own once it runs, which is why Setup is started as a scheduled task and not from Run Command. A reboot that is already pending before the start blocks the preflight — restart first, then start the upgrade.
Under /quiet, Setup cannot ask which image to install, and the upgrade media carries a Core and a Desktop Experience image per edition. Setup logs "Image selection response not found" and gives up with 0xC1900215. A product key does not help. The module lists the images in install.wim, matches EditionId and InstallationType of the guest and passes /installfrom and /imageindex every time.
With the media disk, 37 to 43 minutes end to end in the lab, on Marketplace VMs from 2016, 2019 and 2022. The feature update engine took two to three hours on the same 2022 image. Budget a 60 to 120 minute maintenance window per VM, plus reboots.
Start creates an incremental snapshot of the OS disk before anything changes and returns its name; in fleet mode the runbook also records it in the UpgradeSnapshot tag. Complete keeps it — it stays until you delete it. Restoring means creating a disk from the snapshot and swapping the OS disk; test that once in a lab before you trust it.
Microsoft's Azure documentation says the in-place upgrade causes a disconnection between the VM's data plane and control plane, and that automatic guest patching, automatic OS image upgrades, hotpatching and Azure Update Manager are not officially supported afterwards. The image reference never changes. If you need those features, Microsoft's recommendation is a new VM — and so is mine.
An incremental snapshot of the OS disk, and a managed disk for the upgrade media for the duration of the run. Complete deletes the media disk; the snapshot stays until you remove it. There is no agent, no extension and nothing that keeps running afterwards.
I run this against fleets as part of an Azure engagement — preflight report first, rings and maintenance windows agreed, the failures walked through afterwards.