Repository navigation
Description
Integration Name
AWS [packages/aws]
Dataset Name
aws.cloudtrail
Integration Version
8.1.0
Agent Version
9.6.0
OS Version and Architecture
Not host specific
User Goal
When writing detection rules for AWS IAM Identity Center (AWS SSO). Several high-value detections need to identify, group, and baseline the specific Identity Center user that acted in or was the subject of an event: administrator password resets, portal sign-ins, forced first-login password changes, and directory user or group changes.
For every event whose userIdentity.type is IdentityCenterUser, the acting Identity Center user is present in the raw record only as userIdentity.onBehalfOf.userId (a directory UUID) and userIdentity.onBehalfOf.identityStoreArn. I need those two values mapped to queryable fields so a rule can:
- group or run a New Terms baseline on the acting Identity Center user
- name the affected user in the alert
- build aggregation detections such as one principal forcing password changes across many distinct users
Existing Features
The integration maps userIdentity.type but, for IdentityCenterUser events, leaves userIdentity.arn empty and user.name null, and does not map userIdentity.onBehalfOf at all. There is no aws.cloudtrail.user_identity.onBehalfOf.* field. The only surviving copy of the acting user is inside event.original.
Because of that:
- KQL detection rules can only wildcard-match
event.originalto test the presence ofonBehalfOf; they cannot extract the value into a field, so New Terms and grouping on the Identity Center user are impossible. - The value can be recovered with an ES|QL
GROKoverevent.original, but that forces the rule to be an ES|QL rule, scans the full raw event on every run, and is fragile to JSON key ordering. It is a workaround, not a stable field.
What did you see?
Ingested UpdatePassword document (forced first-login change). The mapped identity fields are empty and the acting user is only inside event.original:
{
"userIdentity": {
"type": "IdentityCenterUser",
"arn": "",
"accountId": "x",
"accessKeyId": "",
"onBehalfOf": {
"userId": "y",
"identityStoreArn": "arn:aws:identitystore::x:identitystore/d-9067e0196b"
},
"credentialId": "z"
},
"eventTime": "2026-09-01T14:33:25Z",
"eventSource": "sso-directory.amazonaws.com",
"eventName": "UpdatePassword",
"awsRegion": "us-east-1",
"sourceIPAddress": "<redacted>",
"requestParameters": null,
"responseElements": null
}Mapped-field view of the same document, showing the value is not queryable:
aws.cloudtrail.user_identity.type = "IdentityCenterUser"
aws.cloudtrail.user_identity.arn = null
user.name = null
aws.cloudtrail.user_identity.onBehalfOf.* = (field does not exist)
Confirmation that the value exists and is recoverable only from event.original, via ES|QL GROK:
FROM logs-aws.cloudtrail-*
| WHERE event.provider == "sso-directory.amazonaws.com" AND event.action == "UpdatePassword"
AND event.original LIKE "*onBehalfOf*"
| GROK event.original """"onBehalfOf":\{"userId":"%{DATA:obo_user_id}","identityStoreArn":"%{DATA:obo_identity_store}"\}"""
| KEEP @timestamp, aws.cloudtrail.user_identity.type, obo_user_id, obo_identity_store
Result:
@timestamp user_identity.type obo_user_id obo_identity_store
2026-09-01T14:33:25Z IdentityCenterUser y arn:aws:identitystore::x:identitystore/d-9067e0196b
Anything else?
Requested mapping:
| Source (CloudTrail) | Requested field | Type |
|---|---|---|
userIdentity.onBehalfOf.userId |
aws.cloudtrail.user_identity.on_behalf_of.user_id |
keyword |
userIdentity.onBehalfOf.identityStoreArn |
aws.cloudtrail.user_identity.on_behalf_of.identity_store_arn |
keyword |
Note: 7.4.0 (PR #20403) added previously-dropped CloudTrail fields and populated related.*, but userIdentity.onBehalfOf was not among them. It remains unmapped through 8.1.0, aws.cloudtrail.user_identity.on_behalf_of.* and .onBehalfOf.* both return Unknown column at query time.
Suggested ECS alignment, so the acting Identity Center user is usable by generic rules and entity analytics:
- populate
user.idwithonBehalfOf.userIdforIdentityCenterUserevents; - add it to
related.useras well, consistent with how 7.1.0 (PR [aws] cloudtrail: move assumed-role session name from user.changes.* to related.user #20318) already usesrelated.userfor assumed-role session names for cross-source correlation.
Reference: AWS CloudTrail userIdentity element, onBehalfOf object — https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-user-identity.html
Activity
infra-vault-gh-plugin-prod commentedon Sep 2, 2026
Pinging @elastic/security-service-integrations (Team:Security-Service Integrations)
brijesh-elastic commentedon Sep 21, 2026
Thanks for the detailed write-up @bryans3c.
Taking a fresh look at the current CloudTrail data stream, both of the requested fields — along with the ECS aliases you suggested — are already mapped. This landed as of
7.4.0via #20403, so a deployment on 8.1.0 should already have it.Field mapping (as requested):
userIdentity.onBehalfOf.userIdaws.cloudtrail.user_identity.on_behalf_of.user_iduserIdentity.onBehalfOf.identityStoreArnaws.cloudtrail.user_identity.on_behalf_of.identity_store_arnECS alignment (also already done):
user.idis populated fromonBehalfOf.userIdforIdentityCenterUserevents.related.user.Pipeline processors (
data_stream/cloudtrail/elasticsearch/ingest_pipeline/default.yml):https://github.com/elastic/integrations/blob/main/packages/aws/data_stream/cloudtrail/elasticsearch/ingest_pipeline/default.yml#L1159-L1174
Verified output — feeding an
IdentityCenterUserevent withonBehalfOfthrough the pipeline produces:check - https://github.com/elastic/integrations/blob/main/packages/aws/data_stream/cloudtrail/_dev/test/pipeline/test-create-db-instance-json.log-expected.json
This is covered by a pipeline test in the package, so the mapping is validated in CI.
bryans3c commentedon Oct 1, 2026
Closing, confirmed fixed.