Skip to main content
Troubleshooting GuideRDPNSG DiagnosticsAzure Bastion

How to Troubleshoot Azure VM RDP Connection Issues: NSG, Firewall, Public IP, and Bastion Fixes

"Remote Desktop can't connect to the remote computer" is Windows' way of saying it has no idea which of six possible layers actually failed. The NSG, a route table, the guest OS firewall, the public IP, the RDP service itself, or the VM's own boot state can each produce the exact same unhelpful message — which is why guessing wastes more time than working the stack in order, top to bottom, with the tools built specifically to answer each layer's question directly.

The failure signature this guide resolves
# The generic client-side error - identical regardless of root cause:

  "Remote Desktop can't connect to the remote computer for one of
   these reasons:
   1) Remote access to the server is not enabled
   2) The remote computer is turned off
   3) The remote computer is not available on the network"

# Documented symptom variants that all trigger this same investigation,
# per Microsoft's own RDP-NSG troubleshooting reference:

  - RDP connection times out or is refused
  - RDP works from one source IP but not another
  - RDP stops working immediately after an NSG rule change
  - A brand-new VM is unreachable via RDP right after deployment
  - Azure Bastion itself returns a generic "Connection failed" with
    no further detail

# The unhelpful part: NONE of these symptoms, by themselves, tell you
# which of the following actually failed:
#   Public IP / DNS  ->  NSG (subnet, NIC, OR security admin rules)
#   ->  Custom route table (UDR)  ->  Guest OS firewall  ->  RDP
#   service (TermService)  ->  VM boot / provisioning state

# Guessing which layer is broken wastes time. Working the stack in
# order, with the specific diagnostic tool built for each layer,
# does not.

Symptom: RDP connection attempts to an Azure VM fail, time out, or are refused, with an error message that gives no indication of which specific layer — network, guest OS, or VM state — is responsible.  Failure point: RDP connectivity depends on a chain of independent layers, any one of which can silently block the connection while producing an identical, generic client-side error.  Default platform behaviour: Azure gives you purpose-built diagnostic tools for nearly every layer in this chain — Network Watcher's IP Flow Verify and NSG Diagnostics, boot diagnostics, serial console, Run Command — but none of them run automatically. Diagnosis requires deliberately working through the stack.

3 layers, 1 tool
Network Watcher's NSG Diagnostics now checks security admin rules (AVNM), subnet NSG, and NIC NSG together — in one pass
Port 3389
The default RDP port — but a correct NSG rule doesn't guarantee the guest OS firewall or TermService are also configured correctly
Deployed = billed
Azure Bastion bills hourly from the moment it's deployed — regardless of whether anyone is actively connecting through it
Stopped ≠ reachable
Bastion cannot connect to a stopped/deallocated VM or one in a failed provisioning state — check VM power state before any network diagnosis

Every RDP connection to an Azure VM has to survive six independent checkpoints in sequence, and a failure at any single one produces the exact same generic client-side error — which is precisely why guessing at the cause wastes far more time than methodically working the stack from the outside in. A public IP has to exist and resolve. An NSG — now potentially three separate rule sets, not just one — has to allow the traffic. A route table, if customized, has to actually send the packet toward the VM rather than somewhere else. The guest operating system's own firewall has to permit the connection. The Remote Desktop service itself has to be running and listening. And the VM has to actually be powered on and healthy enough to accept connections at all. This guide walks each layer in the order that produces the fastest diagnosis, with the specific Azure tool built to answer that layer's question directly, and ends with the fix most organizations should be moving toward regardless: Azure Bastion, which removes an entire category of these failure points by design.

Figure 1 — Six checkpoints, one identical error message if any of them fails
WORK TOP TO BOTTOM — each layer has ONE specific Azure tool built to answer it directly1. PUBLIC IP / DNS — does a path to the VM even exist?Tool: az vm show, nslookup2. NSG RULES (subnet + NIC + security admin) — is 3389 allowed?Tool: IP Flow Verify, NSG Diagnostics3. ROUTE TABLES (UDRs) — is traffic actually routed TO the VM?Tool: Network Watcher Next Hop4. GUEST OS FIREWALL — is Windows Firewall blocking 3389 internally?Tool: Run Command, Serial Console5. RDP SERVICE (TermService) — is the service actually running?Tool: Run Command PowerShell6. VM POWER / BOOT STATE — is the VM even running and healthy?Tool: Boot Diagnostics, Serial ConsoleA failure at ANY layer produces the same client-side error. Skipping layers to guesswastes time; the diagnostic tool for each layer answers that layer's question directly, in minutes.
RDP connectivity is a chain, not a single check — and every link uses a different mechanism entirely (network routing, firewall rules, an OS-level service, VM power state). Each layer has a specific, purpose-built Azure diagnostic tool, and working through them in order — rather than jumping straight to reconfiguring an NSG that might not even be the problem — is consistently the fastest path to a fix.
01The Symptom Determines Nothing — Work the Stack, Not the Error MessageRoot Cause

Microsoft's own RDP-NSG troubleshooting reference lists several distinct symptoms that all point back to the same investigation: connection timeouts, connection refusals, RDP working from one source IP but not another, RDP breaking immediately after an unrelated NSG change, and a freshly-deployed VM being unreachable from the start. None of these symptom variants, on their own, tells you which layer actually failed — a timeout can mean a blocked NSG rule, a misrouted UDR, or a VM that's still booting, and the client gives you no way to distinguish between them from the error message alone.

Symptom patternWhat it suggests investigating first
Works from one IP, not anotherAn NSG rule scoped to a specific source IP or range — check rule source scoping
Broke immediately after an NSG changeThe change itself — review the specific rule modified, check for priority conflicts
New VM, never workedMissing inbound rule entirely, or no public IP assigned at deployment
Worked before, now times out with no recent changesVM power state, guest OS firewall drift, or a platform-level issue — check boot diagnostics first

These patterns are useful hints, not diagnoses — they narrow where to look first, but the actual confirmation always comes from running the specific tool for that layer, not from pattern-matching the symptom alone.

02Layer 1 — Public IP, DNS, and Basic ReachabilityFoundation

Before touching any firewall or NSG configuration, confirm the most basic prerequisite: does a network path to the VM exist at all from where you're connecting?

Azure CLI — confirm the VM has a public IP and it's actually attachedaz vm show \ --resource-group myResourceGroup \ --name myVM \ --show-details \ --query "publicIps" # Empty result means NO public IP is assigned. If you're connecting # from the public internet (not via VPN/ExpressRoute), this alone # fully explains the failure - there is no path for the connection # to take, regardless of how correct every other layer is.
No public IP and not on a private network path is a complete, simple explanation — don't skip past it

If the VM has no public IP and you're not connected via VPN, ExpressRoute, or peering to the VM's virtual network, an internet-based RDP client has no possible path to reach it — this isn't a misconfiguration to fix at the NSG or firewall layer, it's a fundamental connectivity gap. The fix is either assigning a public IP (with the security trade-offs that implies), establishing private connectivity, or — the better modern answer covered in Section 8 — using Azure Bastion, which requires no public IP on the VM at all.

Test from another VM in the same subnet to isolate client-side network issues

If a public IP exists and DNS resolves correctly, but RDP still fails, test connectivity from another VM inside the same virtual network before assuming the problem is Azure-side. This isolates whether the issue is specific to your client's network path (a corporate firewall blocking outbound 3389, for instance) versus something genuinely wrong with the target VM's configuration — a distinction that changes where the rest of this guide's diagnosis should focus.

03Layer 2 — NSG Rules: Subnet, NIC, and Security Admin RulesCore Diagnosis

This is where most RDP failures actually live, and it's also where the diagnosis has gotten more layered than many existing runbooks account for. Traffic to a VM's RDP port can now be blocked at up to three distinct rule layers, evaluated in a specific order.

Rule layerWhere it's managedEvaluated
Security admin rulesAzure Virtual Network Manager (organization-wide, if deployed)First — before any NSG
Subnet NSGAttached to the VM's subnetSecond
NIC NSGAttached directly to the VM's network interfaceThird — combined with subnet NSG into "effective" rules

Common, documented causes of an RDP block at this layer: a missing inbound Allow rule for TCP 3389 entirely, a Deny rule with a lower priority number (higher precedence) than an Allow rule for the same traffic, and — a subtle, frequently-missed one — a conflict between subnet-level and NIC-level NSGs, where one allows the traffic and the other denies it, and only the combined "effective" rule set determines the actual outcome.

Azure CLI — the correct minimal inbound rule for RDPaz network nsg rule create \ --resource-group myResourceGroup \ --nsg-name myVM-nsg \ --name Allow-RDP-3389 \ --priority 100 \ --direction Inbound \ --access Allow \ --protocol Tcp \ --source-address-prefixes '<your-known-source-IP-or-range>' \ --source-port-ranges '*' \ --destination-port-ranges 3389 # Scope source-address-prefixes to a known IP/range whenever possible. # A rule open to the entire internet (0.0.0.0/0) on 3389 is exactly # the exposure Azure Bastion (Section 8) exists to eliminate.
Review effective security rules, not just the rules you think you configured

Because subnet and NIC NSGs combine, and because Azure Virtual Network Manager's security admin rules (if your organization has deployed AVNM) are evaluated before either, the rule you're looking at in the portal may not be the rule actually determining the outcome. Azure's "Effective security rules" view for a NIC shows the fully combined, actually-enforced rule set — this is the source of truth, not any single NSG's rule list in isolation.

04Diagnosing NSG Blocks: IP Flow Verify and NSG DiagnosticsThe Fix

Rather than manually reading through every rule across every layer, Network Watcher provides two purpose-built tools that answer the question directly: is this specific traffic allowed or denied, and which rule is responsible.

IP Flow Verify — fastest for a single, specific traffic test

Azure CLI — test whether inbound RDP from a specific source IP is allowedaz network watcher test-ip-flow \ --resource-group myResourceGroup \ --vm myVM \ --direction Inbound \ --protocol TCP \ --local 10.0.0.4:3389 \ --remote 203.0.113.50:60000 # Replace 10.0.0.4 with the VM's actual private IP, and 203.0.113.50 # with the IP you're connecting FROM. The remote port can be any # unused high port (60000 is just a convention). # Result: "Allowed" or "Denied", PLUS the specific NSG rule name # responsible for that outcome. This is the single fastest way to # find the exact blocking rule, rather than reading rule lists by eye.

NSG Diagnostics — checks all three rule layers together, including AVNM

For environments using Azure Virtual Network Manager, NSG Diagnostics is the more complete tool: it explicitly checks security admin rules (labeled GlobalRules in its output), the subnet NSG, and effectively the NIC NSG together in a single pass — surfacing exactly which of the three layers is responsible for a block, something IP Flow Verify alone won't distinguish when multiple layers are in play.

If IP Flow Verify says "Allowed" but RDP still fails, the problem has moved to a different layer

IP Flow Verify and NSG Diagnostics only answer the NSG question — they confirm whether network-layer traffic is permitted to reach the VM's network interface. A clean "Allowed" result definitively rules out NSGs (and, via NSG Diagnostics, security admin rules) as the cause, and means the investigation needs to move to Sections 5 through 7: route tables, the guest OS firewall, the RDP service itself, or the VM's power/boot state. Don't keep re-checking NSG rules once this tool has confirmed they aren't the problem.

05Layer 3 — Custom Routes (UDRs) Silently Rerouting TrafficDiagnosis

If the subnet has a custom route table attached — common in hub-and-spoke architectures routing traffic through a central firewall or network virtual appliance — RDP traffic that's correctly allowed by every NSG can still fail to reach the VM if a route sends it somewhere other than directly to the VM's network interface.

Azure CLI — verify the actual next hop for traffic destined to the VMaz network watcher show-next-hop \ --resource-group myResourceGroup \ --vm myVM \ --source-ip <your-client-IP> \ --dest-ip 10.0.0.4 # Confirms whether the effective route sends this traffic where you # expect. A route unexpectedly pointing to a firewall/NVA, a VPN # gateway, or "None" instead of the VM's own subnet indicates a UDR # is intercepting or misrouting the traffic before it reaches the VM.
This layer is specifically worth checking when NSGs check out clean but RDP still fails

Custom route tables are the layer most often skipped in RDP troubleshooting, precisely because NSGs get blamed first and route tables are less visible in day-to-day operations. If IP Flow Verify confirms the NSG layer is allowing traffic but RDP still doesn't connect, a UDR silently routing the return traffic (or the inbound traffic itself, in an asymmetric routing scenario) through an unintended path is a strong next suspect, particularly in any environment using a hub-and-spoke or Virtual WAN topology with centralized firewall inspection.

06Layer 4 — Inside the Guest: Windows Firewall and the RDP ServiceDiagnosis

Every layer covered so far operates outside the VM. Once network-layer traffic is confirmed to reach the VM's NIC correctly, the remaining possible failures live inside the guest operating system itself — and since RDP is the thing that's broken, you obviously can't use RDP to check it. Azure Run Command and Serial Console exist specifically for this situation.

Azure CLI — check via Run Command whether TermService is running and firewall allows 3389az vm run-command invoke \ --resource-group myResourceGroup \ --name myVM \ --command-id RunPowerShellScript \ --scripts "Get-Service -Name TermService | Select-Object Status, StartType" # Confirms the Remote Desktop service itself is Running, not Stopped # or Disabled. If it's not running, this alone fully explains the # failure regardless of how correct every network layer is.
Azure CLI — check the built-in Windows Firewall Remote Desktop ruleaz vm run-command invoke \ --resource-group myResourceGroup \ --name myVM \ --command-id RunPowerShellScript \ --scripts "Get-NetFirewallRule -DisplayGroup 'Remote Desktop' | Select-Object DisplayName, Enabled" # The built-in "Remote Desktop" firewall rule group must show # Enabled=True for TCP 3389. If disabled or removed, re-enable it: # Enable-NetFirewallRule -DisplayGroup 'Remote Desktop'
A correct NSG doesn't guarantee a correct guest firewall — these are genuinely independent controls

It's a common assumption that once the NSG allows RDP traffic, the connection should work — but the NSG only controls traffic reaching the VM's network interface at the Azure platform level. The guest operating system's own Windows Firewall is a completely separate, independent control that has to also permit the traffic once it arrives. A Group Policy change, a security hardening script, or manual intervention inside the guest can disable or remove the built-in Remote Desktop firewall rule without touching the NSG at all — producing an identical "can't connect" symptom from a layer the NSG diagnosis already ruled out.

07Layer 5 — VM State: Boot Diagnostics and Serial ConsoleDiagnosis

The final layer, and the one to check when every network and guest-OS-level check has come back clean: is the VM itself actually healthy, fully booted, and in a state capable of accepting connections at all?

  • Boot diagnostics reviews the VM's console logs to determine whether the VM is reporting a startup issue — not enabled on every VM by default, so this step may require enabling it first, but when available it's often the fastest way to spot a VM stuck mid-boot or reporting an OS-level error.
  • Serial console provides direct, text-based access to the VM's console independent of any network path — genuinely useful specifically because it doesn't depend on the network layer working at all, making it usable even when every other diagnostic approach in this guide has been blocked by the same underlying network issue.
Check VM power and provisioning state before any deeper diagnosis, not after

A VM that's stopped (deallocated) or stuck in a failed provisioning state will fail RDP for reasons that have nothing to do with networking, firewalls, or the RDP service — and no amount of NSG or guest-firewall troubleshooting will fix a VM that simply isn't running. az vm show --resource-group myResourceGroup --name myVM --query "provisioningState, powerState" is a fast, first-principles check worth running before investing time in any of the network-layer diagnosis this guide covers — cheap to check, and it fully explains the failure if the answer is anything other than "VM running."

08The Bastion Fix: Bypassing Public Exposure EntirelyThe Real Fix

Everything in Layers 1 through 3 exists specifically to manage the risk of exposing RDP to the public internet at all. Azure Bastion removes that entire category of problem by design: it provides secure RDP and SSH access through the Azure portal or native client, with no public IP required on the target VM at all, no VPN client to manage, and no permanently-open internet-facing RDP port to defend.

AspectDirect RDP (public IP + NSG)Azure Bastion
Public IP on the VMRequiredNot required — VM stays fully private
Internet-facing open port3389 open to some source rangeNone — Bastion itself is the only internet-facing surface
Client requirementAny RDP clientBrowser (HTML5) or native client, via the Azure portal
NSG surface to defendDirect VM-facing rule, ongoing maintenanceBastion subnet rules — centrally managed, once
Recommended by Microsoft as the modern defaultNo — explicitly recommended against for permanent exposureYes, alongside Just-In-Time VM access
Microsoft's own NSG best-practice guidance recommends Bastion or JIT over permanently open RDP

Current Azure networking best-practice documentation states this directly: instead of opening RDP/SSH ports permanently, use Azure Bastion or just-in-time (JIT) VM access for secure management access. This isn't a preference — it's the explicit, current recommendation, and most of the troubleshooting complexity in Sections 2 through 5 of this guide (NSG rule layering, source IP scoping, route table interactions) exists specifically because RDP is exposed to the internet at all. Bastion doesn't just fix connection failures faster; it removes the ongoing operational burden of correctly maintaining that exposure in the first place.

Figure 2 — Bastion's own NSG requirements, on BOTH sides of the connection
TWO SEPARATE NSGs, EACH WITH ITS OWN REQUIRED RULES — miss either and Bastion fails silentlyAzureBastionSubnet NSG(Bastion host's own subnet)Ports 8080 + 5701, VirtualNetwork → VirtualNetworkPort 443, AzureLoadBalancer → (health probes)Omit these = Bastion loses platformupdates + opens future vulnerabilitiesTarget VM Subnet NSG(where the actual VM lives)Ports 3389/22, INBOUND fromAzureBastionSubnet CIDR (NOT the internet)Without this rule, Bastion CANNOTreach the VM - even if Bastion itselfis deployed and configured perfectlyAlso check: is the VM actually running? Bastion cannot connect to a stopped(deallocated) VM or one with a failed provisioning state — this fails identicallyto an NSG misconfiguration, with the same generic "Connection failed" message.
Bastion connection failures most commonly come from a missing rule on one of two separate NSGs: the Bastion host's own subnet NSG (needing internal communication and health-probe ports) and the target VM's subnet NSG (needing to explicitly allow inbound RDP/SSH from the Bastion subnet's address range). Both are required; missing either produces the same unhelpful "Connection failed" message Bastion shows for almost every failure mode.
09Bastion-Specific Connection Failures and Their NSG RequirementsThe Fix

Bastion's generic "Connection failed" error is, if anything, less informative than the direct-RDP client error — which makes knowing its specific, documented requirements even more valuable than trying to interpret the message itself.

Azure CLI — the required inbound rule on the TARGET VM's subnet NSGaz network nsg rule create \ --resource-group myRG \ --nsg-name nsg-vm-subnet \ --name AllowBastionInbound \ --priority 100 \ --direction Inbound \ --access Allow \ --protocol Tcp \ --source-address-prefixes 10.0.1.0/26 \ --source-port-ranges '*' \ --destination-port-ranges 3389 22 # Replace 10.0.1.0/26 with your actual AzureBastionSubnet CIDR range. # Scope the source to the Bastion subnet specifically - not the # broader internet or VirtualNetwork tag - so only Bastion itself # can open these ports.
Bastion connection failure causeWhere to look
Missing rule on VM subnet NSGThe #1 documented cause — verify the rule above exists and is correctly scoped
Missing internal rules on AzureBastionSubnet NSGPorts 8080/5701 (VirtualNetwork-to-VirtualNetwork) and 443 (AzureLoadBalancer) — omitting these blocks platform updates
VM stopped or deallocatedCheck power state directly — Bastion cannot connect regardless of NSG correctness
VM in failed provisioning stateCheck provisioning state — same effect as a stopped VM
Custom port requested on Basic SKUCustom RDP/SSH ports require Standard or Premium SKU — Basic only supports the default ports
Bastion bills hourly from deployment — factor this into non-production environments

Azure Bastion's hourly pricing begins the moment it's deployed, independent of outbound data usage or whether anyone is actively connecting through it. For non-production environments used intermittently, this is worth factoring into the cost model explicitly — deploying Bastion once per environment and leaving it running continuously accrues cost even during periods with zero actual RDP/SSH sessions.

10Step-by-Step: The Complete Diagnostic SequenceHow-To
  1. Check VM power and provisioning state first

    az vm show --resource-group myRG --name myVM --query "provisioningState, powerState" — cheapest possible check, and it fully explains the failure if the VM isn't actually running.

  2. Confirm a network path exists — public IP and DNS, or private connectivity

    Verify the VM has a public IP (if connecting over the internet) or that you have private connectivity (VPN, ExpressRoute, peering) to the VNet. No path means no amount of NSG tuning will help.

  3. Run IP Flow Verify for the specific source IP and port

    az network watcher test-ip-flow against inbound TCP 3389 from your actual connecting IP. A "Denied" result names the exact rule to fix; an "Allowed" result rules out NSGs entirely.

  4. If AVNM is in use, run NSG Diagnostics to check security admin rules too

    Confirms whether a centrally-managed security admin rule — evaluated before any NSG — is the actual source of the block, which IP Flow Verify alone may not fully distinguish.

  5. If NSGs check out clean, verify routing with Next Hop

    az network watcher show-next-hop confirms traffic destined for the VM is actually routed there, rather than intercepted by an unrelated UDR.

  6. Use Run Command to check the guest OS firewall and RDP service

    Confirm TermService is running and the built-in "Remote Desktop" firewall rule group is enabled — both independent of, and invisible to, every network-layer check performed so far.

  7. If still unresolved, review boot diagnostics and serial console

    Rules out a VM stuck mid-boot or reporting an OS-level startup error that no amount of network or firewall correctness would fix.

  8. Test via Azure Bastion as a diagnostic shortcut, regardless of your eventual access method

    If Bastion can connect where direct RDP couldn't, that specifically isolates the problem to the direct-RDP network path (public IP exposure, NSG scoping) rather than the VM or guest OS — a fast way to narrow the investigation even if Bastion isn't your long-term access strategy.

Validation & Verification: Confirm the Fix

Once a fix is applied at whichever layer was actually broken, confirm the specific mechanism, not just "RDP works now" — a connection succeeding once doesn't confirm the underlying issue is durably resolved.

Step 1 — Re-run IP Flow Verify to confirm the NSG layer explicitlyaz network watcher test-ip-flow \ --resource-group myResourceGroup \ --vm myVM \ --direction Inbound \ --protocol TCP \ --local 10.0.0.4:3389 \ --remote <your-actual-source-IP>:60000 # PASS: "Allowed", with the rule name matching your intended fix. # FAIL: still "Denied" - check for a higher-priority Deny rule or # a security admin rule (AVNM) overriding your NSG change.
Step 2 — Confirm the guest OS firewall and service state directlyaz vm run-command invoke \ --resource-group myResourceGroup \ --name myVM \ --command-id RunPowerShellScript \ --scripts "Get-Service TermService; Get-NetFirewallRule -DisplayGroup 'Remote Desktop' | Select DisplayName, Enabled" # PASS: TermService status = Running, firewall rules = Enabled True. # FAIL: investigate whether Group Policy or a hardening script is # re-disabling these on a schedule - a fix that reverts itself.
Step 3 — For Bastion specifically, verify both NSGs independently# Check the target VM subnet NSG allows Bastion's source range az network watcher test-ip-flow \ --resource-group myResourceGroup \ --vm myVM \ --direction Inbound \ --protocol TCP \ --local 10.0.0.4:3389 \ --remote 10.0.1.4:60000 # a representative Bastion subnet IP # PASS: "Allowed" from the Bastion subnet range specifically. # Also re-confirm the AzureBastionSubnet NSG still has its required # internal ports (8080, 5701, 443) - these are easy to accidentally # remove during unrelated NSG cleanup.
What "fixed" actually means here

Three conditions, together. One: the network-layer diagnostic (IP Flow Verify, and NSG Diagnostics if AVNM is in scope) explicitly confirms "Allowed" for the actual source IP and port in question, not just an assumption based on the rule you added. Two: the guest OS layer — TermService running, the Remote Desktop firewall rule group enabled — is confirmed directly via Run Command, since a network-layer fix alone doesn't rule out an independent guest-level block. Three: the fix is tested from the actual client and network path real users will use, not just from a convenient internal test VM — a fix validated only from inside the VNet can still fail for the internet-based or Bastion-based access pattern actual users depend on.

12Anti-PatternsTraps
Anti-patternWhy it feels rightWhy it isn't
Opening RDP to 0.0.0.0/0 "just to get it working" during troubleshooting"Rule out NSG scoping quickly"Creates real, immediate internet exposure — use IP Flow Verify with your actual source IP instead of widening the rule to test
Reconfiguring the NSG repeatedly without running IP Flow Verify first"Let's just try a broader rule"Guessing wastes time the diagnostic tool would have resolved in one call — always confirm the actual blocking rule before changing anything
Assuming a correct NSG means RDP will work"The network layer is the whole story"The guest OS firewall and RDP service are independent controls the NSG has no visibility into — a clean NSG diagnosis doesn't rule these out
Not checking VM power/provisioning state until every other layer is exhausted"Obviously the VM is running"It's the cheapest check in the entire guide — verify it first, not last, since it fully explains the failure when true
Treating Bastion's "Connection failed" as unrelated to standard NSG troubleshooting"Bastion is a different service, different problem"The overwhelming majority of Bastion failures are the same category of NSG misconfiguration this guide already covers — just on two NSGs instead of one
Keeping permanent public RDP exposure after diagnosing and fixing the immediate issue"It's fixed now, no need to change the architecture"Fixing the symptom without addressing the underlying exposure means the same class of problem — and the associated security risk — recurs. Migrate to Bastion or JIT once the immediate fire is out

Key Takeaways

The generic RDP error tells you nothing about which layer failed. Six independent checkpoints can each produce the identical client-side message — work the stack in order, don't guess.
NSG diagnosis now spans up to three rule layers. Security admin rules (AVNM), subnet NSG, and NIC NSG — NSG Diagnostics checks all three together; IP Flow Verify pinpoints the exact blocking rule fastest.
A correct NSG doesn't guarantee a correct guest OS firewall. These are genuinely independent controls — verify TermService and the Windows Firewall rule directly via Run Command, don't assume.
Check VM power state first, not last. It's the cheapest possible check and fully explains the failure when the answer isn't "running."
Bastion requires correct NSG rules on BOTH the Bastion subnet and the target VM subnet. Missing either produces the same unhelpful "Connection failed" message.
Bastion bills hourly from deployment, regardless of usage. Factor this into intermittent, non-production access patterns.
Bastion or JIT access is Microsoft's current explicit recommendation over permanently open RDP. It removes an entire category of the failure modes this guide covers, not just this one incident.

Frequently Asked Questions

What's the fastest way to find which NSG rule is blocking RDP?
Azure Network Watcher's IP Flow Verify tool, run via az network watcher test-ip-flow or the Azure portal, is the fastest way to identify the exact blocking rule. Specify the VM, its network interface, inbound direction, TCP protocol, the VM's private IP with port 3389 as the local endpoint, and your actual connecting IP as the remote endpoint. The result returns "Allowed" or "Denied" along with the specific name of the NSG rule responsible for that outcome — eliminating the need to manually read through every rule across the subnet and NIC NSGs to find the one actually causing the block. If your organization uses Azure Virtual Network Manager, also run NSG Diagnostics, which additionally checks security admin rules (evaluated before any NSG) that IP Flow Verify alone may not fully account for.
Why does RDP fail even though the NSG allows port 3389?
A correctly-configured NSG only controls whether traffic is permitted to reach the VM's network interface at the Azure platform level — it has no visibility into, or control over, what happens once that traffic arrives at the guest operating system. Several independent layers beyond the NSG can still block a connection: the guest OS's own Windows Firewall may be blocking port 3389 internally (check via Run Command, since RDP itself isn't available to check it directly), the Remote Desktop service (TermService) may not be running, a custom route table (UDR) may be routing the traffic somewhere other than directly to the VM, or the VM itself may not be fully booted or healthy. Confirming the NSG allows traffic via IP Flow Verify is a necessary first step, but it only rules out one of several possible causes — the remaining layers this guide covers need to be checked independently.
Why is Azure Bastion showing "Connection failed" with no other detail?
Bastion's generic error message covers a wide range of underlying causes, but the most common one by a significant margin is a missing NSG rule on the target VM's own subnet — Bastion requires an explicit inbound rule allowing TCP 3389 (or 22 for SSH) from the AzureBastionSubnet's specific address range, and without it, Bastion simply cannot reach the VM even though Bastion itself is deployed and configured correctly. The second most common cause is a missing or incomplete rule set on the AzureBastionSubnet's own NSG — specifically, ports 8080 and 5701 for internal Bastion host communication and port 443 from the AzureLoadBalancer service tag for health probes; omitting any of these can block Bastion from receiving platform updates and cause connection failures. Beyond NSG issues, also verify the target VM is actually running — Bastion cannot connect to a stopped (deallocated) VM or one in a failed provisioning state, and this produces the identical generic error message.
Should I keep RDP open to the internet, or switch to Azure Bastion?
Current Azure networking best-practice guidance explicitly recommends against permanently exposing RDP to the internet, favoring Azure Bastion or Just-In-Time (JIT) VM access instead. Bastion requires no public IP on the target VM at all — the VM stays fully private, and Bastion itself is the only internet-facing (or portal-facing) surface, centrally managed with a known, documented set of NSG requirements rather than an ongoing per-VM exposure to defend. This isn't only a security recommendation — it also directly reduces the troubleshooting surface area covered in this guide, since a private VM with no exposed RDP port eliminates several of the failure modes (public IP misconfiguration, overly broad or overly narrow source IP scoping on an internet-facing rule) entirely. The trade-off is Bastion's hourly cost, which begins from deployment regardless of usage — worth factoring in for environments accessed only intermittently, but generally a reasonable cost relative to the security and operational benefit for production environments.

Popular posts from this blog

The Cloud Incumbent: AWS Bedrock Hosts Every Frontier Model and Amazon Is Betting on Neutrality AWS at $37.6 billion quarterly revenue, growing 28%. $13 billion invested in Anthropic. A $100 billion Anthropic-to-AWS commitment. Trainium with $225 billion in customer revenue commitments. The most quietly powerful AI strategy in the race. By Francis Avorgbedor | Azure Engineer  ·  July 4, 2026  ·  14 min read  ·  Amazon · AWS · Cloud AI 74 SEVENAI Momentum Score — Rank #5 $37.6B AWS Q1 2026 revenue — 28% YoY growth ▲ Fastest in 15 quarters $13B Total Amazon investment in Anthropic to date ▲ Strategic anchor 100K+ Customers running Claude on AWS Bedrock ▲ Distribution moat Amazon's AI strategy is built on a thesis that every other Magnificent Seven company is testing against — and that Amazon is uniquely positioned to win regardless of the outcome. The thesis is neutrality. In a race where Microsoft has bet on OpenAI, Google has bet on Gemini, and Meta has bet...
Performance Fix Foundry Local 1.2 Linux ARM64 Embeddings Offline ASR The Edge Latency Drop: Fixing Latency Spikes by Offloading Embeddings to Foundry Local 1.2 You are paying a full cloud round trip — network, TLS, queue, throttle risk — to turn a twelve-word search query into a vector. That is the most expensive way possible to do one of the cheapest computations in your stack. Foundry Local 1.2 now runs on Linux ARM64, which means embeddings and speech recognition can happen on a Raspberry Pi, a Jetson, or a Graviton instance — offline, unmetered, and in single-digit milliseconds. The failure signature this guide resolves # Application Insights — the embedding call, not the LLM, is your tail latency: name p50 p95 p99 calls/day POST /embeddings (cloud) 89 ms 412 ms 3,847 ms 1,240,000 POST /chat/completions (cloud) 940 ms 1,720 ms 2,910 ms 38,000 ^^^^^^^^ ...

How to Reset an Azure Virtual Machine to Factory Settings Using a Managed Disk

How to Reset an Azure Virtual Machine to Factory Settings Using a Managed Disk Azure does not have a single "factory reset" button. What it does have is something better: the OS Disk Swap — a method that swaps out the corrupted or misconfigured OS disk for a clean Windows Server managed disk without deleting the VM, its NICs, its IP addresses, or any attached data disks. Here is how it works, when to use it, and the exact steps to execute it safely. FA Francis Avorgbedor Azure Engineer July 16, 2026 15 min read Azure VMs · Windows Server · Real-World Fix 3 Methods to achieve a clean Windows Server installation on an existing Azure VM ~15min Typical OS Disk Swap duration — VM retains its NICs, IPs, and data disks throughout 0 Data disks affected by an OS Disk Swap — data disks remain attached and untouched 1 Snapshot of the original OS disk you must take before starting — no exceptions Introduction Why Azure Does Not Have a Simple Factory Reset — and What to Do Instead On a ph...

AKS CrashLoopBackOff, Pending Pods, and NotReady Nodes — The Real Fixes Engineers Use

Incident Playbook AKS Kubernetes kubectl 2026 AKS CrashLoopBackOff, Pending Pods, and NotReady Nodes — The Real Fixes Engineers Use Every AKS engineer eventually faces the same nightmare: CrashLoopBackOff at 2am, pods stuck Pending for no clear reason, or nodes flipping to NotReady mid-deployment. The difference between panic and control is knowing the exact diagnostic sequence — and the real fixes that work in production. This guide gives you both. 3 commands get pods, describe pod, and logs diagnose roughly 90% of AKS incidents before you touch anything else Exit 137 The code that means OOMKilled — the container hit its memory limit and was killed by the kernel (128 + SIGKILL 9) Events The bottom of kubectl describe is where the real cause lives — Pending, FailedScheduling, and image errors all surface there CoreDNS The single component behind most "intermittent" production failures — service discovery breaks quietly and looks like an app bug Table of Contents 01 The 3 Comm...
Can I Update My Old Computer to Windows 11 — and How Much Will It Cost? Your i7, 16GB RAM, 512GB SSD machine is powerful enough to run Windows 11 comfortably. The TPM 2.0 and Secure Boot wall is a security checkbox, not a performance ceiling. Here are two proven ways to get past it, what each one costs, and what you are trading away by doing so. $0 Cost of the Windows 11 licence if your existing Windows 10 is genuine — the upgrade remains free in 2026 2 Proven methods to bypass TPM 2.0 and Secure Boot — Rufus (easy) and Registry edit (manual) 25H2 Current Windows 11 version — all known bypass methods tested and confirmed working as of July 2026 Oct 2025 Windows 10 end of life — no more security updates. Staying on Windows 10 now carries real risk. First — Check Your BIOS Before Anything Else You Might Not Actually Need a Bypass Before running any bypass, open your BIOS and look at two settings. Many computers that fail the Windows 11 compatibility check have TPM 2.0 present in the hard...

Top 100 Best AI Tools for Software Engineers and DevOps Professionals in 2026

2026 Edition 100 Tools Software Engineering DevOps AIOps Top 100 Best AI Tools for Software Engineers and DevOps Professionals in 2026 85% of developers now regularly use AI tools. Fully AI-generated code accounts for nearly 28% of all pull requests. The question is no longer whether to use AI tools — it is which ones, in which combination, for which part of the lifecycle. This guide cuts through the noise: 100 tools, 10 categories, honest pricing, real use cases, and a selection framework for building your stack without redundancy. 85% Percentage of developers who now regularly use AI tools, per JetBrains' 2025 State of Developer Ecosystem report — up from near zero three years ago 28% Share of all pull requests containing primarily AI-generated code in 2026 — the metric that signals AI coding assistants have moved from experiment to workflow $50B Cursor's reported valuation in April 2026 Series D talks — the number that signals investor confidence in the AI developer tools ma...

Azure Files vs Azure NetApp Files: Which One Should You Choose?

Azure Files vs Azure NetApp Files: Which One Should You Choose? Performance tiers, protocol support, dual-protocol capability, pricing models, SAP/Oracle/HPC suitability, data management features, and the decision framework that maps each workload type to the right service — with step-by-step setup procedures for both. FA Francis Avorgbedor Azure Engineer July 15, 2026 20 min read Azure Storage · Architecture 4 Azure Files tiers: Premium SSD, Standard Hot, Cool, Tx Optimized 3 ANF performance tiers: Standard, Premium, Ultra — all SSD-backed 4TiB ANF minimum provisioning — significant cost floor for small workloads Dual ANF serves the same data via SMB and NFS simultaneously — AF cannot Introduction Two Services, One Surface Area — Completely Different Purposes Microsoft offers two fully managed, enterprise-grade file storage services in Azure. They share a surface area — both serve file shares over standard protocols, both run on managed infrastructure, and both integrate with Microsof...
Troubleshooting Guide AKS Kubernetes Real Solutions kubectl Azure Kubernetes Service (AKS) Troubleshooting Guide: Real Solutions to Common Problems CrashLoopBackOff at 2am. Pods stuck Pending with no obvious cause. Nodes going NotReady mid-deployment. DNS resolution silently failing in production. Every AKS engineer encounters these — the difference between engineers who panic and engineers who stay calm is knowing the exact sequence of diagnostic commands to run. This guide gives you that sequence, the root cause analysis for each failure mode, and the fix. 3 commands 90% of AKS problems are diagnosed with the same three kubectl commands: describe pod, logs --previous, and get events — in that order, every time Exit 137 The exit code that tells you everything: container killed by SIGKILL — either the Linux OOM killer (memory limit exceeded) or kubelet after grace period expired 5 min The CrashLoopBackOff ceiling: Kubernetes applies exponential backoff (10s → 20s → 40s → 80s → 160s → 3...

How to Deploy an AI Chatbot on Azure Using Azure OpenAI and App Service

Step-by-Step Guide Azure OpenAI App Service Production Python How to Deploy an AI Chatbot on Azure Using Azure OpenAI and App Service From zero to a production-grade AI chatbot: provision Azure OpenAI, write a streaming Flask API backend, deploy it on Azure App Service with Managed Identity, wire in conversation history and content safety, and instrument it with Application Insights — all with complete code and Terraform IaC. No API keys in environment variables. No hardcoded secrets. No half-finished PoC patterns. 7 phases This guide covers the full deployment lifecycle: architecture design → resource provisioning → backend code → App Service deployment → streaming → security → monitoring Zero keys The chatbot authenticates to Azure OpenAI using Managed Identity and DefaultAzureCredential — no API keys stored in environment variables, Key Vault, or code SSE Server-Sent Events stream GPT tokens to the browser as they generate — the same token-by-token typing effect users expect from pr...
Planning Guide Site-to-Site VPN Gateway SKUs BGP & IPsec Azure Site-to-Site VPN Prerequisites: Network Planning, Gateway Requirements, IPsec, and Routing Half of what determines whether a Site-to-Site VPN deployment goes smoothly happens before the gateway exists at all — the subnet size, the SKU, the address space plan, and the ASN choice are all decisions that are painful or impossible to change once traffic is flowing. Get the prerequisites right, and the actual gateway creation is the boring, predictable part. Current status, verified Azure is actively consolidating VPN Gateway SKUs. The Basic SKU and the original VpnGw1–VpnGw5 SKUs are being phased out in favor of zone-redundant AZ SKUs (VpnGw1AZ through VpnGw5AZ), which Microsoft now recommends for all new deployments. After June 2026, Azure will attempt to automatically migrate remaining Standard and High Performance SKU gateways — and that migration can fail outright if the gateway subnet is too small for the target SKU. T...