Query Details
let query_frequency = 1h;
let query_period = 14d;
let _ShellApps = toscalar(
_GetWatchlist("UUID-EntraIdApps")
| where Notes has "[PowerShell]"
| summarize make_list(AppId)
);
// let _HomeTenantId = toscalar(
// _GetWatchlist("UUID-EntraIdTenantIds")
// | where Notes has "[HomeTenant]"
// | summarize make_list(TenantId)
// );
let _ExpectedResultTypes = toscalar(
_GetWatchlist("ResultType-SignInLogsErrorCodes")
| where isnotempty(ResultDescription) and not(Notes has_any ("[Success]", "[Expired]"))
| summarize make_list(ResultType)
);
let MicrosoftAzureCLI_appid = "04b07795-8ddb-461a-bbee-02f9e1bf7b46";
let _MicrosoftAzureCLIExpectedResultTypes = toscalar(
_GetWatchlist("ResultType-SignInLogsErrorCodes")
| where (Notes has_all ("[Success]", "[Interrupt]") and Notes has_any ("[MFA]", "[NotCompliant]")) or (Notes has_all ("[Token]", "[Expired]"))
| summarize make_list(ResultType)
);
let _ExpectedADGroups = toscalar(
_GetWatchlist("RegEx-PrivDomainGroups")
| where Notes has "[PowerShell]"
| summarize RegEx = make_list(RegEx)
| extend RegEx = strcat(@'^(', strcat_array(RegEx, '|'), @')$')
);
let _ExpectedAADRoles = toscalar(
_GetWatchlist("RegEx-PrivAADRoles")
| where Notes has "[PowerShell]"
| summarize RegEx = make_list(RegEx)
| extend RegEx = strcat(@'^(', strcat_array(RegEx, '|'), @')$')
);
let _Users =
IdentityInfo
| where TimeGenerated between (ago(query_period) .. ago(query_frequency))
| summarize arg_max(TimeGenerated, *) by AccountObjectId
| project-away TimeGenerated
;
let _ExpectedUsers =
_Users
| mv-expand GroupMembership, AssignedRoles
| where GroupMembership matches regex _ExpectedADGroups or AssignedRoles matches regex _ExpectedAADRoles
| distinct AccountObjectId
;
union SigninLogs, AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(query_frequency)
| where AppId in (_ShellApps)
//| where ResourceTenantId in (_HomeTenantId) // Remove events where the accessed resource is not from our tenant
| where not(ResultType in (_ExpectedResultTypes))
| where not(UserId in (_ExpectedUsers))
// Remove unsuccessful guest signins from app "Microsoft Azure CLI", when they use this app we assume a signin event happens in all tenants where they are a guest
| where not(AppId == MicrosoftAzureCLI_appid and ResultType in (_MicrosoftAzureCLIExpectedResultTypes))// and (UserType != "Member" or not(HomeTenantId in (_HomeTenantId))))
| summarize arg_max(TimeGenerated, *) by UserId, SessionId, ResultType, IPAddress, AppDisplayName, AppId, ResourceDisplayName, ResourceIdentity
| extend DeviceDetail = coalesce(tostring(DeviceDetail_dynamic), DeviceDetail_string)
| summarize
StartTime = min(TimeGenerated),
EndTime = max(TimeGenerated),
Resources = make_set(ResourceDisplayName),
SessionIds = make_set(SessionId),
CorrelationIds = make_set(CorrelationId),
ResultTypes = make_set(ResultType),
take_anyif(UserPrincipalName, not(UserPrincipalName == UserId)),
take_any(Location, ClientAppUsed, UserAgent, AuthenticationDetails, RiskEventTypes, RiskLevelDuringSignIn, RiskLevelAggregated, DeviceDetail)
by UserId, IPAddress, AppDisplayName, AppId
| join kind=leftouter _Users on $left.UserId == $right.AccountObjectId
| extend UserPrincipalName = tolower(coalesce(UserPrincipalName, MailAddress, AccountUPN))
| project
StartTime,
EndTime,
UserPrincipalName,
AccountDisplayName,
IPAddress,
Location,
ResultTypes,
AppDisplayName,
Resources,
ClientAppUsed,
DeviceDetail,
UserAgent,
UserType,
GroupMembership,
AssignedRoles,
AccountSID,
OnPremisesDistinguishedName,
MailAddress,
JobTitle,
Department,
Manager,
UserAccountControl,
RiskLevel,
RiskState,
AuthenticationDetails,
SessionIds,
CorrelationIds,
RiskEventTypes,
RiskLevelDuringSignIn,
RiskLevelAggregated,
AppId,
UserId
This KQL (Kusto Query Language) query is designed to analyze and summarize sign-in activities related to specific applications and user roles within a given time frame. Here's a simplified breakdown of what the query does:
Define Time Frames:
query_frequency is set to 1 hour, meaning the query looks at data from the last hour.query_period is set to 14 days, which is used to gather user information over the past 14 days.Identify Relevant Applications and Roles:
_ShellApps: Retrieves a list of application IDs related to PowerShell from a watchlist._ExpectedResultTypes: Retrieves a list of error codes from sign-in logs that are not considered successful or expired._MicrosoftAzureCLIExpectedResultTypes: Retrieves specific error codes for the "Microsoft Azure CLI" application that indicate unsuccessful sign-ins._ExpectedADGroups and _ExpectedAADRoles: Retrieve regular expressions for identifying privileged domain groups and Azure AD roles related to PowerShell.Gather User Information:
_Users: Collects the most recent information about users from the past 14 days._ExpectedUsers: Filters users who belong to specific privileged groups or roles.Filter Sign-In Logs:
_ShellApps.Summarize and Join Data:
Output:
Overall, this query is used to identify and summarize unusual or unexpected sign-in activities related to specific applications and roles, helping to detect potential security issues or unauthorized access attempts.

Jose Sebastián Canós
Released: July 30, 2025
Tables
Keywords
Operators