ADR-050: Azure Bastion + Windows Jump Box Replaces VPN Gateway
Status: Accepted Date: 2025-10-24 Deciders: Infrastructure Team, Development Team Technical Story: Simplify dev environment access while reducing costs and improving browser-based UI testing
Context and Problem Statement
Our current development environment uses a Point-to-Site (P2S) VPN Gateway (VpnGw2 SKU) for developer access to private VNet resources. This approach has several operational and cost challenges:
- High Cost: VPN Gateway costs ~$360/month (~$4,320/year) for always-on connectivity
- Client Configuration Complexity: Each developer must install VPN client, configure Azure AD authentication, and troubleshoot connectivity issues
- Limited UI Testing: VPN connection from developer workstations doesn't provide a clean browser environment for testing web UI
- Underutilization: VPN Gateway is only needed during active development (not 24/7)
- Security Surface: P2S VPN exposes gateway endpoint to internet with UDP ports 500, 4500, 1194
Decision Drivers
- Cost Reduction: Need to reduce infrastructure costs for dev/test environments
- Operational Simplicity: Reduce client-side configuration and troubleshooting
- Browser-Based Testing: Need Windows environment with full browser support for UI testing
- Security: Minimize internet-exposed endpoints
- Deployment Speed: Faster to provision Bastion than VPN Gateway
- Pay-per-Use: Only pay when resources are actually being used
Decision
Replace P2S VPN Gateway with Azure Bastion + Windows Server Jump Box VM for development environment access.
Architecture
┌─────────────────────────────────────────────────────────────┐
│ VNet (10.1.0.0/16) │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ AzureBastionSubnet (10.1.1.0/26) │ │
│ │ └── Azure Bastion (Standard SKU) │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Compute Subnet (10.1.0.0/24) │ │
│ │ ├── Windows Server 2022 VM (Jump Box) │ │
│ │ │ - Standard_D2s_v3 (2 vCPU, 8GB RAM) │ │
│ │ │ - Browsers: Edge, Chrome, Firefox │ │
│ │ │ - Dev Tools: VS Code, Git, Azure CLI │ │
│ │ │ - RDP via Bastion │ │
│ │ │ │ │
│ │ └── ACI Container Group (App Services) │ │
│ │ - API, UI, MCP Servers │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Data Subnet (10.1.3.0/24) │ │
│ │ - Private Endpoints (AI Foundry, ACR) │ │
│ └────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Components
- Azure Bastion (Standard SKU)
- Browser-based RDP/SSH access (no client installation)
- Deployed in dedicated
AzureBastionSubnet(minimum /26) - Provides secure connectivity without public IPs on target VMs
-
Pay-per-use: Can be stopped/started as needed
-
Windows Server 2022 Jump Box VM
- Purpose: Browser-based UI testing and VNet resource access
- Size: Standard_D2s_v3 (2 vCPU, 8GB RAM)
- OS: Windows Server 2022 Datacenter
- Software: Edge, Chrome, Firefox, VS Code, Git, Azure CLI, PowerShell
- Access Method: RDP via Azure Bastion (browser-based)
- No Public IP: Fully private, only accessible via Bastion
- Use Cases:
- Test web UI in production-like browser environment
- Run Azure CLI commands from within VNet
- Access private endpoints (AI Foundry, ACR)
- Debug container networking issues
Access Workflow
Developer Access (via Bastion):
1. Developer logs into Azure Portal
2. Navigate to Windows VM resource
3. Click "Connect" → "Bastion"
4. Authenticate with Azure AD credentials
5. Browser-based RDP session opens
6. Access VNet resources from Windows VM:
- http://10.1.0.4:8000 (API)
- http://10.1.0.4:80 (UI)
- Private AI Foundry endpoint
Cost Control:
# Stop VM when not in use (deallocates compute)
az vm deallocate -g ldfdev-rg -n ldfdev-jumpbox-vm
# Start VM when needed
az vm start -g ldfdev-rg -n ldfdev-jumpbox-vm
# Optional: Delete Bastion when not needed for extended periods
az network bastion delete -g ldfdev-rg -n ldfdev-bastion
Consequences
Positive
- Cost Savings: ~$150/month (~$1,800/year) reduction
- VPN Gateway: ~$360/month (VpnGw2 SKU)
- Bastion: ~$140/month (Standard SKU, always-on)
- Windows VM: ~$70/month (Standard_D2s_v3, always-on)
- Total New: ~$210/month
- Savings: $150/month with better testing capabilities
-
Additional Savings: Can stop VM when not in use (~$50/month additional savings)
-
Browser-Based Testing:
- Full Windows environment with multiple browsers
- Production-like testing conditions
-
No need to configure developer workstations
-
Simplified Access:
- No VPN client installation
- No certificate management
- No troubleshooting VPN connectivity issues
-
Works from any device with browser + Azure Portal access
-
Better Security:
- No internet-exposed VPN endpoint
- Bastion managed by Azure (automatic security updates)
- No public IPs on VMs
-
Centralized access auditing via Azure Monitor
-
Faster Provisioning:
- Bastion: 5-10 minutes
-
VPN Gateway: 30-45 minutes
-
Pay-per-Use Model:
- Stop VM when not actively developing (~$50/month savings)
- Delete Bastion during extended breaks (redeployment in 10 min)
Negative
- Requires Azure Portal Access:
- Cannot connect without Azure Portal (or Azure CLI)
- Requires internet connection to Azure Portal
-
Not suitable for offline development
-
Latency:
- RDP session runs through Azure Bastion proxy
- Slightly higher latency than direct VPN connection
-
May affect responsiveness for graphics-intensive tasks
-
Session Management:
- Browser-based RDP sessions have timeout limits
- Need to reconnect after idle timeout
-
Cannot run long-running tasks in RDP session (use VM scheduled tasks instead)
-
Windows VM Maintenance:
- Need to apply Windows updates
- Need to manage installed software
- Need to monitor disk usage and performance
Mitigations
| Risk | Mitigation |
|---|---|
| Bastion cost for unused environments | Delete Bastion in test environments, only keep in active dev |
| VM running when not needed | Document stop/start procedures, set up auto-shutdown schedule |
| Session timeout interrupting work | Use VM scheduled tasks for long-running operations, save work frequently |
| Windows update downtime | Schedule updates during off-hours, use snapshot before updates |
| Insufficient VM resources | Monitor performance, resize VM if needed (flexible SKU) |
Comparison: Bastion vs VPN Gateway
| Aspect | Azure Bastion (Chosen) | P2S VPN Gateway (Previous) |
|---|---|---|
| Cost | ~$210/month (Bastion + VM) | ~$360/month (VpnGw2 SKU) |
| Savings | ~$150/month (~$1,800/year) | Baseline |
| Client Setup | None (browser-based) | VPN client + Azure AD config |
| Access Method | Azure Portal → Connect → Bastion | VPN client with certificate/Azure AD |
| Browser Testing | ✅ Full Windows environment | ❌ Local workstation only |
| Security | ✅ No public IP on VM | ⚠️ VPN endpoint exposed to internet |
| Provisioning | 5-10 minutes | 30-45 minutes |
| Scalability | Add more VMs as needed | Concurrent connection limits |
| Offline Access | ❌ Requires Azure Portal | ✅ Works offline after initial setup |
| Latency | Moderate (Bastion proxy) | Low (direct tunnel) |
| Maintenance | VM updates only | VPN Gateway managed by Azure |
| Multi-User | Each user gets own RDP session | Shared VPN tunnel |
Implementation Details
Azure Bastion Configuration
resource bastion 'Microsoft.Network/bastionHosts@2023-05-01' = {
name: '${prefix}-bastion'
location: location
sku: {
name: 'Standard' // Allows IP-based connection
}
properties: {
ipConfigurations: [
{
name: 'IpConf'
properties: {
subnet: {
id: bastionSubnet.id
}
publicIPAddress: {
id: bastionPublicIp.id
}
}
}
]
}
}
Windows Jump Box VM Configuration
resource jumpBoxVm 'Microsoft.Compute/virtualMachines@2023-03-01' = {
name: '${prefix}-jumpbox-vm'
location: location
properties: {
hardwareProfile: {
vmSize: 'Standard_D2s_v3' // 2 vCPU, 8GB RAM
}
osProfile: {
computerName: 'jumpbox'
adminUsername: adminUsername
adminPassword: adminPassword
windowsConfiguration: {
provisionVMAgent: true
enableAutomaticUpdates: true
}
}
storageProfile: {
imageReference: {
publisher: 'MicrosoftWindowsServer'
offer: 'WindowsServer'
sku: '2022-datacenter-azure-edition'
version: 'latest'
}
osDisk: {
createOption: 'FromImage'
managedDisk: {
storageAccountType: 'Premium_LRS'
}
}
}
networkProfile: {
networkInterfaces: [
{
id: jumpBoxNic.id
}
]
}
}
}
Software Installation (CustomScriptExtension)
# Install Chocolatey
Set-ExecutionPolicy Bypass -Scope Process -Force
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
# Install browsers and dev tools
choco install -y googlechrome
choco install -y firefox
choco install -y vscode
choco install -y git
choco install -y azure-cli
choco install -y powershell-core
Validation
Success Criteria
- ✅ Developer can access jump box VM via Bastion from Azure Portal
- ✅ VM has browsers installed (Edge, Chrome, Firefox)
- ✅ VM can access ACI container group at http://10.1.0.4:8000 (API) and :80 (UI)
- ✅ VM can access AI Foundry private endpoint
- ✅ Cost reduced by ~$150/month
- ✅ VM can be stopped/started for additional cost savings
Testing Plan
-
Deploy Bastion and VM:
-
Test Bastion Connectivity:
- Navigate to VM in Azure Portal
- Click "Connect" → "Bastion"
-
Authenticate and verify RDP session opens
-
Test VNet Access from VM:
-
Test Browser UI Access:
- Open Edge browser on VM
- Navigate to http://10.1.0.4:80
- Verify loan application UI loads correctly
-
Test form submission and agent workflow
-
Verify Cost Savings:
- Compare Azure Cost Management before/after
- Verify VPN Gateway deletion
- Confirm Bastion + VM total cost
Production Considerations
⚠️ This approach is ONLY for dev/test environments.
For production: - Do NOT use Bastion + Jump Box for production access - Use Azure Bastion for emergency break-glass access only - Implement proper CI/CD pipelines for deployments - Use Azure Monitor and Log Analytics for observability - Consider Azure Private Link for application access (if external users)
References
- Azure Bastion Documentation
- Azure Bastion Pricing
- VPN Gateway Pricing
- Azure VM Pricing Calculator
- ADR-025: Point-to-Site VPN for Dev Access (superseded)
Related ADRs
- Supersedes: ADR-025 (Point-to-Site VPN for Dev Access)
- Related: ADR-049 (Container Deployment - ACI vs Container Apps)
- Related: ADR-051 (Infrastructure Simplification - KISS)
Status: Accepted Date Accepted: 2025-10-24 Supersedes: ADR-025 Superseded By: None