Many customers wonder how to protect the workload in Azure and whether everything is publicly accessible. Of course there are all possibilities to separate and protect the workload like on-premises and a firewall is not necessary every time.

There are services that automatically have a public IP address and are publicly accessible, such as the Azure SQL PaaS. It therefore has a separate firewall for protection. Other workloads, such as VMs, usually get a public IP address during provisioning so that SSH and RDP can be used. But of course, the public IP can be removed or the VMs can be structured into subnets, like in the on-premises world.

In our example there are 2 VMs. A VM should be the frontend, i.e. it should be accessible from outside Azure and thus represent a kind of web server/application server. The second VM should simulate our backend, for example a database server. We create 2 subnets within a virtual network and place each VM in its own subnet. However, the frontend should not be accessible from everywhere, but only from a certain IP circle, for example a certain location or a certain customer. The backend should only be accessible from the frontend, i.e. if I need an SSH session in the backend, a bastion host has to be created in the public subet or the frontend server has to be used.

The following GitHub link https://github.com/tzuehlke/scripts/blob/master/PS_with_az/connect-two-subnets.ps1 contains a script that creates the network security groups for the frontend (public) subnet and for the backend (private) subnet, each filled with rules and assigned to the vNET. The VMs themselves have NSGs that configure their ports. However, the NSGs of the subnet are still outside the VMs and the VM NSGs, i.e. whatever is allowed in the VM configuration can be forbidden by the subnet NSGs. In this example, all VMs have public IPs, but these can no longer be used by the enveloping NSGs.

#Preparation if needed
#az login --use-device-code
#Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

$vnet="Test-VNet" #name of the existing vNET with the two subnets
$rg="SubnetTest-rg" 
$pub_sn_nsg="nsg4pub-subnet" #name of the NEW Network Security Group for the public subnet
$pub_sn="public-sn" #name of the existing public subnet
$pub_sn_allowed_ipaddr=@("11.22.33.44", "22.33.44.55")  #allowed ip adresses to access the public subnet
$prv_sn_nsg="nsg4prv-subnet" #name of the NEW Network Security Group for the private subnet
$prv_sn="private-sn" #name of the existing private subnet

# create NSG for the public subnet
az network nsg create -g $rg -n $pub_sn_nsg
# create a new rule for allowed IP adresses
az network nsg rule create -g $rg --nsg-name $pub_sn_nsg -n "AllowFromSpecialIPAddresses" --priority 100  --source-address-prefixes $pub_sn_allowed_ipaddr --source-port-ranges "*" --destination-address-prefixes "*" --destination-port-ranges "*" --access Allow --direction Inbound --protocol "*" --description "Allow public access only from the listed IP addresses"
# assign the NSG to the public subnet
az network vnet subnet update -g $rg -n $pub_sn --vnet-name $vnet --network-security-group $pub_sn_nsg

# get the address prefix of the public subnet, this will be the allowed address range for the private subnet 
$pub_sn_addrPrefix = az network vnet subnet show -g $rg -n $pub_sn --vnet-name $vnet | ConvertFrom-Json | Select addressPrefix
#$pub_sn_addrPrefix.addressPrefix

az network nsg create -g $rg -n $prv_sn_nsg
az network nsg rule create -g $rg --nsg-name $prv_sn_nsg -n "AllowFromPublicSubnet" --priority 100  --source-address-prefixes $pub_sn_addrPrefix.addressPrefix --source-port-ranges "*" --destination-address-prefixes "*" --destination-port-ranges "*" --access Allow --direction Inbound --protocol "*" --description "Allow access only from the public subnet of this vnet"
# rule that denies all other vnet inbound traffic
az network nsg rule create -g $rg --nsg-name $prv_sn_nsg -n "DenyOtherVNetInBound" --priority 200  --source-address-prefixes "VirtualNetwork" --source-port-ranges "*" --destination-address-prefixes "*" --destination-port-ranges "*" --access Deny --direction Inbound --protocol "*" --description "Allow access only from the public subnet of this vnet"
# assign the NSG to the public subnet
az network vnet subnet update -g $rg -n $prv_sn --vnet-name $vnet --network-security-group $prv_sn_nsg
  • Lines 5-11 set some variables and the names of the existing subnets etc.
  • Lines 13-18 create the NSG for the public subnet. The access to this subnet is only allowed by the IP whitelist defined in line 9, afterwards the NSG is assigned to the subnet.
  • Line 21 determines the subnet address range (10.3.0.0\24) from the public subnet, because all systems in the public subnet should be able to access the backend there.
  • Line 24-29 create the NSG for the private subnet and the rule for access from the public subnet. In addition, a deny-rule is created here that prohibits all other traffic from the vNet. By default, all vNet traffic is allowed and these rules cannot be deleted. The NSg is then assigned to the subnet.

Afterwards, the inbound rules for the NSG of the public subnet should look like this:

and the inbound rules of the NSG for the private subnet like this: