PantherFlow Statements
There are two types of PantherFlow query statements
Overview
A PantherFlow query is composed of one or more statements. There are two types of statements: tabular expression statements and let
statements.
Multiple statements in the same query must be separated by semicolons (;
). The final statement does not need a semicolon.
Tabular expression statements
A tabular expression statement is usually what comes to mind when you hear "piped query"—it's composed of a data source and typically one or more operators, separated by the pipe character (|
). Each operator takes in data, performs its operation, then passes the transformed data on to the next operator.
panther_logs.public.aws_cloudtrail
| where accountId != '1234567'
| summarize Count=agg.count() by eventName
| extend tooHigh = Count > 100
| where tooHigh
| sort Count
| limit 10
Data sources
Each PantherFlow query must specify a data source. It's possible to use any of the following as the data source:
Tabular expression statement
let
statements
let
statementsA let
statement assigns a value to a variable that can be used in subsequent statements. let
statements can define two types of variables:
Table variable: Represents a tabular expression, and can be later used like a table
Scalar variable: Represents a scalar value, or an expression that evaluates to a scalar value
Variables must be defined in a let
statement before they are referenced. All let
statements must end in a semicolon (;
) when followed by another statement.
Table variables
A table variable is created when you assign a tabular expression statement to a variable using let
. It can then be referred to like you would a tabular expression statement (i.e., it is one of the possible data sources).
The tabular expression statement assigned to a table variable is not executed until the variable is referred to, or "called," in a subsequent statement.
Naming your query with a table variable can be useful when you:
Don't want to write out the same query more than once
Want to make it easier for others to understand what your query is doing
Examples
The following example declares a table variable, elbOK
. Below, elbOK
is executed exactly as it is defined in the let
statement:
let elbOK = panther_logs.public.aws_alb
| where elbStatusCode == 200;
elbOK
Here, an additional operator is applied to elbOK
within the tabular expression statement:
let elbOK = panther_logs.public.aws_alb
| where elbStatusCode == 200;
elbOK
| where p_event_time > time.ago(1h)
You can use table variables wherever you would refer to a dataset, including with the union
operator:
let ec2Events = panther_logs.public.aws_cloudtrail
| where p_event_time > time.ago(1h)
| where eventSource == "ec2.amazonaws.com";
let s3Events = panther_logs.public.aws_cloudtrail
| where p_event_time > time.ago(1h)
| where eventSource == "s3.amazonaws.com";
union ec2Events, s3Events
| summarize count=agg.count() by eventName, eventSource
| sort count desc
Scalar variables
A scalar variable is created when you assign a non-tabular expression to a variable. Scalar variables can then be referenced throughout subsequent queries.
Declaring scalar variables can be useful when you want to:
Make your queries more readable and maintainable, especially when using the same value multiple times
Name values that would otherwise be misunderstood
Take note of the scalar variable limitations, below.
Examples
The following example declares a scalar variable, threshold
, then references it in a where
clause:
let threshold = 100;
panther_logs.public.aws_cloudtrail
| where p_event_time > time.ago(1d)
| summarize count=agg.count() by eventName
| where count > threshold
The value of an scalar variable can use a function, and a scalar variable can be used as a parameter to a function:
let domain = "example.com";
let searchSuffix = strings.cat("@", domain);
panther_logs.public.aws_cloudtrail
| where strings.ends_with(userIdentity.principalId, searchSuffix)
| summarize count=agg.count() by eventName
You can also use arithmetic expressions with scalar variables:
let hourInSeconds = 60 * 60;
let dayInSeconds = hourInSeconds * 24;
let weekInSeconds = dayInSeconds * 7;
panther_logs.public.aws_cloudtrail
| extend ageInSeconds = time.diff("s", p_event_time, time.now())
| extend ageCategory = case(
ageInSeconds < hourInSeconds, "Less than an hour",
ageInSeconds < dayInSeconds, "Less than a day",
ageInSeconds < weekInSeconds, "Less than a week",
"More than a week"
)
| summarize count=agg.count() by ageCategory
You can combine scalar and table variables for powerful queries:
// Scalar variables
let minSeverity = 3;
let timeRange = 7d;
let criticalServices = ["ec2.amazonaws.com", "iam.amazonaws.com", "s3.amazonaws.com"];
// Table variable
let baseQuery = panther_logs.public.aws_cloudtrail
| where p_event_time > time.ago(timeRange)
| where eventSource in criticalServices;
// Table variable
let failedActions = baseQuery
| where errorCode != ""
| extend severity = case(
errorCode == "AccessDenied", 4,
errorCode == "UnauthorizedOperation", 3,
strings.starts_with(errorCode, "Client"), 2,
1
);
// Tabular expression statement
failedActions
| where severity >= minSeverity
| summarize count=agg.count() by eventSource, errorCode, severity
| sort severity desc, count desc
Variable naming rules
Variable names must adhere to the following rules:
The first character must be a letter, underscore (
_
), or dollar sign ($
).Characters after the first character must be letters, numbers, or underscores.
Existing table names cannot be used as variable names.
For example, if a table already exists named
aws_cloudtrail
, you cannot useaws_cloudtrail
as a variable name.
Once a variable name has been used, it cannot be used again in the same PantherFlow search. That is, variables cannot be redefined.
Examples
myVar123
123myVar
(starts with number)
my_var
my-var
(invalid character)
_my_var
my.var
(invalid character)
$my_var
my_var$
($
only allowed as first character)
Last updated
Was this helpful?