AWS Cloud Trail AWS Account Created
Query
let query_frequency = 10m;
let query_wait = 20m;
AWSCloudTrail
| where TimeGenerated between (ago(query_frequency + query_wait) .. ago(query_wait))
| where EventName in ("CreateAccountResult")//, "CreateAccount")
and EventTypeName in ("AwsServiceEvent")//, "AwsApiCall")
| extend DynamicServiceEventDetails = todynamic(ServiceEventDetails)
| project
TimeGenerated,
UserIdentityAccountId,
RecipientAccountId,
EventName,
ServiceEventDetails,
State = tostring(DynamicServiceEventDetails["createAccountStatus"]["state"]),
AccountId = tostring(DynamicServiceEventDetails["createAccountStatus"]["accountId"])
| join kind=leftouter (
union
(
AWSCloudTrail
| where TimeGenerated > ago(query_frequency + query_wait)
| where EventName in ("ProvisionProduct", "UpdateProvisionedProduct")
| extend
DynamicResponseElements = todynamic(ResponseElements),
DynamicRequestParameters = todynamic(RequestParameters)
| where tostring(DynamicResponseElements["recordDetail"]["provisionedProductType"]) has "ACCOUNT"
| mv-apply ProvisioningParameter = DynamicRequestParameters["provisioningParameters"] on (
summarize ProvisioningParameters = make_bag(bag_pack(tostring(ProvisioningParameter["key"]), ProvisioningParameter["value"]))
)
| extend
Status = tostring(DynamicResponseElements["recordDetail"]["status"]),
ProvisionedProductId = tostring(DynamicResponseElements["recordDetail"]["provisionedProductId"]),
AccountName = tostring(ProvisioningParameters["AccountName"]),
ManagedOrganizationalUnit = tostring(split(tostring(ProvisioningParameters["ManagedOrganizationalUnit"]), " ")[0]),
AccountEmail = tostring(ProvisioningParameters["AccountEmail"]),
SSOUserEmail = tostring(ProvisioningParameters["SSOUserEmail"])
),
(
AWSCloudTrail
| where TimeGenerated > ago(query_frequency + query_wait)
| where EventTypeName == "AwsApiCall" and EventName in ("CreateManagedAccount")//, "UpdateManagedAccount")
| extend DynamicRequestParameters = todynamic(RequestParameters)
| extend
ProvisionedProductId = tostring(DynamicRequestParameters["provisionedProductId"]),
AccountName = tostring(DynamicRequestParameters["accountName"]),
ManagedOrganizationalUnit = tostring(split(tostring(DynamicRequestParameters["parentOrganizationalUnitName"]), " ")[0]),
AccountId = tostring(DynamicRequestParameters["accountId"])
)
| summarize
StartTime = min(TimeGenerated),
EndTime = max(TimeGenerated),
take_any(UserIdentityType),
//take_any(UserIdentityPrincipalid),
take_any(UserIdentityArn),
take_any(UserIdentityAccountId),
take_any(UserIdentityUserName),
take_any(SessionIssuerAccountId),
take_any(SessionIssuerUserName),
SourceIPAddresses = array_sort_asc(make_set_if(SourceIpAddress, not(SourceIpAddress has_any (".amazonaws.com", "AWS Internal")))),
EventSource = array_sort_asc(make_set(EventSource)),
EventName = array_sort_asc(make_set(EventName)),
take_any(AWSRegion),
take_any(Status),
arg_max(TimeGenerated, ManagedOrganizationalUnit),
take_any(AccountId),
take_any(ProvisionedProductId),
UserAgent = array_sort_asc(make_set_if(UserAgent, not(SourceIpAddress has_any (".amazonaws.com", "AWS Internal"))))
by UserIdentityPrincipalid, RecipientAccountId, AccountName
| where EventName has "ProvisionProduct"
| lookup kind=leftouter (
AWSCloudTrail
| where EventName in ("CreateManagedAccount", "UpdateManagedAccount") and EventTypeName == "AwsServiceEvent"
| extend DynamicServiceEventDetails = todynamic(ServiceEventDetails)
| extend ServiceEventDetailsKey = tostring(bag_keys(DynamicServiceEventDetails)[0])
| project
TimeGenerated,
ServiceEventDetails,
AccountName = tostring(DynamicServiceEventDetails[ServiceEventDetailsKey]["account"]["accountName"]),
ManagedOrganizationalUnit = tostring(DynamicServiceEventDetails[ServiceEventDetailsKey]["organizationalUnit"]["organizationalUnitName"]),
AccountId = tostring(DynamicServiceEventDetails[ServiceEventDetailsKey]["account"]["accountId"])
| summarize arg_max(TimeGenerated, *) by AccountName, AccountId
| project-away TimeGenerated
) on AccountName
| extend AccountId = coalesce(AccountId1, AccountId)
| project
StartTime,
EndTime,
RecipientAccountId,
UserIdentityType,
UserIdentityPrincipalid,
UserIdentityArn,
UserIdentityAccountId,
UserIdentityUserName,
SessionIssuerAccountId,
SessionIssuerUserName,
SourceIPAddresses,
EventSource,
ManagedAccount_EventName = EventName,
Status,
ManagedOrganizationalUnit,
AccountName,
AccountId,
ProvisionedProductId,
AWSRegion,
ManagedAccount_ServiceEventDetails = ServiceEventDetails,
UserAgent
) on AccountId, UserIdentityAccountId, RecipientAccountId
| project-away *1
| invoke AWSIdentityRole()Explanation
This KQL (Kusto Query Language) query is designed to analyze AWS CloudTrail logs to track the creation and provisioning of AWS accounts. Here's a simplified breakdown of what the query does:
-
Time Window Setup:
- The query defines two time intervals:
query_frequency(10 minutes) andquery_wait(20 minutes). - It looks for events that occurred between 10 and 20 minutes ago.
- The query defines two time intervals:
-
Initial Filtering:
- It filters the
AWSCloudTraillogs to find events with the name "CreateAccountResult" and of type "AwsServiceEvent" within the specified time window. - It extracts details from the
ServiceEventDetailsfield and projects relevant information such as the time of the event, account IDs, and the state of the account creation.
- It filters the
-
Joining with Other Events:
- The query performs a left outer join with another set of
AWSCloudTraillogs that include events like "ProvisionProduct" and "UpdateProvisionedProduct" that occurred more than 20 minutes ago. - It extracts and processes details related to provisioning parameters, status, and account information.
- The query performs a left outer join with another set of
-
Further Filtering and Lookup:
- It filters for events with the name "ProvisionProduct" and performs a lookup to enrich the data with additional details from events like "CreateManagedAccount" and "UpdateManagedAccount".
- It processes and projects various fields such as user identity details, source IP addresses, event sources, and user agents.
-
Final Projection:
- The query projects a comprehensive list of fields that include start and end times, account and user identity details, event names, statuses, and more.
-
Invoke Additional Function:
- Finally, it invokes an additional function
AWSIdentityRole()to further process or analyze the data.
- Finally, it invokes an additional function
In summary, this query is designed to track and analyze the creation and provisioning of AWS accounts by filtering, joining, and projecting relevant details from AWS CloudTrail logs within a specific time frame.
Details

Jose Sebastián Canós
Released: September 25, 2023
Tables
AWSCloudTrail
Keywords
AWSCloudTrailEventNameEventTypeNameServiceEventDetailsUserIdentityAccountIdRecipientAccountIdAccountIdAccountNameManagedOrganizationalUnitSSOUserEmailSourceIPAddressesEventSourceAWSRegionUserAgent
Operators
letbetweenagoinandextendtodynamicprojecttostringjoinkind=leftouterunionhasmv-applysummarizemake_bagbag_packsplitarray_sort_ascmake_set_ifmake_setarg_maxlookupcoalesceproject-awayinvoke