Azure Bastion is a service that allows you to connect securely to a virtual machine via RDP or SSH in the Azure portal with a browser. No public IP address is required which helps to limit threats.

Azure Bastion overview
Source: https://docs.microsoft.com/nl-nl/azure/bastion/bastion-overview

It is very simple to implement because it’s integrated in the Azure portal. Just open a virtual machine, select “Bastion” in the menu as way to connect. This first time, click on “Create Azure Bastion using defaults” and its ready to use.

Now, you can connect using Bastion every time you need to access the virtual machine.

The only downfall is the costs when you’re not using it and even when your virtual machine is stopped, you will still be charged (something like €0.188 per hour in the basic tier).

So, how could we save up to almost 140 euro a month, when not using Bastion?

Solution

With one of my projects, we came up with a solution. Because it is not possible to stop the Bastion service, we just delete the Bastion Service when we don’t use it.

There are several Azure options to automate this but in this case we choose a Runbook in an Automation Account because the costs should be low. The first 500 minutes of job runtime are free and for every extra minute you will be charged €0.002 per minute (more information about the pricing).

The runbook will execute the deletion of the Bastion service. We add a schedule so it will be executed every night.

The downside of removing the Bastion service every night is that it has to be created every time you want to use it. Because it takes a while to create the Bastion service, users may choose to use other options to connect to the virtual machine. This can be solved by enforcing Bastion for example by disabling the SSH port on the firewall or by a policy. This is not included in this showcase.

Before we start with this showcase, there are some prerequisites:

  • A virtual machine in Azure
  • A Bastion service linked to the virtual machine
  • The name of the Bastion service. The name can be found in the menu item “Bastion” in your virtual machine, as seen in the screenshot below:

Create a runbook

First we start by creating an Automation Account.

  1. Open the Azure portal
  2. Go to the “Automation Accounts” overview and click “Create”
  3. Fill in all the desired settings and create the service. The only thing important in this showcase is that you enable “System assigned” in the managed Identities pane.
  4. After the service is created, open it and go the menu item “Identity”
  5. Enable the status in “System assigned” if not already enabled. (Note: this setting is related to the rights needed by the runbook to get things done in Azure):
  6. Next we need to add “Azure role assignments” so click on the big button at the bottom of the same page. Create a new role assignment (Scope: Subscription, Role: Contributor)
  7. After you saved the role assignment, you can return to the Automation Account (see the arrow in the image below):
  8. Next, open the menu item “Runbook”:
  9. Create a new runbook with runbook type “PowerShell”. You can select other runbook type (Python, Powershell Workflow, Graphical PowerShell or Graphical PowerShell Workflow) if you prefer but the script in the next step is written in PowerShell.
  10. The Runbook will be created and opens directly in the editor mode. You can copy-paste the code below and change the texts between the brackets. Don’t forget to save!
    (If you accidentally close the editor screen, it can be reopened by editing the runbook)
try
{
    "Logging in to Azure..."
    Connect-AzAccount -Identity
}
catch {
    Write-Error -Message $_.Exception
    throw $_.Exception
}
"Selecting subscription"
Select-AzSubscription <SUBSCRIPTIONID>
try
{
    try {
		#get info about bastion
		$nic = Get-AzBastion -Name "<BastionName" -ResourceGroup "<ResourceGroupName>"
		#get id from public ip
		$id = $nic.IpConfigurations.publicipaddress.id
		#extract the name of the id 
		$name = $id.Split("/")[-1]
	} catch {
		#Do nothing
	}
	"Removing bastion..."
	Remove-AzBastion -ResourceGroupName "<ResourceGroupName>" -Name "<BastionName>" -Force
	"Bastion removed"
	if (-not ([string]::IsNullOrEmpty($name)))
	{
		"Removing public IP..."
		Remove-AzPublicIpAddress -ResourceGroupName "<ResourceGroupName>" -Name $name -Force
		"Public IP removed"
	}
}
catch {  
	Write-Error -Message $_.Exception
    throw $_.Exception
}
  1. After you have saved the runbook, we can test the code in the “Test pane”:
  2. This will start your runbook. It will be queued for execution. Once the runbook is executed, the result will be shown in the same page:
  3. Close the “Test pane” and Publish the Runbook.
  4. The last step is to add a schedule in the menu item “Schedules”
  5. When adding a new schedule there are two options (“Schedule” and “Parameters and run settings”). Our runbook script has no parameters, so the second option is not relevant. This is because in our showcase we only need to create a schedule and link it.
  6. Once the schedule is linked to the runbook we are done preparing. Now, it’s time to wait for the schedule to kick off.

Check jobs statuses

To check the statuses of the executed jobs open the “Automation account”. In the opening screen there is already the job statistics of the last 24 hours.

If you want more detailed information, open the runbook. In the overview page you can find the recent jobs and in the menu item “Jobs” the complete history.

When you click on a job you will get detailed information.

In the tabs “Input”, “Output”, “Errors”, “Warning”, “All Logs” and “Exception” you will find even more information.

Conclusion

In this showcase we added an Azure Automation Runbook to remove the Bastion Service every night. This will reduce the costs up to almost 140 euro per month. It is also possible to use the runbooks for other services.