Azure Azure Bastion Azure PowerShell

Azure Bastion: Switch Standard SKU to Basic SKU with Azure PowerShell


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 SKUsBasic 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 scalingspecifying 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 TerminalVisual 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 resourcesAzure Bastion: Azure PowerShell deployment scriptAzure 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

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: