Symptom
You see one or more of these on a Windows VM in Azure:
- The "Activate Windows" watermark on the desktop, or Settings > Activation says Windows is not activated.
slmgr /atofails with 0xC004F074 ("No Key Management Service (KMS) could be contacted") or a timeout such as 0x800705B4.- Application log events from source Security-SPP (for example event 8198) reporting a failed activation attempt.
- After a grace period, Windows reminds users to activate and some personalisation settings are locked.
Marketplace Windows images are set up as KMS clients. They activate against the Azure KMS service at azkms.core.windows.net on TCP 1688. That name resolves to 20.118.99.224 and 40.83.235.53. Microsoft moved all Azure VMs to these addresses in 2023, so allow-lists or routes that only cover the old kms.core.windows.net (23.102.135.246) no longer work.
In most cases the VM can't reach those two IPs, usually because of a forced-tunnel route, a firewall rule or custom DNS.
Quick triage table
| Symptom / error | Likely cause | Fix |
|---|---|---|
0xC004F074, TcpTestSucceeded: False | 1688 blocked by NSG, Azure Firewall, NVA or guest firewall | Allow outbound TCP 1688 to both KMS IPs |
| 0xC004F074, name doesn't resolve | Custom DNS server can't resolve azkms.core.windows.net | Forward to Azure DNS (168.63.129.16) or a resolver that can |
| Works in one spoke, fails in another | 0.0.0.0/0 route to on-premises or an NVA (forced tunnelling) | Add /32 routes with next hop Internet, or allow 1688 on the NVA |
| 0xC004F06C | VM clock is badly skewed | Fix time sync, then retry |
| Error about the product key or "not a KMS client" | Custom or migrated image with a retail/MAK key | Install the right generic volume license key (GVLK) |
slmgr /dlv shows the wrong KMS host | Old GPO or image sets kms.core.windows.net or an on-prem KMS | Point it at azkms.core.windows.net:1688 |
Checks, without logging in
You can run all of these through Run Command if RDP is not available.
1. Licence status and configured KMS host
az vm run-command invoke -g rg-prod -n vm-app01 \
--command-id RunPowerShellScript \
--scripts "cscript //nologo C:\Windows\System32\slmgr.vbs /dlv"
In the output, look at:
- License Status:
Licensed,Initial grace period,Notification. - Description: it should contain
VOLUME_KMSCLIENT channel. If it saysRETAILorMAK, go to the product key section below. - KMS machine name: if it is set to anything other than
azkms.core.windows.net:1688, someone has overridden it.
2. Name resolution and port 1688 from inside the VM
Resolve-DnsName azkms.core.windows.net
Test-NetConnection azkms.core.windows.net -Port 1688
Test-NetConnection 20.118.99.224 -Port 1688
Test-NetConnection 40.83.235.53 -Port 1688
- The name fails to resolve but the IPs connect: this is a DNS problem.
- The name resolves but
TcpTestSucceeded : False: this is a network path problem (steps 3 and 4). - Everything succeeds: go to step 5 (time, key, activation command).
3. Effective routes: is 0.0.0.0/0 going somewhere other than the internet?
az network nic show-effective-route-table -g rg-prod -n vm-app01-nic -o table
az network watcher show-next-hop -g rg-prod --vm vm-app01 \
--source-ip 10.20.1.4 --dest-ip 20.118.99.224
A next hop of VirtualNetworkGateway or VirtualAppliance means the activation traffic goes to on-premises or to a firewall. On-premises proxies normally drop it, because activation traffic must come from an Azure public IP.
4. NSG and firewall rules
az network watcher test-ip-flow -g rg-prod --vm vm-app01 \
--direction Outbound --protocol TCP \
--local 10.20.1.4:50000 --remote 20.118.99.224:1688
If the result is Deny, the output names the NSG rule. If traffic goes through Azure Firewall, search its logs for port 1688. Table and column names vary by version (resource-specific or AzureDiagnostics mode), so check what your workspace has:
AZFWNetworkRule
| where TimeGenerated > ago(1d)
| where DestinationPort == 1688
| project TimeGenerated, SourceIp, DestinationIp, Action, Policy, RuleCollection, Rule
| order by TimeGenerated desc
Inside the guest, check that no outbound Windows Firewall rule or third-party agent blocks 1688:
Get-NetFirewallProfile | Select-Object Name, Enabled, DefaultOutboundAction
Fixes
Forced tunnelling: send KMS traffic straight to the internet
Add two /32 routes to the route table on the VM's subnet. A more specific route wins over 0.0.0.0/0.
az network route-table route create -g rg-network --route-table-name rt-spoke-app \
-n azkms-1 --address-prefix 20.118.99.224/32 --next-hop-type Internet
az network route-table route create -g rg-network --route-table-name rt-spoke-app \
-n azkms-2 --address-prefix 40.83.235.53/32 --next-hop-type Internet
Warning
A next hop of
Internetonly works if the VM has outbound access, through NAT Gateway, a public IP or a load balancer outbound rule. On subnets without default outbound access the route alone won't fix it. If your security policy requires all egress to go through the firewall, allow TCP 1688 to both IPs on the firewall instead of bypassing it.
Azure Firewall: allow the flow
az network firewall policy rule-collection-group collection add-filter-collection \
-g rg-network --policy-name fwpol-hub --rcg-name rcg-platform \
--name allow-azure-kms --collection-priority 200 --action Allow \
--rule-type NetworkRule --rule-name azkms \
--source-addresses 10.0.0.0/8 --ip-protocols TCP \
--destination-addresses 20.118.99.224 40.83.235.53 --destination-ports 1688
For NSGs that deny outbound internet traffic, add an allow rule for TCP 1688 to the same two IPs, with a lower priority number than the deny rule.
Custom DNS
If your DNS servers can't resolve azkms.core.windows.net, add a forwarder (or a conditional forwarder for core.windows.net) to 168.63.129.16, or to your Azure DNS Private Resolver inbound endpoint. Then run ipconfig /flushdns on the VM.
Wrong KMS host, clock or product key
Reset the KMS host and activate:
cscript //nologo C:\Windows\System32\slmgr.vbs /skms azkms.core.windows.net:1688
cscript //nologo C:\Windows\System32\slmgr.vbs /ato
cscript //nologo C:\Windows\System32\slmgr.vbs /xpr
If a Group Policy or a script keeps setting the old host, fix it at the source, or the problem will come back at the next refresh.
If you see 0xC004F06C, fix the time first:
w32tm /query /status
w32tm /resync /force
For custom or migrated images that are not KMS clients, install the GVLK for the exact edition. Get it from Microsoft's "Key Management Services (KMS) client activation and product keys" page; don't copy keys from forums. Then run /ato:
cscript //nologo C:\Windows\System32\slmgr.vbs /ipk <GVLK-for-your-edition>
cscript //nologo C:\Windows\System32\slmgr.vbs /ato
Caution
Windows Server Datacenter: Azure Edition only activates in supported Azure environments. If you move it to a different host, it won't activate, and no network change can fix that.
Verify across the fleet
After a network change, check many VMs at once:
for vm in $(az vm list -g rg-prod --query "[?storageProfile.osDisk.osType=='Windows'].name" -o tsv); do
echo "== $vm"
az vm run-command invoke -g rg-prod -n "$vm" --command-id RunPowerShellScript \
--scripts "(Get-CimInstance SoftwareLicensingProduct -Filter \"PartialProductKey IS NOT NULL AND Name LIKE 'Windows%'\").LicenseStatus" \
--query "value[0].message" -o tsv
done
LicenseStatus 1 means licensed. Any other value needs follow-up.
Prevent it next time
- Put the two KMS /32 routes, or the firewall allow rule, in your hub/spoke templates so every new spoke gets them.
- Use IP addresses in firewall rules and routes, not only the old
kms.core.windows.netname. Review them when Microsoft announces endpoint changes in Service Health. - Include
Test-NetConnection azkms.core.windows.net -Port 1688in your VM build validation. - Collect the Application log through a data collection rule and alert on Security-SPP activation failures. Event IDs vary by Windows version, so confirm them on one failing VM first.
- Keep custom images as KMS clients (sysprep from a Marketplace base) so new VMs activate without anyone touching them.