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 to those VMs.
  • The Standard SKU enables premium features, like host scalingspecify 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 blogpost I already wrote an Azure PowerShell script which upgrades a bastion host from the Basic SKU to the Standard SKU. If you want, you 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 to speed up this proces, I wrote the below Azure PowerShell script which does all of the following:

  • Check if the PowerShell window is running as Administrator (when not running from Cloud Shell), otherwise the Azure PowerShell script will be exited.
  • Suppress breaking change warning messages.
  • Create Bastion resource variable for later use.
  • 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 with Administrator privileges 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 to 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 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. Otherwise, the script will fail. Keep in mind that only the Owner and the User Access Administrator built-in roles can create and delete resource locks.
  • At least Azure Az PowerShell module version 8.1.0 and Az.Network module version 4.18.1
  • Change all the variables in the script where needed to fit your needs.






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. And if you are using multiple Azure subscriptions, select the proper subscription with the Set-AzContext cmdlet before running the script.


If you want to validate or know the Bastion SKU for your Bastion host before running the script and without opening the Azure Portal, you can run following Azure PowerShell cmdlets:

 ## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

$bastion = Get-AzBastion  | Where-Object Name -Match bas
$bastionSku = $bastion.SkuText
Write-Host $bastionSku

## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------




<#
.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:

Check if the PowerShell window is running as Administrator (when not running from Cloud Shell), otherwise the Azure PowerShell script will be exited.
Suppress breaking change warning messages.
Create Bastion resource variable for later use.
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 14 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:  06/10/2022
Author:         Wim Matthyssen
Version:        1.2
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)
Set-AzContext -Subscription "<SubscriptionName>" (if not using the default subscription)
.\Switch-AzureBastion-Standard-SKU-to-Basic-SKU.ps1 <"your Bastion host name here"> 

-> .\Switch-AzureBastion-Standard-SKU-to-Basic-SKU.ps1 bas-hub-myh-01

.LINK

https://wmatthyssen.com/2022/10/05/azure-bastion-switch-standard-sku-to-basic-sku-with-azure-powershell/
#>

## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Parameters

param(
    # $bastionName -> Name of the Azure Bastion host
    [parameter(Mandatory =$true)][ValidateNotNullOrEmpty()] [string] $bastionName
)

## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Variables

$bastionSkuBasic = "Basic"

$global:currenttime= Set-PSBreakpoint -Variable currenttime -Mode Read -Action {$global:currenttime= Get-Date -UFormat "%A %m/%d/%Y %R"}
$foregroundColor1 = "Red"
$foregroundColor2 = "Yellow"
$writeEmptyLine = "`n"
$writeSeperatorSpaces = " - "

## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Check if PowerShell runs as Administrator (when not running from Cloud Shell), otherwise exit the script

if ($PSVersionTable.Platform -eq "Unix") {
    Write-Host ($writeEmptyLine + "# Running in Cloud Shell" + $writeSeperatorSpaces + $currentTime)`
    -foregroundcolor $foregroundColor1 $writeEmptyLine
    
    ## Start script execution    
    Write-Host ($writeEmptyLine + "# Script started. Without any errors, it can take up to 14 minutes to complete" + $writeSeperatorSpaces + $currentTime)`
    -foregroundcolor $foregroundColor1 $writeEmptyLine 
} else {
    $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
    $isAdministrator = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)

        ## Check if running as Administrator, otherwise exit the script
        if ($isAdministrator -eq $false) {
        Write-Host ($writeEmptyLine + "# Please run PowerShell as Administrator" + $writeSeperatorSpaces + $currentTime)`
        -foregroundcolor $foregroundColor1 $writeEmptyLine
        Start-Sleep -s 3
        exit
        }
        else {

        ## If running as Administrator, start script execution    
        Write-Host ($writeEmptyLine + "# Script started. Without any errors, it can take up to 14 minutes to complete" + $writeSeperatorSpaces + $currentTime)`
        -foregroundcolor $foregroundColor1 $writeEmptyLine 
        }
}

## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Suppress breaking change warning messages

Set-Item Env:\SuppressAzurePowerShellBreakingChangeWarnings "true"

## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Create Bastion resource variable

$bastion = Get-AzBastion | Where-Object Name -Match $bastionName

Write-Host ($writeEmptyLine + "# Bastion variable created" + $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

$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 }}

# 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

$rgNameBastion = $bastion.ResourceGroupName
$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.


%d bloggers like this: