Matt Daines

KQL Notebook | Azure Firewall Logs

KQL | Azure Firewall

Azure Firewall logs can be configured to be collected as either resource specific logs or Azure Diagnostics logs. Resource specific logs are preferred as each log category is stored in a separate table, making it easier to query and analyze the data.

Resource Specific Logs

Network and Application Rule Logs

This query unions AZFWNetworkRule and AZFWApplicationRule and groups DestinationIp, Fqdn and TargetUrl into the single column Target for easier filtering.

AZFWNetworkRule
| union AZFWApplicationRule
| extend Target = case(DestinationIp != "", DestinationIp, 
                       Fqdn != "", Fqdn,
                       TargetUrl != "", TargetUrl, 
                       "Unknown")
| summarize Count = count() by SourceIp, Target, Protocol, DestinationPort, IsTlsInspected, Action
| // <Filters>
| sort by Count

Diagnostics Logs

Network and Application Rule Logs

AzureDiagnostics  
| where Category == "AzureFirewallNetworkRule" or Category == "AzureFirewallApplicationRule"  
| extend msg_original = msg_s  
| extend msg_s = replace(@'. Action: Deny. Reason: SNI TLS extension was missing.', @' to no_data:no_data. Action: Deny. Rule Collection: default behavior. Rule: SNI TLS extension missing', msg_s)  
| extend msg_s = replace(@'No rule matched. Proceeding with default action', @'Rule Collection: default behavior. Rule: no rule matched', msg_s)  
| parse msg_s with * " Web Category: " WebCategory  
| extend msg_s = replace(@'(. Web Category:).*','', msg_s)  
| parse msg_s with * ". Rule Collection: " RuleCollection ". Rule: " Rule  
| extend msg_s = replace(@'(. Rule Collection:).*','', msg_s)  
| parse msg_s with * ". Rule Collection Group: " RuleCollectionGroup  
| extend msg_s = replace(@'(. Rule Collection Group:).*','', msg_s)  
| parse msg_s with * ". Policy: " Policy  
| extend msg_s = replace(@'(. Policy:).*','', msg_s)  
| parse msg_s with * ". Signature: " IDSSignatureIDInt ". IDS: " IDSSignatureDescription ". Priority: " IDSPriorityInt ". Classification: " IDSClassification  
| extend msg_s = replace(@'(. Signature:).*','', msg_s)  
| parse msg_s with * " was DNAT'ed to " NatDestination  
| extend msg_s = replace(@"( was DNAT'ed to ).*",". Action: DNAT", msg_s)  
| parse msg_s with * ". ThreatIntel: " ThreatIntel  
| extend msg_s = replace(@'(. ThreatIntel:).*','', msg_s)  
| extend URL = extract(@"(Url: )(.*)(\. Action)",2,msg_s)  
| extend msg_s=replace(@"(Url: .*)(Action)",@"\2",msg_s)  
| parse msg_s with Protocol " request from " SourceIP " to " Target ". Action: " Action  
| extend  
SourceIP = iif(SourceIP contains ":",strcat_array(split(SourceIP,":",0),""),SourceIP),  
SourcePort = iif(SourceIP contains ":",strcat_array(split(SourceIP,":",1),""),""),  
Target = iif(Target contains ":",strcat_array(split(Target,":",0),""),Target),  
TargetPort = iif(SourceIP contains ":",strcat_array(split(Target,":",1),""),""),  
Action = iif(Action contains ".",strcat_array(split(Action,".",0),""),Action),  
Policy = case(RuleCollection contains ":", split(RuleCollection, ":")[0] ,Policy),  
RuleCollectionGroup = case(RuleCollection contains ":", split(RuleCollection, ":")[1], RuleCollectionGroup),  
RuleCollection = case(RuleCollection contains ":", split(RuleCollection, ":")[2], RuleCollection),  
IDSSignatureID = tostring(IDSSignatureIDInt),  
IDSPriority = tostring(IDSPriorityInt)
...
| summarize count() by SourceIP, Protocol,Target,TargetPort,Action, NatDestination, OperationName,ThreatIntel,RuleCollectionGroup,RuleCollection,Rule,WebCategory, Resource