In this blog post, you’ll learn how to connect to an Azure Linux VM using a native client on your local computer through Azure Bastion, using an Azure PowerShell script.
With Azure Bastion, you can now also connect to an Azure Linux virtual machine (VM) by using Secure Shell (SSH) through the native client installed on your local machine.
Just keep in mind that this feature requires the Standard SKU tier for Azure Bastion.
Once you sign in to your target VM, the native client on your computer will open up with your VM SSH session with the use of the SSH CLI extension (az ssh).
If you are also running Azure Windows VMs in your environment, you can also look into another script I wrote that helps you make a secure connection to an Azure Windows VM via Azure Bastion by utilizing the native client installed on your local computer. You can find the blog post and Azure PowerShell script here.
To automate this connection process for an Azure Linux VM, I wrote the below Azure PowerShell script, which does all of the following:
- Remove the breaking change warning messages.
- Check if Azure CLI is already installed and, if required, update it to the latest version. If Azure CLI is not installed, install it.
- Change the current context to the subscription holding the Azure Bastion host.
- Save the Bastion host as a variable and check if it uses the Standard SKU; otherwise, the Azure PowerShell script will be exited.
- Update the Bastion host to enable native client support, if not already enabled.
- Install the ssh extension.
- Validate if the target VM exists, and if so, find the subscription it belongs to; otherwise, the Azure PowerShell script will be exited.
- SSH to the target VM using the native client through Azure Bastion.
To use the script, copy and save it as Connect-SSH-Azure-Linux-VM-using-native-client-via-Azure-Bastion.ps1 or download it from GitHub. Before using the script, adjust all variables to your needs, and then run the customized script from Windows Terminal.*.
*If you currently don’t have Windows Terminal installed on your device, you can download it directly from the Microsoft Store or from the GitHub releases page.
Prerequisites
- An Azure subscription (preferably more than one if you want to follow the Enterprise-Scale architecture).
- An Azure Administrator account with the required Azure RBAC roles.
- An existing Azure Bastion host with the Standard SKU.
- An existing Azure Windows virtual machine (VM) that is connected to a subnet part of a VNet that also holds the AzureBastionSubnet or has a peering relationship with the VNet holding the AzureBastionSubnet.
- Version 9.3.0 of the Azure Az PowerShell module is required.
- Azure CLI version 2.32 or later is required.
- If you are using Just-in-Time access (JIT), don’t forget to request access before connecting.
- If necessary, change the subscription in the script that contains the Azure Bastion host (as shown in one of the screenshots below).



Azure PowerShell script
Don’t forget to sign in with the Connect-AzAccount cmdlet to connect your Azure account when running your script from Windows Terminal.

You can then run the script with the required parameters:
.\Connect-SSH-Azure-Linux-VM-using-native-client-via-Azure-Bastion.ps1 <"your VM name here"> <"your User name here">

<#
.SYNOPSIS
A script used to SSH to a target Azure Linux VM using Tunneling from Azure Bastion with Azure CLI and PowerShell.
.DESCRIPTION
A script used to SSH to a target Azure Linux VM using Tunneling from Azure Bastion with Azure CLI and PowerShell.
The script will do all of the following:
Remove the breaking change warning messages.
Check if Azure CLI is already installed and, if required, update it to the latest version. If Azure CLI is not installed, install it.
Change the current context to the subscription holding the Azure Bastion host.
Save the Bastion host as a variable and check if it uses the Standard SKU; otherwise, exit the script.
Update the Bastion host to enable native client support, if not already enabled.
Install the ssh extension.
Validate if the target VM exists, and if so, find the subscription it belongs to; otherwise, exit the script.
SSH to the target VM using the native client through Azure Bastion.
.NOTES
Filename: Connect-SSH-Azure-Linux-VM-using-native-client-via-Azure-Bastion.ps1
Created: 07/03/2023
Last modified: 07/03/2023
Author: Wim Matthyssen
Version: 2.3
PowerShell: Azure PowerShell
Requires: PowerShell Az (v9.3.0)
CLI: Azure CLI
Requires: azure-cli 2.45.0
Action: Change variables as needed to fit your needs.
Disclaimer: This script is provided "as is" with no warranties.
.EXAMPLE
Connect-AzAccount
Get-AzTenant (if not using the default tenant)
Set-AzContext -tenantID "xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx" (if not using the default tenant)
.\Connect-SSH-Azure-Linux-VM-using-native-client-via-Azure-Bastion.ps1 <"your VM name here"> <"your User name here">
-> Connect-SSH-Azure-Linux-VM-using-native-client-via-Azure-Bastion.ps1 swpadm023 demo_admin
.LINK
https://wmatthyssen.com/2023/03/09/connecting-to-an-azure-linux-vm-using-an-azure-powershell-script-and-native-client-via-azure-bastion/
#>
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Parameters
param(
# $vmName -> Name of the target Azure Windows VM
[parameter(Mandatory =$true)][ValidateNotNullOrEmpty()] [string] $vmName,
# $userName -> Username
[parameter(Mandatory =$true)][ValidateNotNullOrEmpty()] [string] $userName
# $password -> Username
)
## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Variables
$allSubscriptions = Get-AzSubscription | Where-Object { "Enabled" -eq $_.State}
$subscriptionNameVM = ""
$vmObject = $null
$global:currenttime= Set-PSBreakpoint -Variable currenttime -Mode Read -Action {$global:currenttime= Get-Date -UFormat "%A %m/%d/%Y %R"}
$foregroundColor1 = "Green"
$foregroundColor2 = "Yellow"
$foregroundColor3 = "Red"
$writeEmptyLine = "`n"
$writeSeperatorSpaces = " - "
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Remove the breaking change warning messages
Set-Item -Path Env:\SuppressAzurePowerShellBreakingChangeWarnings -Value $true | Out-Null
Update-AzConfig -DisplayBreakingChangeWarning $false | Out-Null
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Write script started
Write-Host ($writeEmptyLine + "# Script has started. Without errors and required installations, it can take up to 1 minute to connect to $vmName" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor1 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Check if Azure CLI is already installed and, if required, update it to the latest version. If Azure CLI is not installed, install it
try {
if ($null -ne (az version)) {
Write-Host ($writeEmptyLine + "# If needed, the Azure CLI will be updated to the latest version, which can take a few minutes to complete" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
& az upgrade --yes 2>$null
}
} catch {
if ($error[0].ToString() -match "The term 'az' is not recognized as a name of a cmdlet") {
Write-Host ($writeEmptyLine + "# Azure CLI is not installed. To proceed, it will now be installed, which can take up to 2 minutes" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
# Install Azure CLI with MSI
$ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest -Uri https://aka.ms/installazurecliwindows -OutFile .\AzureCLI.msi;
Start-Process msiexec.exe -Wait -ArgumentList '/I AzureCLI.msi /quiet';
Remove-Item .\AzureCLI.msi
}
}
Finally {
Write-Host ($writeEmptyLine + "# Azure CLI is installed and running the latest version" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
}
# Enable Azure CLI auto-upgrade
# az config set auto-upgrade.enable=yes
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Change the current context to the subscription holding the Azure Bastion host
# Replace <your subscription purpose name here> with purpose name of your subscription. Example: "*management*"
$subcriptionNameBastion = Get-AzSubscription | Where-Object {$_.Name -like "*management*"}
Set-AzContext -SubscriptionId $subcriptionNameBastion.SubscriptionId | Out-Null
Write-Host ($writeEmptyLine + "# Bastion host subscription in current tenant selected" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Save the Bastion host as a variable and check if it uses the Standard SKU; otherwise, exit the script
$bastion = Get-AzBastion
if ($bastion.SkuText.Contains("Basic")) {
Write-Host ($writeEmptyLine + "# Bastion host runs with the Basic SKU, please upgrade to Standard SKU" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor3 $writeEmptyLine
Start-Sleep -s 3
Write-Host -NoNewLine ("# Press any key to exit the script ..." + $writeEmptyLine)`
-foregroundcolor $foregroundColor1 $writeEmptyLine;
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | Out-Null;
return
}
Write-Host ($writeEmptyLine + "# Bastion host variable created" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Update the Bastion host to enable native client support, if not already enabled
# Set Azure CLI core configuration to only show errors
az config set core.only_show_errors=yes --output none --only-show-errors
if ((az network bastion show --name $bastion.Name --resource-group $bastion.ResourceGroupName --query "enableTunneling") -ne $true) {
Write-Host ($writeEmptyLine + "# Native client support is not enabled. To proceed, it will now be enabled, which can take up to 6 minutes" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
az network bastion update --name $bastion.Name --resource-group $bastion.ResourceGroupName --enable-tunneling
}
Write-Host ($writeEmptyLine + "# Bastion host has native client support enabled" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Install the ssh extension
az extension add --name ssh --output none --only-show-errors
Write-Host ($writeEmptyLine + "# The extension ssh is installed" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Validate if the target VM exists, and if so, find the subscription it belongs to; otherwise, exit the script
if ($allSubscriptions){
foreach ($subscription in $allSubscriptions){
Set-AzContext -Subscription $Subscription.Name | Out-Null
$vmObject = Get-AzVM -Name $vmName
if ($vmObject) {
$subscriptionNameVM = $subscription.Name
Write-Host ($writeEmptyLine + "# Target VM found in subscription $subscriptionNameVM" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
break
}
}
}
if (-not $vmObject) {
Write-Host ($writeEmptyLine + "# VM not found" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor3 $writeEmptyLine
Start-Sleep -s 3
Write-Host -NoNewLine ("# Press any key to exit the script ..." + $writeEmptyLine)`
-foregroundcolor $foregroundColor1 $writeEmptyLine;
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | Out-Null;
return
}
## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## SSH to the target VM using the native client through Azure Bastion
Write-Host ($writeEmptyLine + "# Setting up an SSH connection to target VM $vmName and signing in. You will be prompted for the password" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
$vm = Get-AzVM -Name $vmName
# Azure CLI
az network bastion ssh --name $bastion.Name --resource-group $bastion.ResourceGroupName --target-resource-id $vm.Id --auth-type "password" --username $userName
# Set Azure CLI core configuration only show errors to no
az config set core.only_show_errors=no --output none --only-show-errors
## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Write script completed
Write-Host ($writeEmptyLine + "# Script completed" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor1 $writeEmptyLine
## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------



To exit, just type “exit” and the SSH connection will be closed and the script will be exited.

I hope that this Azure PowerShell script comes in handy when you want to make a secure connection to any Azure Linux VM via Azure Bastion, by utilizing the native client installed on your local computer.
If you have any questions or recommendations about it, feel free to contact me through my Twitter handle (@wmatthyssen) or to just leave a comment.
0 comments on “Connecting to an Azure Linux VM using an Azure PowerShell script and native client via Azure Bastion”