Multiple Unexpected Account Using A Power Shell App In Entra ID
Query
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,
UserIdExplanation
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_frequencyis set to 1 hour, meaning the query looks at data from the last hour.query_periodis 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._ExpectedADGroupsand_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:
- The query examines sign-in logs from the past hour for applications identified in
_ShellApps. - It excludes events with expected error codes and users who are part of expected groups or roles.
- It also filters out unsuccessful guest sign-ins for the "Microsoft Azure CLI" application.
- The query examines sign-in logs from the past hour for applications identified in
-
Summarize and Join Data:
- The query summarizes the filtered sign-in events, grouping them by user and session details.
- It joins this data with user information to enrich the results with additional user attributes.
-
Output:
- The final result includes details such as start and end times of sign-in events, user principal names, IP addresses, application names, and various user attributes like job title and department.
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.
Details

Jose Sebastián Canós
Released: July 30, 2025
Tables
UUID-EntraIdAppsResultType-SignInLogsErrorCodesRegEx-PrivDomainGroupsRegEx-PrivAADRolesIdentityInfoSigninLogsAADNonInteractiveUserSignInLogs
Keywords
QueryFrequencyPeriodShellAppsAppIdResultTypesMicrosoftAzureCLIExpectedADGroupsAADRolesUsersIdentityInfoSigninLogsNonInteractiveUserSignInTimeGeneratedSessionIPAddressDisplayNameResourceDeviceDetailPrincipalLocationClientUsedAgentAuthenticationDetailsRiskEventLevelDuringAggregatedAccountObjectTypeGroupMembershipAssignedSIDOnPremisesDistinguishedMailJobTitleDepartmentManagerControlStateCorrelationIds
Operators
lettoscalar_GetWatchlisthassummarizemake_listisnotemptynothas_anyhas_allstrcatstrcat_arraybetweenagoarg_maxproject-awaymv-expandmatchesregexdistinctunionin==coalescetostringdynamicstringtake_anyifjoinkindleftouterontolowerproject