Introduction
PowerQueries enable DataSet customers to analyze their search results by applying SQL-esque routines and aggregation to log data. The first line of a PowerQuery (or the first line of each JOIN / UNION sub-table) contains a standard search DataSet search query. We recommend making the initial search as concise as possible to improve the PowerQuery's overall performance. The PowerQuery routines are then applied to the search results and rely upon attributes that were extracted by the parser(s) which are associated with the logs. It should be noted that PowerQueries were designed to provide refined search capabilities and won't replace the niche of a data warehouse.
Similar guidelines for creating efficient SQL queries also apply to PowerQueries. Please see this KB article for more some best practices.
DataSet PowerQueries can readily be accessed through our UI ("Search" -> "PowerQueries") or our HTTP API. Furthermore, PowerQuery Monitors write results back to your account on a defined interval; this preprocessed data can then be used within alerts and dashboards.
Examples
These snippets demonstrate the syntax, logic, and capabilities of PowerQueries relative to real world use cases
Description | PowerQueries | Systems |
Authentication Success from 2 or more countries within 1 hour | outcome_result = "SUCCESS" country = * !(actor_alternateId matches ("^foo@domain.com$", "^system@okta.com$", "^demo@domain.com$", "^0")) !(actorDisplayName matches ("^svc", "^ops")) !(ipAddress matches ("^22.55.\\d+.", "^192.168.[0-2].\d+")) | group countries=estimate_distinct(geographicalContext_country) by actorAlternateId | filter countries > 1 |
Okta |
Lateral Movement RDP |
|join lateral_movement = (EventID in (1149) serverHost contains 'Windows'| columns Source_Network_Address, private_ipv4_address, Computer) on lateral_movement.Source_Network_Address = public_login.private_ipv4_address
|
Windows |
Zscaler User Volume by App | serverHost contains 'zscaler' requestsize = * responsesize = * | group total_requests = sum(requestsize), total_responses = sum(responsesize) by user, appname |let total = total_requests + total_responses |sort -total |
Zscaler |
Zscaler Cloud File Sharing |
parser = 'zia' login matches ".*@.*" reqdatasize = * respdatasize = * appclass = 'File Share' !(appname in ('OneDrive')) | group GB_upload = sum(reqdatasize / (1024*1024*1024)) by login, appname | sort -GB_upload | limit 10 |
Zscaler |
Zscaler Threats |
parser = 'zia' threatname != 'None' threatname = * | group count = count() by threatname, host | sort -count
|
Zscaler |
Zscaler Webmail |
parser = 'zia' login matches ".*@.*" reqdatasize = * respdatasize = * appclass = 'Webmail' !(appname in ('Outlook (Office 365)' )) | group GB_upload = sum(reqdatasize / (1024*1024*1024)) by login, appname | sort -GB_upload | limit 10
|
Zscaler |
Zscaler DDOS outbound |
parser = 'zia' refererhost!="None" respcode in ("301","302","403","404","450","502","503","NA") | filter len(url)>150 |
Zscaler |
Zscaler multi OS |
| inner join all = (parser contains 'zia' ua = * ua != 'Unknown' devicehostname != 'NA' | parse "$ua_os_c{regex=(?:win|andr|linu)}$" from ua | let ua_os=lower(ua_os_c)| filter ua_os matches '(win|andr|linu)'| group count=count(),timestamp=max(timestamp) by cip,devicehostname,location,ua=substr(ua,0,60)), filter = (parser contains 'zia' ua = * ua != 'Unknown' devicehostname != 'NA' | parse "$ua_os_c{regex=(?:win|andr|linu)}$" from ua | let ua_os=lower(ua_os_c) | filter ua_os matches '(win|andr|linu)' | group count_ua_os =estimate_distinct(ua_os),any(ua_os),any(ua) by cip,devicehostname,login,location | filter count_ua_os>=2 | sort - count_ua_os | columns count_ua_os,cip,devicehostname,login ) on filter.cip=all.cip, filter.devicehostname = all.devicehostname | sort - count_ua_os,cip,devicehostname
|
Zscaler |
Zscaler Posture | parser = 'zpa-ustatus' Username=* PosturesMiss matches '72061220064068100' | group count=count(), timestamp=max(timestamp), Hostname=any(Hostname), Platform=any(Platform), PublicIP=any(PublicIP) by PrivateIP, Username | columns timestamp,host=upper(Hostname),user=lower(Username),ip=PrivateIP,Platform,PublicIP
|
Zscaler |
List number of queries and computers by length of DNS query | parser='zia-dns' | group count=count(),computers=estimate_distinct(cip),sample_query=lower(any(req)) by len=len(req) | sort -len |
Zscaler DNS |
Outbytes per login, source IP, sorted by average bytes per destination |
parser='zia-fw' action='Allow' | group count=count(),out_bytes=sum(outbytes),in_bytes=sum(inbytes),destinations=estimate_distinct(sdip),sample_dest=any(sdip) by login,datacenter,csip | let out_per_dest=out_bytes/destinations | sort -out_per_dest |
Zscaler Firewall |
User count by Datacenter over the past 24 hours |
parser='zia' datacenter=* | group dc_users=estimate_distinct(login) by timestamp=timebucket(timestamp,'1hour'),datacenter=substr(datacenter,0,3) | transpose datacenter
|
Zscaler Internet Access |
Event Log Cleared | EventID = 1102 Computer = * | columns timestamp, Computer, Account_Name, Account_Domain, Message |
Windows Event Log |
Public IP Login classify by number of attempts. | EventID in (4624,4625) NOT(Source_Network_Address = "-") Source_Network_Address = * NOT(Source_Network_Address = "0.0.0.0" OR Source_Network_Address matches("^10", "^192\\.168", "^172\\.(1[6-9]|2[0-9]|3[01])",", "^::1")) "An account failed to log on" |group attempts = count() by Source_Network_Address, Account_Name |let score = attempts >= 3 AND attempts <= 7 ? "low" : attempts > 7 AND attempts <= 15 ? "medium" : attempts >= 15 ? "high" : "none" |
Windows Event Log |
Counts by Event ID |
| union ( logfile matches 'windows_event_log_monitor\.log$' | group count=count(),computers=estimate_distinct(Event.System.Computer),sample_log=substr(any(Event.RenderingInfo.Message),0,200) by Event.System.EventID,Event.System.Channel,Event.System.Provider.Name ), ( logfile matches 'windows_event_log_monitor\.log$' | group count=count(),dc=estimate_distinct(Event.System.Computer),sample_log='***Total' ) |
Windows Event Log |
Windows Event Logs (Failed Logon) | Event.System.EventID=4625 | parse "Failure Reason\\:..$FailureReason{regex=.+\\.}$" from Event.RenderingInfo.Message | group timestamp=max(timestamp),count=count(),count_users=estimate_distinct(Event.EventData.Data.SubjectUserName.Text),FailureReason=any(FailureReason), Event.EventData.Data.SubjectDomainName.Text =any(Event.EventData.Data.SubjectDomainName.Text), Event.EventData.Data.SubjectUserName.Text =any(Event.EventData.Data.SubjectUserName.Text),Event.EventData.Data.LogonProcessName.Text=any(Event.EventData.Data.LogonProcessName.Text),Event.EventData.Data.AuthenticationPackageName.Text=any(Event.EventData.Data.AuthenticationPackageName.Text) by Event.System.Computer, Event.EventData.Data.ProcessName.Text, Event.EventData.Data.WorkstationName.Text, Event.EventData.Data.IpAddress.Text | filter Event.EventData.Data.IpAddress.Text!='127.0.0.1' and count!=1 | group timestamp=max(timestamp), count_ips= estimate_distinct(Event.EventData.Data.IpAddress.Text), sample_computer=any(Event.EventData.Data.WorkstationName.Text), sample_srcip=any(Event.EventData.Data.IpAddress.Text), FailureReason= any(FailureReason) , Event.EventData.Data.AuthenticationPackageName.Text= any(Event.EventData.Data.AuthenticationPackageName.Text), Event.EventData.Data.SubjectUserName.Text= any(Event.EventData.Data.SubjectUserName.Text) by Event.EventData.Data.ProcessName.Text |
Windows Event Log |
Query data by serverHost. Identify which serverHosts have a matching event in the first and the last hour of the specified time frame |
tag="slowFutureExecution"
| group first = min(timestamp), last = max(timestamp) by serverHost | columns serverHost, missingInFirstHour = first > querystart() + 3_600_000_000_000, missingInLastHour = last < queryend() - 3_600_000_000_000 |
Dataset
|
Create a burndown of errors (think 99.9% uptime SLA) Define error bucket, success bucket then graph it over time. Gives a solid usecase for measuring service level indicators as a burndown. From a high level, a user would be able to define a few params and would be able to determine how much of their error budget is spent for the time duration set in the dashboard. |
| join a = ( serverHost='Kafka-aa.nginx' |
Access Logs |
Authentication Success from 2 or more countries within 1 hour for VIP user | actor_displayName in ("Abc Xyz", "foo") outcome_result = "SUCCESS" client_geographicalContext_country = * !(actor_displayName in ("Okta System", "svc.okta_ad_agent")) | group countries=estimate_distinct(client_geographicalContext_country) by actor_displayName | filter countries > 1 |
Okta |
Suspicious admin account usage | displayMessage='User accessing Okta admin app'(actor_displayName = 'foo foo' OR actor_displayName = 'Jane Foo') !(client_geographicalContext_country = 'United States' OR client_geographicalContext_country = 'India') serverHost='Okta' | Okta |
Authentication Success from 3 or more states within 1 hour | outcome_result = "SUCCESS" client_geographicalContext_state = * actor_type = "User" actor_displayName != "Portal" !(actor_displayName matches ("^svc$")) !(actor_alternateId matches ("^system@okta.com$", "^svc")) !(client_ipAddress matches ("^192.168", "^10.10.")) !(debugContext_debugData_url matches ("^\\\\/api", "\\\\/oauth2")) | group states=estimate_distinct(client_geographicalContext_state) by actor_alternateId | filter states > 2 |
Okta |
Authentication Success from 4 or more IPs within 1 hour | outcome_result = \"SUCCESS\" AND client_ipAddress = * AND actor_type = \"User\" AND NOT(client_ipAddress contains ( '192.168.')) AND NOT(actor_displayName contains 'svc' ) AND NOT(actor_alternateId contains 'svc') AND NOT(debugContext_debugData_url contains '/api') AND NOT(debugContext_debugData_url contains '/api') AND NOT('/oauth2') | group count = estimate_distinct(client_ipAddress) by actor_alternateId | filter count >= 4 |
Okta |
Bruteforce | outcome_result = "FAILURE" outcome_reason = "LOCKED_OUT" | Okta |
In this query, we are grouping all searches and counting by users to get the number of searches a particular user has done. |
(tag='audit' queryGroupRequest.queries contains '"origin":"SEARCH"' action='launchQuery') OR //Search
(tag='audit' action='launchQuery' queryGroupRequest.queries contains 'type":"PLOT' not (queryGroupRequest.queries contains 'origin":"SEARCH')) //Graph | group searches = count() by user |
Dataset Audit
|
In this query, we are grouping on searches and counting by users to get the number of searches a particular user has done. In order to ensure we are pulling all log searches, we are doing a filter where queryGroupRequest.queries contain Origin and Search. | tag='audit' queryGroupRequest.queries contains '"origin":"SEARCH"' action='launchQuery' | group searches = count() by user |
Dataset Audit |
In this query, we are grouping on searches and counting by users where the queryGroupRequest contains the type of PQ to bring back a count of the Power Query searched by a specific user. | tag='audit' action='launchQuery' queryGroupRequest.queries contains 'type":"PQ' | group searches = count() by user |
Dataset Audit |
In this query, we are grouping and counting the users that are currently logged in. We are pulling in the user field to show this. | action= * user = * (serverHost contains 'meta' || k8s-cluster contains 'meta') |group count() by user | columns user |
Dataset Audit |
In this query, we are grouping so we can count the number of times a dashboard has been loaded by a specific user. We are sorting by timestamp. | tag='audit' action='getDashboard' | sort timestamp | group loads = count(), timestamp = last(timestamp) by user, dashboardName | sort -timestamp | columns user, dashboardName, loads, timestamp |
Dataset Audit |
In this query, we are querying on the last 50 searches, we are using the parse command to extract information contained in the filter and show what was searched by. |
(tag='audit' action='launchQuery' queryGroupRequest.queries contains 'type":"PQ') OR //PQ
(tag='audit' queryGroupRequest.queries contains '"origin":"SEARCH"' action='launchQuery') OR //Search (tag='audit' action='launchQuery' queryGroupRequest.queries contains 'type":"PLOT' not (queryGroupRequest.queries contains 'origin":"SEARCH')) //Graph |parse "\"filter\":\"$filter$\"," from queryGroupRequest.queries | columns user, filter, timestamp |sort -timestamp |limit 50 |
Dataset Audit
|
In this query, we are showing the columns for when an alert was created. | tag='audit' action='createAlert' | columns alert.alertAddresses , alert.description , alert.gracePeriod , alert.renotifyPeriod ,alert.trigger |
Dataset Audit |
In this query we are sorting by timestamp, we are also showing the columns timestamp, laststate, trigger and description to show the last state of an alert along with a description of that alert. (note the use of ternary operator) | tag contains 'alert' lastState= * |sort -timestamp | columns description, trigger , lastState = lastState = 1 ? "Not Triggered" : "Triggered" , timestamp | group timestamp = first(timestamp), description = any(description) by lastState, trigger |sort -timestamp | columns timestamp, lastState, trigger, description |
Dataset Audit |
In this query, we are the Union Command in order to execute two subqueries to show the total number of alerts triggered over a set period of time. | |union ( tag contains 'alert' lastState= * |sort -timestamp | columns description, trigger , lastState = lastState = 1 ? "Not Triggered" : "Triggered" , timestamp | group timestamp = first(timestamp), description = any(description) by lastState, trigger |sort -timestamp | columns timestamp, lastState, trigger, description |filter lastState = "Triggered" ), ( tag contains 'alert' lastState= * |sort -timestamp | columns description, trigger , lastState = lastState = 1 ? "Not Triggered" : "Triggered" , timestamp | group timestamp = first(timestamp), description = any(description) by lastState, trigger |sort -timestamp | columns timestamp, lastState, trigger, description |filter lastState = "Triggered" |group value = count(lastState) |columns lastState = value, timestamp = "Sum of Triggered Alerts" ) |
Dataset Audit |
Latencies p(%n) |
(k8s-cluster contains 'nginx') |group average = average(payload.request_time), p10(payload.request_time), pct(25, payload.request_time), p50(payload.request_time), pct(75, payload.request_time), p95(payload.request_time), pct(99, payload.request_time) by payload.pathname |
Nginx
|
Invocations by user |
event_name=* (serverHost contains 'model' || k8s-cluster contains 'model') | group count=count() by metadata.http_headers.Owner-ID | sort -count | sort -count
|
Nginx
|
Api Functions By Usage |
event_name=* (serverHost contains 'model' || k8s-cluster contains 'model-envelope-usage') | group count=count() by event_name | sort -count
|
Nginx
|
Stacked bar of downstream errors |
serverHost = 'nginx' payload.status != 200 payload.http_user_agent = * | group total = count() by payload.host, timestamp=timebucket('auto') | transpose payload.host
|
Nginx
|
Authentication Success from 1 or more countries within 1 hour for VIP user |
(serverHost contains 'okta' || k8s-cluster contains 'okta') client_geographicalContext_country= * outcome_result = 'SUCCESS' actor_type = 'User' actor_displayName = 'Foo Bar' OR actor_displayName = 'Jane Var' | filter client_ipAddress != '66.44.22.111'|group count = estimate_distinct(client_geographicalContext_country) by actor_displayName, actor_secondaryId | filter count >= 1
|
Okta
|
Authentication Success from 4 or more IPs within 1 hour |
outcome_result = \"SUCCESS\" AND client_ipAddress = * AND actor_type = \"User\" AND NOT(actor_alternateId contains 'svc') AND NOT(debugContext_debugData_url contains '/api') AND NOT(debugContext_debugData_url contains '/api') AND NOT('/oauth2')\n| filter client_ipAddress != '11.11.111.111' |group count = estimate_distinct(client_ipAddress) by actor_secondaryId | filter count >= 4
|
OKta
|
OKTA Bruteforce | (serverHost contains 'okta' || k8s-cluster contains 'okta') outcome_reason = 'LOCKED_OUT' outcome_result = 'FAILURE' | group count = count (outcome_result = 'FAILURE') by actor_alternateId | filter count > 100 (actor_secondaryId = 'it@domain.com' ) |
Okta
|
OKTA User Locked out from selected foreign countries |
outcome_reason = 'LOCKED_OUT' (serverHost contains 'okta') !((((((((((((client_geographicalContext_country = 'Bolivia’ || client_geographicalContext_country = ‘Portugal’) || client_geographicalContext_country = 'Belarus') || client_geographicalContext_country = ‘Venezuela’) || client_geographicalContext_country = 'Spain') || client_geographicalContext_country = 'Syria') || client_geographicalContext_country = ‘North Korea’) || client_geographicalContext_country = ‘Cameroon’) || client_geographicalContext_country = 'Bahrain') || client_geographicalContext_country = ‘Mali’)) | group count = count (outcome_reason = 'LOCKED_OUT') by actor_secondaryId | filter count > 0 |
Okta
|
AD attempt to install a service | (serverHost contains 'Active Directory')\nEVENT_NUMBER = 4697 \n!(FORMAT_MESSAGE contains 'security_tool_a' OR FORMAT_MESSAGE contains 'Rapid' OR FORMAT_MESSAGE contains 'MpKs' OR FORMAT_MESSAGE contains 'CS' OR FORMAT_MESSAGE contains 'Cs' OR FORMAT_MESSAGE contains 'CDPUserSvc' OR FORMAT_MESSAGE contains 'OneSyncSvc' OR FORMAT_MESSAGE contains 'PimIndexMaintenanceSvc' OR FORMAT_MESSAGE contains 'UnistoreSvc' OR FORMAT_MESSAGE contains 'UserDataSvc' OR FORMAT_MESSAGE contains 'WpnUserService') | group count= count(EVENT_NUMBER = 4697) by FORMAT_MESSAGE, CALLER_USER_NAME | filter count >=1 |
Active Directory
|
AD Disabled Account Login Attempts |
(serverHost contains 'Active Directory' || k8s-cluster contains 'Active Directory') ERROR_CODE_TEXT = 'Account disabled, expired, or locked out' EVENT_NUMBER = 4768
| group count= count(EVENT_NUMBER = 4768) by USERNAME
| filter count >=1
|
Active Directory
|
AD Suspicious Failed Logins |
(serverHost contains 'Active Directory' || k8s-cluster contains 'Active Directory') EVENT_NUMBER = 4625
| group count= count(EVENT_NUMBER = 4768) by USERNAME, CLIENT_HOST_NAME
| filter count >=1
|
Active Directory
|
AD Use of Terminated Account |
(serverHost contains 'Active Directory' || k8s-cluster contains 'Active Directory') EVENT_TYPE_TEXT = 'Success' AND ATTRIBUTES_NEW_VALUE matches 'OU=Processed, OU=US Terminated Users, OU=Terminated Users, DC=corp, DC=domain, DC=com'
| group count= count(EVENT_TYPE_TEXT = 'Success') by USERNAME, CLIENT_HOST_NAME
| filter count >=1
|
Active Directory
|
AD user account Created/Enabled by the user without Admin rights (1/15) |
(serverHost contains 'Active Directory' || k8s-cluster contains 'Active Directory') EVENT_NUMBER = 4722 !(CALLER_USER_NAME matches 'ad' OR CALLER_USER_NAME = 'DomJoin' OR CALLER_USER_NAME = 'domainexchange' OR CALLER_USER_NAME = 'svc.cloner')
| group count= count(EVENT_NUMBER = 4722) by ACCOUNT_DISPLAY_NAME, CALLER_DISPLAY_NAME, FORMAT_MESSAGE
| filter count >=1
|
Active Directory
|
AD Admin Account created |
(serverHost contains 'Active Directory' || k8s-cluster contains 'Active Directory') EVENT_NUMBER = 4720 USERNAME matches 'ad.*'
| group count= count(EVENT_NUMBER = 4720) by ACCOUNT_DISPLAY_NAME, CALLER_DISPLAY_NAME, FORMAT_MESSAGE
| filter count >=1
|
Active Directory
|
Comments
0 comments
Article is closed for comments.