Whether you are Cloud, DevOps or Software Engineer - you probably have already been in the situation where you need to access a Virtual Machine residing in a private VNet. While you can use a VPN connection to access the VM, this is not always an available option for you. In this project, I will show you how you can easily connect to your VMs in a secure way without exposing them to the public internet, thus saving you money and time.
Instead of exposing Port 22 or 3389 to the public internet, Azure provides a de-facto best practise approach to connect to your VMs in a secure way: Azure Bastion.
Azure Bastion is a fully managed PaaS service that provides secure and seamless SSH and RDP access to your virtual machines directly through the Azure Portal. This is especially useful when you are working with a team and want to provide access to your VMs without exposing them to the public internet.
Background
In my past projects as a Cloud Engineer at a consultancy, I have been tasked with connecting to a variety of VMs in different environments in non-peered VNets. While I would have been able to connect to the public VMs if they were exposed to the internet, I strictly followed leveraging Azure Bastion to connect to the VMs. This is especially important if you do not want to get your VMs passwords tested.
While Azure Bastion is a great service, it comes with a few drawbacks:
- It is charged by the hour, meaning that you have to pay for it even if you are not using it
- It takes some time to provision (up to 30 minutes, in my experience about 10 minutes)
and most importantly:
There is no way to deallocate or pause this service. You can only delete it and recreate it when you need it again.
The 3rd point is a bit annoying since you have to manually create and delete the service if you are not using the basion 24/7 (which is probably the case for most of us, especially for consultants).
The Case for Automation
A Bastion Host (Basic SKU, west europe) costs about 0.19 USD per hour. (as of 07/23) This means that you are paying about 139 USD per month if you leave the smallest configuration running 24/7. While this is not a huge amount of money, it can add up quickly if you are running multiple Bastion Hosts in different environments. With a simple automation script, you can save a some dollars and save some time by not having to wait for the Bastion Host to provision (since you automate it in the background). This is also one of my main motivations for creating this tool.
Following this deploy/destroy approach, you only pay for the time you are using the service and reduce your bill by about 50% (assuming you are using the Bastion Host for 8 hours per day). Or even more if you only need to verify something on the VMs occasionally. In my cases I brought down the costs by about 80% since I only needed to connect to the VMs for a few minutes per day. This means I saved about 110 USD per month per Bastion Host.
The Solution
In a first MVP, I have created a simple bash script that will create a Bastion Host in a given resource group and preconfigured VNet. The script takes advantage of a prebuilt and published module from the terraform registry, which I have just released.
It takes the assumption that you will deploy and delete your bastion in the same persistent network infrastructure. This is a common pattern in my experience.
The graphical overview above shows the basic scenario for which this module was created. In most of the cases, you will find a Hub-Spoke architecture with a central VNet (Hub) and multiple VNets (Spokes) that are peered to the Hub. The Bastion Host is deployed in the Hub VNet and can then be used to connect to the VMs in the Spoke VNets. ezBastion will create a Bastion Host in a predefined Subnet, which is called AzureBastionSubnet in accordance with the Microsoft naming convention. Since there are no charges for VNets and Subnets, you just need to care about the Bastion Host itself.
How to use it
You may have noticed, that there are at least 3 different ways to use this module. I will go through each of them and explain the differences.
1. Using the example script (out-of-the-box)
The published module comes with an example script that you can use to deploy a Bastion Host in your environment. The script is located in the examples
folder and is called automation.sh
. You can quickly replace the variables in the script and run it to deploy a Bastion Host in your environment.
Note that you need to chmod the script to make it executable:
chmod +x automation.sh
2. Using the module as part of CI/CD pipeline
In a later stage of this project, I will add detailed instructions on how to use the module as part of a ci/cd pipeline. This will allow you to deploy the Bastion Host as part of your pipeline.
For this scenario, you want the bastion host to spin up as part of the cicd process. Once the deployment is finished, you can use hooks and triggers to spin up a bastion host to connect to your VMs and run integration tests on the provisioned VMs.
3. Using the module as part of a CLI tool
In the future, I will also provide you with a CLI tool that you can utilize to quickly spin up a Bastion Host in your environment. This will be the easiest way to use the module, but it will also need some time to be developed properly.
Conclusion
In this article, I shared the idea behind my Terraform ezBastion module to quickly spin up a Bastion Host in your environment. This will allow you to save money and time by reducing the lifetime of your bastion hosts and only spin them up when you need them. I also shared the different ways to use the module and how you can get started.
I will continue to work on this module and add more features in the future. If you have any questions or feedback, feel free to reach out to me on LinkedIn.
You can find the module on the Terraform Registry: https://registry.terraform.io/modules/juweins/ezbastion/azurerm/latest
Found this article useful? Consider sharing it with your colleagues and friends & make sure to give me a like/repost on linkedIn! 🤓