SOLVED

Azure Sentinel's "Function" help

%3CLINGO-SUB%20id%3D%22lingo-sub-3182213%22%20slang%3D%22en-US%22%3EAzure%20Sentinel's%20%22Function%22%20help%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-3182213%22%20slang%3D%22en-US%22%3E%3CP%3EHi%2C%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EI%20want%20to%20make%20use%20of%20Function%20to%20set%20up%20exclusion%20rule%2C%20for%20example%2C%20src_ip%3D1.1.1.1%20AND%20dest_ip%3D2.2.2.2%2C%20src_ip%3D3.3.3.3%20AND%20signature%3DAAA.%20However%2C%20when%20I%20create%20the%20Function%20beginning%20with%20%22%7C%22%20or%20%22where%22%2C%20it%20could%20not%20be%20loaded%20in%20the%20original%20search.%20We%20did%20not%20include%20the%20data%20source%20here%20because%20we%20have%20another%20function%20to%20do%20the%20data%20normalization.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3ESo%20could%20I%20still%20use%20Function%20in%20this%20way%20or%20is%20there%20any%20other%20better%20approach%20to%20do%20such%20exclusion%3F%20Thanks!%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-3183706%22%20slang%3D%22en-US%22%3ERe%3A%20Azure%20Sentinel's%20%22Function%22%20help%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-3183706%22%20slang%3D%22en-US%22%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F1310945%22%20target%3D%22_blank%22%3E%40Steven_Su%3C%2FA%3E%26nbsp%3BYou%20will%20need%20to%20pass%20in%20a%20table%20to%20perform%20the%20actions%20against.%26nbsp%3B%20Take%20a%20look%20at%26nbsp%3B%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Fdata-explorer%2Fkusto%2Fquery%2Ffunctions%2Fuser-defined-functions%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3EUser-defined%20functions%20-%20Azure%20Data%20Explorer%20%7C%20Microsoft%20Docs%3C%2FA%3E%26nbsp%3B%20for%20some%20example%20of%20how%20you%20can%20pass%20in%20a%20specific%20table%20or%20one%20an%20unknown%20table.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-3208201%22%20slang%3D%22en-US%22%3ERe%3A%20Azure%20Sentinel's%20%22Function%22%20help%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-3208201%22%20slang%3D%22en-US%22%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F46875%22%20target%3D%22_blank%22%3E%40Gary%20Bushey%3C%2FA%3E%26nbsp%3BHi%20Sorry%20for%20the%20late%20reply%20since%20we%20are%20quite%20new%20to%20Sentinel%20and%20wanna%20migrate%20our%20AWS%20SIEM%20detection%20use%20case%20to%20Sentinel%3C%2FP%3E%3CP%3ENow%20our%20use%20case%20is%20that%20we%20want%20to%20have%202%20function%3A%26nbsp%3B%3C%2FP%3E%3CP%3EFunction%201%3A%20field%20normalization%20(shared%20and%20used%20by%20all%20the%20AWS%20use%20case)%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-applescript%22%3E%3CCODE%3EAWSCloudTrail%0A%7C%20project-rename%20%20%0A%20%20event_name%20%3D%20EventName%2C%0A%20%20src_ip%20%3D%20SourceIpAddress%2C%0A%20%20target_account_id%20%3D%20RecipientAccountId%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3BFunction%202%3A%20exclusion%20rule%20for%20use%20case%20A%20(used%20by%20single%20AWS%20use%20case)%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-applescript%22%3E%3CCODE%3Ewhere%20aws_account_name%20!%3D%20%22DevOps%22%20and%20src_ip%20!%3D%20%2210.10.10.10%22%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3ESo%20now%20we%20are%20writing%20the%20KQL%20for%20the%20case%20A%20which%20requires%20the%20both%20functions.%20However%20it%20seems%20no%20working.%20In%20the%20link%3A%26nbsp%3B%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Fsentinel%2Ffalse-positives%23modify-the-query%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3Ehttps%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Fsentinel%2Ffalse-positives%23modify-the-query%3C%2FA%3E%26nbsp%3Bit%20suggested%20to%20directly%20modify%20the%20query%2C%20but%20we%20still%20want%20to%20see%20if%20it%20is%20possible%20to%20keep%20the%20exception%20condition%20in%20the%20function.%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EThank%20you.%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-3213344%22%20slang%3D%22en-US%22%3ERe%3A%20Azure%20Sentinel's%20%22Function%22%20help%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-3213344%22%20slang%3D%22en-US%22%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F1310945%22%20target%3D%22_blank%22%3E%40Steven_Su%3C%2FA%3E%26nbsp%3BIf%20you%20want%20to%20add%20Function%202%20as%20part%20of%20Function%201%20but%20want%20to%20be%20able%20to%20change%20the%20aws_account_name%20and%20src_ip%2C%20you%20will%20need%20to%20pass%20those%20variables%20into%20the%20function%20and%20then%20use%20the%20variables%20in%20the%20code.%26nbsp%3B%20Something%20like%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-applescript%22%3E%3CCODE%3Elet%20CheckAWS%20%3D%20(account%3Astring%2C%20ipaddress%3A%20string)%20%7B%0AAWSCloudTrail%0A%7C%20where%20aws_account_name%20!%3D%20account%20and%20src_ip%20!%3D%20ipaddress%0A%7D%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-3217625%22%20slang%3D%22en-US%22%3ERe%3A%20Azure%20Sentinel's%20%22Function%22%20help%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-3217625%22%20slang%3D%22en-US%22%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F46875%22%20target%3D%22_blank%22%3E%40Gary%20Bushey%3C%2FA%3E%26nbsp%3B%3C%2FP%3E%3CP%3EHi%20Gary%2C%3C%2FP%3E%3CP%3EThank%20you%20very%20much%20for%20your%20response.%20I%20guest%20maybe%20I%20am%20not%20explaining%20the%20issue%20clearly.%3CBR%20%2F%3EWe%20want%20to%20have%202%20separate%20functions%20so%20that%20we%20could%20invoke%20them%20in%20other%20KQL%20queries%20(different%20AWS%20use%20cases)%20like%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-applescript%22%3E%3CCODE%3EFunction1%0AFunction2%0A%7Csummarize%20...%20by%20...%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3EThe%20Function%201%20doing%20the%20data%20normalization%20is%20the%20same%20for%20all%20the%20queries%20while%20Function%202%20is%20the%20exclusion%20and%20is%20different%20case%20by%20case.%20That's%20why%20we%20need%20to%20create%20dedicated%20function%20for%20it.%20So%20it%20seems%20your%20suggestion%20may%20not%20suit%20the%20requirement.%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-3220380%22%20slang%3D%22en-US%22%3ERe%3A%20Azure%20Sentinel's%20%22Function%22%20help%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-3220380%22%20slang%3D%22en-US%22%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F1310945%22%20target%3D%22_blank%22%3E%40Steven_Su%3C%2FA%3E%26nbsp%3BIf%20I%20understand%20what%20you%20need%2C%20the%20first%20function%20needs%20to%20return%20a%20normalized%20table%20and%20then%20the%20second%20function%20needs%20to%20act%20upon%20that%20table.%26nbsp%3B%20So%20something%20like%3A%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-applescript%22%3E%3CCODE%3Elet%20normalizedTable%20%3D%20Function1()%3B%0Alet%20response%20%3D%20Function2(normalizedTable)%3B%0Aresponse%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3EIf%20that%20is%20the%20case%20then%20Function2%20would%20need%20to%20be%20able%20to%20accept%20the%20table%20that%20you%20want%20to%20perform%20the%20summarize%20on.%26nbsp%3B%20You%20cannot%20just%20start%20a%20function%20with%20%22%7C%20summarize%22.%26nbsp%3B%20It%20can%20return%20its%20own%20table%20and%20then%20you%20can%20just%20display%20that%20table.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-3224775%22%20slang%3D%22en-US%22%3ERe%3A%20Azure%20Sentinel's%20%22Function%22%20help%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-3224775%22%20slang%3D%22en-US%22%3E%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F46875%22%20target%3D%22_blank%22%3E%40Gary%20Bushey%3C%2FA%3E%20Understood%2C%20the%20answer%20becomes%20more%20clear%20now.%20May%20I%20further%20know%20what%20would%20be%20the%20structure%20of%20the%20Function2%3F%20Because%20I%20will%20also%20save%20Function1%20and%20Function2%20as%20functions%20under%20%22Workspace%20functions%22.%20Could%20you%20provide%20me%20some%20ideas%20how%20to%20write%20the%20Function2%3F%20Thanks!%3C%2FLINGO-BODY%3E
Contributor

Hi,

 

I want to make use of Function to set up exclusion rule, for example, src_ip=1.1.1.1 AND dest_ip=2.2.2.2, src_ip=3.3.3.3 AND signature=AAA. However, when I create the Function beginning with "|" or "where", it could not be loaded in the original search. We did not include the data source here because we have another function to do the data normalization.

 

So could I still use Function in this way or is there any other better approach to do such exclusion? Thanks!

13 Replies

@Steven_Su You will need to pass in a table to perform the actions against.  Take a look at User-defined functions - Azure Data Explorer | Microsoft Docs  for some example of how you can pass in a specific table or one an unknown table.

 

 

@Gary Bushey Hi Sorry for the late reply since we are quite new to Sentinel and wanna migrate our AWS SIEM detection use case to Sentinel

Now our use case is that we want to have 2 function: 

Function 1: field normalization (shared and used by all the AWS use case)

AWSCloudTrail
| project-rename  
  event_name = EventName,
  src_ip = SourceIpAddress,
  target_account_id = RecipientAccountId

 Function 2: exclusion rule for use case A (used by single AWS use case)

where aws_account_name != "DevOps" and src_ip != "10.10.10.10"

 

So now we are writing the KQL for the case A which requires the both functions. However it seems no working. In the link: https://docs.microsoft.com/en-us/azure/sentinel/false-positives#modify-the-query it suggested to directly modify the query, but we still want to see if it is possible to keep the exception condition in the function. 

 

Thank you.

@Steven_Su If you want to add Function 2 as part of Function 1 but want to be able to change the aws_account_name and src_ip, you will need to pass those variables into the function and then use the variables in the code.  Something like

let CheckAWS = (account:string, ipaddress: string) {
AWSCloudTrail
| where aws_account_name != account and src_ip != ipaddress
}

 

@Gary Bushey 

Hi Gary,

Thank you very much for your response. I guest maybe I am not explaining the issue clearly.
We want to have 2 separate functions so that we could invoke them in other KQL queries (different AWS use cases) like

Function1
Function2
|summarize ... by ...

The Function 1 doing the data normalization is the same for all the queries while Function 2 is the exclusion and is different case by case. That's why we need to create dedicated function for it. So it seems your suggestion may not suit the requirement.

@Steven_Su If I understand what you need, the first function needs to return a normalized table and then the second function needs to act upon that table.  So something like:

let normalizedTable = Function1();
let response = Function2(normalizedTable);
response

If that is the case then Function2 would need to be able to accept the table that you want to perform the summarize on.  You cannot just start a function with "| summarize".  It can return its own table and then you can just display that table.

 

@Gary Bushey Understood, the answer becomes more clear now. May I further know what would be the structure of the Function2? Because I will also save Function1 and Function2 as functions under "Workspace functions". Could you provide me some ideas how to write the Function2? Thanks!

@Gary Bushey 
Is it possible to pass a tabular argument to a stored function though?

 

I know it is possible with ad-hoc functions defined within the query itself, but I have yet to find a way to do this with stored functions.

https://docs.microsoft.com/en-us/azure/data-explorer/kusto/query/functions/user-defined-functions#in...

 

At least when you use the Azure Portal GUI, it does not appear to be possible to define a parameter of the tabular type, only regular types such as string, long, dynamic etc.

@Jonhed If you look at the Examples section in the URL you listed, it shows how to pass in a table to a function:

let MyFilter = (T:(x:long), v:long) {
  T | where x >= v
};
MyFilter((range x from 1 to 10 step 1), 9)

BTW, the range command returns a table.

@Gary Bushey 

Yes, as I said I know you can do it in ad-hoc functions, where the function is defined within the same query that calls the function. (This is what the example shows)

 

But what @Steven_Su is saying is that he wants to "save Function1 and Function2 as functions under "Workspace functions", meaning you need do it as a stored function.

 

When you save a function, you need to specify the parameters in the dialog, as you can see in the screenshot below, but there is no option to accept a table as a parameter in this case.

There is no mention of how to accept a table in stored functions in the official documents, 

and I have seen other members asking about it, so I am not sure if it is doable in the way @Steven_Su imagines it.

Jonhed_0-1646267700255.png

 

@Jonhed 

Yes, you are correct, it is my question regarding the usage:stareyes:

best response confirmed by Steven_Su (Contributor)
Solution

@Steven_Su OK, now I got it.  Sorry for being so obtuse about this one.  You are right, you cannot save a function that calls a table (seems to be a bit of an oversight if you ask me).   I would suggest following the ASIM model and just create a different function for each table that you need to be normalized for Function 1.

 

If you look at the listing of functions that are available to you, there are a lot that start with _ASim.  Those are the ones that MS created to perform normalization for different tables.

Understood and thank you for your explanation.

@Steven_Su 

Regarding Function 2, it is not exactly what you wanted but you could do it like below.

 

Create function and save as below.

Name: Function2

Parameters:  (string)aws_account_name, (string)src_ip

Function query:

iif(aws_account_name != "DevOps" and src_ip != "10.10.10.10","True","False")

 

Then use it like below.

It is a bit clumsier than what you wanted, but could do the trick.

Function1
| extend Function2 = Function2(aws_account_name,src_ip)
| where Function2 == "True"