Skip to content

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:

  1. High Cost: VPN Gateway costs ~$360/month (~$4,320/year) for always-on connectivity
  2. Client Configuration Complexity: Each developer must install VPN client, configure Azure AD authentication, and troubleshoot connectivity issues
  3. Limited UI Testing: VPN connection from developer workstations doesn't provide a clean browser environment for testing web UI
  4. Underutilization: VPN Gateway is only needed during active development (not 24/7)
  5. 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

  1. Azure Bastion (Standard SKU)
  2. Browser-based RDP/SSH access (no client installation)
  3. Deployed in dedicated AzureBastionSubnet (minimum /26)
  4. Provides secure connectivity without public IPs on target VMs
  5. Pay-per-use: Can be stopped/started as needed

  6. Windows Server 2022 Jump Box VM

  7. Purpose: Browser-based UI testing and VNet resource access
  8. Size: Standard_D2s_v3 (2 vCPU, 8GB RAM)
  9. OS: Windows Server 2022 Datacenter
  10. Software: Edge, Chrome, Firefox, VS Code, Git, Azure CLI, PowerShell
  11. Access Method: RDP via Azure Bastion (browser-based)
  12. No Public IP: Fully private, only accessible via Bastion
  13. 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

  1. Cost Savings: ~$150/month (~$1,800/year) reduction
  2. VPN Gateway: ~$360/month (VpnGw2 SKU)
  3. Bastion: ~$140/month (Standard SKU, always-on)
  4. Windows VM: ~$70/month (Standard_D2s_v3, always-on)
  5. Total New: ~$210/month
  6. Savings: $150/month with better testing capabilities
  7. Additional Savings: Can stop VM when not in use (~$50/month additional savings)

  8. Browser-Based Testing:

  9. Full Windows environment with multiple browsers
  10. Production-like testing conditions
  11. No need to configure developer workstations

  12. Simplified Access:

  13. No VPN client installation
  14. No certificate management
  15. No troubleshooting VPN connectivity issues
  16. Works from any device with browser + Azure Portal access

  17. Better Security:

  18. No internet-exposed VPN endpoint
  19. Bastion managed by Azure (automatic security updates)
  20. No public IPs on VMs
  21. Centralized access auditing via Azure Monitor

  22. Faster Provisioning:

  23. Bastion: 5-10 minutes
  24. VPN Gateway: 30-45 minutes

  25. Pay-per-Use Model:

  26. Stop VM when not actively developing (~$50/month savings)
  27. Delete Bastion during extended breaks (redeployment in 10 min)

Negative

  1. Requires Azure Portal Access:
  2. Cannot connect without Azure Portal (or Azure CLI)
  3. Requires internet connection to Azure Portal
  4. Not suitable for offline development

  5. Latency:

  6. RDP session runs through Azure Bastion proxy
  7. Slightly higher latency than direct VPN connection
  8. May affect responsiveness for graphics-intensive tasks

  9. Session Management:

  10. Browser-based RDP sessions have timeout limits
  11. Need to reconnect after idle timeout
  12. Cannot run long-running tasks in RDP session (use VM scheduled tasks instead)

  13. Windows VM Maintenance:

  14. Need to apply Windows updates
  15. Need to manage installed software
  16. 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

  1. Deploy Bastion and VM:

    az deployment group create \
      -g ldfdev-rg \
      -f infrastructure/bicep/foundation.bicep \
      -p infrastructure/bicep/environments/dev-foundation.parameters.json
    

  2. Test Bastion Connectivity:

  3. Navigate to VM in Azure Portal
  4. Click "Connect" → "Bastion"
  5. Authenticate and verify RDP session opens

  6. Test VNet Access from VM:

    # From jump box VM
    curl http://10.1.0.4:8000/health
    curl http://10.1.0.4:80
    az cognitiveservices account show -g ldfdev-rg -n ldfdev-ai
    

  7. Test Browser UI Access:

  8. Open Edge browser on VM
  9. Navigate to http://10.1.0.4:80
  10. Verify loan application UI loads correctly
  11. Test form submission and agent workflow

  12. Verify Cost Savings:

  13. Compare Azure Cost Management before/after
  14. Verify VPN Gateway deletion
  15. 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

  • 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