Azure Bastion VM Configuration
Overview
This document describes the Azure Bastion and Jump Box VM configuration for secure access to private VNet resources (ADR-050).
Architecture
Components
1. Azure Bastion
- Name:
ldfdev5-dev-bastion - SKU: Basic
- Public IP:
ldfdev5-dev-bastion-pip - Subnet:
AzureBastionSubnet(10.0.4.0/26) - Purpose: Browser-based secure access (no client VPN needed)
2. Jump Box VM
- Name:
ldfdev5-dev-jumpbox - OS: Windows Server 2022 Datacenter Azure Edition
- Size: Standard_D2s_v3
- Private IP: 10.0.0.4
- Subnet:
compute-subnet(10.0.0.0/23) - Purpose: Development access to private VNet resources
Network Security
NSG Rules (compute-subnet)
The following NSG rule allows Bastion to connect to the Jump Box:
{
name: 'AllowBastionSSHInbound'
properties: {
description: 'Allow SSH and RDP from Azure Bastion subnet (ADR-050)'
protocol: 'Tcp'
sourcePortRange: '*'
destinationPortRanges: ['22', '3389']
sourceAddressPrefix: '10.0.4.0/26' // Bastion subnet
destinationAddressPrefix: '10.0.0.0/23' // Compute subnet
access: 'Allow'
priority: 90
direction: 'Inbound'
}
}
Location: infrastructure/bicep/modules/networking.bicep
Credentials Management
Key Vault Storage
VM credentials are stored in Key Vault (ldfdev5-dev-kv) with three secrets:
ldfdev5-foundation-vmadminusername- Plain text usernameldfdev5-foundation-vmadminpassword- Plain text password (secure)vm-credentials-dev- Base64 encoded JSON with all connection info
Bicep Configuration
Location: infrastructure/bicep/foundation.bicep
// Store individual secrets
resource vmUsernameSecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = if (deployBastion) {
name: '${keyVaultName}/vm-admin-username'
properties: {
value: vmAdminUsername
contentType: 'text/plain'
}
}
resource vmPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = if (deployBastion) {
name: '${keyVaultName}/vm-admin-password'
properties: {
value: vmAdminPassword
contentType: 'text/plain'
}
}
// Store combined credentials
resource vmCredentialsSecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = if (deployBastion) {
name: '${keyVaultName}/vm-credentials-${environment}'
properties: {
value: base64(string({
username: vmAdminUsername
password: vmAdminPassword
bastionName: bastionVm.outputs.bastionName
vmName: bastionVm.outputs.vmName
connectionInstructions: 'Connect via Azure Portal: VM → Connect → Bastion'
}))
contentType: 'application/json'
}
}
Deployment Script
Location: infrastructure/scripts/deploy-foundation.sh
The deployment script: 1. Generates a secure random password (25 characters) 2. Passes it as a secure parameter to Bicep deployment 3. Stores it in Key Vault after successful deployment
# Generate VM Admin Password
VM_ADMIN_PASSWORD=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-25)
# Deploy with password parameter
az deployment group create \
--parameters vmAdminPassword="$VM_ADMIN_PASSWORD"
# Store in Key Vault (via helper function)
push_endpoint_to_keyvault "$RESOURCE_GROUP" "foundation" "vmAdminPassword" "$VM_ADMIN_PASSWORD"
Connection Methods
Method 1: Azure Portal (Recommended)
- Navigate to Azure Portal → Virtual Machines →
ldfdev5-dev-jumpbox - Click Connect → Bastion
- Enter credentials from Key Vault
- Click Connect (opens in browser)
Method 2: Azure CLI
# Retrieve credentials
USERNAME=$(az keyvault secret show \
--vault-name ldfdev5-dev-kv \
--name ldfdev5-foundation-vmadminusername \
--query value -o tsv)
PASSWORD=$(az keyvault secret show \
--vault-name ldfdev5-dev-kv \
--name ldfdev5-foundation-vmadminpassword \
--query value -o tsv)
# Connect via Bastion
az network bastion ssh \
--name ldfdev5-dev-bastion \
--resource-group ldfdev5-rg \
--target-resource-id /subscriptions/.../virtualMachines/ldfdev5-dev-jumpbox \
--auth-type password \
--username "$USERNAME"
Password Reset
If the password needs to be reset:
# Generate new password
NEW_PASSWORD=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-25)
# Reset VM password
az vm user update \
--resource-group ldfdev5-rg \
--name ldfdev5-dev-jumpbox \
--username azureuser \
--password "$NEW_PASSWORD"
# Update Key Vault
az keyvault secret set \
--vault-name ldfdev5-dev-kv \
--name ldfdev5-foundation-vmadminpassword \
--value "$NEW_PASSWORD"
Troubleshooting
Issue: "Network connection to Bastion Host appears unstable"
Cause: Missing NSG rule to allow Bastion → VM traffic
Solution: Verify NSG rule exists:
az network nsg rule show \
--resource-group ldfdev5-rg \
--nsg-name ldfdev5-dev-vnet-compute-nsg \
--name AllowBastionSSHInbound
If missing, the rule will be created on next Foundation deployment.
Issue: "Login Failed"
Cause: Password mismatch between VM and Key Vault
Solution: Reset the VM password (see Password Reset section above)
Issue: Cannot access Key Vault secrets
Cause: Insufficient Key Vault permissions
Solution: Grant yourself Key Vault Secrets User role:
az role assignment create \
--role "Key Vault Secrets User" \
--assignee $(az ad signed-in-user show --query id -o tsv) \
--scope $(az keyvault show --name ldfdev5-dev-kv --query id -o tsv)
Files Modified (2025-10-26)
1. infrastructure/bicep/modules/networking.bicep
- Added
AllowBastionSSHInboundNSG rule (priority 90) - Allows SSH (22) and RDP (3389) from Bastion subnet to compute subnet
2. infrastructure/bicep/foundation.bicep
- Enhanced VM credentials storage with separate username and password secrets
- Maintains backward compatibility with combined
vm-credentials-{env}secret - Added proper
dependsOnfor Key Vault resources
3. infrastructure/scripts/deploy-foundation.sh
- Already correctly stores password in Key Vault via helper function
- No changes needed
References
- ADR-050: Azure Bastion replaces VPN Gateway for cost savings and simplicity
- ADR-048: Key Vault for cross-layer dependency management
- Bicep Module:
infrastructure/bicep/modules/bastion-vm.bicep