In real world scenarios you have a data source with field names which are not identical to those in your table. Beside of renaming the fields in your agent which sending logs also Data Collection Rules provide the ability to map fields with transformkql
.
Introduction
In the previous article of this series we saw how to create a table and ingest data to it. There we defined a custom table TestDataAuxiliary_CL
with following fields:
TimeGenerated
, typedatetime
Message
, typestring
Info
, typestring
In the last article we already learned how the Data Collection Rule will deal with the data which are sent to it. Here the important points:
- If field
TimeGenerated
is missing the whole entry get ignored - Additional fields do not harm (are ignored)
- Missing fields do not harm (corresponding table fields are set to
null
; can be queried byisnull()
) - Fields are case sensitive; if they do not match (eg.
TiMeGeNeRatEd
vs.TimeGenerated
) row will be ignored
Now this article will focus on transforming fields; eg. you have often the type of the field attached to the field name: for field Message
and type string
it will be Message_s
.
Transforming field names
A Data Collection Rule (DCR) has the ability to apply transformation with KQL. Unfortunatly in this context only limited KQL commands are supported. A detailed list can be found here: https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/data-collection-transformations-structure#supported-kql-features
So our goal is clear
This is super simple to achieve with a simple KQL statement. You can specify in the Data Collection Rule (DCR) a transformKql
key with a corresponding KQL query.
source | extend Message_s=Message, Info_s=Info | project-away Message, Info
source
is the special keyword to suck the data from the input pipeline. With the extend
statement we add the new fields with the postfix _s
and then we remove the not needed fields with project-away
. Easy, right?
The final definition looks like:
$dcrParams = @'
{
"location": "switzerlandnorth",
"kind": "Direct",
"properties": {
"description": "A direct ingestion rule for TestData logs",
"streamDeclarations": {
"Custom-TestDataAuxiliary": {
"columns": [
{ "name": "TimeGenerated", "type": "datetime" },
{ "name": "Message", "type": "string" },
{ "name": "Info", "type": "string" }
]
}
},
"destinations": {
"logAnalytics": [
{
"workspaceResourceId": "/subscriptions/a2aeb284-51bd-4807-adbe-94095a10b175/resourceGroups/rg-security-logs-prod-001/providers/Microsoft.OperationalInsights/workspaces/log-security-prod-001",
"name": "log-security-prod-001"
}
]
},
"dataFlows": [
{
"streams": [
"Custom-TestDataAuxiliary"
],
"destinations": [
"log-security-prod-001"
],
"outputStream": "Custom-TestDataAuxiliary_CL",
"transformKql": "source | extend Message_s=Message, Info_s=Info | project-away Message, Info"
}
]
}
}
'@
Then sending the definition to Azure with following command:
Invoke-AzRestMethod -Path "/subscriptions/a2aeb284-51bd-4807-adbe-94095a10b175/resourceGroups/rg-security-logs-prod-001/providers/Microsoft.Insights/dataCollectionRules/DCR-Generic-CollectionRule?api-version=2023-03-11" -Method PUT -payload $dcrParams
Unfortunatly we get following message back from the API:
StatusCode : 400
Content : {
"error": {
"code": "InvalidPayload",
"message": "Data collection rule is invalid",
"details": [
{
"code": "InvalidDataFlow",
"message": "Stream (Custom-TestDataAuxiliary) cannot be transformed.Transformation not supported
for Log analytics Auxiliary table destination log-security-prod-001",
"target": "properties.dataFlows[0]"
}
]
}
}
Long story short: As it looks like KQL transformation is not supported by Auxiliary tables.
Verification with Analytics table
To verify that DCR is correct we do the check with a corresponding Analytics table. So first we create the analytics table and change our DCR so it sends data to the new defined table.
$analyticTableParams = @'
{
"properties": {
"totalRetentionInDays": 90,
"plan": "Analytics",
"schema": {
"name": "TestDataAnalytics_CL",
"columns": [
{ "name": "TimeGenerated", "type": "datetime" },
{ "name": "Message_s", "type": "string" },
{ "name": "Info_s", "type": "string" }
]
},
},
}
'@
Invoke-AzRestMethod -Path "/subscriptions/a2aeb284-51bd-4807-adbe-94095a10b175/resourceGroups/rg-security-logs-prod-001/providers/Microsoft.OperationalInsights/workspaces/log-security-prod-001/tables/TestDataAnalytics_CL?api-version=2023-01-01-preview" -Method PUT -payload $analyticTableParams
Now we update our DCR to reflect the new table:
$dcrParams = @'
{
"location": "switzerlandnorth",
"kind": "Direct",
"properties": {
"description": "A direct ingestion rule for TestData logs",
"streamDeclarations": {
"Custom-TestDataAnalytics": {
"columns": [
{ "name": "TimeGenerated", "type": "datetime" },
{ "name": "Message", "type": "string" },
{ "name": "Info", "type": "string" }
]
}
},
"destinations": {
"logAnalytics": [
{
"workspaceResourceId": "/subscriptions/a2aeb284-51bd-4807-adbe-94095a10b175/resourceGroups/rg-security-logs-prod-001/providers/Microsoft.OperationalInsights/workspaces/log-security-prod-001",
"name": "log-security-prod-001"
}
]
},
"dataFlows": [
{
"streams": [
"Custom-TestDataAnalytics"
],
"destinations": [
"log-security-prod-001"
],
"outputStream": "Custom-TestDataAnalytics_CL",
"transformKql": "source | extend Message_s=Message, Info_s=Info | project-away Message, Info"
}
]
}
}
'@
Invoke-AzRestMethod -Path "/subscriptions/a2aeb284-51bd-4807-adbe-94095a10b175/resourceGroups/rg-security-logs-prod-001/providers/Microsoft.Insights/dataCollectionRules/DCR-Generic-CollectionRule?api-version=2023-03-11" -Method PUT -payload $dcrParams
This time we get StatusCode : 200
- the API accepted our DCR. After sending test data (documented here
) we can query our Analytics table:
FAQ
What happens if you overwrite an existing DCR?
The interesting part from operational point of view is to know if endpoint URL changes. The short answer is NO; it does not change (but it takes some minutes until the new DCR is active).
Summary
Data Collection Rules (DCR) provide the possibility to transform incoming data before they get send to the Log Analytics table. But here is the catch: This works only for Analytics and Basic Logs. Auxiliary Logs do not support transformation.
Further Reading
- Data Collection Transforming: https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/data-collection-transformations
- KQL limited support for transform: https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/data-collection-transformations-structure#supported-kql-features