Skip to main content

API activity tracking

CloudTrail answers the question every incident eventually asks: who did that, and when? This standard covers enabling it everywhere, centralising it, and turning it into alerts rather than an archive nobody reads.

The standard. End-to-end API and activity tracking through CloudTrail, with centralised retention, alerting and a queryable audit trail.

What CloudTrail records​

Every API call made to an AWS account — from the console, the CLI, an SDK, or another AWS service — is recorded as an event.

Event typeContentsDefault
Management eventsCreation, modification and deletion of resources; permission changesRecorded
Data eventsObject-level S3 operations, Lambda invocations, DynamoDB item operationsNot recorded by default
Insights eventsAnomalous API call rates and error ratesNot recorded by default

Management events answer "who changed the infrastructure." Data events answer "who read this object." The second is usually the more sensitive question and costs more to store.

Configuration​

Organisation trail​

A single trail in the management account delivers from every member account and region to the log archive account.

resource "aws_cloudtrail" "organisation" {
name = "organisation-trail"
s3_bucket_name = aws_s3_bucket.log_archive.id
include_global_service_events = true
is_multi_region_trail = true
is_organization_trail = true
enable_log_file_validation = true
kms_key_id = aws_kms_key.trail.arn

event_selector {
read_write_type = "All"
include_management_events = true

data_resource {
type = "AWS::S3::Object"
values = ["arn:aws:s3:::sensitive-bucket/"]
}
}

insight_selector {
insight_type = "ApiCallRateInsight"
}
insight_selector {
insight_type = "ApiErrorRateInsight"
}
}
SettingValueReason
Multi-regionYesA trail scoped to one region leaves blind spots
Organisation trailYesNew accounts are covered without per-account setup
Log file validationYesProves logs were not altered after delivery
KMS encryptionYesThe log contains sensitive metadata
Retention1 year hot, 10 years for access logsMatches audit and contractual requirements

Centralised delivery​

Member account ──► CloudTrail ──► Log archive account / S3
│
├─ KMS encrypted
├─ Object Lock (optional, compliance)
└─ Lifecycle → Glacier

The log archive account is write-only from member accounts. Even an administrator in a workload account cannot alter the audit trail.

From logs to alerts​

Metric filters​

aws logs put-metric-filter \
--log-group-name "/aws/cloudtrail/organisation" \
--filter-name "UnauthorizedAPICalls" \
--filter-pattern '{ ($.errorCode = "UnauthorizedOperation") || ($.errorCode = "AccessDenied") }' \
--metric-transformations \
metricName=UnauthorizedAPICalls,metricNamespace=SecurityMetrics,metricValue=1

Alarms worth having​

AlarmFilterWhy
Root account usageuserIdentity.type = "Root"Root should not be used operationally
Unauthorised API callserrorCode = AccessDenied or UnauthorizedOperationMisconfiguration or probing
IAM policy changeeventName = PutRolePolicy / AttachRolePolicyPrivilege escalation path
CloudTrail disabledeventName = StopLogging / DeleteTrailAttacker covering tracks
Security group openedeventName = AuthorizeSecurityGroupIngressExposure of a service
S3 bucket policy changeeventName = PutBucketPolicyData exposure
KMS key scheduled for deletioneventName = ScheduleKeyDeletionData becomes unrecoverable
Console login without MFAeventName = ConsoleLogin with MFA falseCredential compromise
GuardDuty findingEventBridge ruleThreat detection response

EventBridge routing​

CloudTrail events reach EventBridge, where rules route them to an SNS topic, a ticketing system, or an automated response.

{
"source": ["aws.iam"],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": {
"eventName": ["CreateAccessKey", "AttachUserPolicy", "CreateUser"]
}
}

Analysis​

Common investigations​

QuestionQuery approach
Who deleted this resource?eventName = Delete*, filter by resource ARN
When did this permission change?eventName in IAM mutation set, sorted by time
Which principal accessed this bucket?Data events for the bucket prefix
Did anyone use the root account?userIdentity.type = Root
Is this IP normal for this user?Compare source IP across the identity's history

Logs Insights​

fields @timestamp, eventSource, eventName, userIdentity.arn, sourceIPAddress, errorCode
| filter eventName like /Delete|Detach|Put/
| filter @timestamp > ago(24h)
| sort @timestamp desc
| limit 100

Retention and access​

DataRetentionAccess
CloudTrail management events (hot)12 monthsSecurity team; read-only for auditors
CloudTrail archive10 yearsSecurity team; retrieval workflow
Data eventsPer classificationRestricted to the owning team and security
Access logs to the log bucket10 yearsSecurity team
Config historyPer compliance requirementSecurity and compliance

Evidence​

  • Trail configuration export showing multi-region and organisation scope.
  • Log archive bucket policy preventing member-account modification.
  • Alarm inventory with the metric filter behind each.
  • A sample investigation showing a change traced to a principal.
  • Retention configuration.