This blog post will demonstrate how to use the Bastion Shareable Link feature* to connect to one of your Azure virtual machines (VMs) without using the Azure Portal.
* At the moment, the Bastion Shareable Link feature is generally available (GA), and you can now also connect to VMs in peered virtual networks (VNets) across subscriptions and across regions. It is also supported for national clouds.
Like most of you will probably already know, Azure Bastion is an Azure PaaS service that you provision inside a VNet, preferably the HUB VNet. When configured, it then allows you to securely connect to your Azure VMs via RDP or SSH, and this is done directly from the Azure portal over SSL.
But with the use of the Bastion Shareable Link feature, you can now let users connect to a VM or VM scale set using Azure Bastion without accessing the Azure portal.
A shareable link is a link that can be shared, which allows the user (internal or external) who receives it to connect directly to that VM via an Azure Bastion session. After clicking on the link, the user will be redirected to the Azure portal, and after proper authentication, they can directly connect to the VM using Azure Bastion.
Depending on the configuration, a VM can be accessed by a user with a username and password or with a private key. And this is because the link itself doesn’t contain any authentication information.
By default, users in your organization will have only read access to shared links, which allows them to view and use shareable links, but they will not be able to create or delete a link.
So, this feature will come in handy during all kinds of IT projects or support cases where temporary or one-time access is required to perform any kind of administrative or troubleshooting tasks on one or more Azure VMs.
To enable, create, and use a Bastion Shareable Link, you can follow the steps below.
Prerequisites
- An Azure subscription.
- An Azure Administrator account with the necessary RBAC roles.
- An existing Azure Bastion host with the Standard SKU.
- An existing VNet and a subnet with an associated network security group (NSG), located in the same or in a different subscription and/or region as the Bastion host.
- An existing VM, connected to the above subnet.


If you need to deploy a new Azure Bastion host or if you need to upgrade the SKU, you can always check out one of my previous blog posts: Azure Bastion: Azure PowerShell deployment script – Azure Bastion: Upgrade Basic SKU to Standard SKU with Azure PowerShell
Enable the Bastion Shareable Link feature
First of all, you need to enable the Shareable Link feature if it’s not already enabled.
To do so, first of all, logon to the Azure Portal and type in “bastion” in the Global search bar. Then click on Bastions.

On the Bastions page, click on your bastion resource.

On the Bastion page, click Configuration. Then select Shareable Link and click Apply.
Keep in mind that updating the settings for your bastion host can take up to 7 to 10 minutes.



Create and use a shareable link via the Azure Portal
In the Azure Portal, go back to your bastion resource, and on the Bastion page under Settings, click Shareable links. Then, to create your shareable link, click +Add.

In the next step, you then need to specify the resource group and the VM(s) you would like to create your shareable link for. You can select a single, multiple, or all VMs in the selected resource group.
For each VM you select, a separate shareable link will be created. When you have selected your VM(s), click Apply to create the link(s).


Once the links are successfully created, you can view them on the Shareable links page. To share the link, click on the copy button and share it with the user who requires it.

The user then just needs to paste the link into a browser, fill in a username and password in the correct fields (if it is the first time connecting, you should also click Allow in the pop-up), and click Login.


Azure PowerShell script to create and get a shareable link
To automate the creation of a shareable link for a specific Azure VM running in your environment, I wrote the below Azure PowerShell script, which does all of the following:
- Remove the breaking change warning messages.
- Validate if the target VM exists, and if so, find the subscription it belongs to; otherwise, the Azure PowerShell script will be exited.
- Save the Bastion host as a variable and check if it uses the Standard SKU; otherwise, the Azure PowerShell script will be exited.
- Create the shareable link for the VM using the REST API.
- Get the shareable link for the VM using the REST API.
To use the script, copy and save it as Create-Azure-Bastion-shareable-link.ps1 or download it from GitHub. Then, run the script from Windows Terminal, Visual Studio Code, or Windows PowerShell. Or you can simply run it from Cloud Shell.
Don’t forget to sign in with the Connect-AzAccount cmdlet to connect your Azure account when running your script from Windows Terminal or Windows PowerShell.

You can then run the script with the required parameters:
.\Create-Azure-Bastion-shareable-link.ps1 -VMName <"your VM name here">

<#
.SYNOPSIS
A script used to create an Azure Bastion shareable link for a specific VM.
.DESCRIPTION
A script used to create an Azure Bastion shareable link for a specific VM.
The script will do all of the following:
Remove the breaking change warning messages.
Validate if the target VM exists, and if so, find the subscription it belongs to; otherwise, exit the script.
Validate if an Azure Bastion host exists, and if so, save the Bastion host as a variable if it uses the Standard SKU; otherwise, exit the script.
Create the shareable link for the VM using the REST API.
Get the shareable link for the VM using the REST API.
.NOTES
Filename: Create-Azure-Bastion-shareable-link.ps1
Created: 20/01/2023
Last modified: 23/03/2023
Author: Wim Matthyssen
Version: 2.0
PowerShell: Azure PowerShell
Requires: PowerShell Az (v9.3.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)
.\Create-Azure-Bastion-shareable-link.ps1 -VMName <"your VM name here">
-> Create-Azure-Bastion-shareable-link.ps1 -VMName swpdc003
.LINK
https://wmatthyssen.com/2023/01/11/azure-bastion-connect-to-an-azure-vm-without-accessing-the-azure-portal-by-using-a-shareable-link/
#>
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Parameters
param(
# $vmName -> Name of the target Azure Windows VM
[parameter(Mandatory =$true)][ValidateNotNullOrEmpty()] [string] $vmName
)
## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Variables
$subscriptionNameVM = ""
$vmObject = $null
$httpsUriStart = "https://management.azure.com/subscriptions/"
$createApiVersion = "createShareableLinks?api-version=2022-07-01"
$getApiVersion = "GetShareableLinks?api-version=2022-07-01"
$authenticationType = "Bearer"
$method = "Post"
$contentType = "application/json"
$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 started. Without errors, it takes up to 1 minute to complete" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor1 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Validate if the target VM exists, and if so, find the subscription it belongs to; otherwise, exit the script
$allSubscriptions = Get-AzSubscription | Where-Object { "Enabled" -eq $_.State}
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 $vmName 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 ($writeEmptyLine + "# Press any key to exit the script ..." + $writeEmptyLine)`
-foregroundcolor $foregroundColor1 $writeEmptyLine;
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | Out-Null;
return
}
## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Validate if an Azure Bastion host exists, and if so, save the Bastion host as a variable if it uses the Standard SKU; otherwise, exit the script
$bastionObject = Get-AzBastion
$bastionName = ($bastionObject).Name
if ($null -eq $bastionObject){
Write-Host ($writeEmptyLine + "# There is no Bastion host included in the current subscription" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
Start-Sleep -s 3
Write-Host ($writeEmptyLine + "# Press any key to exit the script ..." + $writeEmptyLine)`
-foregroundcolor $foregroundColor1 $writeEmptyLine;
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | Out-Null;
return
} else {
if ($bastionObject.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 ($writeEmptyLine + "# Press any key to exit the script ..." + $writeEmptyLine)`
-foregroundcolor $foregroundColor1 $writeEmptyLine;
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | Out-Null;
return
} else {
Write-Host ($writeEmptyLine + "# Bastion host $bastionName exists in the current subscription; the script will continue" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
}
}
## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Create the shareable link for the VM using the REST API
# Get subscription ID
$subscriptionObject = Get-AzContext | Select-Object Subscription
$subscriptionID = $subscriptionObject.Subscription.Id
# Get Bastion parameters
$bastionResourceGroupName = ($bastionObject).ResourceGroupName
# Create REST API parameters
$uri = $httpsUriStart + $subscriptionID + "/resourceGroups/$($bastionResourceGroupName)/providers/Microsoft.Network/bastionHosts/$bastionName/$createApiVersion"
$token = (Get-AzAccessToken).Token | ConvertTo-SecureString -AsPlainText -Force
$body = @{vms = @(@{vm = @{id = $(($vmObject).Id) }})} | ConvertTo-Json -Depth 10
# Send an HTTP or HTTPS request to a REST API endpoint
Invoke-WebRequest -Uri $uri -Authentication $authenticationType -Token $token -Method $method -Body $body -ContentType $contentType | Out-Null
# Wait for the link to be created, to avoid errors
Write-Host ($writeEmptyLine + "# Waiting for the shareable link for VM $vmName to be created" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
Start-Sleep -Seconds 5
## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Get the shareable link for the VM using the REST API
# Get REST API parameters
$uri = $httpsUriStart + $subscriptionID + "/resourceGroups/$($bastionResourceGroupName)/providers/Microsoft.Network/bastionHosts/$bastionName/$getApiVersion"
$getShareableLink = Invoke-RestMethod -Uri $uri -Authentication $authenticationType -Token $token -Method $method -Body $body -ContentType $contentType
$shareableLink = $getShareableLink.value.bsl
Write-Host ($writeEmptyLine + "# Shareable link for VM $vmName available: $shareableLink" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Write script completed
Write-Host ($writeEmptyLine + "# Script completed" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor1 $writeEmptyLine
## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Conclusion
Azure Bastion Shareable Link is a feature that lets Azure administrators provide secure remote access to an Azure VM. With the use of this link, users with the appropriate sign-in credentials can then access that VM without the need to open the Azure portal or use any Azure VPN.
If you have any questions related to the use of Bastion Shareable Link in your environment, feel free to contact me through my Twitter handle (@wmatthyssen) or to just leave a comment. I am always happy to help.
0 comments on “Azure Bastion: Connect to an Azure VM without accessing the Azure portal by using a shareable link”