Kusto user-defined function for common actions

%3CLINGO-SUB%20id%3D%22lingo-sub-1588828%22%20slang%3D%22en-US%22%3EKusto%20user-defined%20function%20for%20common%20actions%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1588828%22%20slang%3D%22en-US%22%3E%3CP%3EI'm%20looking%20to%20leverage%20common%20functions%20across%20a%20number%20of%20queries%20so%20we%20can%20update%20in%20one%20place%20rather%20than%20in%20every%20analytic.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EFirst%20question%2C%20would%20it%20be%20possible%20to%20have%20a%20function%20that%20just%20defines%20a%20dynamic%20variable%20that%20can%20be%20used%20in%20other%20analytics.%20E.g.%20a%20function%20that%20defines%20a%20list%20and%20saved%20as%20lb_primaries.%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-powerquery%22%3E%3CCODE%3Elet%20lb_primaries%20%3D%20dynamic(%5B%22127.0.0.1%22%2C%20%22127.0.0.2%22%2C%20%22127.0.0.3%22%5D)%3B%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3EThis%20way%20we%20could%20reference%20lb_primaries%20in%20a%20number%20of%20analytics%2C%20but%20only%20update%20in%20a%20single%20place%3F%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EThe%20second%20question%2C%20using%20an%20example%20of%20a%20basic%20lookup%20(I'm%20aware%20of%20externaldata)%20where%20we%20can%20return%20a%20true%20or%20false%20based%20on%20the%20input.%20E.g.%20is_primary_fn%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-powerquery%22%3E%3CCODE%3Elet%20is_primary%20%3D%20(ip%3Astring)%20%7B%0A%20%20%20%20iif(dynamic(%5B%0A%20%20%20%20%20%20%20%20%22127.0.0.1%22%2C%0A%20%20%20%20%20%20%20%20%22127.0.0.2%22%2C%0A%20%20%20%20%20%20%20%20%22127.0.0.3%22%0A%20%20%20%20%5D)%20contains%20ip%2C%20true%2C%20false)%0A%7D%3B%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3EThen%20using%20that%20with%20a%20query%20like%3A%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-powerquery%22%3E%3CCODE%3ENetworkData%0A%7C%20where%20is_primary_fn(IPAddress)%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3EWhich%20in%20this%20example%20fails%20with%20%22%3CSPAN%3EBody%20of%20the%20callable%20expression%20cannot%20be%20empty%22.%20I've%20tried%20a%20few%20different%20way%20to%20get%20this%20working%20but%20so%20far%20not%20having%20any%20luck%20%3A(%3C%2Fimg%3E%3C%2FSPAN%3E%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-1589961%22%20slang%3D%22en-US%22%3ERe%3A%20Kusto%20user-defined%20function%20for%20common%20actions%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1589961%22%20slang%3D%22en-US%22%3EAs%20far%20as%20I%20know%2C%20you%20should%20invoke%20the%20function.%3CBR%20%2F%3ENetworkData%3CBR%20%2F%3E%7C%20invoke%20is_primary(tostring(IPAddress))%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-1596034%22%20slang%3D%22en-US%22%3ERe%3A%20Kusto%20user-defined%20function%20for%20common%20actions%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1596034%22%20slang%3D%22en-US%22%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F456796%22%20target%3D%22_blank%22%3E%40pemontto%3C%2FA%3E%26nbsp%3B%3C%2FP%3E%3CP%3Ei%20have%20same%20issue.%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-1596105%22%20slang%3D%22en-US%22%3ERe%3A%20Kusto%20user-defined%20function%20for%20common%20actions%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1596105%22%20slang%3D%22en-US%22%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F456796%22%20target%3D%22_blank%22%3E%40pemontto%3C%2FA%3E%26nbsp%3Bthe%20below%20query%20works%20without%20any%20issue%20for%20me.%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-sql%22%3E%3CCODE%3Elet%20NetworkData%20%3D%20datatable%20(Address%3Astring%20)%0A%5B%0A%22127.0.0.1%22%0A%5D%3B%0Alet%20is_primary%20%3D%20(ip%3Astring)%20%7B%0A%20%20%20%20iif(dynamic(%5B%0A%20%20%20%20%20%20%20%20%22127.0.0.1%22%2C%0A%20%20%20%20%20%20%20%20%22127.0.0.2%22%2C%0A%20%20%20%20%20%20%20%20%22127.0.0.3%22%0A%20%20%20%20%5D)%20contains%20ip%2C%20true%2C%20false)%0A%7D%3B%0ANetworkData%0A%7C%20where%20is_primary(Address)%20%3D%3D%20%22true%22%3C%2FCODE%3E%3C%2FPRE%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-1596113%22%20slang%3D%22en-US%22%3ERe%3A%20Kusto%20user-defined%20function%20for%20common%20actions%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1596113%22%20slang%3D%22en-US%22%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F666494%22%20target%3D%22_blank%22%3E%40Cyb3rMonk%3C%2FA%3E%26nbsp%3B%3C%2FP%3E%3CP%3EDid%20you%20try%20saving%20the%20function%20under%20KQL%20queries%2C%20then%20invoking%20it%20remotely%20from%20a%20KQL%20editor%20window%20%3F%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EIn%20my%20case%2C%20if%20all%20code%20is%20together%2C%20like%20in%20the%20snippet%20you%20shared%2C%20it%20works.%20IF%20i%20save%20the%20func%20and%20invoke%20it%2C%20it%20won't%20work.%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-1596133%22%20slang%3D%22en-US%22%3ERe%3A%20Kusto%20user-defined%20function%20for%20common%20actions%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1596133%22%20slang%3D%22en-US%22%3ELooks%20like%20the%20only%20way%20to%20create%20a%20parameterized%20function%20is%20to%20use%20resource%20templates.%3CBR%20%2F%3E%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Fazure-monitor%2Fsamples%2Fresource-manager-log-queries%23parameterized-function%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%22%3Ehttps%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fazure%2Fazure-monitor%2Fsamples%2Fresource-manager-log-queries%23parameterized-function%3C%2FA%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-1596644%22%20slang%3D%22en-US%22%3ERe%3A%20Kusto%20user-defined%20function%20for%20common%20actions%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1596644%22%20slang%3D%22en-US%22%3E%3CP%3E%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F666494%22%20target%3D%22_blank%22%3E%40Cyb3rMonk%3C%2FA%3E%26nbsp%3Bwhich%20aren't%20supported%20in%20Log%20Analytics%20yet%20right%3F%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EAnd%20yes%2C%20I%20could%20get%20the%20functions%20working%20in%20the%20same%20query%20but%20as%26nbsp%3B%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2Fuser%2Fviewprofilepage%2Fuser-id%2F540154%22%20target%3D%22_blank%22%3E%40majo01%3C%2FA%3E%26nbsp%3Bsaid%2C%20never%20when%20saved%20as%20a%20function%20to%20use%20in%20another%20query.%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-1597011%22%20slang%3D%22en-US%22%3ERe%3A%20Kusto%20user-defined%20function%20for%20common%20actions%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-1597011%22%20slang%3D%22en-US%22%3EI%20didn't%20try%20it%20but%20if%20you%20look%20at%20the%20template%20on%20the%20page%2C%20it%20uses%20log%20analytic%20workspaces.%20Azure%20monitor%20is%20based%20on%20log%20analytics%20as%20well.%3C%2FLINGO-BODY%3E
Contributor

I'm looking to leverage common functions across a number of queries so we can update in one place rather than in every analytic.

 

First question, would it be possible to have a function that just defines a dynamic variable that can be used in other analytics. E.g. a function that defines a list and saved as lb_primaries.

let lb_primaries = dynamic(["127.0.0.1", "127.0.0.2", "127.0.0.3"]);

This way we could reference lb_primaries in a number of analytics, but only update in a single place?

 

The second question, using an example of a basic lookup (I'm aware of externaldata) where we can return a true or false based on the input. E.g. is_primary_fn

let is_primary = (ip:string) {
    iif(dynamic([
        "127.0.0.1",
        "127.0.0.2",
        "127.0.0.3"
    ]) contains ip, true, false)
};

Then using that with a query like:

NetworkData
| where is_primary_fn(IPAddress)

Which in this example fails with "Body of the callable expression cannot be empty". I've tried a few different way to get this working but so far not having any luck :(

7 Replies
As far as I know, you should invoke the function.
NetworkData
| invoke is_primary(tostring(IPAddress))

@pemontto 

i have same issue.

@pemontto the below query works without any issue for me.

let NetworkData = datatable (Address:string )
[
"127.0.0.1"
];
let is_primary = (ip:string) {
    iif(dynamic([
        "127.0.0.1",
        "127.0.0.2",
        "127.0.0.3"
    ]) contains ip, true, false)
};
NetworkData
| where is_primary(Address) == "true"

@mergene 

Did you try saving the function under KQL queries, then invoking it remotely from a KQL editor window ?

 

In my case, if all code is together, like in the snippet you shared, it works. IF i save the func and invoke it, it won't work.

Looks like the only way to create a parameterized function is to use resource templates.
https://docs.microsoft.com/en-us/azure/azure-monitor/samples/resource-manager-log-queries#parameteri...

@mergene which aren't supported in Log Analytics yet right?

 

And yes, I could get the functions working in the same query but as @majo01 said, never when saved as a function to use in another query.

I didn't try it but if you look at the template on the page, it uses log analytic workspaces. Azure monitor is based on log analytics as well.