Query Details

Analytics AWS Identity Role

Query

// This will have to be updated after 13th January 2025 - https://aws.amazon.com/es/blogs/security/modifications-to-aws-cloudtrail-event-data-of-iam-identity-center/
let _AWSAccounts = _GetWatchlist("AccountId-AuditAWSAccounts");
T
| extend
    RecipientAccountId = column_ifexists("RecipientAccountId", ""),
    UserIdentityAccountId = column_ifexists("UserIdentityAccountId", ""),
    UserIdentityType = column_ifexists("UserIdentityType", ""),
    UserIdentityPrincipalid = column_ifexists("UserIdentityPrincipalid", ""),
    UserIdentityArn = column_ifexists("UserIdentityArn", ""),
    SessionIssuerType = column_ifexists("SessionIssuerType", ""),
    EventName = column_ifexists("EventName", ""),
    RequestParameters = column_ifexists("RequestParameters", ""),
    UserIdentityInvokedBy = column_ifexists("UserIdentityInvokedBy", ""),
    UserIdentityUserName = column_ifexists("UserIdentityUserName", ""),
    UserIdentityAccessKeyId = column_ifexists("UserIdentityAccessKeyId", ""),
    SessionIssuerUserName = column_ifexists("SessionIssuerUserName", ""),
    AdditionalEventData = column_ifexists("AdditionalEventData", ""),
    ErrorMessage = column_ifexists("ErrorMessage", "")
| lookup (_AWSAccounts | project RecipientAccountId = AccountId, RecipientAccountName = AccountName) on RecipientAccountId
| lookup (_AWSAccounts | project UserIdentityAccountId = AccountId, UserIdentityAccountName = AccountName) on UserIdentityAccountId
| extend
    Identity = case(
        UserIdentityType == "Root", strcat(coalesce(UserIdentityAccountName, UserIdentityAccountId), ":", extract(@"\:([^\:]+$)", 1, UserIdentityArn)),
        UserIdentityType == "IAMUser", strcat(coalesce(UserIdentityAccountName, UserIdentityAccountId), ":", coalesce(extract(@"\:([^\:]+$)", 1, UserIdentityArn), strcat("user/", UserIdentityUserName))),
        UserIdentityType == "AssumedRole" and SessionIssuerType in ("", "Role"), strcat(coalesce(UserIdentityAccountName, UserIdentityAccountId), ":", "roleSessionName/", extract(@"\:([^\:]+$)", 1, UserIdentityPrincipalid)),
        UserIdentityType == "AWSAccount" and isempty(UserIdentityPrincipalid), strcat(coalesce(UserIdentityAccountName, UserIdentityAccountId), ":"),
        UserIdentityType == "AWSAccount" and UserIdentityPrincipalid matches regex @"^[A-Z0-9]{21}$", strcat(coalesce(UserIdentityAccountName, UserIdentityAccountId), ":", UserIdentityPrincipalid),
        UserIdentityType == "AWSAccount" and UserIdentityPrincipalid matches regex @"^[A-Z0-9]{21}\:[^\:]+$", strcat(coalesce(UserIdentityAccountName, UserIdentityAccountId), ":", "roleSessionName/", extract(@"\:([^\:]+$)", 1, UserIdentityPrincipalid)),
        UserIdentityType == "AWSService", UserIdentityInvokedBy,
        UserIdentityType == "SAMLUser" and EventName == "AssumeRoleWithSAML", UserIdentityUserName,
        UserIdentityType == "WebIdentityUser" and EventName == "AssumeRoleWithWebIdentity", UserIdentityUserName,
        UserIdentityType == "Unknown" and isnotempty(UserIdentityAccountId) and UserIdentityAccountId == UserIdentityPrincipalid, coalesce(UserIdentityAccountName, UserIdentityAccountId),
        UserIdentityType == "Unknown" and isnotempty(UserIdentityAccountId) and not(UserIdentityAccountId == UserIdentityPrincipalid), coalesce(UserIdentityUserName, UserIdentityPrincipalid, UserIdentityAccessKeyId),
        UserIdentityType == "", coalesce(UserIdentityInvokedBy, extract(@"(.+)\-[a-f0-9]{17}$", 1, tostring(todynamic(RequestParameters)["sessionId"]))),
        strcat("UnexpectedUserIdentityType", ":", extract(@"\:([^\:]+$)", 1, UserIdentityPrincipalid))
    ),
    ActorRole = case(
        UserIdentityType == "AssumedRole", coalesce(SessionIssuerUserName, extract(@"\:assumed-role\/([^\/]+)\/", 1, UserIdentityArn)),
        ""
    )
| extend
    TargetRole = case(
        EventName in ("SwitchRole", "ExitRole", "RenewRole") and UserIdentityType == "AssumedRole", ActorRole,
        EventName in ("SwitchRole", "ExitRole") and not(UserIdentityType == "AssumedRole"), coalesce(extract(@"\:assumed-role\/([^\/]+)\/", 1, tostring(todynamic(AdditionalEventData)["SwitchFrom"])), extract(@"\/([^\/]+)$", 1, tostring(todynamic(AdditionalEventData)["SwitchTo"]))),
        EventName matches regex "^AssumeRole", coalesce(tostring(split(todynamic(RequestParameters)["roleArn"], "/")[-1]), extract(@"AssumeRole\w* on resource\: \S+\/([^\/]+)$", 1, ErrorMessage)),
        UserIdentityType == "Unknown" and EventName in ("Federate", "GetRoleCredentials"), tostring(todynamic(ServiceEventDetails)["role_name"]),
        ""
    ),
    TargetRoleSessionName = case(
        EventName matches regex "^AssumeRole", tostring(todynamic(RequestParameters)["roleSessionName"]),
        ""
    )

Explanation

This KQL (Kusto Query Language) query is designed to analyze AWS CloudTrail logs, specifically focusing on user identity and role-related events. Here's a simplified explanation of what the query does:

  1. Preparation:

    • It starts by retrieving a list of AWS account IDs from a watchlist named "AccountId-AuditAWSAccounts". This list is used to map account IDs to account names later in the query.
  2. Data Extraction:

    • The query extracts various fields from the input data table T, such as RecipientAccountId, UserIdentityAccountId, UserIdentityType, and others. These fields are extracted using the column_ifexists function, which ensures that the query doesn't fail if a column is missing.
  3. Account Mapping:

    • It performs two lookups to map RecipientAccountId and UserIdentityAccountId to their respective account names using the previously retrieved watchlist.
  4. Identity Construction:

    • The query constructs a human-readable Identity string based on the UserIdentityType. It uses different logic for different identity types (e.g., Root, IAMUser, AssumedRole, etc.) to create a meaningful identifier for the user or role involved in the event.
  5. Role Identification:

    • It identifies the ActorRole for events involving assumed roles, extracting the role name from the session issuer or the user identity ARN.
  6. Target Role and Session Name:

    • The query determines the TargetRole and TargetRoleSessionName for events related to role switching or assuming roles. It uses event names and additional event data to extract this information.
  7. Special Cases:

    • The query handles various special cases, such as unknown user identity types or specific event names, to ensure accurate identification and mapping of roles and identities.

Overall, this query is designed to provide insights into AWS identity and access management activities by constructing detailed identity and role information from CloudTrail logs. It is particularly useful for auditing and security analysis purposes. Note that the query includes a comment indicating that it may need to be updated after January 13, 2025, due to potential changes in AWS CloudTrail event data.

Details

Jose Sebastián Canós profile picture

Jose Sebastián Canós

Released: November 20, 2024

Tables

T

Keywords

AWSAccountsIAMIdentityUserSessionIssuerEventNameRequestParametersAdditionalDataErrorMessageActorRoleTarget

Operators

letextendcolumn_ifexistslookupprojectoncasecoalesceextractmatchesregexisemptyisnotemptynotsplittostringtodynamicstrcat

Actions

GitHub