How To – Enable Web Application Firewall Geomatch Custom Rules

At the end of July, Microsoft announced the general availability of geomatching via Custom Rules in Web Application Firewall. This is a feature I am quite fond of, and is excellent at reducing your attack surface. Thankfully, it’s also quite simple to implement!

First, what is Web Application Firewall (WAF)? Well it’s a service that provides protection for your web apps from common exploits and vulnerabilities. It can be deployed with Azure Application Gateway, Azure Front Door, and Azure Content Delivery Network (CDNN is preview for now). WAF allows for central management, meaning you can react to threats faster, instead of securing each individual web application. There are a couple of specific differences depending on the service you attach a WAF policy to, and you can read about them here.

WAF overview
Nice graphical explainer of WAF from Microsoft

Next, what are Custom Rules? As part of each WAF policy, you have to configure a set of standard rules, such as Prevention or Detection mode, and managed rules (OWASP). However, you can also create your own rules, and in WAF these are simply called Custom Rules (CR). A CR is made up of one or more conditions followed by an action. All CRs for a WAF policy are match rules. You can have multiple CRs per policy and they are processed in order of priority in a range of 1-100, with 1 being highest priority, or processed earliest.

Now that we have the foundation, let’s look at the geomatch option. This is available within your CR as a match Condition.

Create your CR with an appropriate name and priority, then choose ‘Geo location’ from the Match type drop down as above. Next, you’ll want to ensure you choose RemoteAddr as the match variable, and decide what logic you want to apply. By logic I mean the pattern that will fire the rule. In this example, I want all traffic except Ireland blocked. So I will choose the Operation ‘Is not’, then location Ireland, then Deny. If I wanted all traffic allowed and Ireland blocked, I would simply choose the Operation ‘Is’. I recommend figuring out your pattern then working your way through the final section of the CR.

So, based on my example with Ireland, my CR now looks like this:

Now most commonly, I would see a geomatch policy applied to an Application Gateway, but it can be applied to Front Door also. On of the nice features of a WAF policy when using it with Application Gateway is that you get association choices. This means you can be as granular as having different policies per listener, or even per path rule. Giving you huge flexibility when combined with CRs.

Finally, I have obviously shown you how to create this CR in the portal, but there are other methods, like Powershell. These can be very handy if you need to recreate a geomatch CR, especially if you have multiple conditions with many countries (there is a max of 10 per condition). Note, if using these methods, you need to use country codes, like IE, and these are all listed here.

And that’s it! You have your CR created and associated. WAFs take effect pretty quickly so you should be able to test within a couple of minutes. If you want to perform a simple verify, you can switch the action from Deny traffic to Allow and retest.

Finally, a nice tip when you’re happy with your configuration is to setup an alert against your WAF should there be a change made, or worse, deleted (apply resource locks people). This runs as a signal alert from Azure Monitor against an action group as per standard

That’s it for this post, as always, if you have any questions, please get in touch!

AZ-500 Microsoft Azure Security Technologies – Study Guide

Updated February 2021

Azure has a sole security focused exam, AZ-500 Microsoft Azure Security Technologies. Passing this single exam will allow you to earn a Microsoft Certified: Azure Security Engineer Associate certification.

So, if you’re interested and wondering if you should take this exam? Here is what Microsoft have to say:

Candidates for this exam are Microsoft Azure security engineers who implement security controls, maintain the security posture, manages identity and access, and protects data, applications, and networks. Candidates identify and remediate vulnerabilities by using a variety of security tools, implements threat protection, and responds to security incident escalations. As a Microsoft Azure security engineer, candidates often serve as part of a larger team dedicated to cloud-based management and security and may also secure hybrid environments as part of an end-to-end infrastructure.

Candidates for this exam should have strong skills in scripting and automation, a deep understanding of networking, virtualization, and cloud N-tier architecture, and a strong familiarity with cloud capabilities, Microsoft Azure products and services, and other Microsoft products and services.

Below, I’ve put together a collection of links relevant to the sections highlighted as being part of the skills measured for this exam. As always, these are only guide links, sometimes you need to explore a topic much more deeply if you are not familiar with it.

If you spot something, or have a better link for a topic, get in touch! I will update this post as regularly as possible and always appreciate any feedback.

A good place to start is the Azure Security Documentation page. This site includes most of the key concepts and services covered in this exam, as well as several best practice approaches you should consider.

Manage Identity and Access (30-35%)

Manage Azure Active Directory identities
Configure secure access by using Azure AD
Manage application access
Manage access control

Implement Platform Protection (15-20%)

Implement advanced network security
Configure advanced security for compute

Manage Security Operations (25-30%)

Monitor security by using Azure Monitor
Monitor security by using Azure Security Center
Monitor security by using Azure Sentinel
Configure Security Policies

Secure Data and Applications (20-25%)

Configure security for storage
Configure security for databases
Configure and manage Key Vault

AZ-303: Microsoft Azure Architect Technologies (beta) – Study Guide

Microsoft continues to update it’s role based exams and next on the list is AZ-303. This is the updated version of AZ-300 and should launch at the end of June 2020. Currently this is arguably the most difficult exam certification path as it is one of only two Expert level certifications for Azure. This post will cover AZ-303 and here is what Microsoft have to say about it:

Candidates for this exam are Azure Solutions Architects who advise stakeholders and translate business requirements into secure, scalable, and reliable solutions. Candidates should have advanced experience and knowledge of IT operations, including networking, virtualization, identity, security, business continuity, disaster recovery, data platform, budgeting, and governance. This role requires managing how decisions in each area affects an overall solution. Candidates must have expert-level skills in Azure administration and have experience with Azure development processes and DevOps processes.

As the exam is still in beta, if you take it, it will not be scored immediately. So bear that in mind before you sit it! Of course, if you pass the exam, once scored, it will count towards the certification.

If you do want to sit the exam and register before August 10th 2020, there are 300 discounted exams on offer if you use the code – AZ303DonSuperStar

As always, a great place to start is Microsoft Learn. There are several interactive learning paths that are free that you can work through at your own pace. I find this a great way to study and gain greater understanding of the services by actually using them and you will need to be very familiar with Azure to pass this exam.

Below I’ve put together a collection of links relevant to the sections Microsoft have highlighted as being part of the skills measured for this exam. These are only guide links, sometimes you need to explore a topic much more deeply if you are not familiar with it. Hopefully these study materials will help guide you to successfully passing AZ-303!

Implement and Monitor an Azure Infrastructure (50-55%)

Implement cloud infrastructure monitoring

Implement storage accounts

Implement VMs for Windows and Linux

Automate deployment and configuration of resources

Implement virtual networking

Implement Azure Active Directory

Implement and manage hybrid identities

Implement Management and Security Solutions (25-30%)

Manage workloads in Azure

Implement load balancing and network security

Implement and manage Azure governance solutions

Manage security for applications

Implement Solutions for Apps (10-15%)

Implement an application infrastructure

Implement container-based applications

Implement and Manage Data Platforms (10-15%)

Implement NoSQL databases

Implement Azure SQL databases

AZ-104: Microsoft Azure Administrator – Study Guide

Updated 2020-06-25!

As Microsoft progresses it’s new strategy of role based certification, they are constantly updating the exams required for certification. As the Azure Administrator Role was the first to launch back in 2018, it’s no surprise to see it being updated again. If you’re interested in the exam, here is what Microsoft have to say about it:

The Azure Administrator implements, manages, and monitors identity, governance, storage, compute, and virtual networks in a cloud environment. The Azure Administrator will provision, size, monitor, and adjust resources as appropriate. Candidates should have a minimum of six months of hands-on experience administering Azure. Candidates should have a strong understanding of core Azure services, Azure workloads, security, and governance. Candidates for this exam should have experience in using PowerShell, the Command Line Interface, Azure Portal, and ARM templates.

As the exam is still in beta, if you take it, it will not be scored immediately. However, if you pass the exam, it will count towards certification and you will receive a Microsoft Certified: Azure Administrator Associate certification.

As always, a great place to start is Microsoft Learn. There are several interactive learning paths that are free that you can work through at your own pace. I find this a great way to study and gain greater understanding of the services by actually using them and you will need to have used Azure to pass this exam.

Below I’ve put together a collection of links relevant to the sections Microsoft have highlighted as being part of the skills measured for this exam. These are only guide links, sometimes you need to explore a topic much more deeply if you are not familiar with it. Hopefully these study materials will help guide you to successfully passing AZ-104!

Manage Azure identities and governance (15-20%)

Manage Azure AD objects

Manage role-based access control (RBAC)

Manage subscriptions and governance

Implement and manage storage (10-15%)

Manage storage accounts

Manage data in Azure Storage

Configure Azure files and Azure blob storage

Deploy and manage Azure compute resources (25-30%)

Configure VMs for high availability and scalability

Automate deployment and configuration of VMs

Create and configure VMs

Create and configure containers

Create and configure Web Apps

Configure and manage virtual networking (30-35%

Implement and manage virtual networking

Configure name resolution

Secure access to virtual networks

Configure load balancing

Monitor and troubleshoot virtual networking

Integrate an on-premises network with an Azure virtual network

Monitor and back up Azure resources (10-15%)Manage Identities

Monitor resources by using Azure Monitor

Implement backup and recovery

How to – Troubleshoot Azure Firewall

Networking in Azure is one of my favourite topics. As my work has me focus primarily on Azure Virtual Datacenter builds, networking is key. When Microsoft introduced Azure Firewall (AFW), I was excited to see a platform based option as a hopeful alternative to the traditional NVAs. Feature wise in preview, AFW lacked some key functionality. Once it went GA a lot of the asks from the community were rectified however there are still some outstanding issues like cost, but all of that is for a blog post for another day!

AFW is used in a lot of environments. It’s simple to deploy, resilient and relatively straight forward to configure. However, once active in the environment, I noticed that finding out what is going wrong can be tricky. Hopefully this post helps with that and can save you some valuable time!

I don’t know about you, but the first thing I always check when trying to solve a problem is the most simple solution. For AFW that check is to make sure it’s not stopped. Yes that’s right you can “stop” AFW. It’s quick and easy to do via shell:

# Stop an existing firewall

$azfw = Get-AzFirewall -Name "FW Name" -ResourceGroupName "RG Name"
$azfw.Deallocate()
Set-AzFirewall -AzureFirewall $azfw

But how do you check if it has been stopped? Very simply, via the Azure Portal. On the overview blade for AFW it shows provisioning state. If this is anything but “Succeeded” you most likely have an issue.

So, how do you enable it again should you find your AFW deallocated? Again, quite simply via shell, however, it must be allocated to the original resource group and subscription. Also, while it deallocates almost instantly, it takes roughly the same amount of time to allocate AFW as it does to create one from scratch.

# Start a firewall

$azfw = Get-AzFirewall -Name "FW Name" -ResourceGroupName "RG Name"
$vnet = Get-AzVirtualNetwork -ResourceGroupName "RG Name" -Name "VNet Name"
$publicip = Get-AzPublicIpAddress -Name "Public IP Name" -ResourceGroupName " RG Name"
$azfw.Allocate($vnet,$publicip)
Set-AzFirewall -AzureFirewall $azfw

So, your AFW is active and receiving traffic via whatever method (NAT, Custom Route Tables etc.) and you have created rules to allow traffic as required. Don’t forget all traffic is blocked by default until you create rules.

By default the only detail you can get from AFW are metrics. These can show a small range of traffic with no granular detail, such as rules hit count.


To get detailed logs, like other Azure services, you need to enable them. I recommend doing this as part of your creation process. In terms of what to do, you have to add a diagnostic setting. There are two logs available, and I recommend choosing both.

  • AzureFirewallApplicationRule
  • AzureFirewallNetworkRule

In terms of where to send the logs, I like the integration offered by Azure Monitor Logs and there is a filtered shortcut right within the AFW blade too.

Once enabled, you should start seeing logs flowing into Azure Monitor Logs within five to ten minutes. One aspect that can be viewed as a slight negative is that logs are sent in JSON. As a result most of the interesting data you want is part of an object array:

{
  "category": "AzureFirewallNetworkRule",
  "time": "2018-06-14T23:44:11.0590400Z",
  "resourceId": "/SUBSCRIPTIONS/{subscriptionId}/RESOURCEGROUPS/{resourceGroupName}/PROVIDERS/MICROSOFT.NETWORK/AZUREFIREWALLS/{resourceName}",
  "operationName": "AzureFirewallNetworkRuleLog",
  "properties": {
      "msg": "TCP request from 111.35.136.173:12518 to 13.78.143.217:2323. Action: Deny"
  }
}

So, when running your queries, you need to parse that data. For those who have strong experience in Kusto, this will be no problem. For those who don’t, Microsoft thankfully provide guidance on how to parse both logs including explanatory comments

For ApplicationRule log

AzureDiagnostics
| where Category == "AzureFirewallApplicationRule"
//using :int makes it easier to pars but later we'll convert to string as we're not interested to do mathematical functions on these fields
//this first parse statement is valid for all entries as they all start with this format
| parse msg_s with Protocol " request from " SourceIP ":" SourcePortInt:int " " TempDetails
//case 1: for records that end with: "was denied. Reason: SNI TLS extension was missing."
| parse TempDetails with "was " Action1 ". Reason: " Rule1
//case 2: for records that end with
//"to ocsp.digicert.com:80. Action: Allow. Rule Collection: RC1. Rule: Rule1"
//"to v10.vortex-win.data.microsoft.com:443. Action: Deny. No rule matched. Proceeding with default action"
| parse TempDetails with "to " FQDN ":" TargetPortInt:int ". Action: " Action2 "." *
//case 2a: for records that end with:
//"to ocsp.digicert.com:80. Action: Allow. Rule Collection: RC1. Rule: Rule1"
| parse TempDetails with * ". Rule Collection: " RuleCollection2a ". Rule:" Rule2a
//case 2b: for records that end with:
//for records that end with: "to v10.vortex-win.data.microsoft.com:443. Action: Deny. No rule matched. Proceeding with default action"
| parse TempDetails with * "Deny." RuleCollection2b ". Proceeding with" Rule2b
| extend 
SourcePort = tostring(SourcePortInt)
|extend
TargetPort = tostring(TargetPortInt)
| extend
//make sure we only have Allowed / Deny in the Action Field
Action1 = case(Action1 == "Deny","Deny","Unknown Action")
| extend
    Action = case(Action2 == "",Action1,Action2),
    Rule = case(Rule2a == "",case(Rule1 == "",case(Rule2b == "","N/A", Rule2b),Rule1),Rule2a), 
    RuleCollection = case(RuleCollection2b == "",case(RuleCollection2a == "","No rule matched",RuleCollection2a),RuleCollection2b),
    FQDN = case(FQDN == "", "N/A", FQDN),
    TargetPort = case(TargetPort == "", "N/A", TargetPort)
| project TimeGenerated, msg_s, Protocol, SourceIP, SourcePort, FQDN, TargetPort, Action ,RuleCollection, Rule

For NetworkRule log

AzureDiagnostics
| where Category == "AzureFirewallNetworkRule"
//using :int makes it easier to pars but later we'll convert to string as we're not interested to do mathematical functions on these fields
//case 1: for records that look like this:
//TCP request from 10.0.2.4:51990 to 13.69.65.17:443. Action: Deny//Allow
//UDP request from 10.0.3.4:123 to 51.141.32.51:123. Action: Deny/Allow
//TCP request from 193.238.46.72:50522 to 40.119.154.83:3389 was DNAT'ed to 10.0.2.4:3389
| parse msg_s with Protocol " request from " SourceIP ":" SourcePortInt:int " to " TargetIP ":" TargetPortInt:int *
//case 1a: for regular network rules
//TCP request from 10.0.2.4:51990 to 13.69.65.17:443. Action: Deny//Allow
//UDP request from 10.0.3.4:123 to 51.141.32.51:123. Action: Deny/Allow
| parse msg_s with * ". Action: " Action1a
//case 1b: for NAT rules
//TCP request from 193.238.46.72:50522 to 40.119.154.83:3389 was DNAT'ed to 10.0.2.4:3389
| parse msg_s with * " was " Action1b " to " NatDestination
//case 2: for ICMP records
//ICMP request from 10.0.2.4 to 10.0.3.4. Action: Allow
| parse msg_s with Protocol2 " request from " SourceIP2 " to " TargetIP2 ". Action: " Action2
| extend
SourcePort = tostring(SourcePortInt),
TargetPort = tostring(TargetPortInt)
| extend 
    Action = case(Action1a == "", case(Action1b == "",Action2,Action1b), Action1a),
    Protocol = case(Protocol == "", Protocol2, Protocol),
    SourceIP = case(SourceIP == "", SourceIP2, SourceIP),
    TargetIP = case(TargetIP == "", TargetIP2, TargetIP),
    //ICMP records don't have port information
    SourcePort = case(SourcePort == "", "N/A", SourcePort),
    TargetPort = case(TargetPort == "", "N/A", TargetPort),
    //Regular network rules don't have a DNAT destination
    NatDestination = case(NatDestination == "", "N/A", NatDestination)
| project TimeGenerated, msg_s, Protocol, SourceIP,SourcePort,TargetIP,TargetPort,Action, NatDestination

Using either query gives you clear readable that you can filter. One tip however, is to add a sort command to the end of the queries, normally I use by TimeGenerated to show me the latest data. So to condense and add that for the NetworkRule query above, it would look like:

AzureDiagnostics
| where Category == "AzureFirewallNetworkRule"
| parse msg_s with Protocol " request from " SourceIP ":" SourcePortInt:int " to " TargetIP ":" TargetPortInt:int *
| parse msg_s with * ". Action: " Action1a
| parse msg_s with * " was " Action1b " to " NatDestination
| parse msg_s with Protocol2 " request from " SourceIP2 " to " TargetIP2 ". Action: " Action2
| extend SourcePort = tostring(SourcePortInt),TargetPort = tostring(TargetPortInt)
| extend Action = case(Action1a == "", case(Action1b == "",Action2,Action1b), Action1a),Protocol = case(Protocol == "", Protocol2, Protocol),SourceIP = case(SourceIP == "", SourceIP2, SourceIP),TargetIP = case(TargetIP == "", TargetIP2, TargetIP),SourcePort = case(SourcePort == "", "N/A", SourcePort),TargetPort = case(TargetPort == "", "N/A", TargetPort),NatDestination = case(NatDestination == "", "N/A", NatDestination)
| project TimeGenerated, msg_s, Protocol, SourceIP,SourcePort,TargetIP,TargetPort,Action, NatDestination
| sort by TimeGenerated desc

Using parsed data, you can immediately see all the traffic hitting AFW and, for example, filter on options such as Action to see only denied traffic.

Microsoft also provide pre-cooked visualisation should you prefer it, you can download from here – https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/azure-firewall/AzureFirewall.omsview – then import into Azure Monitor. The detail is great for quick glance work, I really like the ApplicationRule breakout

Application rule log data

That about sums it up. Hopefully you are now informed and equipped to troubleshoot traffic issues in your Azure Firewall instance. As always, if there are any questions, please get in touch!

If you need more info on how to enable logs – https://docs.microsoft.com/en-us/azure/firewall/tutorial-diagnostics

Log and metrics concepts – https://docs.microsoft.com/en-us/azure/firewall/logs-and-metrics