Collect Azure WAF logs
This document explains how to export Azure Web Application Firewall (WAF) logs to Google Security Operations using an Azure Storage Account. The parser handles logs in JSON format, transforming them into UDM. It processes logs containing a recordsarray by iterating through each record and mapping specific fields to UDM properties. If the recordsarray is absent, the parser handles the log as a single event, extracting and mapping fields accordingly.
Before you begin
Ensure you have the following prerequisites:
- Google SecOps instance
- An active Azure tenant
- Privileged access to Azure
Configure Azure Storage Account
- In the Azure console, search for Storage accounts .
- Click Create.
- Specify values for the following input parameters: - Subscription: Select the subscription.
- Resource Group: Select the resource group.
- Region: Select the region.
- Performance: Select the performance (Standard recommended).
- Redundancy: Select the redundancy (GRS or LRS recommended).
- Storage account name: Enter a name for the new storage account.
 
- Click Review + create.
- Review the overview of the account and click Create.
- From the Storage Account Overviewpage, select the Access keyssubmenu in Security + networking.
- Click Shownext to key1or key2.
- Click Copy to clipboardto copy the key.
- Save the key in a secure location for later use.
- From the Storage Account Overviewpage, select the Endpointssubmenu in Settings.
- Click Copy to clipboardto copy the Blob serviceendpoint URL; for example, https://<storageaccountname>.blob.core.windows.net.
- Save the endpoint URL in a secure location for later use.
How to configure Log Export for Azure WAF Logs
- Sign in to the Azure Portalusing your privileged account.
- Go to Web Application Firewall (WAF) rulesand select a WAF to monitor.
- Select Monitoring > Diagnostic Settings.
- Click + Add diagnostic setting. - Enter a descriptive name for the diagnostic setting.
 
- Select allLogs.
- Select the Archive to a storage accountcheckbox as the destination. - Specify the Subscriptionand Storage Account.
 
- Click Save.
Set up feeds
There are two different entry points to set up feeds in the Google SecOps platform:
- SIEM Settings > Feeds > Add New Feed
- Content Hub > Content Packs > Get Started
How to set up the Azure WAF feed
- Click the Azure Platformpack.
- Locate the Azure WAFlog type and click Add new feed.
-  Specify values for the following fields: - Source Type: Microsoft Azure Blob Storage V2.
-  Azure URI: The blob endpoint URL. -  ENDPOINT_URL/BLOB_NAME- Replace the following: -  ENDPOINT_URL: The blob endpoint URL (https://<storageaccountname>.blob.core.windows.net)
-  BLOB_NAME: The name of the blob (such as,<logname>-logs)
 
-  
 
- Replace the following: 
 
-  
-  Source deletion options: Select the deletion option according to your ingestion preferences.
Note: If you select the Delete transferred filesorDelete transferred files and empty directoriesoption, make sure that you granted appropriate permissions to the service account.
- Maximum File Age: Includes files modified in the last number of days. Default is 180 days.
-  Shared key: The access key to the Azure Blob Storage. Advanced options 
-  Feed Name: A prepopulated value that identifies the feed. 
-  Asset Namespace: Namespace associated with the feed . 
-  Ingestion Labels: Labels applied to all events from this feed. 
 
-  Click Create feed. 
For more information about configuring multiple feeds for different log types within this product family, see Configure feeds by product .
UDM Mapping Table
| Log Field | UDM Mapping | Logic | 
|---|---|---|
| backendPoolName | additional.fields[?key=='backendPoolName'].value.string_value | The value is taken from the backendPoolNamefield in the raw log. | 
| backendSettingName | additional.fields[?key=='backendSettingName'].value.string_value | The value is taken from the backendSettingNamefield in the raw log. | 
| category | metadata.product_event_type | The value is taken from the categoryfield in the raw log. | 
| EventEnqueuedUtcTime | additional.fields[?key=='EventEnqueuedUtcTime'].value.string_value | The value is taken from the EventEnqueuedUtcTimefield in the raw log whenrecordsfield exists. | 
| EventProcessedUtcTime | additional.fields[?key=='EventProcessedUtcTime'].value.string_value | The value is taken from the EventProcessedUtcTimefield in the raw log whenrecordsfield exists. | 
| operationName | additional.fields[?key=='operationName'].value.string_value | The value is taken from the operationNamefield in the raw log. | 
| properties.action | additional.fields[?key=='action'].value.string_value | The value is taken from the properties.actionfield in the raw log whenrecordsfield exists. | 
| properties.action | security_result.action_details | The value is taken from the properties.actionfield in the raw log whenrecordsfield does not exist. | 
| properties.clientIP,properties.clientIp | principal.asset.ip,principal.ip | The value is taken from either the properties.clientIPorproperties.clientIpfield in the raw log, prioritizingclientIP. | 
| properties.clientPort | principal.port | The value is taken from the properties.clientPortfield in the raw log. | 
| properties.clientResponseTime | principal.resource.attribute.labels[?key=='Client Response Time'].value | The value is taken from the properties.clientResponseTimefield in the raw log whenrecordsfield does not exist. | 
| properties.details.data | additional.fields[?key=='Properties data'].value.string_value | The value is taken from the properties.details.datafield in the raw log whenrecordsfield exists. | 
| properties.details.file | principal.process.file.full_path | The value is taken from the properties.details.filefield in the raw log whenrecordsfield does not exist. | 
| properties.details.matches[].matchVariableName,properties.details.matches[].matchVariableValue | additional.fields[?key.startsWith('%{idx} ')].value.string_value | The value is taken from the properties.details.matchesarray in the raw log. Thekeyin the UDM is constructed using the index (idx) andmatchVariableName. Thevalueis taken frommatchVariableValue. | 
| properties.details.message | metadata.description | The value is taken from the properties.details.messagefield in the raw log after removing backslashes and quotes. | 
| properties.details.msg | metadata.description | The value is taken from the properties.details.msgfield in the raw log whenrecordsfield exists. | 
| properties.httpMethod | network.http.method | The value is taken from the properties.httpMethodfield in the raw log. | 
| properties.httpStatus | network.http.response_code | The value is taken from the properties.httpStatusfield in the raw log. | 
| properties.httpVersion | network.application_protocol | If the properties.httpVersionfield containsHTTP, the valueHTTPis assigned. | 
| properties.host,properties.hostname,properties.originalHost | principal.asset.hostname,principal.hostname | The value is taken from one of properties.originalHost,properties.host, orproperties.hostname, prioritizing them in that order. | 
| properties.policyId | security_result.detection_fields[?key=='policyId'].value | The value is taken from the properties.policyIdfield in the raw log. | 
| properties.policyMode | security_result.detection_fields[?key=='policyMode'].value | The value is taken from the properties.policyModefield in the raw log whenrecordsfield exists. | 
| properties.policy | additional.fields[?key=='Properties policy'].value.string_value | The value is taken from the properties.policyfield in the raw log whenrecordsfield exists. | 
| properties.receivedBytes | network.received_bytes | The value is taken from the properties.receivedBytesfield in the raw log. | 
| properties.requestUri | target.url | The value is taken from the properties.requestUrifield in the raw log. | 
| properties.ruleId | security_result.rule_id | The value is taken from the properties.ruleIdfield in the raw log. | 
| properties.ruleName | security_result.rule_name | The value is taken from the properties.ruleNamefield in the raw log whenrecordsfield exists. | 
| properties.ruleName,ruleSetType | security_result.rule_name | The value is taken from the properties.ruleNamefield, or if empty, from theruleSetTypefield in the raw log whenrecordsfield does not exist. | 
| properties.ruleSetVersion | security_result.detection_fields[?key=='ruleSetVersion'].value | The value is taken from the properties.ruleSetVersionfield in the raw log. | 
| properties.sentBytes | network.sent_bytes | The value is taken from the properties.sentBytesfield in the raw log. | 
| properties.serverResponseLatency | additional.fields[?key=='Server Response Latency'].value.string_value | The value is taken from the properties.serverResponseLatencyfield in the raw log whenrecordsfield does not exist. | 
| properties.serverRouted | target.asset.ip,target.ip,target.port | The IP and port are extracted from the properties.serverRoutedfield. | 
| properties.sslCipher | network.tls.cipher | The value is taken from the properties.sslCipherfield in the raw log. | 
| properties.sslClientCertificateIssuerName | network.tls.server.certificate.issuer | The value is taken from the properties.sslClientCertificateIssuerNamefield in the raw log. | 
| properties.sslProtocol | network.tls.version | The value is taken from the properties.sslProtocolfield in the raw log. | 
| properties.timeTaken | additional.fields[?key=='Properties Timetaken'].value.string_value | The value is taken from the properties.timeTakenfield in the raw log whenrecordsfield does not exist. | 
| properties.trackingReference | additional.fields[?key=='trackingReference'].value.string_value | The value is taken from the properties.trackingReferencefield in the raw log whenrecordsfield exists. | 
| properties.transactionId | network.session_id | The value is taken from the properties.transactionIdfield in the raw log. | 
| properties.userAgent | network.http.user_agent | The value is taken from the properties.userAgentfield in the raw log. | 
| properties.WAFEvaluationTime | additional.fields[?key=='Properties WAFEvaluationTime'].value.string_value | The value is taken from the properties.WAFEvaluationTimefield in the raw log whenrecordsfield does not exist. | 
| properties.WAFMode | additional.fields[?key=='Properties WAFMode'].value.string_value | The value is taken from the properties.WAFModefield in the raw log whenrecordsfield does not exist. | 
| rec.category | metadata.product_event_type | The value is taken from the rec.categoryfield in the raw log whenrecordsfield exists. | 
| rec.operationName | additional.fields[?key=='operationName'].value.string_value | The value is taken from the rec.operationNamefield in the raw log whenrecordsfield exists. | 
| rec.properties.clientIP,rec.properties.clientIp | principal.asset.ip,principal.ip | The value is taken from either the rec.properties.clientIPorrec.properties.clientIpfield in the raw log, prioritizingclientIPwhenrecordsfield exists. | 
| rec.properties.clientPort | principal.port | The value is taken from the rec.properties.clientPortfield in the raw log whenrecordsfield exists. | 
| rec.properties.host | principal.asset.hostname,principal.hostname | The value is taken from the rec.properties.hostfield in the raw log whenrecordsfield exists. | 
| rec.properties.policy | additional.fields[?key=='Properties policy'].value.string_value | The value is taken from the rec.properties.policyfield in the raw log whenrecordsfield exists. | 
| rec.properties.requestUri | target.url | The value is taken from the rec.properties.requestUrifield in the raw log whenrecordsfield exists. | 
| rec.properties.ruleName | security_result.rule_name | The value is taken from the rec.properties.ruleNamefield in the raw log whenrecordsfield exists. | 
| rec.properties.trackingReference | additional.fields[?key=='trackingReference'].value.string_value | The value is taken from the rec.properties.trackingReferencefield in the raw log whenrecordsfield exists. | 
| rec.resourceId | target.resource.id | The value is taken from the rec.resourceIdfield in the raw log whenrecordsfield exists. | 
| rec.time | metadata.event_timestamp | The value is taken from the rec.timefield in the raw log whenrecordsfield exists. | 
| resourceId | target.resource.id | The value is taken from the resourceIdfield in the raw log whenrecordsfield does not exist. | 
| timeStamp | metadata.event_timestamp | The value is taken from the timeStampfield in the raw log whenrecordsfield does not exist. | 
|   
N/A | metadata.event_type | The value is set to NETWORK_CONNECTIONif both principal (hostname or client IP) and destination IP are present. It's set toSTATUS_UPDATEif a principal is present but destination IP is missing. Otherwise, it defaults toGENERIC_EVENTor the value of theevent_typefield. | 
|   
N/A | metadata.log_type | The value is hardcoded to AZURE_WAF. | 
|   
N/A | metadata.product_name | The value is hardcoded to Azure WAF Logs. | 
|   
N/A | metadata.vendor_name | The value is hardcoded to Microsoft. | 
|   
N/A | security_result.action | The value is set to ALLOWifproperties.actionisMatched,BLOCKifproperties.actionisBlock. | 
Need more help? Get answers from Community members and Google SecOps professionals.

