A modular, cross-platform PowerShell environment with a declarative Azure Bastion routing engine. Direct port of bash_environment — same commands, same config file, pick your shell.
| Command | Bash | PowerShell | Description |
|---|---|---|---|
bastion |
✅ | ✅ | Show help and list VMs |
bastion <alias> |
✅ | ✅ | Background tunnel |
bastion <alias> ssh |
✅ | ✅ | Interactive SSH session |
list_vms |
✅ | ✅ | List configured VMs |
list_connections |
✅ | ✅ | Show active tunnels |
cleanup_tunnels |
✅ | ✅ | Tear down connections |
connect-vm |
✅ | ✅ | Escape hatch wrapper |
start<alias> |
✅ | ✅ | Start VM (auto-generated) |
stop<alias> |
✅ | ✅ | Stop VM (auto-generated) |
Both shells read the same .bastion_topology.conf file — configure once, use from either shell.
profile.ps1 # Entry point — sources profile.d/* in order
profile.d/
15-ssh_agent.ps1 # Cross-platform SSH agent management
20-editor.ps1 # PSReadLine vi-mode, editor config
30-history.ps1 # History settings
30-azure_routing.ps1 # Core Azure Bastion routing engine (~500 lines)
.bastion_profiles/ # Escape hatch scripts for edge-case topologies
scripts/ # Standalone utilities
Add this line to your $PROFILE:
. ~/git/pwsh_environment/profile.ps1To find your profile location:
$PROFILE
# Usually: C:\Users\<you>\Documents\PowerShell\Microsoft.PowerShell_profile.ps1New-Item -ItemType SymbolicLink -Path $PROFILE -Target ~/git/pwsh_environment/profile.ps1 -Force# Create profile.d if it doesn't exist
$profileDir = Join-Path (Split-Path $PROFILE) "profile.d"
New-Item -ItemType Directory -Path $profileDir -Force
# Copy modules
Copy-Item ~/git/pwsh_environment/profile.d/*.ps1 $profileDir/
# Add loader to profile
Add-Content $PROFILE @'
# Load modules from profile.d
$profileDir = Join-Path (Split-Path $PROFILE) "profile.d"
if (Test-Path $profileDir) {
Get-ChildItem -Path $profileDir -Filter "*.ps1" | Sort-Object Name | ForEach-Object {
. $_.FullName
}
}
'@Uses the same .bastion_topology.conf as the bash version. Copy and configure:
Copy-Item ~/git/bash_environment/.bastion_topology.conf.example ~/.bastion_topology.conf
notepad ~/.bastion_topology.conf# alias|azure_name|resource_group|subscription|port|type|autostart|auth_type
BASTION_VMS=(
"api0|vm-api-dev-01|rg-project-dev|Sub_Dev|12022|flat|true|azureuser"
"jbox|vm-jumpbox-01|rg-sandbox|Sub_Sandbox|10022|tiered|false|entra"
"secvdi|vm-secure-vdi-01|rg-secure|Sub_Secure|20000|flat-entra|false|entra"
)
# Dynamic properties
declare -A VM_PROPS
VM_PROPS["jbox_az_tunnels"]="2022:10.0.1.5:22 13389:10.0.1.6:3389"
VM_PROPS["global_default_bastion"]="bst-default-region-01"# Show help and all configured VMs
bastion
# Establish a background tunnel (sets up port forwards)
bastion api0
# Drop into an interactive SSH session
bastion api0 ssh
# List all configured VMs and their routing settings
list_vms
# Show active connections and forwarded ports
list_connections
# Tear down tunnels
cleanup_tunnels api0 # Specific alias
cleanup_tunnels all # Everything
# Override SSH username for a single connection
$env:BASTION_USER = "root"; bastion api0 ssh
# Start/stop VMs directly (auto-generated functions)
startapi0
stopapi0Standard Azure Bastion tunnel directly to target VM.
- Backgrounds
az network bastion tunnel - Supports dynamic port forwarding via
VM_PROPS[<alias>_az_tunnels]
Azure Bastion with Entra ID (AAD) authentication.
- Uses
az network bastion ssh --auth-type AAD - Note: Dynamic port forwards (
-L) not supported in interactive mode
Multi-hop routing via jumpbox with nested port forwarding.
az ssh vm --ip <jumpbox>with-Lforwarding- Supports nested plink/ssh tunnels via
VM_PROPS[<alias>_plink_tunnels]
For VMs that don't fit standard topologies (JIT access, VPN, reverse shells), create a profile script:
# ~/.bastion_profiles/<vm-alias>.ps1
function Connect-VMProfile {
param([string]$Mode = "")
# Custom connection logic here
Write-Host "Requesting JIT access..."
# az security jit-policy initiate ...
Start-Sleep -Seconds 30
bastion -VM "my-vm" -Mode $Mode
}The connect-vm command will automatically use this instead of the standard engine.
| Feature | Bash | PowerShell |
|---|---|---|
| Background processes | & (disown) |
Start-Job |
| Associative arrays | declare -A |
@{} hashtables |
| Process detection | ps -ef | grep |
Get-Process + CIM |
| Port checking | netstat | egrep |
netstat + Select-String |
| Escape hatch function | connect() |
Connect-VMProfile |
- PowerShell 7+ (recommended) or Windows PowerShell 5.1
- Azure CLI (
az) installed and authenticated - OpenSSH client (built into Windows 10+)
- For tiered topology: PuTTY (
plink.exe) optional
This environment works on:
- Windows — Native PowerShell, uses Windows OpenSSH Agent service
- Linux/macOS — PowerShell Core, uses ssh-agent process management
- WSL — Either bash_environment or pwsh_environment
- bash_environment — The original bash version
- ARCHITECTURE.md — Network topology diagrams
Private repository. Internal use only.