Back to Blog
3 min read

Azure Bastion: Secure VM Access Without Public IPs

Azure Bastion provides secure RDP and SSH access to VMs without exposing public IPs. Browser-based access through the Azure portal—no jump boxes needed.

How Bastion Works

User → Azure Portal → Bastion → VM (Private IP)
         HTTPS            Private Network
  • No public IP on VMs
  • No exposed RDP/SSH ports
  • Traffic stays in Azure backbone
  • SSL/TLS encrypted

Creating Bastion

# Create AzureBastionSubnet (required name)
az network vnet subnet create \
    --resource-group myRG \
    --vnet-name myVNet \
    --name AzureBastionSubnet \
    --address-prefix 10.0.255.0/27

# Create public IP for Bastion
az network public-ip create \
    --resource-group myRG \
    --name bastion-pip \
    --sku Standard \
    --allocation-method Static

# Create Bastion
az network bastion create \
    --name my-bastion \
    --resource-group myRG \
    --vnet-name myVNet \
    --public-ip-address bastion-pip \
    --sku Standard

SKU Comparison

FeatureBasicStandard
Concurrent sessions2550+ (scale units)
Native clientNoYes
Upload/downloadNoYes
Shareable linkNoYes
Kerberos authNoYes

Connect via Portal

  1. Navigate to VM in Azure Portal
  2. Click “Connect” → “Bastion”
  3. Enter username and password
  4. Click “Connect”

Browser opens RDP/SSH session directly.

Native Client Connection

# Enable native client
az network bastion update \
    --name my-bastion \
    --resource-group myRG \
    --enable-tunneling true

# RDP via native client
az network bastion rdp \
    --name my-bastion \
    --resource-group myRG \
    --target-resource-id /subscriptions/.../virtualMachines/myVM

# SSH via native client
az network bastion ssh \
    --name my-bastion \
    --resource-group myRG \
    --target-resource-id /subscriptions/.../virtualMachines/myVM \
    --auth-type password \
    --username azureuser

Tunnel for Custom Tools

# Create tunnel
az network bastion tunnel \
    --name my-bastion \
    --resource-group myRG \
    --target-resource-id /subscriptions/.../virtualMachines/myVM \
    --resource-port 22 \
    --port 2222

# Connect through tunnel
ssh azureuser@localhost -p 2222
# Create shareable link (temporary access)
az network bastion shareable-link create \
    --bastion-host-name my-bastion \
    --resource-group myRG \
    --vms "/subscriptions/.../virtualMachines/myVM"

# List shareable links
az network bastion shareable-link list \
    --bastion-host-name my-bastion \
    --resource-group myRG

# Delete shareable link
az network bastion shareable-link delete \
    --bastion-host-name my-bastion \
    --resource-group myRG \
    --vms "/subscriptions/.../virtualMachines/myVM"

Scale Units (Standard SKU)

# Scale for more connections
az network bastion update \
    --name my-bastion \
    --resource-group myRG \
    --scale-units 4
Scale UnitsMax Sessions
2 (default)50
4100
10250
501250

Monitoring

// Bastion connection logs
AzureDiagnostics
| where Category == "BastionAuditLogs"
| project TimeGenerated, UserName = userName_s, TargetVMIPAddress = targetVMIPAddress_s,
    Protocol = protocol_s, Duration = duration_s
| order by TimeGenerated desc

Best Practices

  1. Use NSG on AzureBastionSubnet
  2. Enable diagnostic logging
  3. Use JIT access with Bastion
  4. Rotate shareable links regularly
  5. Monitor failed connection attempts

Azure Bastion: secure, private VM access made simple.

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.