Windows Server VMs on Azure
-
Phase 0: Before you start
-
Phase 1: The foundation
-
Phase 2: Day-to-day operations
-
Phase 3: Enterprise architecture
-
Phase 4: Troubleshooting step by step
-
Phase 5: Final project and career
A Windows Server Azure VM is where most company apps still run: file servers, line-of-business apps, SQL Server, domain controllers. Building one is similar to Linux, but the details differ: passwords instead of SSH keys, RDP instead of SSH, licensing, and Windows activation.
In this lesson you build win01 with Windows Server 2025 Datacenter: Azure Edition, Server Core. It fits the free account’s small B2ats v2 size, has no public IP, and you log in through Bastion.
Then you find a problem that catches many teams in 2026: a new Windows VM in a private subnet can’t activate, because Windows activation needs to reach Azure’s licensing servers over the internet.
| Time | You need | Cost | By the end you can |
|---|---|---|---|
| About 50 minutes | vnet-saat, the CLI, a password manager | About USD 0.05 if you clean up the temporary NAT Gateway within 1 hour; the VM uses the free account’s Windows B2ats v2 hours | Build a private Windows Server VM, run PowerShell on it without RDP, connect with Bastion, and fix activation |
What it is, in one line
A Windows Server VM on Azure is a licensed Windows server you rent by the minute, reached with RDP through Bastion and managed with PowerShell.
In Simple Words
The same flat, but with a front-desk register
A Windows VM is the same rented flat as the Linux one, with two differences. You get in with a password (Windows doesn’t use SSH keys for RDP), and the flat has to be registered with the building office before the lights stop flickering. That’s Windows activation.
The registration office is outside your gated colony. If your colony has no exit gate (no outbound internet), the flat can’t register. Everything still works for a while, then the warning signs appear on the walls.
Engineer's View
Images, sizes, licensing and activation
- Images: publisher
MicrosoftWindowsServer, offerWindowsServer, SKUs like2025-datacenter-azure-edition(Desktop Experience) and2025-datacenter-azure-edition-core(Server Core). Azure Edition runs only on Azure and supports hotpatching.-smalldiskvariants have a 30 GB OS disk instead of 127 GB. - Size: Desktop Experience is comfortable from 4 GiB of memory. B2ats v2 has 1 GiB, so the course uses Server Core, which runs well in less memory and is what many production servers use anyway.
- Login: Windows VMs need
--admin-password. The CLI checks it locally: 12 to 123 characters, with three of lower case, upper case, numbers and symbols. Names likeadminandadministratorare refused. - Licensing: pay-as-you-go images include the Windows Server licence in the hourly price (that’s why Windows costs more than Linux in Lesson 1). With Software Assurance you can use Azure Hybrid Benefit (
--license-type Windows_Server) and pay the Linux rate. - Activation: Azure VMs activate against Azure’s KMS servers at
azkms.core.windows.neton TCP 1688. Those are public endpoints, so the VM needs outbound internet (NAT Gateway, firewall, or a route) to reach them. 168.63.129.16 doesn’t do activation.
Run Command works the same way as on Linux: RunPowerShellScript runs PowerShell as SYSTEM through the VM agent, with no RDP and no open port. It’s the fastest way to check or fix a Windows VM you can’t log in to.
How it works, step by step
The first hour of a new Windows VM looks like this:
- Provisioning: Azure creates the NIC and disk, sets the admin account, and runs Windows setup (sysprep’s “specialise” pass). This takes a few minutes longer than Linux.
- Agent: the Azure VM agent starts and reports “Ready” through 168.63.129.16. Run Command and extensions now work.
- Activation: Windows tries to activate against Azure KMS on port 1688. Without outbound access, this fails quietly at first.
- Updates: Windows Update also needs outbound access to Microsoft’s update servers (or a patching service, Lesson 30).
- Login: you RDP in through Bastion with the admin account.
Steps 3 and 4 are why every Windows subnet needs an explicit outbound method from day one, even if the server itself never talks to the internet for its job.
In the lab you build win01 in snet-mgmt, check it with Run Command, connect with Bastion, and then deal with activation.
Hands-on lab: build and activate win01
win01 stays (deallocated) for later Windows lessons. Load the network IDs first:
$v = Get-Content C:\azure-lab\vnet-saat.json | ConvertFrom-Json1Find the Windows Server 2025 image names
Run this:
az vm image list-skus --location centralindia --publisher MicrosoftWindowsServer --offer WindowsServer --query "[?starts_with(name,'2025')].name" -o tsvYou should see something like this (shortened):
2025-datacenter
2025-datacenter-azure-edition
2025-datacenter-azure-edition-core
2025-datacenter-azure-edition-core-smalldisk
2025-datacenter-azure-edition-smalldisk
2025-datacenter-core
2025-datacenter-core-smalldisk
2025-datacenter-smalldisk
...Pick 2025-datacenter-azure-edition-core-smalldisk: Azure Edition, Server Core, 30 GB image. You’ll grow the disk to 64 GB, which the free account covers. If your list shows a slightly different name, use the one with “azure-edition-core-smalldisk”.
2Create win01 with a generated password
Run this (about four minutes):
$pw = 'Lab-' + [guid]::NewGuid().ToString('N').Substring(0, 12) + '!'
"Save this password for win01 / azureuser: $pw"
az vm create --resource-group rg-saat-vms --name win01 --image MicrosoftWindowsServer:WindowsServer:2025-datacenter-azure-edition-core-smalldisk:latest --size Standard_B2ats_v2 --subnet $v.'snet-mgmt' --public-ip-address '""' --nsg '""' --admin-username azureuser --admin-password $pw --os-disk-size-gb 64 --tags course=saatvedha-azure lesson=16 keep=true role=admin --query "{name:name, power:powerState, privateIp:privateIpAddress}" -o tableYou should see something like this:
Save this password for win01 / azureuser: Lab-5e1c09a7b3d4!
Name Power PrivateIp
------ ---------- -----------
win01 VM running 10.30.10.4Copy the password into your password manager now. You’ll need it for Bastion. Never reuse it anywhere else.
3Check the VM with Run Command
Run this:
az vm run-command invoke --resource-group rg-saat-vms --name win01 --command-id RunPowerShellScript --scripts 'Get-CimInstance Win32_OperatingSystem | Select-Object Caption, Version, TotalVisibleMemorySize | Format-List' --query "value[0].message" -o tsvYou should see something like this:
Caption : Microsoft Windows Server 2025 Datacenter: Azure Edition
Version : 10.0.26100
TotalVisibleMemorySize : 967416The memory is in kilobytes: about 945 MB usable of the 1 GiB. The single quotes around the script keep your laptop’s PowerShell from changing anything in it, and the script itself uses no double quotes, because Windows PowerShell 5.1 drops inner double quotes when it passes arguments to az. The script runs on the VM as SYSTEM.
4Turn on auto-shutdown
Run this:
az vm auto-shutdown --resource-group rg-saat-vms --name win01 --time 1330 -o none5Connect with Bastion Developer
- In the portal, open win01 > Connect > Connect via Bastion and use the Developer option.
- Authentication type: VM Password. Username
azureuser, the password you saved. - A browser RDP session opens to Server Core: a command window with the SConfig menu. Type
15and Enter to exit to PowerShell, then runhostname.


6Check activation
Back on your laptop, run this:
az vm run-command invoke --resource-group rg-saat-vms --name win01 --command-id RunPowerShellScript --scripts 'cscript //nologo C:\Windows\System32\slmgr.vbs /dli' --query "value[0].message" -o tsvYou should see something like this:
Name: Windows(R), ServerDatacenterAzureEdition edition
Description: Windows(R) Operating System, VOLUME_KMSCLIENT channel
Partial Product Key: XXXXX
License Status: Notification
Notification Reason: 0xC004F056.“Notification” means Windows is not activated. It hasn’t managed to reach a licensing server. Hold that thought: you’ll find out why in “Break It”.
✅ Checkpoint: win01 runs Windows Server 2025 Azure Edition Core with no public IP, you logged in through Bastion, and you’ve noticed it isn’t activated yet.
Break It, Then Fix It
The first mistake stops you before anything is built. The second shows up days later as a warning on every login, unless you know where to look.
1Break it: a password the CLI won’t accept
Run this:
az vm create --resource-group rg-saat-vms --name win99 --image MicrosoftWindowsServer:WindowsServer:2025-datacenter-azure-edition-core-smalldisk:latest --size Standard_B2ats_v2 --subnet $v.'snet-mgmt' --public-ip-address '""' --nsg '""' --admin-username azureuser --admin-password 'Winter2026'You’ll get this error:
The password length must be between 12 and 123. Password must have the 3 of the following: 1 lower case character, 1 upper case character, 1 number and 1 special character.The CLI checked it on your laptop, so nothing was sent to Azure. Winter2026 is 10 characters with no symbol: exactly the kind of password that password-spray attacks try first. Generate passwords instead, as in step 2.
2Break it: activate Windows from a private subnet
Ask Windows to activate now:
az vm run-command invoke --resource-group rg-saat-vms --name win01 --command-id RunPowerShellScript --scripts 'cscript //nologo C:\Windows\System32\slmgr.vbs /ato; Test-NetConnection azkms.core.windows.net -Port 1688 | Select-Object ComputerName, TcpTestSucceeded' --query "value[0].message" -o tsvYou’ll get output like this:
Activating Windows(R), ServerDatacenterAzureEdition edition (...) ...
Error: 0xC004F074 The Software Licensing Service reported that the computer could not be activated. No Key Management Service (KMS) could be contacted. Please see the Application Event Log for additional information.
ComputerName TcpTestSucceeded
------------ ----------------
azkms.core.windows.net FalsePort 1688 to Azure KMS can’t be reached, because snet-mgmt is a private subnet with no outbound method. Older VNets had default outbound access, so this used to “just work”. In new networks, unactivated servers show a watermark and remind users to activate, and some features are limited.
Fix it: give the subnet a way out for a moment, as in Lesson 13, and activate:
az group create --name rg-saat-l16 --tags course=saatvedha-azure lesson=16 -o none
az network public-ip create --resource-group rg-saat-l16 --name pip-saat-l16 --sku Standard --allocation-method Static --tags course=saatvedha-azure lesson=16 -o none
az network nat gateway create --resource-group rg-saat-l16 --name nat-saat-l16 --public-ip-addresses pip-saat-l16 --tags course=saatvedha-azure lesson=16 -o none
$nat = az network nat gateway show --resource-group rg-saat-l16 --name nat-saat-l16 --query id -o tsv
az network vnet subnet update --ids $v.'snet-mgmt' --nat-gateway $nat -o none
Start-Sleep -Seconds 60
az vm run-command invoke --resource-group rg-saat-vms --name win01 --command-id RunPowerShellScript --scripts 'cscript //nologo C:\Windows\System32\slmgr.vbs /ato; cscript //nologo C:\Windows\System32\slmgr.vbs /dli | Select-String License' --query "value[0].message" -o tsvYou should see something like this:
Activating Windows(R), ServerDatacenterAzureEdition edition (...) ...
Product activated successfully.
License Status: LicensedKMS activation lasts 180 days and renews itself every 7 days when the server can reach KMS. Later lessons give snet-mgmt a permanent outbound path; for now, remove the temporary NAT Gateway.
Clean up and deallocate win01:
az network vnet subnet update --ids $v.'snet-mgmt' --remove natGateway -o none
az group delete --name rg-saat-l16 --yes
az vm deallocate --resource-group rg-saat-vms --name win01
C:\azure-lab\Find-LabResources.ps1 -Lesson 16You should see this at the end (win01 and its disk and NIC stay, tagged lesson=16 and keep=true):
Resources:
name type group lesson
---- ---- ----- ------
win01 Microsoft.Compute/virtualMachines rg-saat-vms 16
win01_OsDisk_1_... Microsoft.Compute/disks RG-SAAT-VMS 16
win01VMNic Microsoft.Network/networkInterfaces rg-saat-vms 16
shutdown-computevm-win01 microsoft.devtestlab/schedules rg-saat-vms 16
✅ Checkpoint: win01 shows “Product activated successfully”, the temporary NAT Gateway is gone, and win01 is deallocated.
This happened at work
“Activate Windows” on 60 new servers
A bank built a new landing zone in 2026 with private subnets everywhere and a firewall that allowed only traffic the security team had approved. The migration team moved 60 Windows servers into it over a month. Everything worked, and the project was marked done.
A few weeks later the service desk started getting tickets: “My screen says Activate Windows”. Every migrated server was unactivated. The firewall had no rule for Azure KMS on port 1688, and with no default outbound access the servers couldn’t reach it.
Nobody had noticed during testing, because activation failures don’t stop applications from running.
What they changed: the firewall got an application rule for azkms.core.windows.net on TCP 1688, plus the Windows Update endpoints. The build checklist now includes slmgr /dli and a check that Test-NetConnection azkms.core.windows.net -Port 1688 succeeds. A monthly Run Command report across all Windows VMs lists any server whose licence status isn’t “Licensed”.
Common mistakes and troubleshooting
| Mistake | What you see | What to do |
|---|---|---|
| No outbound path for Windows subnets | “Activate Windows”, 0xC004F074, update failures | Allow TCP 1688 to azkms.core.windows.net through NAT or the firewall. |
| Desktop Experience on a 1 GiB size | Very slow logins and hangs | Use Server Core, or a size with 4 GiB or more. |
| Weak or reused admin passwords | CLI refuses, or later a compromised server | Generate passwords, store them in a password manager or Key Vault. |
| 127 GB default disk on the free account | A P10 disk you pay for | Use a smalldisk image and set 64 GB. |
| Opening RDP 3389 to the internet | Thousands of failed logins a day | No public IP; use Bastion (Lesson 27) or JIT (Lesson 28). |
| Double quotes in Run Command scripts | Laptop PowerShell eats $ variables | Use single quotes around the script, and no double quotes inside it. |
Interview questions
Click a question to open it. Each one is written the way an interviewer would ask it. Under it you will see why they ask it and one way you could answer, in your own words.
Fresher level
1. How is creating a Windows VM different from a Linux VM in Azure?
Why they ask this: They want the practical differences.
How you might answer: Windows needs an admin password instead of SSH keys, you connect with RDP instead of SSH, the image includes the Windows licence unless you use Azure Hybrid Benefit, and Windows must activate against Azure KMS. Provisioning also takes a few minutes longer.
2. What is Azure Hybrid Benefit for Windows Server?
Why they ask this: A common licensing question.
How you might answer: If the company owns Windows Server licences with Software Assurance, it can apply them to Azure VMs with --license-type Windows_Server and pay the base compute rate instead of the rate that includes the licence.
3. What does Run Command let you do on a Windows VM?
Why they ask this: They want to know your break-glass tools.
How you might answer: It runs a PowerShell script as SYSTEM through the VM agent, without RDP or any open port. I use it to check services, read logs, reset settings, or fix the firewall when I can’t log in.
2–5 years experience
4. New Windows VMs show “Activate Windows”. What’s your first check?
Why they ask this: They’re checking a very current, real-world issue.
How you might answer: I’d run slmgr /dli and test TCP 1688 to azkms.core.windows.net from the VM. In private subnets or behind a strict firewall, the VM often can’t reach Azure KMS. The fix is an outbound path or a firewall rule for KMS, then slmgr /ato.
5. Why would you choose Server Core over Desktop Experience?
Why they ask this: They want reasons beyond “it’s smaller”.
How you might answer: Server Core uses less memory and disk, has fewer components to patch and attack, and reboots less often. Most roles can be managed remotely with PowerShell, Windows Admin Center or Run Command, so a GUI on the server isn’t needed.
6. How do you manage Windows admin passwords for many VMs?
Why they ask this: They want secure, scalable practice.
How you might answer: Each VM gets a unique random password at build time, stored in Key Vault, and ideally Windows LAPS rotates the local admin password afterwards. Daily admin access goes through domain or Entra ID accounts, not the local admin.
Senior level
7. You’re designing a landing zone with private subnets and a firewall. What must Windows servers be allowed to reach?
Why they ask this: They want a concrete list, not “the internet”.
How you might answer: At minimum Azure KMS on TCP 1688 for activation, Windows Update or the company’s patch source, the Azure monitoring and management endpoints the agents use, and any certificate revocation endpoints. I’d use the firewall’s FQDN tags where they exist and test each one from a new server before migration.
8. How would you check licence status across 300 Windows VMs?
Why they ask this: They want a scalable method.
How you might answer: A Run Command script or Azure Policy guest configuration that runs slmgr /dli or reads the SoftwareLicensingProduct class, and reports anything not “Licensed”. Results go to Log Analytics, where a KQL query lists the failures for the team.
9. When would you still use Desktop Experience on Azure?
Why they ask this: They want balanced judgement.
How you might answer: For apps that need a GUI on the server, like some legacy line-of-business tools, or for Remote Desktop Session Hosts. I’d size those VMs with enough memory and keep the number small.
Quick quiz
Q1. Which image did the lab use for win01?
- 2025-datacenter (Desktop)
- 2025-datacenter-azure-edition-core-smalldisk
- 2022-datacenter
- windows-11
Show answer
B — Azure Edition, Server Core, with a 30 GB image grown to 64 GB.
Q2. Why did activation fail at first?
- Wrong product key
- The private subnet had no outbound path to Azure KMS on port 1688
- Server Core can’t be activated
- Bastion blocks it
Show answer
B — Activation needs outbound access to azkms.core.windows.net.
Q3. What does “License Status: Notification” mean?
- Activated
- Not activated
- Evaluation edition
- Licence expired permanently
Show answer
B — Notification means Windows isn’t activated.
Q4. Why put the Run Command script in single quotes in PowerShell?
- They’re faster
- So your laptop doesn’t expand $ variables before sending the script
- Azure requires them
- To hide the script
Show answer
B — The script then runs on the VM exactly as written.
Q5. Which minimum password length does the CLI enforce for VMs?
- 8
- 10
- 12
- 16
Show answer
C — 12 to 123 characters with three character types.
Quick recap
You can now build a private Windows server, manage it without RDP, and fix the activation problem that new networks bring.
- Windows VMs use passwords and RDP; generate strong passwords and store them safely.
- Server Core on B2ats v2 fits the free account; Desktop Experience needs more memory.
- Azure Edition runs only on Azure and supports hotpatching.
- Activation needs TCP 1688 to azkms.core.windows.net: private subnets need an outbound path.
- win01 stays deallocated in rg-saat-vms for later lessons.
Next lesson
Lesson 17: Azure VM sizes: B, D, E and F series → You learn to read VM size names, compare families, and resize web01 safely.
Further reading: Windows Server Azure VM
Want to go deeper into Windows Server Azure VM? See Microsoft’s Create a Windows virtual machine with the Azure CLI and Troubleshoot Azure Windows virtual machine activation problems. You can also see the full plan for this course on the Azure Cloud Administration course page.
