Monday, July 11, 2022

Azure Firewall with Azure policy and IP groups using Bicep

 In the previous post I deployed a firewall with some additional components such as VMs, NSGs, and route tables to have a working test setup. In this post it will mainly be the FW, the FW policies and the IP groups that will be deployed.

This post is based on the this Quickstart template with only a few modifications. The main reason I do this is to verify that the files work in my environment to inspect deployed resources.

I have uploaded the modified files to Github (fw-with-policy-001.bicep and fw-with-policy-001.parameters.json). 

The resources deployed can be seen below:


The two IP groups are used in combination with the policy. The policy is associated with the firewall. The are two rule collection groups defined with a total of three rules, see below two screenshots:






Sunday, July 10, 2022

Azure: Deploy Azure Firewall in availability zones using Bicep

 Microsoft has some great Quickstart templates for deploying resources as codes. For this post I've looked at deploying an Azure Firewall into multiple availability zones using Bicep.

The Quickstart guide used can be found here.

The template needs only a few changes. I updated the VNet name and IP address info and inserted a reference to a key vault for the VM admin password.

My updated files can be found on Github (fw-conn-weu-001.bicep and fw-conn-weu-001.parameters.json).

The firewall is deployed into three availability zones in West Europe. This increases the availability to 99.99%. There is still only one logical firewall deployed, so the increased availability is managed in the background by Microsoft. And from a Azure Portal point of view it looks the same as if only deploying to one availability zone.

The availability zones can be verified under Azure Firewall -> Properties, see below:


There is no immediate additional cost to deploying to multiple availability zones, however, there is an increased bandwidth cost, see below (or link):




The bicep file deploys the following resources:


If you are testing in a Visual Studio subscription make sure to delete resources again when done as the cost of especially the FW can run up quickly.

One network rule and one application rule are added under Firewall -> Rules (classic) -> Network/Application rule collection tabs, see below. Usually a Firewall Policy will be created and associated with the FW but for testing purposes this can be used.


Using the Bicep visualizer from VS Code you can get a nice graphical overview of the resources to be deployed. To do this, right click the bicep file in VS code and choose "Open Bicep Visualizer", see below: 







Friday, July 8, 2022

ARM template to create VNet with multiple subnets, NSGs, and security rules using copy loop

 At current client we have a scenario where a number of spoke landing zones (LZs) have been created which contain, typically, a VNet and multiple subnets. The landing zones are filtered via an Azure Firewall in the Hub, however, there is an additional requirement to deny inter-VNet traffic between subnets using NSG rules (traffic not leaving the VNet and therefore won't reach the firewall).

The ARM templates for the LZs use copy loops to create the subnets and NSGs. What I wanted to add was a specific security rule (deny) to each of the NSGs that only contained the other subnets (so multiple IP address ranges) in the VNet but not the subnet that is associated with that subnet. I was struggling a bit with the syntax (around adding multiple ranges) and got some assistance in the end from Microsoft to get it to work.

I've collected the whole thing in a template and uploaded to Github (files: VNet-with-3xSubnet-3xNsgs-3xRules-copyloop.json and VNet-with-3xSubnet-3xNsgs-3xRules-copyloop.parameters.json). See this MS article for more info on copy loops.

It creates a VNet with three subnets using a copy loop. It then creates three NSGs, one for each subnet, and two security rules for each NSG. The first rule is static/identical to all NSGs and the second rule varies according to content of the nsgPrefixes array. The second rule relies on an array within an array that adds the IP ranges of the two other subnets to a deny rule. 

I'll paste the content of the ARM template below, but formatting is not great, so suggest getting the files from Github linked above:

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "vnetName": {
            "type": "String"
        },
        "location": {
            "type": "string"
        },
        "addressPrefixes": {
            "type": "string"
        },
        "snetAddressPrefixes": {
            "type": "array"
        },
        "snetNames": {
            "type": "array"
        },
        "nsgNames": {
            "type": "array"
        },
        "nsgPrefixes": {
            "type": "array",
            "metadata": {
                "description": "description"
            }
        }
    },    
    "variables": {},
    "resources": [
        {
            "type": "Microsoft.Network/virtualNetworks",
            "apiVersion": "2020-11-01",
            "name": "[parameters('vnetName')]",
            "location": "[parameters('location')]",
            "properties": {
                "addressSpace": {
                    "addressPrefixes": [
                        "[parameters('addressPrefixes')]"
                    ]
                },
                "copy": [
                    {
                        "name": "subnets",
                        "dependsOn": [
                            "[resourceId('Microsoft.Network/networkSecurityGroups/', parameters('nsgNames')[copyIndex('subnets')])]"
                        ],
                        "count": "[length(parameters('snetNames'))]",
                        "input": {
                        "name": "[concat(parameters('snetNames')[copyIndex('subnets')])]",
                        "properties": {
                            "addressPrefix": "[parameters('snetAddressPrefixes')[copyIndex('subnets')]]",
                            "networkSecurityGroup": {
                                "id": "[resourceId('Microsoft.Network/networkSecurityGroups/', parameters('nsgNames')[copyIndex('subnets')])]"
                                }
                            }
                        }
                    }
                   
                ]
           
            }        
               
        },
        {
            "type": "Microsoft.Network/networkSecurityGroups",
            "apiVersion": "2021-08-01",
            "name": "[concat(parameters('nsgNames')[copyIndex()])]",
            "location": "[parameters('location')]",
            "properties": {
                "securityRules": [
                    {
                        "name": "Port_3389",
                        "properties": {
                            "protocol": "*",
                            "sourcePortRange": "*",
                            "destinationPortRange": "3389",
                            "sourceAddressPrefix": "10.XXX.XXX.XXX",
                            "destinationAddressPrefix": "*",
                            "access": "Allow",
                            "priority": 100,
                            "direction": "Inbound",
                            "sourcePortRanges": [],
                            "destinationPortRanges": [],
                            "sourceAddressPrefixes": [],
                            "destinationAddressPrefixes": []
                        }
                    },
                    {
                        "name": "DenyInternalSubnetInbound",
                        "properties": {
                            "protocol": "*",
                            "sourcePortRange": "*",
                            "destinationPortRange": "*",
                            "destinationAddressPrefix": "*",
                            "access": "Deny",
                            "priority": 4096,
                            "direction": "Inbound",
                            "sourcePortRanges": [],
                            "destinationPortRanges": [],
                            "sourceAddressPrefixes":
                                "[concat(parameters('nsgPrefixes')[copyIndex()])]",
                            "destinationAddressPrefixes": []
                        }
                    }

                ]
            },
            "copy": {
            "name": "NSGcopy",
            "count": "[length(parameters('nsgNames'))]"
            }
        }    
    ]
       
}

-------

Parameters file:

-------

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "vnetName": {
            "value": "vnet-conn-weu-001"
        },
        "location": {
            "value": "westeurope"
        },
        "addressPrefixes": {
            "value": "10.100.0.0/18"
        },
        "snetAddressPrefixes": {
            "value": [
                "10.100.0.0/24",
                "10.100.1.0/24",
                "10.100.2.0/24"
            ]
        },
        "snetNames": {
            "value": [
                "snet-conn-weu-001",
                "snet-conn-weu-002",
                "snet-conn-weu-003"
            ]
        },
        "nsgNames": {
            "value": [
                "nsg-snet-conn-weu-001",
                "nsg-snet-conn-weu-002",
                "nsg-snet-conn-weu-003"
            ]
        },
        "nsgPrefixes": {
            "value": [
                ["10.100.1.0/24", "10.100.2.0/24"],              
                ["10.100.0.0/24", "10.100.2.0/24"],
                ["10.100.0.0/24", "10.100.1.0/24"]
            ]
        }
    }    
}

Wednesday, July 6, 2022

Azure: Load balancer with pre-allocated IPs in backend pool

 When deploying a load balancer there are two different types of backend pool configurations: NIC based and IP based, see second screenshot below. When using IP based pools, you can pre-allocate IP addresses and then when VM's are added at a later stage they can be assigned the allocated IP addresses.

If you add VMs to the backend pool manually via the portal post deployment, the default choice is NIC based. If you want to deploy the NIC based setup with the ARM templates it's a bit more tricky. Then you have to deploy a couple of vNICs and then associate them with the pool. And then when the backend VMs are deployed, you can associate them with the already deployed NICs.

Note that there are some limitations around using IP based pools and private link services. According to this article "A load balancer with IP based Backend Pool can’t function as a Private Link service".

The content in this post uses this Microsoft article as a starting point.

The ARM template can be found on Github (lb-netw-dv-weu-001.json and lb-netw-dv-weu-001.parameters.json).

The LB is internal, has a frontend IP, one backend pool with two local IPs pre-allocated, one http probe and a load balancing rule that forwards requests on port 80, see more details in screenshots below.