Multiple Activity From Infrequent Country
Query
let query_frequency = 5m;
let query_period = 2d;
AADUserRiskEvents
| where TimeGenerated > ago(query_period)
| where Source == "MicrosoftCloudAppSecurity" and RiskEventType == "newCountry"
| summarize FirstTimeGenerated = min(TimeGenerated), arg_max(TimeGenerated, *) by Id
| where FirstTimeGenerated > ago(query_frequency)
| mv-apply Auxiliar = AdditionalInfo on (
summarize AdditionalInfoBag = make_bag(bag_pack(tostring(Auxiliar["Key"]), Auxiliar["Value"]))
)
| extend VendorOriginalId = tostring(split(tostring(AdditionalInfoBag["alertUrl"]), "/")[-1])
| project
//TimeGenerated,
ActivityDateTime,
DetectedDateTime,
Source,
Activity,
DetectionTimingType,
UserDisplayName,
UserPrincipalName,
UserId,
IpAddress,
RequestId,
CorrelationId,
TokenIssuerType,
RiskEventType,
RiskDetail,
RiskLevel,
RiskState,
AdditionalInfo,
Id,
VendorOriginalId
| extend AltAlertLink = strcat("https://entra.microsoft.com/#blade/Microsoft_AAD_IAM/RiskDetectionsBlade/riskState~/[]/userId/", UserId, "/riskLevel/[]/daysBack/90")// Someone wrote "90s" incorrectly in Defender XDR portal
| join kind=leftouter (
SecurityAlert
| where ProviderName == "MCAS" and ProductName == "Microsoft Cloud App Security"// and AlertType == "MCAS_ALERT_ANUBIS_???"
| summarize arg_max(TimeGenerated, *) by VendorOriginalId
| project
AlertName,
AlertSeverity,
Description,
AlertStatus = Status,
Entities,
ExtendedProperties,
VendorName,
ProviderName,
ProductName,
ProductComponentName,
RemediationSteps,
Tactics,
Techniques,
SubTechniques,
VendorOriginalId,
SystemAlertId,
CompromisedEntity,
AlertLink
) on VendorOriginalId
| extend
AlertLink = coalesce(AlertLink, AltAlertLink),
ExtendedPropertiesDynamic = todynamic(ExtendedProperties)
| as _Events
| lookup kind=leftouter (
union SigninLogs, AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(query_period)
//| where RiskEventTypes_V2 has "newCountry"
| where OriginalRequestId in (toscalar(_Events | summarize make_list(RequestId)))
| extend TimeReceived = _TimeReceived
| summarize arg_max(TimeReceived, *) by OriginalRequestId
| invoke UnifySignInLogs()
| project
TimeGenerated,
CreatedDateTime,
Type,
//UserDisplayName,
//UserPrincipalName,
//UserId,
AlternateSignInName,
SignInIdentifier,
UserType,
IPAddress,
AutonomousSystemNumber,
Location,
NetworkLocationDetails,
ResultType,
ResultSignature,
ResultDescription,
ClientAppUsed,
AppDisplayName,
ResourceDisplayName,
DeviceDetail,
UserAgent,
Status,
MfaDetail,
AuthenticationContextClassReferences,
AuthenticationDetails,
AuthenticationProcessingDetails,
AuthenticationProtocol,
AuthenticationRequirement,
AuthenticationRequirementPolicies,
SessionLifetimePolicies,
//TokenIssuerType,
IncomingTokenType,
TokenProtectionStatusDetails,
ConditionalAccessStatus,
ConditionalAccessPolicies,
SignInLogs_RiskDetail = RiskDetail,
RiskEventTypes,
RiskEventTypes_V2,
RiskLevelAggregated,
RiskLevelDuringSignIn,
SignInLogs_RiskState = RiskState,
HomeTenantId,
ResourceTenantId,
CrossTenantAccessType,
AppId,
ResourceIdentity,
UniqueTokenIdentifier,
SessionId,
OriginalRequestId//,
//CorrelationId
) on $left.RequestId == $right.OriginalRequestId
// | where case(
// // AlertStatus == "Resolved" and tostring(todynamic(ExtendedProperties)["State"]) == "Closed", false,
// SignInLogs_RiskState == "dismissed" and SignInLogs_RiskDetail == "aiConfirmedSigninSafe", false,
// SignInLogs_RiskState == "remediated" and SignInLogs_RiskDetail == "userPassedMFADrivenByRiskBasedPolicy", false,
// true
// )
| project
//TimeGenerated,
ActivityDateTime,
DetectedDateTime,
Source,
Activity,
DetectionTimingType,
UserDisplayName,
UserPrincipalName,
UserId,
IpAddress,
RequestId,
CorrelationId,
TokenIssuerType,
RiskEventType,
RiskDetail,
RiskLevel,
RiskState,
AdditionalInfo,
Id,
AlertName,
AlertSeverity,
Description,
AlertStatus,
Entities,
ExtendedProperties,
VendorName,
ProviderName,
ProductName,
ProductComponentName,
RemediationSteps,
Tactics,
Techniques,
SubTechniques,
VendorOriginalId,
SystemAlertId,
CompromisedEntity,
AlertLink,
TimeGenerated,
CreatedDateTime,
Type,
//UserDisplayName,
//UserPrincipalName,
//UserId,
AlternateSignInName,
SignInIdentifier,
UserType,
IPAddress,
AutonomousSystemNumber,
Location,
NetworkLocationDetails,
ResultType,
ResultSignature,
ResultDescription,
ClientAppUsed,
AppDisplayName,
ResourceDisplayName,
DeviceDetail,
UserAgent,
Status,
MfaDetail,
AuthenticationContextClassReferences,
AuthenticationDetails,
AuthenticationProcessingDetails,
AuthenticationProtocol,
AuthenticationRequirement,
AuthenticationRequirementPolicies,
SessionLifetimePolicies,
//TokenIssuerType,
IncomingTokenType,
TokenProtectionStatusDetails,
ConditionalAccessStatus,
ConditionalAccessPolicies,
SignInLogs_RiskDetail,
RiskEventTypes,
RiskEventTypes_V2,
RiskLevelAggregated,
RiskLevelDuringSignIn,
SignInLogs_RiskState,
HomeTenantId,
ResourceTenantId,
CrossTenantAccessType,
AppId,
ResourceIdentity,
UniqueTokenIdentifier,
SessionId//,
//OriginalRequestId,
//CorrelationIdExplanation
This KQL query is designed to analyze user risk events related to sign-ins from new countries, using data from Azure Active Directory (AAD) and Microsoft Cloud App Security (MCAS). Here's a simplified breakdown of what the query does:
-
Define Timeframes:
query_frequencyis set to 5 minutes, andquery_periodis set to 2 days. These are used to filter events based on their timestamps.
-
Filter Risk Events:
- The query looks at the
AADUserRiskEventstable for events generated in the last 2 days (query_period). - It specifically filters for events where the source is "MicrosoftCloudAppSecurity" and the risk event type is "newCountry".
- The query looks at the
-
Summarize Events:
- It summarizes these events to find the first time they were generated and keeps the most recent event details for each unique
Id.
- It summarizes these events to find the first time they were generated and keeps the most recent event details for each unique
-
Extract Additional Information:
- The query extracts additional information from the
AdditionalInfofield and creates aVendorOriginalIdfrom thealertUrl.
- The query extracts additional information from the
-
Project Relevant Fields:
- It selects specific fields to keep for further analysis, such as user details, risk details, and event identifiers.
-
Create Alternative Alert Link:
- An alternative alert link is constructed for each event using the
UserId.
- An alternative alert link is constructed for each event using the
-
Join with Security Alerts:
- The query performs a left outer join with the
SecurityAlerttable to enrich the data with alert details from MCAS, matching onVendorOriginalId.
- The query performs a left outer join with the
-
Extend and Coalesce Alert Links:
- It ensures that the
AlertLinkfield is populated, using the alternative link if necessary.
- It ensures that the
-
Lookup Sign-In Logs:
- The query looks up sign-in logs from
SigninLogsandAADNonInteractiveUserSignInLogstables for the same 2-day period. - It matches these logs with the events using
RequestIdandOriginalRequestId.
- The query looks up sign-in logs from
-
Project Final Fields:
- Finally, it selects a comprehensive set of fields from both the risk events and the sign-in logs for the final output.
In essence, this query is designed to identify and analyze risky sign-in events from new countries, enrich them with additional security alert information, and correlate them with sign-in logs to provide a detailed view of potential security incidents.
Details

Jose Sebastián Canós
Released: September 26, 2025
Tables
Keywords
Operators