Image: Nahel Abdul Hadi

Blocking hackers with Azure Firewall efficiently

Or just 'Using Terraform and CSV files with Azure Firewall'

Caption: 1337_h4x0r

Picking up the same theme as my last blog, and enticing you dear reader with a another provocative title, in this blog post I'll further expand on using CSV files, but target my displeasure of writing the equivalent of War in Peace but in Terraform, at another great Azure service- Azure Firewall.

Specifically with this example, Azure Firewall Network Rule Collections, there's another opportunity to reduce the amount of writing of Terraform code required to achieve the target state.

How this came about was because of the outcomes of my previous blogs in needing to route lots of CIDR ranges though Azure Firewall - via Route Tables / UDRs. To allow that traffic through Azure Firewall now requires the relevant allow rules to complete the puzzle.

First some caveats

  • CSV files are not very smart, but smart at the same time. Is that an oxymoron? I feel like one for writing that.
    • It's easy to get all happy from the prospect of saving time. With great power comes great responsibility.
    • Dependencies are very helpful in Terraform. Using CSV files, those go out the window. You cant reference ‘azurerm_resource_group.resource_group.name’ for example. Just hard coding values…
  • Likely my lack of knowledge, but I couldn’t reference any material that outlined the use of lists in CSV files.
    • By that I mean multiple values in a row/line within a cell.
      • Something like ["Example1", "Example2", "Example3"]
    • That's kind of important since AzFW rules can have multiple values. Some inputs need to be in a list format. More on this later.
    • Therefore like me you may end up just creating more rules to accommodate this limitation?.
    • Important to remember that there is a 10,000-rule limit on Azure Firewall. That's the default.

The solution

I took my learnings from using CSVDECODE outlined in a previous blog I did (Terraform + CSVs = Netflix) and tweaked the recipe slightly so that the same functionality could be leveraged. Network Rules are like Routes in Route Tables. Similar in that once you have the Network Rule Collection akin to have a Route Table setup, the Rules in the Network Rule Collection can be pretty much rince and repeat. So using a CSV and Excel's power, smashing out dozens (or in my requirement 100's) of rules saves a lot of time. Again, see above caveats for.. err caveats?

One last point I wanted to mention is around the rule itself in terraform. There are certain constraints around formatting that need to be met. I assume this is because of the way the provider is built and interacts with the Azure API.

So more caveats? Just two quick points, more pro-tips really around the module, specificly the Rule itself.

Firstly, the syntax does not allow us to use each.value.<reference> since the specific block is 'dynamic "rule"', so my understanding is we need to reference that name. That's why we have rule.value.name.

The second and last point is that “v0.12, the Terraform language has built-in syntax for creating lists using the [ and ] delimiters.” Thank you official Terraform doco. So, where we have lists required, that being the values

  • source_addresses
  • destination_ports
  • destination_addresses
  • protocols

Apart from that, the rest is quite similar in the module flow. Below is an example of what that looks like, including an example CSV file:

locals {
  collection_rules = csvdecode(file("${path.module}/network_collection_rules.csv"))
}
resource "azurerm_firewall_network_rule_collection" "Example-Collection" {
  name                = "Example-Allow-Collection"
  azure_firewall_name = azure_firewall.name
  resource_group_name = resource_group.name
  priority            = "1000"
  action              = "Allow"
  dynamic "rule" {
    for_each = { for netrule in local.collection_rules  : netrule.name => netrule }
    content {
      name                  = rule.value.name
      source_addresses      = [rule.value.source_addresses]
      destination_ports     = [rule.value.destination_ports]
      destination_addresses = [rule.value.destination_addresses]
      protocols             = [rule.value.protocols]
    }
  }
}

Finally, here's an example of the CSV file that contains the details that's used to create the Network Rule Collection.

name,source_addresses,destination_ports,destination_addresses,protocols
CloudFlareDNSPrimary,10.0.0.0/8,53,1.1.1.1/32,Any
CloudFlareDNSSecondary,10.0.0.0/8,53,1.0.0.1/32,Any

Final thoughts

Within weeks of figuring my first usage of CSVDECODE and using CSV files to streamline my Terraforming of Azure resources, I found another. As I said before “there's more resources this can be applied to than just Route Tables".

Cheers!


Discussion 💬