How to Troubleshoot Azure Bastion Connection Issues

Microsoft Fix Intermediate 14 min read Official Docs Grounded Updated April 20, 2026

Why Azure Bastion Troubleshooting Is So Frustrating

I've seen this exact scenario on dozens of Azure environments: you spin up Azure Bastion to give your team secure, browser-based RDP and SSH access without exposing VMs to the public internet , and then it just... doesn't work. You click Connect in the Azure portal, wait, and get a blank screen, a timeout, or some cryptic error like "Connection refused" or "Failed to connect to the remote computer". No useful details. No obvious place to start.

I know this is frustrating , especially when Azure Bastion is supposed to make secure access simpler, not harder. The problem is that Azure Bastion sits at the intersection of four or five different Azure networking constructs, and a misconfiguration in any one of them will silently kill your sessions. The portal's error messages rarely tell you which layer is broken.

Here's what's actually going on under the hood. Azure Bastion works by deploying a managed PaaS gateway inside a dedicated subnet called AzureBastionSubnet inside your virtual network. When you initiate a session, your browser connects to Bastion over HTTPS (port 443), and Bastion then proxies an RDP (port 3389) or SSH (port 22) connection to your target VM, all without your VM needing a public IP address. That flow crosses Network Security Groups (NSGs) twice: once on the Bastion subnet and once on the VM's NIC or subnet. It also depends on the Bastion host being in a Succeeded provisioning state, the correct SKU being selected, and the target VM being reachable within the VNet.

The most common root causes I see in the field, ordered by frequency:

  • NSG rules on AzureBastionSubnet blocking required control-plane traffic (GatewayManager, AzureLoadBalancer, AzureCloud service tags)
  • NSG rules on the target VM's subnet or NIC blocking inbound traffic from the VirtualNetwork service tag on ports 3389 or 22
  • Azure Bastion host stuck in a Failed or Updating provisioning state after a deployment error
  • The AzureBastionSubnet being too small (minimum /26 CIDR block required) or having a user-defined route (UDR) with a default route pointing to an NVA
  • Using the Basic SKU when you need features only available in Standard SKU, like native client support or IP-based connections
  • Browser or network proxy blocking WebSocket connections required by the portal-based client
  • Token expiration or authentication issues when using the Azure Bastion native client with az network bastion CLI commands

Each one of those root causes has a different fix. This guide walks through all of them systematically, starting with the fastest win. Browse all Microsoft fix guides →

The Quick Fix, Try This First

Before you spend an hour digging through NSG rules, start here. About 40% of Azure Bastion troubleshooting cases I've worked on resolve with this single check: verify your AzureBastionSubnet NSG is configured correctly.

Go to the Azure portal, navigate to your Virtual Network, click Subnets, and find AzureBastionSubnet. Click it and check whether an NSG is attached. If one is attached, click through to it and review the inbound and outbound security rules.

The NSG on AzureBastionSubnet must allow these inbound rules at minimum:

  • Port 443 from source Internet (your users connect here)
  • Port 443 from source service tag GatewayManager (Azure control plane)
  • Ports 8080 and 5701 from source service tag AzureLoadBalancer (health probes)
  • Ports 8080 and 5701 from source service tag VirtualNetwork (internal data plane)

And these outbound rules:

  • Ports 3389 and 22 to destination VirtualNetwork (RDP/SSH to target VMs)
  • Port 443 to destination service tag AzureCloud (Bastion control plane calls home)
  • Ports 8080 and 5701 to destination VirtualNetwork (data plane between Bastion instances)
  • Port 80 to destination Internet (certificate revocation list checks)

If any of those rules are missing or if a higher-priority Deny All rule is trumping them, that's your problem right there. Add the missing rules with a priority lower than any deny-all rule (lower number = higher priority in Azure NSG logic, a rule with priority 100 beats one with priority 4096).

After adding the rules, wait about 60–90 seconds for propagation, then retry your connection. If that doesn't fix it, move through the step-by-step section below.

Pro Tip
Don't attach an NSG to AzureBastionSubnet at all if you can avoid it, Microsoft explicitly supports running the subnet without one. The only time you need an NSG there is if your organization mandates it for compliance. If you do attach one, keep it minimal. Every extra deny rule is another potential failure point during Azure Bastion troubleshooting.
1
Verify Your Azure Bastion Host Provisioning State

The very first thing to check during any Azure Bastion troubleshooting session is whether the Bastion host itself is healthy. A host in a Failed provisioning state will accept connection requests and then silently drop them, making it look like an NSG or VM problem when it's actually the Bastion host itself.

In the Azure portal: go to All resources, search for your Bastion resource by name, and click on it. On the Overview blade, check the Provisioning state field. You want to see Succeeded. If you see Failed, Updating, or Deleting, that's your issue.

You can also check this via PowerShell, which gives you more detail:

Get-AzBastion -ResourceGroupName "your-rg-name" -Name "your-bastion-name" | Select-Object Name, ProvisioningState, Location, Sku

Or with Azure CLI:

az network bastion show \
  --name "your-bastion-name" \
  --resource-group "your-rg-name" \
  --query "{Name:name, State:provisioningState, SKU:sku.name}" \
  --output table

If the provisioning state is Failed, the fastest fix is usually to delete and redeploy the Bastion host. Before you do that, make sure the AzureBastionSubnet is at least a /26, a /27 will cause a deployment failure every time. You can check subnet size under Virtual Network → Subnets → AzureBastionSubnet → Address range. A /26 gives you 64 IPs; the Standard SKU needs room to scale out its instances.

If the state is Succeeded and you still can't connect, the host is fine and the problem is elsewhere, keep going through the steps below.

2
Audit and Fix NSG Rules on AzureBastionSubnet

You checked the quick fix above, but let's do a proper audit now. Open the NSG attached to your AzureBastionSubnet and navigate to Inbound security rules. Click Add if any of the following are missing. Here are the exact rules with recommended priority values:

Required Inbound Rules:

Priority 100  | Allow | Source: Internet         | Destination: Any    | Port: 443  | Protocol: TCP  | Name: Allow_Inbound_HTTPS_Internet
Priority 110  | Allow | Source: GatewayManager   | Destination: Any    | Port: 443  | Protocol: TCP  | Name: Allow_Inbound_GatewayManager
Priority 120  | Allow | Source: AzureLoadBalancer | Destination: Any   | Port: 8080,5701 | Protocol: Any | Name: Allow_Inbound_LoadBalancer
Priority 130  | Allow | Source: VirtualNetwork   | Destination: VirtualNetwork | Port: 8080,5701 | Protocol: Any | Name: Allow_Inbound_DataPlane

Required Outbound Rules:

Priority 100  | Allow | Source: Any  | Destination: VirtualNetwork | Port: 3389,22 | Protocol: Any | Name: Allow_Outbound_RDP_SSH
Priority 110  | Allow | Source: Any  | Destination: AzureCloud     | Port: 443     | Protocol: TCP | Name: Allow_Outbound_AzureCloud
Priority 120  | Allow | Source: Any  | Destination: VirtualNetwork | Port: 8080,5701 | Protocol: Any | Name: Allow_Outbound_DataPlane
Priority 130  | Allow | Source: Any  | Destination: Internet       | Port: 80      | Protocol: TCP | Name: Allow_Outbound_CRL

One gotcha I see constantly: someone adds a blanket Deny All Outbound rule at priority 1000, then wonders why Bastion can't reach the VMs or call back to the Azure control plane. Check your outbound rules carefully. If there's a deny-all at a low priority number (like 200), you need to add the allow rules above it with priority values below 200.

After saving, give it 90 seconds and test again. You'll know it worked when the connection progress indicator in the portal moves past the initial handshake phase.

3
Fix NSG Rules on the Target VM's NIC and Subnet

Even when the Bastion subnet NSG is perfect, Azure Bastion connection refused errors happen because of a second NSG, the one protecting your target VM. Bastion connects to your VM from within the VNet, so the VM's NSG needs to accept that traffic.

Navigate to your target VM in the portal. Under Networking, you'll see both the NIC-level NSG and any subnet-level NSG. Check both. The rule you need is:

Priority: 200 (or lower than any deny rule)
Allow | Inbound
Source: VirtualNetwork (service tag)
Source port ranges: *
Destination: Any
Destination port ranges: 3389 (for RDP) or 22 (for SSH)
Protocol: TCP
Action: Allow

Using the VirtualNetwork service tag rather than a specific IP range is important here. Bastion scales out to multiple instances depending on load, and those instances can have different private IPs within the VNet. Pinning to a specific source IP will break when Bastion adds a second instance during scale-out.

You can also use PowerShell's IP flow verify tool to test this without guessing. Go to your VM, click Networking → Network Watcher → IP flow verify, then set:

  • Direction: Inbound
  • Protocol: TCP
  • Local port: 3389 or 22
  • Remote IP: (any IP in the AzureBastionSubnet range)
  • Remote port: any (e.g., 54321)

If IP flow verify returns Access denied, the blocking rule name will be shown, that's exactly which rule you need to modify. This tool saves enormous amounts of time during Azure Bastion SSH not working or Azure Bastion RDP not working investigations.

4
Diagnose and Fix Black Screen and Session Errors

You've connected, the Bastion session opens, and you're staring at a black screen. Or the screen flickers and then drops. This is one of the more disorienting Azure Bastion problems to hit, because technically the connection succeeded, but something is wrong at the OS level or the session configuration level.

The black screen on RDP through Azure Bastion is almost always one of three things:

1. The Windows VM's Remote Desktop Services are not running. RDP sessions through Bastion still go through the normal RDP stack inside the VM. Connect to the VM via serial console (go to VM → Support + troubleshooting → Serial console) and run:

sc query termservice
net start termservice

2. The NLA (Network Level Authentication) configuration mismatch. If NLA is enforced on the VM but credentials aren't being passed correctly through the Bastion session, you'll get a black screen or immediate disconnect. In the portal, when you click Connect via Bastion, make sure you're entering credentials in the Username and Password fields, not leaving them blank and expecting Windows to prompt you.

3. GPU or display driver issues on GPU VMs. Azure GPU VMs (NV-series) sometimes render the RDP session to the GPU rather than the virtual display adapter. Via serial console, set the display adapter back to the basic Microsoft display driver:

Get-PnpDevice | Where-Object {$_.Class -eq "Display"} | Format-List

For SSH black screen issues (rare, but it happens), check that the SSH daemon is running on the VM and that it's listening on port 22:

sudo systemctl status sshd
sudo ss -tlnp | grep :22

If the SSH service crashed, restart it with sudo systemctl restart sshd and reconnect.

5
Fix Azure Bastion Native Client and Token Authentication Errors

The Azure Bastion native client, accessed via az network bastion rdp or az network bastion ssh, is a Standard SKU feature that lets you use your local RDP or SSH client through Bastion's tunnel. It's great for power users, but it has its own set of failure modes.

First, confirm you're running Standard SKU. Basic SKU does not support native client. Check with:

az network bastion show \
  --name "your-bastion-name" \
  --resource-group "your-rg-name" \
  --query "sku.name" \
  --output tsv

If it returns Basic, you'll need to upgrade to Standard. You can do this in the portal under Bastion → Configuration → Tier → Standard and save. There's no downtime for the upgrade, but it takes 5–10 minutes to complete.

The most common native client error is an expired or invalid token, you'll see something like ERROR: The token has expired or Failed to authenticate in your terminal. This happens when your Azure CLI session token is stale. Fix it with:

az logout
az login
az account set --subscription "your-subscription-id"

Then retry your native client command. For SSH specifically:

az network bastion ssh \
  --name "your-bastion-name" \
  --resource-group "your-rg-name" \
  --target-resource-id "/subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.Compute/virtualMachines/{vm-name}" \
  --auth-type "ssh-key" \
  --username "azureuser" \
  --ssh-key "~/.ssh/id_rsa"

Also make sure you also have the tunnel feature flag enabled. Without it, native client commands will fail silently. Enable it with:

az extension add --name bastion
az feature register --name AllowBastionNativeClientSupport --namespace Microsoft.Network

After enabling the feature, you may need to wait up to 30 minutes for it to propagate to your subscription.

Advanced Azure Bastion Troubleshooting

If the five steps above haven't solved it, you're dealing with something deeper. Here's where enterprise environments, VNet peering, and routing get involved.

Diagnosing with Azure Bastion Diagnostic Logs

Enable diagnostic logging on your Bastion resource to get actual session telemetry. In the portal, go to Bastion → Monitoring → Diagnostic settings → Add diagnostic setting. Enable the BastionAuditLogs category and send it to a Log Analytics workspace. Once logs are flowing (allow 5–10 minutes), run this KQL query:

AzureDiagnostics
| where ResourceType == "BASTIONHOSTS"
| where TimeGenerated > ago(1h)
| project TimeGenerated, operationName, resultType, resultDescription, targetVMIPAddress_s, protocol_s
| order by TimeGenerated desc

The resultDescription field is where the real error detail lives. Look for values like ConnectionFailed, AuthenticationFailed, or VMUnreachable. These map directly to the layer where the failure is occurring.

VNet Peering Scenarios

Azure Bastion does not natively work across VNet peers unless you're using Standard SKU with the IP-based connection feature or VNet peering support enabled. If your Bastion is in VNet-A and you're trying to connect to a VM in VNet-B (peered to VNet-A), you must:

  1. Upgrade to Standard SKU
  2. Enable IP-based connections in Bastion → Configuration
  3. Ensure the peering on both VNets has Allow gateway transit configured correctly
  4. Connect using the VM's private IP address directly (not the VM resource picker)

User-Defined Routes Blocking Bastion Traffic

This is a sneaky one. If your organization uses a hub-spoke topology with a forced tunneling UDR (a 0.0.0.0/0 route pointing to an NVA or VPN gateway), that route will break Bastion's outbound traffic to the Azure control plane. The AzureBastionSubnet must not have a UDR that routes internet-bound traffic through an NVA unless that NVA explicitly allows Bastion's control plane traffic on port 443 to the AzureCloud service tag.

Check your subnet's effective routes in the portal: Bastion subnet → Route table. If you see a 0.0.0.0/0 route pointing to anything other than Internet, test removing it (or exempting AzureCloud service tag destinations from the forced tunnel) to verify this is the cause.

Azure Bastion and Azure Firewall

If you're routing Bastion traffic through Azure Firewall, you need explicit application rules allowing outbound to AzureCloud on port 443 and network rules allowing the internal Bastion-to-VM traffic. Azure Firewall's default deny-all posture will block Bastion silently without these rules in place.

Checking Event Viewer Inside the Target VM

For RDP failures that make it past the network layer but fail at authentication, check Event Viewer → Windows Logs → Security on the target VM for event ID 4625 (failed logon). If you see 4625 events timed to your connection attempts, the credentials you're entering in the Bastion portal panel are wrong, or the account is locked. Also check Application and Services Logs → Microsoft → Windows → TerminalServices-RemoteConnectionManager → Operational for event ID 1149 (successful network connection), if that event is missing, RDP isn't even reaching the OS.

When to Call Microsoft Support

If your Bastion host is stuck in a Failed provisioning state and redeployment doesn't resolve it, if you're seeing internal Azure platform errors in diagnostic logs (error codes starting with 500 or 5xx in the resultType field), or if your connection works intermittently and you've confirmed all network configuration is correct, these are platform-side issues that require Microsoft to investigate backend infrastructure. Open a support request at Microsoft Support with the Bastion resource ID, the approximate time windows where failures occurred, and any diagnostic log entries you've collected. Having that data ready cuts the resolution time significantly.

Prevention & Best Practices for Azure Bastion

Once you've fixed the immediate problem, here's how to keep it from coming back. I've watched the same Azure Bastion SSH connection failed or RDP connection refused errors repeat every few months in environments that skip these basics.

Get your subnet sizing right from the start. The minimum is a /26, but if you're running Standard SKU with auto-scaling enabled, plan for a /25. Each Bastion instance consumes a handful of IPs, and when sessions spike and Bastion scales out, a /26 can get tight. A /25 gives you plenty of headroom. Once the subnet is created and Bastion is deployed, changing the address space requires a full teardown, you can't resize it in place.

Always use Standard SKU for production workloads. Basic SKU lacks native client support, VNet peering connectivity, IP-based connections, file transfer, and session recording. The per-hour cost difference between Basic and Standard is small compared to the operational friction of hitting a Basic SKU limitation during an incident. Upgrade early, not under pressure.

Lock down the AzureBastionSubnet NSG once and document it. Use Azure Policy to enforce that the NSG rules on AzureBastionSubnet are not modified by non-privileged users. I've seen runaway automation scripts accidentally delete the GatewayManager allow rule during NSG cleanup passes, which immediately breaks all Bastion sessions. A policy-enforced deny on NSG rule deletion for that subnet prevents that class of accident.

Enable diagnostic logs before you need them. Set up the BastionAuditLogs diagnostic setting on day one and route it to Log Analytics. When a connection failure happens at 11 PM on a Friday, having 90 days of session logs already available means you can diagnose in 10 minutes instead of 10 hours. The storage cost is negligible.

Test your Bastion connection after every major networking change. If you modify route tables, update NSG rules anywhere in the hub/spoke topology, or change Azure Firewall policy, take 60 seconds to verify Bastion still connects to a test VM. Catching a regression immediately beats finding it when a developer reports they can't get into a production VM at 3 AM.

Quick Wins
  • Use Azure Policy's built-in Azure Bastion should be configured for the virtual network initiative to audit missing Bastion deployments in VNets that contain VMs
  • Enable session recording (Standard SKU feature), it provides an audit trail and is often required for compliance frameworks like SOC 2 and PCI-DSS
  • Tag your Bastion resources with environment and owner metadata so on-call engineers can identify the right Bastion host quickly during an incident
  • Set up an Azure Monitor alert on the BastionAuditLogs table to fire when resultType == "ConnectionFailed" exceeds a threshold, catch cascading Bastion failures before users start reporting them

Frequently Asked Questions

Why does Azure Bastion show a spinning wheel and then time out when I try to connect?

This almost always means Bastion can't reach your VM on port 3389 (RDP) or 22 (SSH) within the VNet. The browser successfully connects to Bastion over port 443, but Bastion's outbound connection to the VM times out. Check the NSG on the VM's NIC and subnet and make sure there's an inbound allow rule for the relevant port from the VirtualNetwork service tag. Also confirm the VM is actually running, a deallocated VM looks the same from the network as one with a blocking NSG. Go to VM → Overview and check the Status field; it should say Running.

Can I use Azure Bastion to connect to a VM in a different VNet or subscription?

Yes, but only with Standard SKU and the right configuration. For VMs in a peered VNet, you need Standard SKU with IP-based connections enabled and the VNet peering configured with proper gateway settings. For VMs in a different subscription, the Bastion host and the target VM just need to be in VNets that are peered, subscription boundaries don't block this as long as the network path exists and your Azure RBAC permissions cover both resources. You connect using the VM's private IP address rather than selecting it from the resource picker, which only shows VMs in the same subscription.

My Azure Bastion native client SSH keeps saying "token has expired", how do I fix it permanently?

Azure CLI access tokens expire after a period of inactivity, typically one hour for most Azure AD configurations. Run az login to refresh your session before using native client commands. For automation or scripts, use a service principal with az login --service-principal so you control the token lifetime. If your organization uses Conditional Access policies with short token lifetimes, you may need to work with your Azure AD admin to adjust the token lifetime policy for CLI access, or switch to using Azure Bastion through the portal for interactive sessions instead of the native client.

Does Azure Bastion work with Linux VMs using SSH key authentication instead of passwords?

Yes, both the portal-based client and the native client support SSH key authentication for Linux VMs. In the portal, when you click Connect → Bastion on a Linux VM, change the Authentication Type dropdown from Password to SSH Private Key from Local File and upload your private key file. For the native client, use the --auth-type ssh-key flag along with --ssh-key pointing to your local private key path. The Standard SKU also supports SSH private keys stored in Azure Key Vault via the --auth-type ssh-key combined with a Key Vault reference, which is great for shared team access without distributing private key files.

How do I fix Azure Bastion after getting error "Subnet AzureBastionSubnet is too small"?

You cannot resize an existing subnet in Azure if it has resources deployed in it, and the Bastion host itself counts as a resource. The only path forward is to delete the Azure Bastion host, then resize the subnet to /26 or larger, and then redeploy Bastion. Before deletion, note all the Bastion configuration settings (SKU, public IP name, resource group) so you can recreate it quickly. If downtime is a concern, you can deploy a temporary Bastion host in a different VNet that peers to the affected VNet, give it IP-based connection access, and use it for continuity while you fix the original subnet. It's a bit of work but keeps access available during the migration.

Can I copy and paste text between my local machine and a VM through Azure Bastion?

Yes, the portal-based Bastion client supports clipboard copy/paste, but it works differently than a native RDP session. In the Bastion session window, there's a small clipboard icon on the left-side toolbar, click it to open the Bastion clipboard panel. To paste text into the VM, paste it into the Bastion clipboard panel first, then it becomes available inside the VM session. To copy text out of the VM, select it in the VM session, and it will appear in the Bastion clipboard panel for you to copy locally. It's a two-step process, not the direct Ctrl+C/Ctrl+V you're used to with native RDP. If you need seamless clipboard integration, use the native client (Standard SKU) with your local RDP client, it supports native clipboard passthrough.

Related Microsoft Fix Guides

H
Sai Kiran Pandrala
Our team includes certified Microsoft engineers, Azure architects, and system administrators with 10+ years of enterprise IT experience. Every guide is written from hands-on troubleshooting, not guesswork. We test every fix before publishing.