So, recently I was working on a project where I had to deploy a new environment in Azure. This included deploying a Virtual Network (VNet) that was not going to be connected to the rest of the network and must be isolated. Virtual Machines (VMs) deployed here should also be accessible via Remote Desktop Protocol and Secure Shell (SSH). Thankfully Microsoft has a solution for this with Azure Bastion. To integrate Azure Bastion with your VNet, you need to have a dedicated subnet called AzureBastionSubnet (no variations on the name are supported) with at least a /26 address space. Another of the requirements is to have a Network Security Group (NSG) with the correct rules which essentially acts as a layer 4 firewall as documented here – Working with VMs and NSGs in Azure Bastion | Microsoft Learn. Whilst this documentation is good, it doesn’t give you a code-based way to deploy the required rules.
I’ve created both Bicep and ARM templates that will create the NSG, deploy the required rules. My implementation of the rules go even further than the documented rules where anything not specified in rules further up in the priority list is blocked by default with explicit deny rules.
Feel free to use and share. I’ve also put the code in my GitHub here
Bicep Template
param networkSecurityGroupName string param resourceLocation string resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2022-11-01' = { name: networkSecurityGroupName location: resourceLocation properties: { flushConnection: false securityRules: [ { name: 'AllowHttpsInbound' properties: { access: 'Allow' destinationAddressPrefix: '*' destinationPortRange: '443' direction: 'Inbound' priority: 100 protocol: 'TCP' sourceAddressPrefix: 'Internet' sourcePortRange: '*' } } { name: 'AllowGatewayManagerInbound' properties: { access: 'Allow' destinationAddressPrefix: '*' destinationPortRange: '443' direction: 'Inbound' priority: 110 protocol: 'TCP' sourceAddressPrefix: 'GatewayManager' sourcePortRange: '*' } } { name: 'AllowBastionHostCommunication' properties: { access: 'Allow' destinationAddressPrefix: 'VirtualNetwork' destinationPortRanges: [ '5701' '8080' ] direction: 'Inbound' priority: 120 protocol: '*' sourceAddressPrefix: 'VirtualNetwork' sourcePortRange: '*' } } { name: 'AllowAzureLoadBalancerInbound' properties: { access: 'Allow' destinationAddressPrefix: '*' destinationPortRange: '443' direction: 'Inbound' priority: 4095 protocol: 'TCP' sourceAddressPrefix: 'AzureLoadBalancer' sourcePortRange: '*' } } { name: 'DenyAllInbound' properties: { access: 'Deny' destinationAddressPrefix: '*' destinationPortRange: '*' direction: 'Inbound' priority: 4096 protocol: '*' sourceAddressPrefix: '*' sourcePortRange: '*' } } { name: 'AllowSshRDPOutbound' properties: { access: 'Allow' destinationAddressPrefix: 'VirtualNetwork' destinationPortRanges: [ '22' '3389' ] direction: 'Outbound' priority: 100 protocol: '*' sourceAddressPrefix: '*' sourcePortRange: '*' } } { name: 'AllowAzureCloudOutbound' properties: { access: 'Allow' destinationAddressPrefix: 'AzureCloud' destinationPortRange: '443' direction: 'Outbound' priority: 110 protocol: 'TCP' sourceAddressPrefix: '*' sourcePortRange: '*' } } { name: 'AllowBastionCommunication' properties: { access: 'Allow' destinationAddressPrefix: 'VirtualNetwork' destinationPortRanges: [ '5701' '8080' ] direction: 'Outbound' priority: 120 protocol: '*' sourceAddressPrefix: 'VirtualNetwork' sourcePortRange: '*' } } { name: 'AllowHttpOutbound' properties: { access: 'Allow' destinationAddressPrefix: 'Internet' destinationPortRange: '80' direction: 'Outbound' priority: 130 protocol: 'TCP' sourceAddressPrefix: '*' sourcePortRange: '*' } } { name: 'DenyAllOutbound' properties: { access: 'Deny' destinationAddressPrefix: '*' destinationPortRange: '*' direction: 'Outbound' priority: 140 protocol: '*' sourceAddressPrefix: '*' sourcePortRange: '*' } } ] } }
ARM Template
{ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "metadata": { "_generator": { "name": "bicep", "version": "0.17.1.54307", "templateHash": "4751223750055747166" } }, "parameters": { "networkSecurityGroupName": { "type": "string" }, "resourceLocation": { "type": "string" } }, "resources": [ { "type": "Microsoft.Network/networkSecurityGroups", "apiVersion": "2022-11-01", "name": "[parameters('networkSecurityGroupName')]", "location": "[parameters('resourceLocation')]", "properties": { "flushConnection": false, "securityRules": [ { "name": "AllowHttpsInbound", "properties": { "access": "Allow", "destinationAddressPrefix": "*", "destinationPortRange": "443", "direction": "Inbound", "priority": 100, "protocol": "TCP", "sourceAddressPrefix": "Internet", "sourcePortRange": "*" } }, { "name": "AllowGatewayManagerInbound", "properties": { "access": "Allow", "destinationAddressPrefix": "*", "destinationPortRange": "443", "direction": "Inbound", "priority": 110, "protocol": "TCP", "sourceAddressPrefix": "GatewayManager", "sourcePortRange": "*" } }, { "name": "AllowBastionHostCommunication", "properties": { "access": "Allow", "destinationAddressPrefix": "VirtualNetwork", "destinationPortRanges": [ "5701", "8080" ], "direction": "Inbound", "priority": 120, "protocol": "*", "sourceAddressPrefix": "VirtualNetwork", "sourcePortRange": "*" } }, { "name": "AllowAzureLoadBalancerInbound", "properties": { "access": "Allow", "destinationAddressPrefix": "*", "destinationPortRange": "443", "direction": "Inbound", "priority": 4095, "protocol": "TCP", "sourceAddressPrefix": "AzureLoadBalancer", "sourcePortRange": "*" } }, { "name": "DenyAllInbound", "properties": { "access": "Deny", "destinationAddressPrefix": "*", "destinationPortRange": "*", "direction": "Inbound", "priority": 4096, "protocol": "*", "sourceAddressPrefix": "*", "sourcePortRange": "*" } }, { "name": "AllowSshRDPOutbound", "properties": { "access": "Allow", "destinationAddressPrefix": "VirtualNetwork", "destinationPortRanges": [ "22", "3389" ], "direction": "Outbound", "priority": 100, "protocol": "*", "sourceAddressPrefix": "*", "sourcePortRange": "*" } }, { "name": "AllowAzureCloudOutbound", "properties": { "access": "Allow", "destinationAddressPrefix": "AzureCloud", "destinationPortRange": "443", "direction": "Outbound", "priority": 110, "protocol": "TCP", "sourceAddressPrefix": "*", "sourcePortRange": "*" } }, { "name": "AllowBastionCommunication", "properties": { "access": "Allow", "destinationAddressPrefix": "VirtualNetwork", "destinationPortRanges": [ "5701", "8080" ], "direction": "Outbound", "priority": 120, "protocol": "*", "sourceAddressPrefix": "VirtualNetwork", "sourcePortRange": "*" } }, { "name": "AllowHttpOutbound", "properties": { "access": "Allow", "destinationAddressPrefix": "Internet", "destinationPortRange": "80", "direction": "Outbound", "priority": 130, "protocol": "TCP", "sourceAddressPrefix": "*", "sourcePortRange": "*" } }, { "name": "DenyAllOutbound", "properties": { "access": "Deny", "destinationAddressPrefix": "*", "destinationPortRange": "*", "direction": "Outbound", "priority": 140, "protocol": "*", "sourceAddressPrefix": "*", "sourcePortRange": "*" } } ] } } ] }