Azure Bastion Overview: Secure VM Access Without Public SSH
Azure Bastion is a managed service that lets you connect to Azure VMs over SSH and RDP through your browser, using the Azure portal. No public IP on the VM, no open SSH or RDP ports in the NSG, no VPN client required. Bastion acts as a secure gateway between the internet and your private VMs, with all session activity logged to Azure Monitor.
What problem Bastion solves
The traditional way to SSH into an Azure VM is to give it a public IP and open port 22 in the Network Security Group. That works, but creates two problems:
- Attack surface. Any public IP with port 22 open immediately attracts brute-force attempts, credential stuffing, and exploit probing. Even with key-based auth blocking password guessing, you are still exposing the SSH daemon and any vulnerabilities in it.
- Operational complexity. Maintaining allowlists of IP ranges for developers, updating them when IPs change, and auditing who connected when is ongoing manual work.
Bastion removes both problems. The VM has no public IP. Port 22 and 3389 are not open in the NSG. Your developers connect through the Azure portal using their Entra ID credentials — the same credentials that control access to every other Azure resource. Access is revoked by removing the RBAC role assignment, not by editing NSG rules or SSH key files.
How Bastion works
Azure Bastion deploys into a dedicated subnet called AzureBastionSubnet (the name is required — Azure will not recognise a differently-named subnet). The Bastion service gets a public IP and exposes an HTTPS endpoint. When you connect through the portal:
- Your browser opens an HTTPS connection to the Bastion service’s public IP.
- Bastion authenticates you with your Entra ID session.
- Bastion opens an SSH or RDP connection to your VM using the VM’s private IP inside the VNet.
- The terminal or remote desktop session runs in your browser over WebSocket.
The VM sees an SSH connection originating from the AzureBastionSubnet range, not from the internet. The NSG on the VM needs to allow SSH from that subnet only.
Deploying Azure Bastion
Bastion requires a dedicated subnet in the VNet where your VMs live. The subnet must be named exactly AzureBastionSubnet and must be at least /26 (64 addresses).
# Add the AzureBastionSubnet to your existing VNet
az network vnet subnet create \
--resource-group my-rg \
--vnet-name my-vnet \
--name AzureBastionSubnet \
--address-prefix 10.0.255.0/26
# Create a public IP for Bastion (Standard SKU required)
az network public-ip create \
--resource-group my-rg \
--name my-bastion-pip \
--sku Standard \
--location eastus
# Create the Bastion host (Basic SKU)
az network bastion create \
--resource-group my-rg \
--name my-bastion \
--vnet-name my-vnet \
--public-ip-address my-bastion-pip \
--location eastus \
--sku BasicBastion provisioning takes 5–10 minutes. Once deployed, navigate to any VM in the portal, click Connect, and choose Bastion. You authenticate with your Entra ID account (and the VM’s admin username and SSH key or password), and a browser-based terminal opens.
The Developer SKU is a lower-cost single-session option useful for personal accounts or small teams where concurrent access is not needed. It shares the Bastion infrastructure with other customers (similar to burstable VM tiers) and does not require a dedicated Bastion subnet — making it easier to test.
Native client support (Standard SKU)
The Standard SKU adds the ability to use your local SSH or RDP client rather than the browser. This is useful when you need SSH features that the browser terminal does not support — such as SCP file transfers, port forwarding, or terminal multiplexers.
# Connect to a VM using the Azure CLI and your local SSH client
az network bastion ssh \
--resource-group my-rg \
--name my-bastion \
--target-resource-id /subscriptions/.../virtualMachines/my-vm \
--auth-type ssh-key \
--username azureuser \
--ssh-key ~/.ssh/azure-vm-keyWith native client support, Bastion tunnels the SSH session through HTTPS. Your local SSH client connects to a localhost port that Bastion maps to the VM’s private SSH port. The session looks and behaves like a normal SSH connection, but traffic never leaves Azure’s network unencrypted and no public IP is needed on the VM.
SKU comparison
| Feature | Developer | Basic | Standard |
|---|---|---|---|
| Dedicated subnet required | No | Yes | Yes |
| Concurrent sessions | 1 | Up to 25 | Up to 50 (scalable) |
| Native client (local SSH/RDP) | No | No | Yes |
| VNet peering support | No | No | Yes |
| Shareable links | No | No | Yes |
| Approximate cost | ~$0.04/hour | ~$0.19/hour | ~$0.29/hour base |
When Bastion is not the right tool
Bastion is a good fit for most teams, but not all:
- If you need SCP or SFTP file transfers, the basic browser terminal does not support them. The Standard SKU with native client solves this, but adds cost.
- If you run dozens of VMs and need bulk SSH automation, tools like Ansible, Azure Run Command, or SSH via a VPN are more practical than connecting through Bastion for each VM.
- If cost is a hard constraint, a jump host (a small VM in the same VNet with a public IP) can serve the same isolation purpose at lower cost — though with more operational overhead.
Common Bastion mistakes
- Naming the subnet incorrectly. The subnet must be named exactly
AzureBastionSubnet— Azure will reject any other name. This is a hard requirement, not a recommendation. - Sizing AzureBastionSubnet too small. The minimum is /26 (64 addresses). Azure reserves addresses within the subnet for Bastion’s internal components. A /28 subnet will fail deployment. For Standard SKU with scaling enabled, use /26 or larger.
- Still leaving port 22 open on VM NSGs. Deploying Bastion but not removing the direct SSH access rule in the VM’s NSG means you paid for Bastion without getting the security benefit. After deploying Bastion, update VM NSGs to allow SSH only from the AzureBastionSubnet address range, not from the internet.
Summary
- Azure Bastion provides browser-based SSH and RDP access to VMs without requiring a public IP on the VM or open SSH/RDP ports in the NSG.
- Bastion deploys into a dedicated subnet named
AzureBastionSubnet(minimum /26). Access is controlled through Entra ID credentials and RBAC. - Basic SKU handles up to 25 concurrent browser sessions. Standard SKU adds native client support, VNet peering access, and scalable session limits.
- After deploying Bastion, update VM NSGs to restrict SSH to the AzureBastionSubnet range only — removing the internet-facing attack surface entirely.
- For teams that need local SSH tool features (SCP, port forwarding), use Standard SKU with native client support and the
az network bastion sshcommand.
Frequently asked questions
Do I need a public IP on my VM to use Azure Bastion?
No. That is the point. Bastion connects to your VM using its private IP inside the Virtual Network. The VM needs no public IP and no open port 22 or 3389 in its NSG. All the public-facing infrastructure lives in the AzureBastionSubnet, not on the VM itself.
How much does Azure Bastion cost?
Bastion is billed per hour the resource exists, plus data transfer. The Basic SKU costs roughly $0.19/hour in most regions (about $140/month). Developer SKU is cheaper but limited to one concurrent session. Standard SKU is more expensive but adds features like native client support, scaling for concurrent sessions, and shareable links. For a team of developers connecting regularly, Bastion is usually worth the cost compared to the risk of exposed SSH ports.
Can I use Azure Bastion with VMs in a peered VNet?
Yes, with the Standard SKU and VNet peering enabled on the Bastion host. The Basic SKU only allows connections to VMs in the same VNet. If you have a hub-and-spoke network topology, deploy Bastion in the hub VNet with Standard SKU to reach VMs in all spoke VNets through peering.