AWS CloudTrail (Audit Logging)
AWS CloudTrail is the audit log for your AWS account. Every time someone (a person, a script, or another AWS service) makes a call to the AWS API (Application Programming Interface, the way you control AWS), CloudTrail records who did it, what they did, when, and from where. If a security group suddenly opens port 22 to the world, or an S3 bucket gets deleted, CloudTrail is how you find out who pressed the button. It is your “black box flight recorder” for the cloud, and it is essential for security investigations and compliance audits.
What CloudTrail actually records
CloudTrail captures events. Each event is a JSON record describing a single API call. A typical event tells you:
- Who — the IAM (Identity and Access Management) user, role, or service that made the call (
userIdentity). - What — the action, like
RunInstancesorDeleteBucket(eventName). - When — a precise timestamp (
eventTime). - From where — the source IP address and the AWS Region (
sourceIPAddress,awsRegion).
CloudTrail sorts events into three types, and the difference matters a lot for both visibility and cost:
| Event type | What it logs | On by default? | Cost |
|---|---|---|---|
| Management events | Control-plane operations: create/modify/delete resources, change permissions, configure logging. E.g. RunInstances, CreateUser, AttachRolePolicy. | Yes (90-day history) | Free for the first copy |
| Data events | Data-plane operations: object-level reads/writes inside resources. E.g. S3 GetObject/PutObject, Lambda Invoke, DynamoDB item changes. | No | Charged per event |
| Insights events | Automatically detected unusual API activity (e.g. a sudden spike in DeleteSnapshot). | No | Charged per analysis |
Gotcha: Data events are the high-volume, high-value records — they show exactly which objects in a bucket were read or which functions were invoked. They are OFF by default and cost extra. This means object-level access to sensitive data is invisible in CloudTrail unless you deliberately turn data events on for that resource.
Event history vs a trail
There are two ways to get at your CloudTrail data, and confusing them is the most common mistake.
Event history (always-on, free, 90 days)
Every account automatically gets Event history: a free, searchable record of management events for the last 90 days. You don’t enable anything — it’s just there. It’s great for “what changed yesterday?” investigations.
The catch: it only keeps 90 days, it only covers management events (no data events), and you cannot query it across long time ranges or archive it. For anything beyond a quick lookback, it is not enough.
A trail (persistent, to S3, forever)
A trail is a configuration you create that continuously delivers events to an S3 (Simple Storage Service) bucket you own. Once delivered, the logs live as long as you keep them — months or years — and you can analyze them with Amazon Athena, ship them to a SIEM (Security Information and Event Management tool), or hand them to auditors.
Tip: For compliance and forensics you almost always need a trail, not event history. The 90-day window disappears silently, and by the time you’re investigating a breach the evidence may be gone. Create a trail on day one.
When to use which:
| Event history | Trail to S3 | |
|---|---|---|
| Retention | 90 days | As long as you keep the objects |
| Covers data events | No | Yes (if enabled) |
| Long-term forensics / compliance | No | Yes |
| Cost | Free | First management trail free; S3 storage + data events billed |
| Setup needed | None | Create a trail |
Creating a trail
The recommended setup is a single organization trail (covering all accounts and all Regions) that logs management events to a dedicated, locked-down S3 bucket.
Console steps
- Open the CloudTrail console and choose Trails > Create trail.
- Enter a Trail name, e.g.
org-audit-trail. - Under Storage location, create a new S3 bucket (e.g.
my-org-cloudtrail-logs-0a1b2c3d) or pick an existing one. - (Recommended) Enable Log file SSE-KMS encryption and Log file validation (this lets you prove logs weren’t tampered with).
- Choose CloudWatch Logs delivery if you want to alarm on events in near-real-time. Click Next.
- Under Event type, keep Management events ticked. To capture object access, tick Data events and add your sensitive S3 buckets and Lambda functions.
- Review and choose Create trail.
CLI equivalent
CloudTrail CLI v2. First create and configure the trail:
aws cloudtrail create-trail \
--name org-audit-trail \
--s3-bucket-name my-org-cloudtrail-logs-0a1b2c3d \
--is-multi-region-trail \
--enable-log-file-validation
aws cloudtrail start-logging --name org-audit-trail
Output:
{
"Name": "org-audit-trail",
"S3BucketName": "my-org-cloudtrail-logs-0a1b2c3d",
"IncludeGlobalServiceEvents": true,
"IsMultiRegionTrail": true,
"TrailARN": "arn:aws:cloudtrail:us-east-1:111122223333:trail/org-audit-trail",
"LogFileValidationEnabled": true
}
Turning on data events for one bucket
Data events are configured separately because they are billed and high-volume. This example logs all object-level reads and writes for one bucket:
aws cloudtrail put-event-selectors \
--trail-name org-audit-trail \
--event-selectors '[{
"ReadWriteType": "All",
"IncludeManagementEvents": true,
"DataResources": [{
"Type": "AWS::S3::Object",
"Values": ["arn:aws:s3:::my-sensitive-bucket/"]
}]
}]'
Cost note: Management events for the first trail are free. Data events cost about $0.10 per 100,000 events delivered. A busy bucket serving millions of
GetObjectcalls per day can run into real money, so scope data events to the buckets and functions that genuinely hold sensitive data — don’t log everything.
Reading and searching events
Quick lookups against the free 90-day history don’t need a trail:
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=RunInstances \
--max-results 1
Output:
{
"Events": [{
"EventId": "8f2a1b3c-...",
"EventName": "RunInstances",
"EventTime": "2026-06-14T09:31:22+00:00",
"Username": "deploy-bot",
"Resources": [{"ResourceType": "AWS::EC2::Instance", "ResourceName": "i-0a1b2c3d4e5f"}]
}]
}
For long-term analysis, point Amazon Athena at the trail’s S3 bucket and run SQL over years of logs.
Defining a trail as code
A minimal CloudFormation trail (the S3 bucket and its policy are assumed to exist):
Resources:
AuditTrail:
Type: AWS::CloudTrail::Trail
Properties:
TrailName: org-audit-trail
S3BucketName: my-org-cloudtrail-logs-0a1b2c3d
IsLogging: true
IsMultiRegionTrail: true
EnableLogFileValidation: true
IncludeGlobalServiceEvents: true
Best Practices
- Create at least one multi-Region trail to S3 on day one — don’t rely on the 90-day event history for anything you might need later.
- Enable log file validation so you can prove logs weren’t altered after the fact.
- Lock down the log bucket: block public access, enable encryption, and turn on S3 Object Lock or a strict bucket policy so even an admin can’t quietly delete evidence.
- Enable data events only for sensitive resources (buckets with PII, key Lambda functions) to balance visibility against cost.
- Use an organization trail if you run AWS Organizations, so member accounts can’t disable their own auditing.
- Send a copy to CloudWatch Logs and alarm on dangerous events like
StopLogging,DeleteTrail, or root-user logins. - Turn on CloudTrail Insights to catch unusual spikes in API activity automatically.