ASIM Normalisation in Microsoft Sentinel: A Complete Practical Guide
ASIM Normalisation in Microsoft Sentinel: Write Queries Once, Run Against Any Source
📋 Table of Contents
Every security data source uses its own field names, value formats, and log structures. A Palo Alto firewall calls the source address src; Check Point calls it orig; Windows calls it IpAddress. Without normalisation, writing a detection rule for “suspicious outbound connection” means maintaining a separate version for every data source — a maintenance nightmare that breaks every time a new vendor appears.
The Advanced Security Information Model (ASIM) solves this by providing a unified field naming standard and a parser framework. Write your detection query once against the normalised schema; ASIM parsers automatically translate it to every connected data source.
1. What Is ASIM?
ASIM is Microsoft’s open-source normalisation framework for Microsoft Sentinel. It defines:
- Schemas — standardised field names, types, and value enumerations for each event category (network sessions, authentication, DNS, file events, process creation, etc.)
- Parsers — KQL functions that translate raw vendor log fields into the ASIM schema
- Unifying parsers — a single parser per schema that queries all source-specific parsers simultaneously, returning a unified result set
/ASIM. Community contributions are welcome. Parsers are deployed to your workspace as KQL saved functions.
2. Why ASIM Matters for Detection
Without ASIM, a detection engineer writing a rule to detect port scanning must write separate versions for Palo Alto, Cisco ASA, Check Point, Windows Firewall, and every other connected firewall. Adding a new data source means rewriting every affected rule.
With ASIM, you write one query against imNetworkSession (the network session unifying parser) and it automatically queries all connected firewall and proxy sources simultaneously. New data source? Deploy an ASIM parser for it, and all existing ASIM-based rules immediately cover it.
Without ASIM
With ASIM
Schemas Available
Built-in Parsers
3. Key ASIM Schemas
| Schema | Unifying Parser | Covers |
|---|---|---|
| Network Session | imNetworkSession | Firewalls, proxies, NSG flows, VPC flow logs |
| Authentication | imAuthentication | Sign-in logs, Windows logon events, Linux PAM, VPN auth |
| DNS | imDns | DNS query logs from all sources |
| Process Event | imProcessCreate / imProcessTerminate | Sysmon, Defender for Endpoint, Linux audit |
| File Event | imFileEvent | File creation, modification, deletion across endpoints |
| Registry Event | imRegistryEvent | Windows registry modifications |
| Web Session | imWebSession | Web proxy, application gateway, WAF logs |
| Audit Event | imAuditEvent | Azure AD audit, Linux audit, application audit logs |
4. Working with ASIM Parsers
ASIM parsers are deployed to your Sentinel workspace as saved KQL functions. You call them like any other KQL function — no special syntax required.
// List all ASIM parsers deployed in your workspace
_GetWatchlist('ASimParsers') // Not available this way — use the following:
workspace("your-workspace").functions
| where Name startswith "im" or Name startswith "vim" or Name startswith "ASim"
// Call a unifying parser — queries ALL connected sources
imNetworkSession
| where TimeGenerated > ago(1h)
| where DstPortNumber == 445 // SMB
| where NetworkDirection == "Outbound"
| summarize Count = count() by SrcIpAddr, DstIpAddr, DstPortNumber
| order by Count desc
// Scoped parser — query only a specific source
vimNetworkSessionPaloAltoCEF
| where TimeGenerated > ago(1h)
| where DstPortNumber in (22, 23, 3389)
| project TimeGenerated, SrcIpAddr, DstIpAddr, DstPortNumber, NetworkAction
im parsers in analytic rules for maximum coverage.
5. Writing ASIM-Normalised Queries
ASIM schemas define standard field names. Here are the most important ones across schemas you should memorise:
| Category | ASIM Field | Meaning |
|---|---|---|
| Network | SrcIpAddr / DstIpAddr | Source / Destination IP |
| Network | SrcPortNumber / DstPortNumber | Source / Destination port |
| Network | NetworkAction | Allow / Deny / Drop |
| Network | SrcBytes / DstBytes | Bytes transferred |
| Auth | ActorUsername | The authenticating user |
| Auth | EventResult | Success / Failure |
| Auth | SrcIpAddr | Source IP of authentication |
| Process | ActingProcessName | Parent process name |
| Process | TargetProcessName | Child/spawned process name |
| Process | ActorUsername | User who initiated the process |
Detection: Brute force across all auth sources
// Works against ALL connected auth sources simultaneously
imAuthentication
| where TimeGenerated > ago(1h)
| where EventResult == "Failure"
| summarize
FailureCount = count(),
TargetUsers = make_set(TargetUsername),
SrcIPs = make_set(SrcIpAddr)
by ActorUsername, bin(TimeGenerated, 5m)
| where FailureCount > 20
| order by FailureCount desc
Detection: DNS beaconing
// DNS queries to same domain at suspiciously regular intervals
imDns
| where TimeGenerated > ago(24h)
| where EventType == "Query"
| summarize
QueryCount = count(),
UniqueIPs = dcount(SrcIpAddr),
FirstQuery = min(TimeGenerated),
LastQuery = max(TimeGenerated)
by DnsQuery = tostring(DnsQueryName)
| extend DurationHours = datetime_diff('hour', LastQuery, FirstQuery)
| where QueryCount > 100 and UniqueIPs == 1
| order by QueryCount desc
Detection: Suspicious child process spawned
// Powershell or cmd spawned by Office apps — common phishing indicator
imProcessCreate
| where TimeGenerated > ago(24h)
| where ActingProcessName has_any ("winword.exe","excel.exe","outlook.exe","powerpnt.exe")
| where TargetProcessName has_any ("powershell.exe","cmd.exe","wscript.exe","mshta.exe","regsvr32.exe")
| project TimeGenerated, ActorUsername, Dvc,
ParentProcess = ActingProcessName,
ChildProcess = TargetProcessName,
CommandLine = TargetProcessCommandLine
6. Building Custom ASIM Parsers
If you have a data source without an existing ASIM parser, you can build one. A parser is simply a KQL saved function that maps your raw table fields to ASIM schema fields.
// Example: Custom ASIM Network Session parser for a custom firewall table
// Save this as a KQL function named: vimNetworkSessionMyFirewall
let vimNetworkSessionMyFirewall = (
starttime: datetime = datetime(null),
endtime: datetime = datetime(null)
) {
MyFirewallLogs
| where TimeGenerated > ago(1d)
| where (isnull(starttime) or TimeGenerated >= starttime)
| where (isnull(endtime) or TimeGenerated <= endtime)
| project
TimeGenerated,
EventVendor = "MyVendor",
EventProduct = "MyFirewall",
EventSchema = "NetworkSession",
SrcIpAddr = src_ip,
SrcPortNumber = toint(src_port),
DstIpAddr = dst_ip,
DstPortNumber = toint(dst_port),
NetworkAction = case(action == "permit", "Allow", action == "deny", "Deny", "Unknown"),
NetworkProtocol = toupper(protocol),
SrcBytes = tolong(bytes_in),
DstBytes = tolong(bytes_out)
};
vimNetworkSessionMyFirewall
7. Best Practices
- Always use
imunifying parsers in analytic rules — never query raw tables directly for cross-source detections - Include
starttimeandendtimeparameters in your parser calls to enable time scoping - Validate your parsers against the ASIM schema checker before deploying to production
- Contribute custom parsers back to the Sentinel GitHub — the community benefits and Microsoft may officially adopt them
- When a built-in parser does not exist for your source, file a GitHub issue and build the
vimversion yourself as a bridge
