This blog post will show you how you can switch Azure Bastion from the Standard SKU to the Basic SKU (Tier) via the use of an Azure PowerShell script.
Currently, Azure Bastion is available in two SKUs: Basic and Standard.
- The Basic SKU provides basic functionality, which enables Azure Bastion to manage RDP/SSH connectivity to virtual machines (VMs) without exposing public IP addresses (PIP) on any of those VMs.
- The Standard SKU enables premium features, like host scaling, specifying custom inbound ports when using Azure Bastion, etc.
These days, you can easily upgrade from the Basic SKU to the Standard SKU in the Azure Portal or with Azure PowerShell*.
* In a previous blog post, I already wrote an Azure PowerShell script that upgrades a bastion host from the Basic SKU to the Standard SKU. If you want, you can take a look at it over here: Azure Bastion: Upgrade Basic SKU to Standard SKU with Azure PowerShell
However, downgrading from the Standard SKU to the Basic SKU is not possible and not supported at the moment.

So, to switch a Standard SKU Azure Bastion host to the Basic SKU, you must delete and recreate that Bastion host. To automate and speed up this process, I wrote the below Azure PowerShell script, which does all of the following:
- Remove the breaking change warning messages.
- Save the Bastion host if it exists in the subscription as a variable and check if it uses the Basic SKU; if so, exit the script, otherwise the script will continue.
- Check if the Bastion resource group has a resource lock; if so, exit the script.
- Store the specified set of Azure Bastion host tags in a hash table.
- Delete Azure Bastion host with Standard SKU.
- Redeploy same Azure Bastion host with Basic SKU.
- Lock the Azure Bastion resource group with a CanNotDelete lock.
To use the script, copy and save it as Switch-AzureBastion-Standard-SKU-to-Basic-SKU.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.
If you want, you can also look at my previous blog posts, which can help you securely deploy and configure Azure Bastion and all associated resources: Azure Bastion: Azure PowerShell deployment script, Azure Bastion: Set the minimum required roles to access a virtual machine and Azure Bastion: Set Azure Bastion NSG Inbound security rules on the Target VM Subnet with Azure PowerShell
Prerequisites
- An Azure subscription.
- An Azure Administrator account with the necessary RBAC roles.
- An existing Azure Bastion host.
- The Azure Bastion host’s Public IP Address (PIP) must be located in the same resource group as your Azure Bastion host.
- If you have a resource lock on the resource group holding the Azure Bastion host, remove it temporarily while running the script. Keep in mind that only the Owner and the User Access Administrator built-in roles can create and delete resource locks.
- Azure Az PowerShell module version 8.1.0 and Az.Network module version 4.18.1 are required.



Azure PowerShell script
If you are not running the script from Cloud Shell, don’t forget to sign in with the Connect-AzAccount cmdlet to connect your Azure account.

If you want to validate or know the Bastion SKU for your Bastion host before running the script and without opening the Azure Portal. First of all, set the context to the subscription holding the Azure Bastion host(s), and then run the following Azure PowerShell cmdlets:
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
$bastion = Get-AzBastion | Where-Object Name -Match bas
$bastionSku = $bastion.SkuText
Write-Host $bastionSku
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


You can then run the script with the required parameters:
.\Switch-AzureBastion-Standard-SKU-to-Basic-SKU.ps1 <"your azure bastion host subscription name here"> <"your bastion host name here">

<#
.SYNOPSIS
A script used to switch an Azure Bastion host with Standard SKU to the Basic SKU.
.DESCRIPTION
A script used to switch an Azure Bastion host with Standard SKU to the Basic SKU.
The script will do all of the following:
Remove the breaking change warning messages.
Change the current context to the subscription holding the Azure Bastion host, if the subscription exists; otherwise, exit the script.
Save the Bastion host if it exists in the subscription as a variable and check if it uses the Basic SKU; if so, exit the script, otherwise the script will continue.
Check if the Bastion resource group has a resource lock; if so, exit the script.
Store the specified set of Azure Bastion host tags in a hash table.
Delete Azure Bastion host with Standard SKU.
Redeploy same Azure Bastion host with Basic SKU.
Lock the Azure Bastion resource group with a CanNotDelete lock.
** Keep in mind running this script can take up to 19 minutes. **
** If you have a resource lock on the resource group holding the bastion host, remove it temporarily while running the script. **
.NOTES
Filename: Switch-AzureBastion-Standard-SKU-to-Basic-SKU.ps1
Created: 04/10/2022
Last modified: 05/03/2023
Author: Wim Matthyssen
Version: 2.3
PowerShell: Azure Cloud Shell or Azure PowerShell
Requires: PowerShell Az (v8.1.0) and Az.Network (v4.18.0)
Action: Change variables were 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)
.\Switch-AzureBastion-Standard-SKU-to-Basic-SKU.ps1 <"your azure bastion host subscription name here"> <"your bastion host name here">
-> .\Switch-AzureBastion-Standard-SKU-to-Basic-SKU.ps1 sub-hub-myh-management-01 bas-hub-myh-01
.LINK
https://wmatthyssen.com/2022/10/05/azure-bastion-switch-standard-sku-to-basic-sku-with-azure-powershell/
#>
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Parameters
param(
# $subscriptionName -> Name of the subscription holding the Azure Bastion host
[parameter(Mandatory =$true)][ValidateNotNullOrEmpty()] [string] $subscriptionName,
# $bastionName -> Name of the Azure Bastion host
[parameter(Mandatory =$true)][ValidateNotNullOrEmpty()] [string] $bastionName
)
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Variables
$bastionSkuBasic = "Basic"
$bastionSkuStandard = "Standard"
$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 Env:\SuppressAzurePowerShellBreakingChangeWarnings "true" | Out-Null
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Write script started
Write-Host ($writeEmptyLine + "# Script started. Without errors, it can take up to 19 minutes to complete" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor1 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Change the current context to the subscription holding the Azure Bastion host, if the subscription exists; otherwise, exit the script
Get-AzSubscription -SubscriptionName $subscriptionName -ErrorVariable subscriptionNotPresent -ErrorAction SilentlyContinue | Out-Null
if ($subscriptionNotPresent) {
Write-Host ($writeEmptyLine + "# Subscription with name $subscriptionName does not exist in the current tenant" + $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
} else {
Set-AzContext -Subscription $subscriptionName | Out-Null
Write-Host ($writeEmptyLine + "# Subscription with name $subscriptionName in current tenant selected" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
}
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Save the Bastion host if it exists in the subscription as a variable and check if it uses the Basic SKU; if so, exit the script, otherwise the script will continue
$bastion = Get-AzBastion | Where-Object Name -Match $bastionName
# Check if a Bastion host exists in the subscription; otherwise, exit the script
if ($null -eq $bastion){
Write-Host ($writeEmptyLine + "# No Bastion host exists in the current subscription, please select the correct context and rerun the script" + $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
}
# Check if the Bastion host is running the Standard SKU; otherwise, exit the script
if ($bastion.SkuText.Contains("Basic")) {
Write-Host ($writeEmptyLine + "# Bastion host already using the Basic 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
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Check if the Bastion resource group has a resource lock; if so, exit the script
$lock = Get-AzResourceLock -ResourceGroupName $bastion.ResourceGroupName
$rgNameBastion = $bastion.ResourceGroupName
if ($null -ne $lock){
Write-Host ($writeEmptyLine + "# Bastion resource group has a resource lock; please remove it and rerun the script" + $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 + "# Resource group $rgNameBastion has no resource lock; the script will continue" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Store the specified set of Azure Bastion host tags in a hash table
$bastionTags = (Get-AzResource -ResourceGroupName $bastion.ResourceGroupName -ResourceName $bastion.Name).Tags
Write-Host ($writeEmptyLine + "# Specified set of tags available to add" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Delete Bastion host with Standard SKU
Write-Host ($writeEmptyLine + "# Delete bastion host $bastionName with the $bastionSkuStandard SKU, which can take up to 8 minutes to complete" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor1 $writeEmptyLine
$bastionName = $bastion.Name
Remove-AzBastion -InputObject $bastion -Force | Out-Null
Write-Host ($writeEmptyLine + "# Bastion host $bastionName temporarily removed" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Redeploy Bastion host with Basic SKU
$pipNameBastion = Get-AzPublicIpAddress -ResourceGroupName $bastion.ResourceGroupName
$vnetName = Get-AzVirtualNetwork | ForEach-Object {if($_.Subnets.Name.Contains("AzureBastionSubnet")){return $_.Name}}
$rgNameNetworking = Get-AzVirtualNetwork | ForEach-Object {if($_.Subnets.Name.Contains("AzureBastionSubnet")){return $_.ResourceGroupName }}
Write-Host ($writeEmptyLine + "# Redeploy bastion host $bastionName with $bastionSkuBasic SKU, which can take up to 10 minutes to complete" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor1 $writeEmptyLine
# Redeploy Bastion host with Basic SKU
New-AzBastion -ResourceGroupName $bastion.ResourceGroupName -Name $bastion.Name -PublicIpAddress $pipNameBastion -VirtualNetworkRgName $rgNameNetworking `
-VirtualNetworkName $vnetName -Sku $bastionSkuBasic | Out-Null
# Set tags on Bastion host
Set-AzBastion -InputObject $bastion -Tag $bastionTags -Force | Out-Null
Write-Host ($writeEmptyLine + "# Bastion host $bastionName with $bastionSkuBasic SKU available" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Lock the Azure Bastion resource group with a CanNotDelete lock
$lock = Get-AzResourceLock -ResourceGroupName $bastion.ResourceGroupName
if ($null -eq $lock){
New-AzResourceLock -LockName DoNotDeleteLock -LockLevel CanNotDelete -ResourceGroupName $bastion.ResourceGroupName -LockNotes "Prevent $rgNameBastion from deletion" -Force | Out-Null
}
Write-Host ($writeEmptyLine + "# Resource group $rgNameBastion locked" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Write script completed
Write-Host ($writeEmptyLine + "# Script completed" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor1 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------




I hope this Azure PowerShell script is useful for you whenever you need to switch an existing Bastion host from the Standard SKU to the Basic SKU.
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 “Azure Bastion: Switch Standard SKU to Basic SKU with Azure PowerShell”