Manage Existing Azure Firewall With Firewall Policy Using Bicep

In this post, I want to discuss how I recently took over the management of an existing Azure Firewall using Firewall Policy/Azure Firewall Manager and Bicep.

Background

We had a customer set up many years ago using our old templated Azure deployment based on ARM. At the centre of their network is Azure Firewall. That firewall plays a big role in the customer’s micro-segmented network, with over 40,000 lines of ARM code defining the many firewall rules.

The firewall was deployed before Azure Firewall Manager (AFM) was released. AFM is a pretty GUI that enables the management of several Azure networking resource types, including Azure Firewall. But when it comes to managing the firewall, AFM uses a resource called Firewall Policy; you don’t have to touch AFM at all – you can deploy a Firewall Policy, link the firewall to it (via Resource ID), and edit the Firewall Policy directly (Azure Portal or code) to manage the firewall settings or code.


One of the nicest features of Azure Firewall is a result of it being an Azure PaaS resource. Like every other resource type (there are exceptions sometimes) Azure Firewall is completely manageable via code. Not only can you deploy the firewall. You can operate it on a day-to-day basis using ARM/Bicep/Terraform/Pulumi if you want: the settings and the firewall rules. That means you can have complete change control and rollback using the features of Git in DevOps, GitHub, etc.


All new features in Azure Firewall have surfaced only via Firewall Policy since the general availability release of AFM. A legacy Azure Firewall that doesn’t have a Firewall Policy is missing many security and management features. The team that works regularly with this customer approached me about adding Firewall Policy to the customer’s deployment and including that in the code.

The Old Code

As I said before, the old code was written in ARM. I won’t get into it here, but we couldn’t add the required code to do the following without significant risk:

  • A module for Firewall Policy
  • Updating the module for Azure Firewall to include the link to the FIrewall Policy.

I got a peer to give me a second opinion and he agreed with my original assessment. We should:

  1. Create a new set of code to manage the Azure Firewall using Bicep.
  2. Introduce Firewall Policy via Bicep.
  3. Remove the ARM module for Azure Firewall from the ARM code.
  4. Leave the rest of the hub as is (ARM) because this is a mission-critical environment.

The High-Level Plan

I decided to do the following:

  1. Set up a new repo just for the Azure Firewall and Firewall Policy.
  2. Deploy the new code in there.
  3. Create a test environment and test like crazy there.
  4. The existing Azure Firewall public IP could not change because it was used in DNAT rules and by remote parties in their firewall rules.
  5. We agreed that there should be “no” downtime in the process but I wanted time for a rollback just in case. I would create non-parameterised ARM exports of the entire hub, the GatewaySubnet route table (critical to routing intent and a risk point in this kind of work), and the Azure Firewall. Our primary rollback plan would be to run the un-modified ARM code to restore everything as it was.

The Build

I needed an environment to work in. I did a non-parameterised export of the hub, including the Azure Firewall. I decompiled that to Bicep and deployed it to a dedicated test subscription. This did require some clean-up:

  • The public IP of the firewall would be different so DNAT rules would need a new destination IP.
  • Every rules collection group (many hundreds of them) had a resource ID that needed to be removed – see regex searches in Visual Studio Code.

The deployment into the test environment was a two-stage job – I needed the public IP address to obtain the destination address value for the DNAT rules.

Now I had a clone of the production environment, including all the settings and firewall rules.

The Bicep Code

I’ve been doing a lot of Bicep since the Spring of this year (2024). I’ve been using Azure Verified Modules (AVM) since early Summer – it’s what we’ve decided should be our standard approach, emulating the styling of Azure Verified Solutions.


We don’t use Microsoft’s landing zones. I have dug into them and found a commonality. The code is too impressive. The developer has been too clever. Very often, “customer configuration” is hard-coded into the Bicep. For example, the image template for Azure Image Builder (in the AVD landing zone) is broken up across many variables which are unioned until a single variable is produced. The image template is file that should be easy to get at and commonly updated.

A managed service provider knows that architecture (the code) should be separated from customer configuration. This allows the customer configuration to be frequently updated separately from the architecture. And, in turn, it should be possible to update the architecture without having to re-import the customer configuration.


My code design is simple:

  • Main.bicep which deploys the Azure Firewall (AVM) and the Firewal Policy (AVM).
  • A two-property paramater controls the true/false (bool) condition of whether or not the two resources are deployed.
  • A main.bicepparam supplies parameters to configure the SKUs/features/settings of the Azure Firewall and Firewall Policy using custom types (enabling complete Intellisense in VS Code).
  • A simple module documents the Rules Collections in single array. This array is returned as an output to main.bicep and fed as a single value to the Firewall Policy module.

I did attempt to document the Rules Collections as ARM and use the Bicep function to load an ARM file. This was my preference because it would simplify producing the firewall rules from the Azure Portal and inputting them into the file, both for the migration and for future operations. However, the Bicep function to load a file is limited to too few characters. The eventual Rules Colleciton Group module had over 40,000 lines!

My test process eventually gave me a clean result from start to finish.

The Migration

The migration was scheduled for late at night. Earlier in the afternoon, a freeze was put in place on the firewall rules. That enabled me to:

  1. Use Azure Firewall Manager to start the process of producing a Firewall Policy. I chose the option to import the rules from the existing production firewall. I then clicked the link to export the rules to ARM and saved the file locally.
  2. I decompiled the ARM code to Bicep. I copied and pasted the 3 Rules Collection Groups into my Rules Collection Group module.
  3. I then ran the deployment with no resources enabled. This told me that the pipeline was function correctly against the production environment.
  4. When the time came, I made my “backups” of the production hub and firewall.
  5. I updated the parameters to enable the deployment of the Firewall Policy. That was a quick run – the Azure Firewall was not touched so there was no udpate to the Firewall. This gave me one last chance to compare the firewall settings and rules before the final steps began.
  6. I removed the DNS settings from the Azure Firewall. I found in testing that I could not attach a Firewall Policy to an Azure Firewall if both contained DNS settings. I had to remove those settings from the production firewall. This could have caused some downtime to any clients using the firewall as their DNS server but the feature is not rolled out yet.
  7. I updated the parameters to enable management of the Azure Firewall. The code here included the name of the in-place Public IP Address. The parameters also included the resource IDs of the hub Virtual Network and the Log Analytics Workspace (Resource Specfic tables in the code). The pipeline ran … this was the key part because the Bicep code was updating the firewall with the resource ID of the Firewall Policy. Everything worked perfectly … almost … the old diagnostics settings were still there and had to be removed because the new code used a new naming standard. One quick deletion and a re-run and all was good.
  8. One of my colleagues ran a bunch of pre-documented and pre-verified tests to confirm that all was was.
  9. I then commented out the code for the Azure Firewall from the old ARM code for the hub. I re-ran the pipeline and cleaned up some errors until we had a repeated clean run.

The technical job was done:

  • Azure Firewall was managed using a Firewall Policy.
  • Azure Firewall had modern diagnostics settings.
  • The configuration is being done using code (Bicep).

You might say “Aidan, there’s a PowerShell script to do that job”. Yes there is, but it wasn’t going to produce the code that we needed to leave in place. This task did the work and has left the customer with code that is extremely flexible with every resource property available as a mandatory/optional property through a documented type specific to the resource type. As long as no bugs are found, the code can be used as is to configure any settings/features/rules in Azure Firewall or Azure Firewall manager either through the parameters files (SKUs and settings) or the Rules Collection Groups module (firewall rules).

Azure Firewall Deep Dive Training

If you thought that this post was interesting then please do check out my Azure Firewall Deep Dive course that is running on February 12th – February 13th, 2025 from 09:30-16:00 UK/Irish time/10:30-17:00 Amsterdam/Berlin time. I’ve run this course twice in the last two weeks and the feedback has been super.

Rethinking Firewall Management With Azure Firewall Manager

Microsoft has just announced the general availability a feature that I’ve been waiting for since I first learned about it last Autumn, called Azure Firewall Manager. Azure Firewall Manager allows you to centrally manage one or more Azure Firewall instances through a central, policy-driven, user interface. And it’s those policies, Azure Firewall Policies, that made me re-think Azure Firewall management a few months ago when I was writing my Cloud Mechanix course (running next ONLINE on July 30th) “Securing Azure Services & Data Through Azure Networking”.

Azure Firewall Policy

This is a new resource type that is generally available today. Azure Firewall Policy outsources the configuration and management of the firewall to a policy resource; that means that the usual settings in the Azure Firewall for things like rules and Threat Intelligence move from the firewall resource to a policy when a policy is associated with the firewall.

Policies can be created in a hierarchy. You can create a parent/global policy that will contain configurations and rules that will apply to all/a number of firewall instances. Then you create a child policy that inherits from the parent; note that rules changes in the parent instantly appear in the child. The child is associated with a firewall and applies configurations/rules from the parent policy and the child policy instantly to the firewall.

Problem

I’ve deployed and configured multiple customers where we have virtual data centers (VDCs, which are governed & secured hub and spoke architectures) across multiple regions. Creating rules configurations to allow flows from a spoke/service in one region to another spoke/service in another region is a royal pain in the tushie. Here’s the network flow (as I documented with routing here):

  1. Source device
  2. Outbound NSG rules in source spoke
  3. Firewall in source hub
  4. Firewall in destination hub
  5. Inbound NSG rules at destination spoke
  6. Destination device

There are potentially 4 sets of rules to configure for a simple service running on a single protocol/port. Today I configured Microsoft Identity Management for this scenario and there were dozens of protocol/port combinations across three spokes. The work took hours to complete – which I did in code and it provided a working result for the identity consulting team.

I minimise the work by controlling outbound flows in the local hub firewall, not in the NSG. So the NSGs do not control outbound flows at all. I could allow all via the firewall, even to other private networks, but that goes against the idea of compartmentalisation or micro-segmentation to combat modern network threats – so I need to configure both firewalls for a flow.

Solution

Re-think the firewall for a moment. Imagine you had one virtual firewall that spanned all of your Azure regional deployments. You can control all global flows with one configuration in that global virtual firewall. The global virtual firewall has instances in each Azure region. Any local flows can be configured just in that instance. That’s what Firewall Policy allows.

  • Parent Policy: Place all your global configurations in here. Some configurations will be company-wide, such as Threat Intelligence. Some rules, like allowing access to Microsoft URIs or Azure services (service tags) will be global too. And this is where you put the rules to allow flows between one regional deployment and another. This global management takes all your local Azure Firewall resources and treats them as a single security service.
  • Child Policies: A child policy will be created for each Azure Firewall instance. This policy will inherit the above from the parent applying the global configuration. Local rules, to allow north-south access to/from local services (Internet or on-prem) or east-west (spoke-to-spoke in the same regional deployment) will be configured here. RBAC can be enabled to allow local network admins to do their own thing, but unable to undo what the parent has done.

I haven’t had a chance to test Azure Firewall Policy out yet since the GA announcement, but I’m hoping that the third tier in rules (Rules Groups) made it from preview to GA. I do have groupings of rules collections based on buckets of priorities. This organisation would be awesome in my vision of Azure Firewall management.

Microsoft Ignite 2019 – Securing Your Cloud Perimeter With Azure Network Security

  • Speaker: Sinead O’Donvan (Irish, by the accent)

Zero Trust Architecture document

7 pillars:

  • Identity
  • Devices
  • Data
  • Apps
  • Infrastructure
  • Networking – the focus here

Verify explicitly every access control

  • Being on the network is not enough

Use least privilege access

  • IP address is not enough

Assume breach

  • No one is perfectly secure. Identify the breach. Contain the breach. Do your best to stop breaches in the first place.

You cannot claim success:

  • It requires constant improvement.

Network Maturity Model

  • Traditional (most customers)
    • Few network security perimeters and flat open network
    • Minimal threat protection and static filtering
    • Internal traffic is not encrypted
  • Advanced
    • Many ingress/egress cloud micro-perimeters with some micro-segmentation
    • Cloud native filtering and protection for known threats
    • User to app internal traffic is encrypted
  • Optimal
    • Fully distributed ingress/egress cloud micro-perimeters and deeper micro-segmentation
    • ML-based threat protection and filtering with context-based signals
    • All traffic is encrypted

Three Cores of Azure Network Security

  • Segment – prevent lateral movement and data exfiltration
  • Protect – secure network with threat intelligence
  • Connect – embrace distributed connectivity … or face revolt from the users/devs

Deploy securely across DevOps process

Azure Features

  • Azure Firewall
  • Azure WAF
  • Azure Private Link
  • Azure DD0S Protection

Plus:

  • VNets
  • NSGs
  • UDRs
  • Load Balancer

Network Segmentation

3 approaches:

  • Host-based: an agent on the VM implements it
  • Hypervisor: Example, VMware SNX
  • Network controls

Azure Network Segmentation Controls

  • Subscription: RABC, logic isolation for all resources
  • Virtual network: An isolated and highly secure environment to run your VMs and apps. “This is the hero of segmentation”
  • NSG: Enforce and control network traffic security rules that allow or deny network traffic for a VNet or a VM.
  • WAF: Define application specific policies to protect web workloads.
  • Azure Firewall: Create and enforce connectivity policies using application, network and threat intelligence filtering across subscription(s) and VNet(s).

Multi-Level Segmentation

  • Connectivity:
    • Use both public or private IP. Public app interface is public, backend is private.
    • Choose cloud transit approach VNet peering or Virtual WAN.
    • Carefully control routing
  • Infrastructure
    • Segment across subscription, vnet, and subnet boundaries
    • Managed at an org level
  • Application
    • Enable application aware segmentation
    • Easily create micro perimeters
    • Managed at an application level

Azure Firewall Manager (Preview)

  • Central deployment and configuration
    • Deploy and configure multiple Azure Firewall instances
    • Optimized for DevOps with hierarchical policies
  • Automated Routing
    • Easily direct traffic to your secured hub for filtering and logging without UDRs
  • And more

Azure Web Application Firewall

Preview:

  • Microsoft threat intelligence
    • Protect apps against automated attacks
    • Manage good/bad bots with Azure BotManager RuleSet
  • Site and URI patch specific WAF policies
    • Customise WAF policies at regional WAF for finer grained protection at each host/listener or URI path level
  • Geo-filtering on regional WAF
    • Enhanced custom rule matching criterion

MS sees 20/30 DDoS attacks per day.

WAF as a Service

  • Barracuda
  • Radware

Both run in Azure.

Connectivity

It’s time to transform your network.

  • User to app moves to Internet centric connectivity
  • Application to backend resources use private connectivity
  • Redesign your network and network security models to optimize user experience for cloud
  • Continue to extend app delivery models and network security to the edge

Azure Firewall Manager

  • Easily create multiple secured virtual hubs (DMZ Hubs) in Azure
  • Use Azure Firewall or 3rd party security
  • Create global and local policies
  • Easy to set up connectivity
  • Roadmap:
    • Split routing – optimized O365 and Azure public PaaS

CheckPoint CloudGuard Connect will debut soon as a partner extension.

Azure Private Link

Highly secure and private connectivity solution for Azure Platform.

  • Private access from VNet resources, peered networks and on-premises networks
  • In-built data exfiltration protection
  • Predictable private IP addresses for PaaS resources
  • Unified experience across PaaS customer owned and marketplace services

Microsoft taking this very seriously. All new PaaS services “from Spring onwards” must support Private Link.

Azure Bastion

See previous posts on this – it requires more work IMO because it lacks VNet peering support and requires login via the Azure Portal – doesn’t support MSTSC or SSH clients.

Key Takeaways

  • Embrace zero trust network model
  • Segment your network and create micro-perimters with Azure Firewall, NSG, etc
  • Use a defense in depth security strategy with cloud native services
  • Enable WAF and DDoS
  • Explore Azure as your secure Internet edge with Azure Firewall Manager