Azure Bastion is the more secure way to enable Remote Desktop (RDP) support for Azure Virtual Machines (VMs). Basically, an Azure Bastion host gets deployed to an Azure Virtual Network (VNet). Once deployed, the Azure Bastion host enables the ability to securely use Remote Desktop to connect to Virtual Machines within that Virtual Network directly within the Azure Portal. This enables the Azure Portal to be used to connect to VMs within that VNet without any need to create a Public IP for those VMs. This means Azure Bastion enables you to use RDP to connect to VMs in Azure while keeping them completely secure by only allowing private network connections.

There are a couple requirements that need to be met in order to deploy an Azure Bastion host into an existing Virtual Network:

  1. The Virtual Network must to have a Subnet created that will be used solely by Azure Bastion. A couple appropriate name ideas for this Subnet are AzureBastionSubnet or AzureBastion as to keep it obvious what that Subnet is for.
  2. There must be a Public IP Address (PIP) for Azure Bastion to use. This enables an IP Address that the Azure Portal can use to connect to the Azure Bastion host.

Once those requirements are met, the Azure bastion host is deployed with configuration to use the Public IP Address and connect to the Virtual Network via the Subnet dedicated for the Azure Bastion host.

The following is a full Azure Bicep code example template that can be used to deploy Azure Bastion. It also includes the necessary Bicep code to create the required Subnet and Public IP Address resources. Also, notice there are a couple parameters on this Bicep to help you use it with minimal code changes, outside of ensuring the resource names are defined to match your organizations Azure Resource naming convention.

@description('The VNet to deploy Azure Bastion to.')
param vnetName string = 'b59-vnet'

@description('The name of the Subnet to create for Azure Bastion')
param azureBastionSubnetName string = 'AzureBastionSubnet'

@description('The Address Prefix to use for the Azure Bastion Subnet')
param azureBastionSubnetAddressPrefix string = '10.1.0.0/24'

// Create Subnet for Azure Bastion to use within the VNet
resource bastion_subnet 'Microsoft.Network/virtualNetworks/subnets@2020-11-01' = {
    name: '${vnetName}/${azureBastionSubnetName}'
    properties: {
        addressPrefix: azureBastionSubnetAddressPrefix
    }
}

// Create the Public IP Address (PIP) for Azure Bastion to use
resource bastion_public_ip 'Microsoft.Network/publicIPAddresses@2020-11-01' = {
    name: 'b59-bastion-pip'
    location: resourceGroup().location
    sku: {
        name: 'Standard'
    }
    properties: {
        publicIPAddressVersion: 'IPv4'
        publicIPAllocationMethod: 'Static'
    }
}

// Create the Azure Bastion resource
resource bastion 'Microsoft.Network/bastionHosts@2020-11-01' = {
    name: 'b59-bastion'
    location: resourceGroup().location
    sku: {
        name: 'Basic'
    }
    properties: {
        ipConfigurations: [
            {
                name: 'IpConf'
                properties: {
                    privateIPAllocationMethod: 'Dynamic'
                    publicIPAddress: {
                        id: bastion_public_ip.id
                    }
                    subnet: {
                        id: bastion_subnet.id
                    }
                }
            }
        ]
    }
}

If you want to convert this to an ARM Template JSON for deployment instead, then you can use the bicep build {filename} Azure Bicep CLI command to compile this code into an ARM Template. Then you could use this compiled ARM Template directly in the Azure Portal to run this as a Custom Deployment, as the Azure Portal currently only supports ARM Templates. Otherwise, you can use the Azure CLI to perform Azure Bicep deployments, which is probably what you’ll be doing as part of your DevOps process anyway.

Happy automating Azure Resource deployment using Azure Bicep!

Microsoft MVP

Chris Pietschmann is a Microsoft MVP (Azure & IoT) and HashiCorp Ambassador (2021) with 20+ years of experience designing and building Cloud & Enterprise systems. He has worked with companies of all sizes from startups to Fortune 100. He is also a Microsoft Certified Azure Solutions Architect and developer, a Microsoft Certified Trainer (MCT), and Cloud Advocate. He has a passion for technology and sharing what he learns with others to help enable them to learn faster and be more productive.