Skip to main content

CloudTrail Multi-Region Management Events Logging

Overview

This check verifies that your AWS account has a CloudTrail trail that records management events (read and write API operations) across all AWS regions and is actively logging. Management events include actions like creating EC2 instances, modifying IAM policies, or changing security groups. A multi-region trail ensures you capture these events no matter which region they occur in.

Risk

Without multi-region management event logging, your security visibility has gaps:

  • Blind spots in unused regions: Attackers can spin up resources in regions you do not monitor, avoiding detection
  • Missed privilege escalation: IAM changes or role assumptions in any region go untracked
  • Incomplete audit trails: Compliance frameworks (SOC 2, PCI-DSS, HIPAA) require comprehensive logging
  • Delayed incident response: You cannot investigate what you cannot see, making forensics difficult
  • Shadow IT exposure: Developers may accidentally (or intentionally) use unmonitored regions

Remediation Steps

Prerequisites

You need:

  • AWS Console access with permissions to modify CloudTrail
  • An S3 bucket for CloudTrail logs (or permission to create one)
Required IAM permissions (for administrators)

Your IAM user or role needs these permissions:

  • cloudtrail:CreateTrail
  • cloudtrail:UpdateTrail
  • cloudtrail:StartLogging
  • cloudtrail:PutEventSelectors
  • cloudtrail:DescribeTrails
  • cloudtrail:GetTrailStatus
  • s3:CreateBucket (if creating a new bucket)
  • s3:PutBucketPolicy

AWS Console Method

  1. Open CloudTrail in the AWS Console

  2. Create or edit a trail

    • If you have an existing trail: Click Trails in the left sidebar, then click the trail name
    • If creating a new trail: Click Create trail
  3. Configure multi-region logging

    • For a new trail:
      • Enter a trail name (e.g., organization-trail)
      • Under Storage location, choose an existing S3 bucket or create a new one
      • Under Trail settings, ensure Enable for all accounts in my organization is checked if using AWS Organizations
    • For an existing trail:
      • Click Edit next to General details
  4. Enable multi-region

    • Find the Multi-region trail option
    • Set it to Yes (or check the box to enable it)
    • This ensures the trail captures events from all AWS regions
  5. Configure management events

    • Scroll to Management events section (or click Edit if editing)
    • Set API activity to All (captures both Read and Write events)
    • Alternatively, select both Read and Write checkboxes
  6. Save and verify logging is active

    • Click Save changes (or Create trail for new trails)
    • Verify the trail shows Logging: On in the trail list
AWS CLI (optional)

Option A: Create a new multi-region trail

First, create an S3 bucket for logs (skip if you already have one):

aws s3 mb s3://my-cloudtrail-logs-bucket-<account-id> \
--region us-east-1

Create the S3 bucket policy to allow CloudTrail to write logs:

cat > /tmp/bucket-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AWSCloudTrailAclCheck",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:GetBucketAcl",
"Resource": "arn:aws:s3:::my-cloudtrail-logs-bucket-<account-id>"
},
{
"Sid": "AWSCloudTrailWrite",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::my-cloudtrail-logs-bucket-<account-id>/AWSLogs/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
}
]
}
EOF

aws s3api put-bucket-policy \
--bucket my-cloudtrail-logs-bucket-<account-id> \
--policy file:///tmp/bucket-policy.json

Create the trail with multi-region enabled:

aws cloudtrail create-trail \
--name organization-trail \
--s3-bucket-name my-cloudtrail-logs-bucket-<account-id> \
--is-multi-region-trail \
--region us-east-1

Configure management events (both read and write):

aws cloudtrail put-event-selectors \
--trail-name organization-trail \
--event-selectors '[{
"ReadWriteType": "All",
"IncludeManagementEvents": true,
"DataResources": []
}]' \
--region us-east-1

Start logging:

aws cloudtrail start-logging \
--name organization-trail \
--region us-east-1

Option B: Update an existing trail

Enable multi-region on an existing trail:

aws cloudtrail update-trail \
--name <trail-name> \
--is-multi-region-trail \
--region us-east-1

Update event selectors to capture all management events:

aws cloudtrail put-event-selectors \
--trail-name <trail-name> \
--event-selectors '[{
"ReadWriteType": "All",
"IncludeManagementEvents": true,
"DataResources": []
}]' \
--region us-east-1

Ensure logging is active:

aws cloudtrail start-logging \
--name <trail-name> \
--region us-east-1
CloudFormation (optional)

This template creates a multi-region CloudTrail trail with management event logging:

AWSTemplateFormatVersion: '2010-09-09'
Description: Multi-region CloudTrail with management events logging

Parameters:
TrailName:
Type: String
Description: Name for the CloudTrail trail
Default: organization-trail

BucketName:
Type: String
Description: S3 bucket name for CloudTrail logs

Resources:
CloudTrailBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
Properties:
BucketName: !Ref BucketName
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
VersioningConfiguration:
Status: Enabled

CloudTrailBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref CloudTrailBucket
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AWSCloudTrailAclCheck
Effect: Allow
Principal:
Service: cloudtrail.amazonaws.com
Action: s3:GetBucketAcl
Resource: !GetAtt CloudTrailBucket.Arn
- Sid: AWSCloudTrailWrite
Effect: Allow
Principal:
Service: cloudtrail.amazonaws.com
Action: s3:PutObject
Resource: !Sub '${CloudTrailBucket.Arn}/AWSLogs/${AWS::AccountId}/*'
Condition:
StringEquals:
s3:x-amz-acl: bucket-owner-full-control

MultiRegionTrail:
Type: AWS::CloudTrail::Trail
DependsOn: CloudTrailBucketPolicy
Properties:
TrailName: !Ref TrailName
S3BucketName: !Ref CloudTrailBucket
IsMultiRegionTrail: true
IsLogging: true
EnableLogFileValidation: true
IncludeGlobalServiceEvents: true
EventSelectors:
- ReadWriteType: All
IncludeManagementEvents: true
DataResources: []

Outputs:
TrailArn:
Description: CloudTrail trail ARN
Value: !GetAtt MultiRegionTrail.Arn

BucketArn:
Description: S3 bucket ARN for CloudTrail logs
Value: !GetAtt CloudTrailBucket.Arn

Deploy with:

aws cloudformation deploy \
--template-file cloudtrail-multiregion.yaml \
--stack-name cloudtrail-multiregion \
--parameter-overrides \
TrailName=organization-trail \
BucketName=my-cloudtrail-logs-bucket-123456789012 \
--region us-east-1
Terraform (optional)
# Variables
variable "trail_name" {
description = "Name of the CloudTrail trail"
type = string
default = "organization-trail"
}

variable "bucket_name" {
description = "S3 bucket name for CloudTrail logs"
type = string
}

# Data sources
data "aws_caller_identity" "current" {}

# S3 bucket for CloudTrail logs
resource "aws_s3_bucket" "cloudtrail" {
bucket = var.bucket_name
}

resource "aws_s3_bucket_versioning" "cloudtrail" {
bucket = aws_s3_bucket.cloudtrail.id
versioning_configuration {
status = "Enabled"
}
}

resource "aws_s3_bucket_server_side_encryption_configuration" "cloudtrail" {
bucket = aws_s3_bucket.cloudtrail.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}

resource "aws_s3_bucket_public_access_block" "cloudtrail" {
bucket = aws_s3_bucket.cloudtrail.id

block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}

# S3 bucket policy for CloudTrail
resource "aws_s3_bucket_policy" "cloudtrail" {
bucket = aws_s3_bucket.cloudtrail.id

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AWSCloudTrailAclCheck"
Effect = "Allow"
Principal = {
Service = "cloudtrail.amazonaws.com"
}
Action = "s3:GetBucketAcl"
Resource = aws_s3_bucket.cloudtrail.arn
},
{
Sid = "AWSCloudTrailWrite"
Effect = "Allow"
Principal = {
Service = "cloudtrail.amazonaws.com"
}
Action = "s3:PutObject"
Resource = "${aws_s3_bucket.cloudtrail.arn}/AWSLogs/${data.aws_caller_identity.current.account_id}/*"
Condition = {
StringEquals = {
"s3:x-amz-acl" = "bucket-owner-full-control"
}
}
}
]
})
}

# Multi-region CloudTrail trail
resource "aws_cloudtrail" "main" {
name = var.trail_name
s3_bucket_name = aws_s3_bucket.cloudtrail.id
is_multi_region_trail = true
include_global_service_events = true
enable_log_file_validation = true

event_selector {
read_write_type = "All"
include_management_events = true
}

depends_on = [aws_s3_bucket_policy.cloudtrail]
}

# Outputs
output "trail_arn" {
description = "CloudTrail trail ARN"
value = aws_cloudtrail.main.arn
}

output "bucket_arn" {
description = "S3 bucket ARN for CloudTrail logs"
value = aws_s3_bucket.cloudtrail.arn
}

Deploy with:

terraform init
terraform plan -var="trail_name=organization-trail" -var="bucket_name=my-cloudtrail-logs-123456789012"
terraform apply -var="trail_name=organization-trail" -var="bucket_name=my-cloudtrail-logs-123456789012"

Verification

After making changes, verify your configuration:

  1. In the AWS Console:

    • Go to CloudTrail > Trails and select your trail
    • Confirm Multi-region trail shows Yes
    • Confirm Logging shows On
    • Under Management events, verify Read/write events shows All (or both Read and Write)
  2. Test event capture:

    • Perform an action in a different region (e.g., list EC2 instances in us-west-2)
    • Wait 10-15 minutes
    • Check the S3 bucket for log files from that region
CLI verification commands

Check trail configuration:

aws cloudtrail describe-trails \
--trail-name-list <trail-name> \
--region us-east-1 \
--query 'trailList[0].{Name:Name,IsMultiRegionTrail:IsMultiRegionTrail,IncludeGlobalServiceEvents:IncludeGlobalServiceEvents}'

Expected output should show:

{
"Name": "organization-trail",
"IsMultiRegionTrail": true,
"IncludeGlobalServiceEvents": true
}

Check event selectors:

aws cloudtrail get-event-selectors \
--trail-name <trail-name> \
--region us-east-1

Look for "ReadWriteType": "All" and "IncludeManagementEvents": true.

Check trail status:

aws cloudtrail get-trail-status \
--name <trail-name> \
--region us-east-1

Verify "IsLogging": true and check LatestDeliveryTime for recent activity.

Additional Resources

Notes

  • One multi-region trail is enough: A single multi-region trail captures events from all regions. You do not need separate trails per region.
  • Global services: Events from global services (IAM, CloudFront, Route 53) are logged in us-east-1 by default. Multi-region trails automatically include these when IncludeGlobalServiceEvents is true.
  • Cost considerations: CloudTrail charges per 100,000 management events after the first trail (which is free). Multi-region trails may increase event volume.
  • Organization trails: If using AWS Organizations, consider creating an organization trail that logs events for all member accounts.
  • Log file validation: Enable log file validation to detect if log files have been tampered with after delivery.
  • Encryption: Consider enabling KMS encryption on your CloudTrail logs for additional security.