You will write the same baseline comparison, the same entity enrichment, and the same VPN exclusion logic across dozens of hunts. Copy-pasting queries between campaigns introduces errors and makes updates painful. KQL user-defined functions and Sentinel saved queries let you write the logic once and reference it everywhere — keeping your hunt queries clean and your exclusion lists maintainable.
Deliverable: The ability to create and use KQL functions for hunting — including per-user IP baselines, VPN exclusion lists, and role-based entity lookups — and to manage them through Sentinel saved queries or Advanced Hunting custom functions.
⏱ Estimated completion: 25 minutes
Write once, hunt many times
Three patterns appear in nearly every campaign module:
The per-user IP baseline (TH4, TH6, TH7, TH10, TH13)
The VPN/proxy exclusion filter (TH4, TH5, TH8, TH13)
The admin account identification (TH6, TH7, TH8)
Writing these from scratch in each hunt wastes time and introduces inconsistency. A function encapsulates the logic. The hunt query calls the function.
Inline functions with let
KQL supports inline function definitions using let. These exist only for the duration of the query — they are not persisted.
Admin account identification: Reused across TH6, TH7, TH8, TH13 — some hunts need to include admins, others need to exclude them.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Saveasfunction:HuntGetAdminUsers//Noparameters—returnscurrentadminroleholdersIdentityInfo|whereTimeGenerated>ago(1d)|whereAssignedRoleshas_any("Global Administrator","Exchange Administrator","Security Administrator","Conditional Access Administrator","Privileged Role Administrator","User Administrator")|distinctAccountUPN,AssignedRoles//IfIdentityInfoisnotingested,alternative://AuditLogs→extractuserswithactiveroleassignments//Useinhunts://Excludeadmins:|whereUserPrincipalName!in(HuntGetAdminUsers())//Targetadminsonly:|whereUserPrincipalNamein(HuntGetAdminUsers())
Function maintenance discipline
Functions rot the same way detection rules do. The IP baseline function built against a 30-day window when your organization had 500 users may need adjustment when you grow to 2,000 — the make_set limits and performance characteristics change.
Review saved functions quarterly alongside your detection rule maintenance cycle:
Are the functions still called by active hunts? (Orphaned functions = dead code)
Are the parameters still appropriate? (Baseline windows, thresholds)
Are the underlying table schemas unchanged? (Microsoft may rename columns)
Are the watchlists referenced by functions still current? (VPN IPs, admin accounts)
Watchlists for exclusion data
VPN IP ranges, known admin accounts, and approved application lists change over time. Hardcoding them in queries means updating every query when the list changes. Sentinel watchlists store the lists centrally.
//WatchlistforapprovedOAuthapplications(TH5/TH6/TH11exclusion)//Applicationsonthislistarepre-approvedandshouldnot//triggerOAuthconsenthuntingalertsletapprovedApps=_GetWatchlist('HuntExclusions_ApprovedApps')|projectAppName;AuditLogs|whereTimeGenerated>ago(90d)|whereOperationName=="Consent to application"|extendAppName=tostring(TargetResources[0].displayName)|whereAppName!in(approvedApps)//OnlyconsentsforUNAPPROVEDapplications—thehuntingtargets//Reviewandupdatetheapprovedappswatchlistquarterly//Newlegitimateappsgetadded;deprecatedappsgetremoved//Eachadditionshouldbedocumentedwithbusinessjustification
Figure TH2.13 — Three mechanisms for reusable hunting components. Inline for single queries, saved functions for persistent logic, watchlists for updatable data.
Try it yourself
Exercise: Create a VPN exclusion watchlist
Identify your organization's VPN egress IP ranges. Create a Sentinel watchlist named "HuntExclusions_VPNIPs" with columns IPRange and Description. Add 3–5 IP ranges.
Then run one of the authentication baseline queries from TH2.1 with the watchlist exclusion. Compare the result count with and without the exclusion — the reduction shows how many false positives the watchlist eliminates. This watchlist will be used in TH4, TH5, and every authentication-based campaign.
⚠ Compliance Myth: "Saved functions are a security risk — they could be modified to hide threats"
The myth: A malicious insider could modify a saved function or watchlist to exclude attacker IPs from all hunt queries — creating a blind spot.
The reality: This is a legitimate concern, not a myth. Saved functions and watchlists should be managed with the same change control as detection rules. Restrict write access to the Log Analytics workspace. Audit changes to functions and watchlists through Azure Activity Logs. Review exclusion lists quarterly to confirm each entry is still valid. The risk is real but manageable — and the alternative (hardcoded exclusions in every query) is harder to audit because the exclusions are scattered across dozens of queries rather than centralized in one reviewable list.
Extend this approach
Advanced Hunting in Defender XDR supports custom functions through the "Custom functions" feature. These persist across sessions and are available to all analysts with access to the portal. If your primary hunting platform is Defender XDR rather than Sentinel, use custom functions there instead of Sentinel saved functions. The KQL syntax is identical. TH16 covers function management across both platforms.
The full course continues with advanced topics, production detection rules, worked investigation scenarios, and deployable artifacts. Premium subscribers get access to all courses.