Accessing VMs via Azure Bastion Tunnel + RDP
Overview
All eSHARS VMs have had their public IPs removed as part of the Defender remediation (HM-8334). Access is now exclusively through Azure Bastion tunnels, which create an encrypted tunnel from your local machine to the VM via the Azure backbone — no public IP or VPN required.
Prerequisites
-
Azure CLI installed
- Windows:
winget install Microsoft.AzureCLI - macOS:
brew install azure-cli - Linux:
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
- Windows:
-
Azure subscription access — in the HISD subscription (
9639039c-86c6-4859-b014-d28688863cd5):- Portal / read-only access to VMs & Bastion:
Readerrole on the relevant resources or resource group. - Creating Bastion tunnels (required for this guide):
Bastion Tunnel Userrole (or an equivalent custom role that permits Bastion tunnel creation) on the Bastion resource.
- Portal / read-only access to VMs & Bastion:
-
RDP client
- Windows:
mstsc.exe(built-in) - macOS: Microsoft Remote Desktop from the App Store
- Linux:
xfreerdp(sudo apt install freerdp2-x11)
- Windows:
-
VM credentials — Windows local admin credentials (stored in Azure Key Vault
kv-eshars-mgmt)
Azure Login
Log in to the HISD tenant (MFA required):
az login --tenant b07ee812-5705-40b5-b227-3df824080db8Set the correct subscription:
az account set --subscription 9639039c-86c6-4859-b014-d28688863cd5Quick Start — Using the Helper Scripts
Helper scripts are available in the eshars-ops/scripts/bastion/ repo. Prod and nonprod use separate scripts to prevent accidental prod connections.
Nonprod (PowerShell / Bash)
# List all available nonprod VMs and active tunnels.\bastion-rdp-nonprod.ps1 -List
# Connect to a VM.\bastion-rdp-nonprod.ps1 vm-db-api
# Kill an existing tunnel.\bastion-rdp-nonprod.ps1 -Kill vm-db-api./bastion-rdp-nonprod.sh --list./bastion-rdp-nonprod.sh vm-db-api./bastion-rdp-nonprod.sh --kill vm-db-apiProd (PowerShell / Bash)
Prod scripts prompt for confirmation before connecting.
# List all available prod VMs.\bastion-rdp-prod.ps1 -List
# Connect (prompts for confirmation).\bastion-rdp-prod.ps1 vm-rns-prod
# Skip confirmation.\bastion-rdp-prod.ps1 vm-rns-prod -Yes
# Map D:\ into the RDP session (for file transfer).\bastion-rdp-prod.ps1 vm-rns-prod -MapDrives
# Map both C:\ and D:\.\bastion-rdp-prod.ps1 vm-rns-prod -MapDrives -MapC./bastion-rdp-prod.sh --list./bastion-rdp-prod.sh vm-rns-prod./bastion-rdp-prod.sh --yes vm-rns-prod./bastion-rdp-prod.sh --map-drives vm-rns-prod # D:\ only./bastion-rdp-prod.sh --map-drives --map-c vm-rns-prod # C:\ and D:\All scripts handle:
- Subscription validation and switching
- Tunnel creation in the background (reuses existing tunnels)
- RDP file generation with clipboard redirection
- RDP client launch
Note: Drive mapping behavior differs between environments. Nonprod scripts map all local drives automatically. Prod scripts do not map any local drives by default — use
-MapDrivesto map D:, and add-MapCto also include C:\ (contains system/profile data).
Manual Steps
If you prefer to connect manually or need to access a VM not in the helper scripts:
Step 1 — Get the VM Resource ID
az vm show -g <RESOURCE_GROUP> -n <VM_NAME> --query id -o tsvStep 2 — Start the Bastion Tunnel
az network bastion tunnel \ --name <BASTION_NAME> \ --resource-group <BASTION_RG> \ --target-resource-id "<FULL_VM_RESOURCE_ID>" \ --resource-port 3389 \ --port <LOCAL_PORT>Keep this terminal open — the tunnel runs in the foreground.
Step 3 — Connect via RDP
Open a second terminal:
mstsc /v:localhost:<LOCAL_PORT>To include local drive mapping, create a .rdp file:
full address:s:localhost:<LOCAL_PORT>prompt for credentials:i:1screen mode id:i:2use multimon:i:1drivestoredirect:s:*redirectclipboard:i:1Save it and open with mstsc (Windows), open (macOS), or xfreerdp (Linux).
VM Registry
Nonprod VMs
| VM Name | Local Port | Resource Group | Bastion |
|---|---|---|---|
| vm-sql16-np | 33891 | rg-eshars-nonprod | bastion-eshars-nonprod |
| vm-sql16-1-np | 33892 | rg-eshars-nonprod-centralus | bastion-eshars-nonprod |
| vm-ops-nonprod | 33893 | rg-eshars-nonprod | bastion-eshars-nonprod |
| vm-successed-nonprod | 33894 | rg-eshars-nonprod | bastion-eshars-nonprod |
| vm-db-api | 33895 | rg-eshars-nonprod | bastion-eshars-nonprod |
| vm-cleanup-jobs | 33896 | rg-eshars-nonprod | bastion-eshars-nonprod |
| vm-jobscheduler-np-001 | 33897 | rg-eshars-nonprod | bastion-eshars-nonprod |
| vm-jobscheduler | 33898 | rg-eshars-nonprod | bastion-eshars-nonprod |
Prod VMs
| VM Name | Local Port | Resource Group | Bastion |
|---|---|---|---|
| vm-rns-prod | 33900 | rg-eshars-prod | bastion-eshars-prod |
| vm-jobscheduler-prod | — | rg-eshars-prod | bastion-eshars-prod |
| vm-tmhp-int | — | rg-eshars-prod | bastion-eshars-prod |
| vm-tmhp-prod | — | rg-eshars-prod | bastion-eshars-prod |
| vm-ssrs16-prod | — | rg-eshars-prod | bastion-eshars-prod |
| vm-ops-prod | — | rg-eshars-prod | bastion-eshars-prod |
VMs with a — for local port are not yet in the helper script. Use the manual steps above, or add them to bastion-rdp-prod.ps1 / .sh.
Bastion Instances
| Bastion | Resource Group | SKU | VNet | Subnet | Tunneling |
|---|---|---|---|---|---|
| bastion-eshars-nonprod | rg-eshars-nonprod | Standard | vnet-eshars-nonprod (10.1.0.0/16) | AzureBastionSubnet (10.1.1.0/26) | Yes |
| bastion-eshars-prod | rg-eshars-prod | Developer | vnet-eshars (10.0.0.0/16) | AzureBastionSubnet (10.0.1.0/26) | Yes (native client supported) |
Enabling File Sharing in RDP
To access your local drives from within the remote VM:
Using the Helper Scripts
The scripts automatically configure drive mapping. Your local drives will appear under “This PC” in File Explorer on the remote VM as redirected drives.
Using mstsc Manually (Windows)
- Open
mstsc - Click Show Options
- Go to Local Resources tab
- Under “Local devices and resources”, click More…
- Check the drives you want to share
- Click OK, then Connect
Using an .rdp File
Add this line to your .rdp file:
- Map all drives:
drivestoredirect:s:* - Map specific drives:
drivestoredirect:s:C:\;D:\
Using Microsoft Remote Desktop (macOS)
- Open Microsoft Remote Desktop
- Edit the connection (or create a new one to
localhost:<PORT>) - Go to the Folders tab
- Enable Redirect folders
- Click + to add folders you want to share
Using xfreerdp (Linux)
xfreerdp /v:localhost:<PORT> /drive:home,$HOME /clipboard /dynamic-resolutionNSG Requirements
Every VM must have an NSG rule allowing Bastion traffic. Without this rule, the tunnel will connect but RDP will hang.
Name: AllowBastionRDPPriority: 200Access: AllowProtocol: TCPDirection: InboundSource: <bastion-subnet-cidr>, 168.63.129.16Dest Ports: 3389, 22az CLI:
az network nsg rule create \ --nsg-name <NSG_NAME> -g <RG> \ -n AllowBastionRDP --priority 200 \ --access Allow --protocol Tcp --direction Inbound \ --source-address-prefixes <BASTION_SUBNET> 168.63.129.16 \ --destination-port-ranges 3389 22 \ --description "Allow RDP/SSH from Azure Bastion subnet"Bastion subnet CIDRs:
- Prod:
10.0.1.0/26 - Nonprod:
10.1.1.0/26
Troubleshooting
”Tunnel is ready” but RDP won’t connect
- Verify the VM’s NSG has the
AllowBastionRDPrule (see above) - Ensure the VM is running:
az vm show -g <RG> -n <VM> --query powerState -o tsv - Check that port 3389 (RDP) is enabled inside the VM’s Windows Firewall
az login errors with AADSTS50076 (MFA required)
Log in with the specific tenant:
az login --tenant b07ee812-5705-40b5-b227-3df824080db8“unrecognized arguments: —target-vm-name”
The tunnel command requires --target-resource-id (the full ARM resource ID), not --target-vm-name.
Port already in use
Another tunnel or process is using that local port. Either:
- Use the
--kill/-Killflag in the helper script to stop the old tunnel - Choose a different local port
- Find and kill the process:
lsof -i :<PORT>(macOS/Linux) ornetstat -ano | findstr <PORT>(Windows)
macOS: “line 18: vm: unbound variable”
macOS ships with bash 3.x which doesn’t support associative arrays. Use the bastion-rdp.sh script (v2+) which is compatible with bash 3.x, or install modern bash: brew install bash.
Access denied / no subscription access
You need the Bastion Tunnel User custom role or equivalent permissions. Ask a subscription admin to add you to the Bastion Tunnel Users security group.